Procházet zdrojové kódy

移动端浏览器处理吊坠报警

master
libx před 4 roky
revize
220bd9952c
54 změnil soubory, kde provedl 13505 přidání a 0 odebrání
  1. +3
    -0
      .browserslistrc
  2. +14
    -0
      .eslintrc.js
  3. +21
    -0
      .gitignore
  4. +27
    -0
      README.md
  5. binární
      docs/favicon.ico
  6. +1
    -0
      docs/index.html
  7. +1
    -0
      docs/static/css/app.640e1136.css
  8. +1
    -0
      docs/static/css/chunk-4ca71562.c4fd982f.css
  9. +1
    -0
      docs/static/css/chunk-vendors.3bd1146c.css
  10. +1
    -0
      docs/static/js/app.58b1b7dc.js
  11. +1
    -0
      docs/static/js/chunk-2d0df214.55b65f36.js
  12. +1
    -0
      docs/static/js/chunk-4ca71562.02fcf80f.js
  13. +38
    -0
      docs/static/js/chunk-vendors.6e82d040.js
  14. +12186
    -0
      package-lock.json
  15. +48
    -0
      package.json
  16. +5
    -0
      postcss.config.js
  17. binární
      public/favicon.ico
  18. +17
    -0
      public/index.html
  19. +65
    -0
      src/App.vue
  20. +18
    -0
      src/api/alarm.js
  21. +65
    -0
      src/api/index.js
  22. +18
    -0
      src/api/setting.js
  23. +26
    -0
      src/api/user.js
  24. binární
      src/assets/address.png
  25. binární
      src/assets/card.png
  26. binární
      src/assets/develop.png
  27. binární
      src/assets/income.png
  28. binární
      src/assets/logo.png
  29. binární
      src/assets/order.png
  30. binární
      src/assets/qrcode.png
  31. binární
      src/assets/scan.png
  32. binární
      src/assets/search.png
  33. binární
      src/assets/service.png
  34. binární
      src/assets/shopping.png
  35. binární
      src/assets/table.png
  36. binární
      src/assets/team.png
  37. binární
      src/assets/temp.jpeg
  38. +18
    -0
      src/main.js
  39. +38
    -0
      src/permission.js
  40. +76
    -0
      src/router/index.js
  41. +8
    -0
      src/store/getters.js
  42. +19
    -0
      src/store/index.js
  43. +63
    -0
      src/store/modules/alarm.js
  44. +47
    -0
      src/store/modules/setting.js
  45. +82
    -0
      src/store/modules/user.js
  46. +35
    -0
      src/styles/icon.less
  47. +14
    -0
      src/styles/index.less
  48. +15
    -0
      src/utils/auth.js
  49. +74
    -0
      src/utils/index.js
  50. +98
    -0
      src/utils/request.js
  51. +122
    -0
      src/views/Home.vue
  52. +127
    -0
      src/views/Login/index.vue
  53. +71
    -0
      src/views/Setting/index.vue
  54. +40
    -0
      vue.config.js

+ 3
- 0
.browserslistrc Zobrazit soubor

@@ -0,0 +1,3 @@
> 1%
last 2 versions
not ie <= 8

+ 14
- 0
.eslintrc.js Zobrazit soubor

@@ -0,0 +1,14 @@
module.exports = {
root: true,
env: {
node: true
},
extends: ["plugin:vue/essential"],
rules: {
"no-console": process.env.NODE_ENV === "production" ? "error" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off"
},
parserOptions: {
parser: "babel-eslint"
}
};

+ 21
- 0
.gitignore Zobrazit soubor

@@ -0,0 +1,21 @@
.DS_Store
node_modules
/dist

# local env files
.env.local
.env.*.local

# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*

+ 27
- 0
README.md Zobrazit soubor

@@ -0,0 +1,27 @@

# mobile-admin

## Project setup
```
npm install
```

### Compiles and hot-reloads for development
```
npm run serve
```

### Compiles and minifies for production
```
npm run build
```

### Run your tests
```
npm run test
```

### Lints and fixes files
```
npm run lint
```

binární
docs/favicon.ico Zobrazit soubor

Před Za

+ 1
- 0
docs/index.html Zobrazit soubor

@@ -0,0 +1 @@
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="maximum-scale=1,minimum-scale=1,user-scalable=1,width=device-width,initial-scale=1"><link rel=icon href=/favicon.ico><title>mobile-admin</title><link href=/static/css/chunk-4ca71562.c4fd982f.css rel=prefetch><link href=/static/js/chunk-2d0df214.55b65f36.js rel=prefetch><link href=/static/js/chunk-4ca71562.02fcf80f.js rel=prefetch><link href=/static/css/app.640e1136.css rel=preload as=style><link href=/static/css/chunk-vendors.3bd1146c.css rel=preload as=style><link href=/static/js/app.58b1b7dc.js rel=preload as=script><link href=/static/js/chunk-vendors.6e82d040.js rel=preload as=script><link href=/static/css/chunk-vendors.3bd1146c.css rel=stylesheet><link href=/static/css/app.640e1136.css rel=stylesheet></head><body style="background-color: #f7f8fa;"><noscript><strong>We're sorry but mobile-admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/static/js/chunk-vendors.6e82d040.js></script><script src=/static/js/app.58b1b7dc.js></script></body></html>

+ 1
- 0
docs/static/css/app.640e1136.css Zobrazit soubor

@@ -0,0 +1 @@
#app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#2c3e50;overflow:hidden}a:-webkit-any-link{text-decoration:none}input:-webkit-autofill{-webkit-box-shadow:0 0 0 26.66667rem #fff inset}input{-webkit-filter:none!important;filter:none!important}.panel-container{margin-bottom:.13333rem}.panel-container .panel-content{padding:.26667rem .4rem}.van-hairline--top-bottom:after{border:none!important}.van-cell{padding-bottom:0!important}.van-cell .van-cell__title{-webkit-box-flex:3;-ms-flex:3;flex:3}@font-face{font-family:iconfont;src:url(//at.alicdn.com/t/font_927880_2ihl8qth6yg.eot);src:url(//at.alicdn.com/t/font_927880_2ihl8qth6yg.eot#iefix) format("embedded-opentype"),url(//at.alicdn.com/t/font_927880_2ihl8qth6yg.woff) format("woff"),url(//at.alicdn.com/t/font_927880_2ihl8qth6yg.ttf) format("truetype"),url(//at.alicdn.com/t/font_927880_2ihl8qth6yg.svg#iconfont) format("svg")}@font-face{font-family:iconfont;src:url(//at.alicdn.com/t/font_2723835_y1jsf7gbkz.woff2?t=1628136189738) format("woff2"),url(//at.alicdn.com/t/font_2723835_y1jsf7gbkz.woff?t=1628136189738) format("woff"),url(//at.alicdn.com/t/font_2723835_y1jsf7gbkz.ttf?t=1628136189738) format("truetype")}.iconfont{font-family:iconfont!important;font-size:.74667rem;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon-home:before{content:"\E620"}.icon-shu:before{content:"\E634"}.icon-ziyuan:before{content:"\E621"}.icon-username:before{content:"\EAC3";position:absolute;left:-.13333rem;bottom:.05333rem;font-size:.58667rem}.icon-password:before{content:"\EAC2";position:absolute;left:-.13333rem;bottom:.02667rem;font-size:.64rem}.icon-yanzhengma:before{content:"\E63F";position:absolute;left:-.05333rem;bottom:.08rem;font-size:.48rem;font-weight:700}

+ 1
- 0
docs/static/css/chunk-4ca71562.c4fd982f.css Zobrazit soubor

@@ -0,0 +1 @@
.login-container[data-v-3d4a5790]{height:100vh;background:#fff!important;text-align:center}.login-container .input-box[data-v-3d4a5790]{display:inline-block;position:relative}.login-container .input-box input[data-v-3d4a5790]{outline:none;border-bottom:.02667rem solid #c8b185;border-left-width:0;border-top-width:0;border-right-width:0;text-align:left;padding:0 .8rem;width:6.66667rem;-webkit-box-sizing:border-box;box-sizing:border-box;font-size:.42667rem;border-radius:0}.login-container .van-button[data-v-3d4a5790]{margin-top:1.33333rem;width:6.66667rem}

+ 1
- 0
docs/static/css/chunk-vendors.3bd1146c.css
Diff nebyl zobrazen, protože je příliš veliký
Zobrazit soubor


+ 1
- 0
docs/static/js/app.58b1b7dc.js
Diff nebyl zobrazen, protože je příliš veliký
Zobrazit soubor


+ 1
- 0
docs/static/js/chunk-2d0df214.55b65f36.js Zobrazit soubor

@@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0df214"],{8930:function(t,e,n){"use strict";n.r(e);var s=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticStyle:{height:"calc(100vh - 2.5rem)",overflow:"auto","margin-top":"1.3rem"}},[t.sysConfig?n("van-cell",{attrs:{center:"",title:"报警提醒"}},[[n("van-switch",{attrs:{value:t.checked,size:"24"},on:{input:t.onInput}})]],2):n("div",{staticClass:"van-list__finished-text"},[t._v("服务器异常,请稍后重试~~")]),n("van-row",{attrs:{type:"flex",justify:"space-around"}},[n("van-col",[n("van-button",{attrs:{type:"info",size:"small"},on:{click:t.handleLogout}},[t._v("退出登录")])],1)],1)],1)},i=[],o=n("b970"),a=n("2f62"),c={data(){return{checked:!0}},computed:{...Object(a["c"])(["sysConfig"])},mounted(){this.getSysConfig("limit=10&page=1&sidx=&order=asc&key=IS_NOTIFY_SERVICE").then(t=>{0===t.code&&(this.checked="1"===this.sysConfig["value"])})},methods:{...Object(a["b"])({getSysConfig:"setting/fetchSysConfig",handleSysConfigs:"setting/handleSysConfigs"}),onInput(t){let e=t?"开启":"关闭";o["a"].confirm({title:"提醒",message:`是否${e}报警通知?`}).then(()=>{this.sysConfig["value"]=t?"1":"0",this.handleSysConfigs(this.sysConfig).then(e=>{0===e.code?this.checked=t:o["b"].error("设置失败,请稍后重试~~")})}).catch(()=>{})},handleLogout(){o["a"].confirm({title:"退出登录",message:"是否立即退出登录?"}).then(()=>{this.$store.dispatch("LogOut",(new Date).getTime()).then(t=>{window.location.href="/login"})}).catch(()=>{})}}},l=c,h=n("2877"),r=Object(h["a"])(l,s,i,!1,null,null,null);r.options.__file="index.vue";e["default"]=r.exports}}]);

+ 1
- 0
docs/static/js/chunk-4ca71562.02fcf80f.js Zobrazit soubor

@@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-4ca71562"],{d9c9:function(t,i,e){"use strict";e.r(i);var n=function(){var t=this,i=t.$createElement,e=t._self._c||i;return e("div",{staticClass:"login-container"},[e("header",{staticStyle:{"padding-top":"50px","font-size":"0.7rem"}},[t._v("综合管理平台")]),e("div",[e("div",{staticClass:"input-box",staticStyle:{"margin-top":"100px"}},[e("i",{staticClass:"iconfont icon-username"}),e("input",{directives:[{name:"model",rawName:"v-model",value:t.loginInfo.username,expression:"loginInfo.username"}],attrs:{type:"text"},domProps:{value:t.loginInfo.username},on:{input:function(i){i.target.composing||t.$set(t.loginInfo,"username",i.target.value)}}})]),e("div",{staticClass:"input-box",staticStyle:{"margin-top":"10px"}},[e("i",{staticClass:"iconfont icon-password"}),e("input",{directives:[{name:"model",rawName:"v-model",value:t.loginInfo.password,expression:"loginInfo.password"}],attrs:{type:"password"},domProps:{value:t.loginInfo.password},on:{input:function(i){i.target.composing||t.$set(t.loginInfo,"password",i.target.value)}}})]),e("div",{staticClass:"input-box",staticStyle:{"margin-top":"10px"}},[e("i",{staticClass:"iconfont icon-yanzhengma"}),e("input",{directives:[{name:"model",rawName:"v-model",value:t.loginInfo.captcha,expression:"loginInfo.captcha"}],domProps:{value:t.loginInfo.captcha},on:{input:function(i){i.target.composing||t.$set(t.loginInfo,"captcha",i.target.value)}}}),e("div",{staticStyle:{color:"darkgray",position:"absolute",right:"-2px",bottom:"1px"}},[e("img",{staticStyle:{width:"3.3rem","vertical-align":"middle","line-height":"16px","font-size":"11px","border-radius":"4px 4px 0 0"},attrs:{src:"/platform-framework/captcha.jpg?t="+t.loginInfo.time,alt:"验证码获取失败~~"},on:{click:t.handleClickFreshCaptcha}})])])]),e("van-button",{attrs:{loading:t.isLogin,type:"info"},nativeOn:{click:function(i){return t.login(i)}}},[t._v("登录")])],1)},o=[],a=e("b970"),s=e("ed08"),r={data(){return{loginInfo:{username:"",password:"",captcha:"",time:""},isLogin:!1,redirect:void 0}},created(){this.loginInfo.username=this.$route.query.username||"",this.loginInfo.password=this.$route.query.password||""},mounted(){},watch:{$route:{handler:function(t){this.redirect=t.query&&t.query.redirect},immediate:!0}},methods:{async login(){if(Object(s["a"])(this.loginInfo.username)&&""!==this.loginInfo.password){const{username:i,password:e,captcha:n}=this.loginInfo;if(i&&e&&n){this.isLogin=!0;try{const o=await this.$store.dispatch("Login",`username=${i.trim()}&password=${e}&captcha=${n}`);if(this.isLogin=!1,this.handleClickFreshCaptcha(),0!==o.code)return Object(a["b"])(o.msg);this.$router.push({path:this.redirect||"/"})}catch(t){this.isLogin=!1,Object(a["b"])(t.toString())}}else Object(a["b"])("请检查账号、密码以及验证码是否填写完毕 ~~")}},handleClickFreshCaptcha(){this.loginInfo.time=(new Date).getTime()}}},c=r,l=(e("fc32"),e("2877")),p=Object(l["a"])(c,n,o,!1,null,"3d4a5790",null);p.options.__file="index.vue";i["default"]=p.exports},f565:function(t,i,e){},fc32:function(t,i,e){"use strict";var n=e("f565"),o=e.n(n);o.a}}]);

+ 38
- 0
docs/static/js/chunk-vendors.6e82d040.js
Diff nebyl zobrazen, protože je příliš veliký
Zobrazit soubor


+ 12186
- 0
package-lock.json
Diff nebyl zobrazen, protože je příliš veliký
Zobrazit soubor


+ 48
- 0
package.json Zobrazit soubor

@@ -0,0 +1,48 @@
{
"name": "mobile-admin",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"start": "concurrently -k \"npm run server\" \"npm run dev\""
},
"dependencies": {
"amfe-flexible": "^2.2.1",
"axios": "^0.18.0",
"bcryptjs": "^2.4.3",
"clipboard": "^2.0.4",
"js-cookie": "^2.2.0",
"koa": "^2.6.2",
"koa-bodyparser": "^4.2.1",
"koa-jwt": "^3.5.1",
"koa-router": "^7.4.0",
"mysql2": "^1.6.4",
"vant": "^1.6.28",
"vue": "^2.5.17",
"vue-router": "^3.0.1",
"vuex": "^3.0.1",
"weixin-js-sdk": "^1.4.0-test"
},
"devDependencies": {
"@vue/cli-plugin-eslint": "^3.0.5",
"@vue/cli-service": "^3.0.5",
"@vue/eslint-config-prettier": "^4.0.0",
"babel-core": "^6.26.3",
"babel-eslint": "^10.0.1",
"babel-preset-env": "^1.7.0",
"concurrently": "^4.1.0",
"eslint": "^5.8.0",
"eslint-plugin-vue": "^5.0.0-0",
"ioredis": "^4.2.0",
"less": "^3.0.4",
"less-loader": "^4.1.0",
"node-uuid": "^1.4.8",
"nodemon": "^1.18.7",
"postcss-pxtorem": "^4.0.1",
"request-promise": "^4.2.2",
"sha1": "^1.1.1",
"vue-template-compiler": "^2.5.17"
}
}

+ 5
- 0
postcss.config.js Zobrazit soubor

@@ -0,0 +1,5 @@
module.exports = {
plugins: {
autoprefixer: {}
}
};

binární
public/favicon.ico Zobrazit soubor

Před Za

+ 17
- 0
public/index.html Zobrazit soubor

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="maximum-scale=1.0,minimum-scale=1.0,user-scalable=1,width=device-width,initial-scale=1.0"/>
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>mobile-admin</title>
</head>
<body style=" background-color: #f7f8fa;">
<noscript>
<strong>We're sorry but mobile-admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

+ 65
- 0
src/App.vue Zobrazit soubor

@@ -0,0 +1,65 @@
<template>
<div id="app">
<van-nav-bar v-if="path !== '/login'" :title="navTitle" fixed />
<router-view />
<van-tabbar v-model="active" v-if="path !== '/login'" @change="handleChangeTabbar">
<van-tabbar-item icon="wap-home" :to="{ path: '/' }">首页</van-tabbar-item>
<van-tabbar-item icon="setting" :to="{ path: '/setting' }">设置</van-tabbar-item>
</van-tabbar>
</div>
</template>
<script>
export default {
data() {
return {
navTitle: '警报列表'
}
},
computed: {
path() {
return this.$route.path
},
active: {
get: function() {
return this.$route.path === '/' ? 0 : 1
},
set: function() {}
}
},
mounted() {
const navTitle = localStorage.getItem('navTitle')
if (navTitle) {
this.navTitle = navTitle
} else {
localStorage.setItem('navTitle', this.navTitle)
}
},
methods: {
handleChangeTabbar(active) {
this.navTitle = active === 0 ? '警报列表': '设置中心'
localStorage.setItem('navTitle', this.navTitle)
}
}
}
</script>
<style lang="less">
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
//text-align: center;
color: #2c3e50;
overflow: hidden;
}
a:-webkit-any-link {
text-decoration: none;
}

input:-webkit-autofill {
-webkit-box-shadow: 0 0 0px 1000px white inset;
}

input {
filter: none !important;
}
</style>

+ 18
- 0
src/api/alarm.js Zobrazit soubor

@@ -0,0 +1,18 @@
import request from '@/utils/request'

/* 获取紧急报警列表 */
export function getAlarmList(data) {
return request({
url: `/alarm/list?${data}`,
method: 'get'
})
}

/* 处理紧急报警 */
export function handleAlarm(data) {
return request({
url: '/alarm/handle',
method: 'post',
data
})
}

+ 65
- 0
src/api/index.js Zobrazit soubor

@@ -0,0 +1,65 @@
import request from '@/utils/request';

// export function login(data) {
// return request({
// url: '/user/login',
// method: 'post',
// data
// })
// }

// export function getUserInfo(id) {
// return request({
// url: '/user/getUserInfo',
// method: 'get',
// params: { id }
// })
// }

export function getWxUserInfo(data) {
return request({
url: '/user/getWxUserInfo',
method: 'post',
data
})
}

export function registerUser(data) {
return request({
url: '/user/register',
method: 'post',
data
})
}

export function getJsapiSignature(url) {
return request({
url: '/user/getJsapiSignature',
method: 'get',
params: { url }
})
}

export function checkAuthorizeCode(data) {
return request({
url: '/user/checkAuthorizeCode',
method: 'post',
data
})
}

export function saveUrlToken(data) {
return request({
url: '/user/saveUrlToken',
method: 'post',
data
})
}

export function getProxyList(data) {
return request({
url: '/user/getProxyList',
method: 'post',
data
})
}

+ 18
- 0
src/api/setting.js Zobrazit soubor

@@ -0,0 +1,18 @@
import request from '@/utils/request'

/* 获取系统参数配置 */
export function getSysConfig(data) {
return request({
url: `/sys/config/list?${data}`,
method: 'get'
})
}

/* 修改系统参数配置 */
export function handleSysConfig(data) {
return request({
url: `/sys/config/update`,
method: 'post',
data
})
}

+ 26
- 0
src/api/user.js Zobrazit soubor

@@ -0,0 +1,26 @@
import request from '@/utils/request'

/* 登录 */
export function login(data) {
return request({
url: '/sys/login',
method: 'post',
data
})
}

/* 获取用户信息 */
export function getInfo(time) {
return request({
url: `/sys/user/info?${time}`,
method: 'get'
})
}

/* 退出登录 */
export function logout(time) {
return request({
url: `/logout?${time}`,
method: 'get'
})
}

binární
src/assets/address.png Zobrazit soubor

Před Za
Šířka: 200  |  Výška: 200  |  Velikost: 9.5KB

binární
src/assets/card.png Zobrazit soubor

Před Za
Šířka: 200  |  Výška: 200  |  Velikost: 8.5KB

binární
src/assets/develop.png Zobrazit soubor

Před Za
Šířka: 200  |  Výška: 200  |  Velikost: 8.9KB

binární
src/assets/income.png Zobrazit soubor

Před Za
Šířka: 200  |  Výška: 200  |  Velikost: 8.8KB

binární
src/assets/logo.png Zobrazit soubor

Před Za
Šířka: 200  |  Výška: 200  |  Velikost: 6.7KB

binární
src/assets/order.png Zobrazit soubor

Před Za
Šířka: 200  |  Výška: 200  |  Velikost: 6.1KB

binární
src/assets/qrcode.png Zobrazit soubor

Před Za
Šířka: 200  |  Výška: 200  |  Velikost: 6.8KB

binární
src/assets/scan.png Zobrazit soubor

Před Za
Šířka: 200  |  Výška: 200  |  Velikost: 4.9KB

binární
src/assets/search.png Zobrazit soubor

Před Za
Šířka: 200  |  Výška: 200  |  Velikost: 7.4KB

binární
src/assets/service.png Zobrazit soubor

Před Za
Šířka: 200  |  Výška: 200  |  Velikost: 5.3KB

binární
src/assets/shopping.png Zobrazit soubor

Před Za
Šířka: 200  |  Výška: 200  |  Velikost: 5.1KB

binární
src/assets/table.png Zobrazit soubor

Před Za
Šířka: 200  |  Výška: 200  |  Velikost: 6.5KB

binární
src/assets/team.png Zobrazit soubor

Před Za
Šířka: 200  |  Výška: 200  |  Velikost: 8.8KB

binární
src/assets/temp.jpeg Zobrazit soubor

Před Za
Šířka: 600  |  Výška: 1060  |  Velikost: 79KB

+ 18
- 0
src/main.js Zobrazit soubor

@@ -0,0 +1,18 @@
import Vue from "vue"
import App from "./App.vue"
import router from "@/router"
import store from "@/store"
import '@/styles/index.less' // global css
import '@/permission' // permission control
import Vant from 'vant'
import 'vant/lib/index.css'
import 'amfe-flexible'

Vue.use(Vant)
Vue.config.productionTip = false

new Vue({
router,
store,
render: h => h(App)
}).$mount("#app")

+ 38
- 0
src/permission.js Zobrazit soubor

@@ -0,0 +1,38 @@
import router from '@/router';
import store from '@/store';
import { getToken } from '@/utils/auth'; // 验权

const whiteList = ['/login']; // 不重定向白名单
router.beforeEach(async (to, from, next) => {
if (getToken()) {
if (to.path === '/login') {
next({ path: '/' })
} else {
if (!store.getters.userInfo.userId) {
// store.dispatch('GetUserInfo').then(res => {
// // 如果拉取用户信息成功则跳转页面,否则重新登录
// if (res) next()
// else store.dispatch('FedLogOut').then(() => { next({ path: '/' }) })
// }).catch(err => {
// store.dispatch('FedLogOut').then(() => {
// next({ path: '/' })
// });
// })
const res = await store.dispatch('GetUserInfo')
if (res === 0) next()
else store.dispatch('FedLogOut').then(() => {
//next({ path: '/' })
next(`/login?redirect=${to.path}`)
})
} else {
next()
}
}
} else {
if (whiteList.indexOf(to.path) !== -1 || whiteList.indexOf(to.path.split('to/')[0] + 'to') !== -1) {
next()
} else {
next(`/login?redirect=${to.path}`)
}
}
})

+ 76
- 0
src/router/index.js Zobrazit soubor

@@ -0,0 +1,76 @@
import Vue from 'vue';
import Router from 'vue-router';
import Home from '@/views/Home.vue';

Vue.use(Router);

export default new Router({
mode: 'history',
// base: process.env.BASE_URL,
routes: [
{
path: '/login',
component: () => import('@/views/Login/index'),
hidden: true
},
{
path: '/',
name: 'home',
component: Home,
meta: {
// 页面标题title
title: '警报列表'
}
},
{
path: '/setting',
name: 'setting',
component: () => import('@/views/Setting/index'),
meta: {
// 页面标题title
title: '设置中心'
}
}
// {
// path: '/answer',
// name: 'answer',
// // route level code-splitting
// // this generates a separate chunk (about.[hash].js) for this route
// // which is lazy-loaded when the route is visited.
// component: () => import(/* webpackChunkName: 'about' */ '@/views/Answer')
// },
// {
// path: '/personal',
// name: 'personal',
// component: () => import('@/views/Personal')
// },
// {
// path: '/personal/order',
// component: () => import('@/views/Personal/Order')
// },
// {
// path: '/personal/address',
// component: () => import('@/views/Personal/Address')
// },
// {
// path: '/personal/develop',
// component: () => import('@/views/Personal/Develop')
// },
// {
// path: '/personal/develop/share',
// component: () => import('@/views/Personal/Develop/Share')
// },
// {
// path: '/personal/develop/to/:state',
// component: () => import('@/views/Personal/Develop/To')
// },
// {
// path: '/login',
// component: () => import('@/views/Login')
// },
// {
// path: '/product/list',
// component: () => import('@/views/Product')
// }
]
});

+ 8
- 0
src/store/getters.js Zobrazit soubor

@@ -0,0 +1,8 @@
const getters = {
token: state => state.user.token,
userInfo: state => state.user.userInfo,
alarmList: state => state.alarm.alarmList,
alarmMsg: state => state.alarm.alarmMsg,
sysConfig: state => state.setting.sysConfig,
}
export default getters

+ 19
- 0
src/store/index.js Zobrazit soubor

@@ -0,0 +1,19 @@
import Vue from 'vue';
import Vuex from 'vuex';
import user from './modules/user';
import alarm from './modules/alarm';
import setting from './modules/setting';
import getters from './getters';

Vue.use(Vuex)

const store = new Vuex.Store({
modules: {
user,
alarm,
setting
},
getters
})

export default store

+ 63
- 0
src/store/modules/alarm.js Zobrazit soubor

@@ -0,0 +1,63 @@
import { getAlarmList, handleAlarm } from '@/api/alarm'

const state = {
alarmList: '',
alarmMsg: ''
}

const mutations = {
SET_ALARM_LIST: (state, payload) => {
state.alarmList = payload
},
SET_ALARM_MSG: (state, payload) => {
state.alarmMsg = payload
}
}

const actions = {
fetchAlarmList({ commit }, option) {
return new Promise((resolve, reject) => {
getAlarmList(option).then((res) => {
if (res.code === 0) {
const { page } = res
commit('SET_ALARM_LIST', page.list)
}

if (res.msg == '没有权限,请联系管理员授权') {
commit('SET_ALARM_LIST', [])
}
resolve(res)
}).catch((err) => {
reject(err)
})
})
},
handleAlarms({ commit }, ids) {
return new Promise((resolve, reject) => {
handleAlarm(ids).then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
}
// fetchVideoMsg({ commit }, videoId) {
// return new Promise((resolve, reject) => {
// getVideoMsg(videoId).then((res) => {
// const { video } = res
// video['label'] = video['label'] ? video['label'].split(',') : []
// commit('SET_VIDEO_MSG', video)
// resolve()
// }).catch((err) => {
// reject(err)
// })
// })
// },
}

export default {
namespaced: true,
state,
mutations,
actions
}

+ 47
- 0
src/store/modules/setting.js Zobrazit soubor

@@ -0,0 +1,47 @@
import { getSysConfig, handleSysConfig } from '@/api/setting'

const state = {
sysConfig: ''
}

const mutations = {
SET_SYS_CONFIG: (state, payload) => {
state.sysConfig = payload
}
}

const actions = {
fetchSysConfig({ commit }, option) {
return new Promise((resolve, reject) => {
getSysConfig(option).then((res) => {
if (res.code === 0) {
const { page } = res
commit('SET_SYS_CONFIG', ...page.list)
}

if (res.msg == '没有权限,请联系管理员授权') {
commit('SET_SYS_CONFIG', '')
}
resolve(res)
}).catch((err) => {
reject(err)
})
})
},
handleSysConfigs({ commit }, option) {
return new Promise((resolve, reject) => {
handleSysConfig(option).then(res => {
resolve(res)
}).catch(err => {
reject(err)
})
})
}
}

export default {
namespaced: true,
state,
mutations,
actions
}

+ 82
- 0
src/store/modules/user.js Zobrazit soubor

@@ -0,0 +1,82 @@
import { login, getInfo, logout } from '@/api/user'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { uuid } from '@/utils'

const user = {
state: {
token: getToken(),
userInfo: {}
},

mutations: {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_USERINFO: (state, userInfo) => {
state.userInfo = userInfo
}
},

actions: {
// 登录
Login({ commit }, loginInfo) {
return new Promise((resolve, reject) => {
login(loginInfo).then(data => {
// const { success, msg, authData } = data
//console.log(data, 'login success')
// resolve({ success, msg })
const { code } = data
resolve(data)
if (code !== 0) return
const token = uuid()
setToken(token)
commit('SET_TOKEN', token)
//setToken(authData.token)
//commit('SET_TOKEN', authData.token)
}).catch(error => {
reject(error)
})
})
},

// 获取用户信息
GetUserInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo(new Date().getTime()).then(data => {
const { code, user } = data
if (code === 0) commit('SET_USERINFO', user)
resolve(code)
}).catch(error => {
reject(error)
})
})
},

// 登出
LogOut({ commit, time }) {
return new Promise((resolve, reject) => {
logout(time)
.then(() => {
commit('SET_TOKEN', '')
//commit('SET_ROLES', [])
removeToken()
resolve()
})
.catch(error => {
reject(error)
})
})
},

// 前端 登出
FedLogOut({ commit }) {
return new Promise(resolve => {
commit('SET_TOKEN', '')
removeToken()
resolve()
})
}
}
}

export default user

+ 35
- 0
src/styles/icon.less Zobrazit soubor

@@ -0,0 +1,35 @@
@font-face {
font-family: 'iconfont'; /* project id 927880 */
src: url('//at.alicdn.com/t/font_927880_2ihl8qth6yg.eot');
src: url('//at.alicdn.com/t/font_927880_2ihl8qth6yg.eot?#iefix') format('embedded-opentype'),
url('//at.alicdn.com/t/font_927880_2ihl8qth6yg.woff') format('woff'),
url('//at.alicdn.com/t/font_927880_2ihl8qth6yg.ttf') format('truetype'),
url('//at.alicdn.com/t/font_927880_2ihl8qth6yg.svg#iconfont') format('svg');
}

@font-face {
font-family: 'iconfont'; /* Project id 2723835 */
src: url('//at.alicdn.com/t/font_2723835_y1jsf7gbkz.woff2?t=1628136189738') format('woff2'),
url('//at.alicdn.com/t/font_2723835_y1jsf7gbkz.woff?t=1628136189738') format('woff'),
url('//at.alicdn.com/t/font_2723835_y1jsf7gbkz.ttf?t=1628136189738') format('truetype');
}

.iconfont {
font-family:"iconfont" !important;
font-size: 28px;
font-style:normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

.icon-home:before { content: "\e620"; }

.icon-shu:before { content: "\e634"; }

.icon-ziyuan:before { content: "\e621"; }

.icon-username:before { content: "\eac3"; position: absolute; left: -5px; bottom: 2px; font-size: 22px;}

.icon-password:before { content: "\eac2"; position: absolute; left: -5px; bottom: 1px; font-size: 24px;}

.icon-yanzhengma:before { content: "\e63f"; position: absolute; left: -2px; bottom: 3px; font-size: 18px; font-weight: bold;}

+ 14
- 0
src/styles/index.less Zobrazit soubor

@@ -0,0 +1,14 @@
@import './icon.less';

// .van-tabs__nav--card {
// border: 1px solid #c8b185 !important;

// .van-tab {
// color: #c8b185 !important;
// border-right: 1px solid #c8b185 !important;
// }
// .van-tab--active {
// color: #fff!important;
// background-color: #c8b185 !important;
// }
// }

+ 15
- 0
src/utils/auth.js Zobrazit soubor

@@ -0,0 +1,15 @@
import Cookies from 'js-cookie';

const TokenKey = 'User-Token';

export function getToken() {
return Cookies.get(TokenKey)
}

export function setToken(token) {
return Cookies.set(TokenKey, token)
}

export function removeToken() {
return Cookies.remove(TokenKey)
}

+ 74
- 0
src/utils/index.js Zobrazit soubor

@@ -0,0 +1,74 @@
/* 合法手机号*/
export function checkPhone(value) {
const reg = /^1[34578]\d{9}$/;
return reg.test(value)
}

export function param2Obj(url) {
const search = url.split('?')[1]
if (!search) {
return {}
}
return JSON.parse(
'{"' +
decodeURIComponent(search)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"') +
'"}'
)
}

export function trim(str) {
return str.replace(/(^\s*)|(\s*$)/g, "")
}

export function check_user_name(str) {
var str2 = "该用户名合法";
if ("" == str) {
str2 = "用户名为空";
return str2;
} else if ((str.length < 5) || (str.length > 20)) {
str2 = "用户名必须为5 ~ 20位";
return str2;
} else if (check_other_char(str)) {
str2 = "不能含有特殊字符";
return str2;
}
return str2;
}
// 验证用户名是否含有特殊字符
function check_other_char(str) {
var arr = ["&", "\\", "/", "*", ">", "<", "@", "!"];
for (var i = 0; i < arr.length; i++) {
for (var j = 0; j < str.length; j++) {
if (arr[i] == str.charAt(j)) {
return true;
}
}
}
return false;
}

export function getUrlToken() {
let urlToken = ''
for (let i = 0; i < 6; i++) {
urlToken += Math.floor(Math.random() * 10)
}
const date = new Date()
return urlToken = date.getFullYear() + '' + (date.getMonth() + 1) + '' + date.getDate() + '' + urlToken
}

export function uuid() {
const s = []
const hexDigits = '0123456789abcdef'
for (let i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1)
}
s[14] = '4' // bits 12-15 of the time_hi_and_version field to 0010
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1) // bits 6-7 of the clock_seq_hi_and_reserved to 01
s[8] = s[13] = s[18] = s[23] = '-'

const uuid = s.join('')
return uuid
};

+ 98
- 0
src/utils/request.js Zobrazit soubor

@@ -0,0 +1,98 @@
import axios from 'axios';
import store from '@/store';
import { getToken } from '@/utils/auth';

// 创建axios实例
const service = axios.create({
baseURL: '/platform-framework', // api 的 base_url
timeout: 5000 // 请求超时时间
})

// request拦截器
service.interceptors.request.use(
config => {
// if (store.getters.token) {
// config.headers.authorization = getToken() // jwt token
// }
return config
},
error => {
// Do something with request error
// console.log(error) // for debug
Promise.reject(error)
}
)

// response 拦截器
service.interceptors.response.use(
// response => response,
/**
* 下面的注释为通过在response里,自定义code来标示请求状态
* 当code返回如下情况则说明权限有问题,登出并返回到登录页
* 如想通过 xmlhttprequest 来状态码标识 逻辑可写在下面error中
* 以下代码均为样例,请结合自生需求加以修改,若不需要,则可删除
*/
res => {
// const res = response.data
// if (res.code !== 20000 && res.code !== 0) {
// Message({
// message: res.message,
// type: 'error',
// duration: 5 * 1000
// })
// // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了;
// if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// // 请自行在引入 MessageBox
// // import { Message, MessageBox } from 'element-ui'
// MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
// confirmButtonText: '重新登录',
// cancelButtonText: '取消',
// type: 'warning'
// }).then(() => {
// store.dispatch('FedLogOut').then(() => {
// location.reload() // 为了重新实例化vue-router对象 避免bug
// })
// })
// }
// return Promise.reject('error')
// } else {
// return response.data
// }
if (res.code && res.code !== 0) {
Message({
message: res.msg || 'Error',
type: 'error',
duration: 5 * 1000
})

// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code && res.code === 401 || res.msg == "没有权限,请联系管理员授权") {
// to re-login
MessageBox.confirm('您已经退出登录,请重新重新登录', '退出登录', {
confirmButtonText: '重新登录',
// cancelButtonText: '返回',
showCancelButton: false,
type: 'warning'
}).then(() => {
store.dispatch('FedLogOut').then(() => {
location.reload()
})
})
}
return Promise.reject(new Error(res.msg || 'Error'))
} else {
return res.data || res
}
},
error => {
console.log('err' + error) // for debug
// Message({
// message: error.message,
// type: 'error',
// duration: 5 * 1000
// })
return Promise.reject(error)
}
)

export default service

+ 122
- 0
src/views/Home.vue Zobrazit soubor

@@ -0,0 +1,122 @@
<template>
<div style="height: calc(100vh - 2.5rem); overflow: auto; margin-top: 1.3rem;">
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
<van-list
v-model="loading"
:finished="finished"
:finished-text="finishedText"
@load="onLoad"
>
<van-cell v-for="item in list" :key="item.id" class="panel-container">
<van-panel :title="`${item.userName ? item.userName : ''} ${item.userMobile ? item.userMobile : ''}`" :desc="`${item.createTime}`" status="紧急报警">
<div class="panel-content">
<div style="font-size: 0.27rem;" v-if="item.address && item.longitude !== 0 && item.latitude !== 0">设备{{item.imei}},在{{item.address}}附近发起紧急报警。</div>
<div style="font-size: 0.27rem;" v-else>设备{{item.imei}}, {{item.address}}</div>
<div style="text-align: right">
<van-button size="small" type="primary" @click.native="handleDetailAlarm(item)">处理</van-button>
</div>
</div>
</van-panel>
</van-cell>
</van-list>
</van-pull-refresh>
</div>
</template>

<script>
import { Toast, Dialog } from "vant";
import { mapGetters, mapActions } from "vuex";

export default {
data() {
return {
list: [],
dialogLoading: false,
loading: false,
finished: false,
refreshing: false,
page: 1,
finishedText: '已经到底啦~~'
};
},
computed: {
...mapGetters(["alarmList"]),
},
methods: {
...mapActions({
getAlarmList: "alarm/fetchAlarmList",
handleAlarms: "alarm/handleAlarms"
}),
onLoad() {
setTimeout(() => {
if (this.refreshing) {
this.list = [];
this.refreshing = false;
}
this.getAlarmList(`startTime=&endTime=&state=0&limit=10&page=${this.page}&type=0&sidx=&order=asc&serviceTime=1`).then((res) => {
this.loading = false;
if (res.code === 0) {
this.list.push(...this.alarmList);
if (this.page == res.totalPage) {
this.finishedText = '已经到底啦~~'
this.finished = true;
}
this.page++;
} else {
this.finished = true;
this.finishedText = '服务器异常,请稍后重试~~'
}
});
}, 500);
},
onRefresh() {
// 清空列表数据
this.finished = false;

// 重新加载数据
// 将 loading 设置为 true,表示处于加载状态
this.loading = true;
this.page = 1;
this.onLoad();
},
handleDetailAlarm(alarm) {
Dialog.confirm({
title: '报警处理',
message: '是否处理该紧急报警?',
beforeClose: (action, done) => {
if (action === 'confirm') {
this.handleAlarms([alarm.id]).then(res => {
if (res.code === 0) {
Toast.success('处理成功!');
this.list = []
this.onRefresh()
}
done()
})
} else done()
}
}).catch(() => {
console.log('返回')
})
}
},
};
</script>
<style lang="less">
.panel-container {
margin-bottom: 5px;
.panel-content {
padding: 0.26667rem 0.4rem;
}
}
.van-hairline--top-bottom::after {
border: none !important;
}

.van-cell {
padding-bottom: 0 !important;
.van-cell__title {
flex: 3;
}
}
</style>

+ 127
- 0
src/views/Login/index.vue Zobrazit soubor

@@ -0,0 +1,127 @@
<template>
<div class="login-container">
<header style="padding-top: 50px;font-size: 0.7rem;">综合管理平台</header>
<div>
<div class="input-box" style="margin-top: 100px;">
<i class="iconfont icon-username" />
<input v-model="loginInfo.username" type="text">
</div>
<div class="input-box" style="margin-top: 10px;">
<i class="iconfont icon-password" />
<input v-model="loginInfo.password" type="password">
</div>
<div class="input-box" style="margin-top: 10px;">
<i class="iconfont icon-yanzhengma" />
<input v-model="loginInfo.captcha">
<div style="color: darkgray;position: absolute;right: -2px; bottom: 1px;">
<img
:src="`/platform-framework/captcha.jpg?t=${loginInfo.time}`"
alt="验证码获取失败~~"
style="width: 3.3rem;vertical-align: middle;line-height: 16px; font-size: 11px;border-radius: 4px 4px 0 0;"
@click="handleClickFreshCaptcha"
>
</div>
</div>
</div>
<van-button :loading="isLogin" type="info" @click.native="login">登录</van-button>
</div>
</template>
<script>
import { Toast } from "vant";
import { check_user_name } from "@/utils";

export default {
data() {
return {
loginInfo: {
username: "",
password: "",
captcha: "",
time: "",
},
isLogin: false,
redirect: undefined,
};
},
created() {
this.loginInfo.username = this.$route.query.username || "";
this.loginInfo.password = this.$route.query.password || "";
},
mounted() {

},
watch: {
$route: {
handler: function(route) {
this.redirect = route.query && route.query.redirect;
},
immediate: true,
},
},
methods: {
async login() {
if (
check_user_name(this.loginInfo.username) &&
this.loginInfo.password !== ""
) {
// 调用登录接口
// 登录成功后继续跳转到被拦截的页面 this.$router.push({ path: this.redirect })
const { username, password, captcha } = this.loginInfo
if (username && password && captcha) {
this.isLogin = true;
try {
const res = await this.$store.dispatch("Login", `username=${username.trim()}&password=${password}&captcha=${captcha}`);
this.isLogin = false;
this.handleClickFreshCaptcha();
if (res.code !== 0) return Toast(res.msg);
this.$router.push({ path: this.redirect || "/" });
} catch (e) {
this.isLogin = false;
Toast(e.toString());
}
} else {
Toast('请检查账号、密码以及验证码是否填写完毕 ~~');
}
}
},
handleClickFreshCaptcha() {
this.loginInfo.time = new Date().getTime()
}
},
};
</script>
<style lang="less" scoped>
.login-container {
height: 100vh;
// display: flex;
// align-items: center;
// flex-direction: column;
// justify-content: center;
// padding: 0 10px;
background: #fff !important;
text-align: center;
.input-box {
display: inline-block;
position: relative;
input {
outline: none;
border-bottom: 1px solid #c8b185;
border-left-width: 0px;
border-top-width: 0px;
border-right-width: 0px;
text-align: left;
padding: 0 30px;
width: 250px;
box-sizing: border-box;
font-size: 16px;
border-radius: 0;
}
}
.van-button {
margin-top: 50px;
width: 250px;
// background: #c8b185;
// color: #fff;
}
}
</style>

+ 71
- 0
src/views/Setting/index.vue Zobrazit soubor

@@ -0,0 +1,71 @@
<template>
<div style="height: calc(100vh - 2.5rem); overflow: auto; margin-top: 1.3rem;">
<van-cell center title="报警提醒" v-if="sysConfig">
<template #right-icon>
<van-switch :value="checked" @input="onInput" size="24" />
</template>
</van-cell>
<div class="van-list__finished-text" v-else>服务器异常,请稍后重试~~</div>
<van-row type="flex" justify="space-around">
<van-col>
<van-button type="info" size="small" @click="handleLogout">退出登录</van-button>
</van-col>
</van-row>
</div>
</template>
<script>
import { Toast, Dialog } from "vant";
import { mapGetters, mapActions } from "vuex";

export default {
data() {
return {
checked: true
};
},
computed: {
...mapGetters(["sysConfig"]),
},
mounted() {
this.getSysConfig('limit=10&page=1&sidx=&order=asc&key=IS_NOTIFY_SERVICE').then(res => {
if (res.code === 0) {
this.checked = this.sysConfig['value'] === '1' ? true : false
}
})
},
methods: {
...mapActions({
getSysConfig: "setting/fetchSysConfig",
handleSysConfigs: "setting/handleSysConfigs",
}),
onInput(checked) {
let msg = checked ? '开启' : '关闭'
Dialog.confirm({
title: '提醒',
message: `是否${msg}报警通知?`,
}).then(() => {
this.sysConfig['value'] = checked ? '1' : '0'
this.handleSysConfigs(this.sysConfig).then(res => {
if (res.code === 0) {
this.checked = checked;
} else {
Toast.error('设置失败,请稍后重试~~');
}
})
}).catch(() => {})
},
handleLogout() {
Dialog.confirm({
title: '退出登录',
message: `是否立即退出登录?`,
}).then(() => {
this.$store.dispatch('LogOut', new Date().getTime()).then(res => {
//this.$router.push(`/login?redirect=${this.$route.fullPath}`)
window.location.href = '/login'
})
}).catch(() => {})
}
},
}
</script>

+ 40
- 0
vue.config.js Zobrazit soubor

@@ -0,0 +1,40 @@
const autoprefixer = require('autoprefixer')
const pxtorem = require('postcss-pxtorem')

module.exports = {
outputDir: 'docs',
baseUrl: '/',
assetsDir: 'static',
productionSourceMap: false,
css: {
loaderOptions: {
postcss: {
plugins: [
autoprefixer(),
pxtorem({
rootValue: 37.5,
propList: ['*']
})
]
}
}
},
devServer: {
port: 9090, // 端口号
https: false, // https:{type:Boolean}
open: true, //配置自动启动浏览器
disableHostCheck: true,
proxy: {
// '/api': {
// target: 'http://localhost:8889',
// changeOrigin: true
// },
'/platform-framework': {
// target: `http://127.0.0.1:${port}/mock`,
target: `http://192.168.1.83:8080`,
// target: 'http://192.168.1.169:8086/',
changeOrigin: true
}
}
}
}

Načítá se…
Zrušit
Uložit