Browse Source

初始化

wzh 2 years atrás
commit
26e08fce71
74 changed files with 10051 additions and 0 deletions
  1. 16 0
      .gitignore
  2. 34 0
      App.vue
  3. 21 0
      LICENSE
  4. 52 0
      README.md
  5. 8 0
      api/device/chargerecord.js
  6. 56 0
      api/device/current.js
  7. 56 0
      api/device/device.js
  8. 20 0
      api/device/plan.js
  9. 63 0
      api/login.js
  10. 41 0
      api/system/user.js
  11. 167 0
      components/uni-section/uni-section.vue
  12. 28 0
      config.js
  13. 20 0
      main.js
  14. 69 0
      manifest.json
  15. 132 0
      pages.json
  16. 43 0
      pages/common/textview/index.vue
  17. 34 0
      pages/common/webview/index.vue
  18. 133 0
      pages/index.vue
  19. 424 0
      pages/login.vue
  20. 75 0
      pages/mine/about/index.vue
  21. 631 0
      pages/mine/avatar/index.vue
  22. 112 0
      pages/mine/help/index.vue
  23. 169 0
      pages/mine/index.vue
  24. 127 0
      pages/mine/info/edit.vue
  25. 44 0
      pages/mine/info/index.vue
  26. 96 0
      pages/mine/pwd/index.vue
  27. 78 0
      pages/mine/setting/index.vue
  28. 117 0
      pages/weitiandi/device/chargerecord.vue
  29. 443 0
      pages/weitiandi/device/index.vue
  30. 305 0
      pages/weitiandi/device/plan.vue
  31. 134 0
      pages/weitiandi/device/planrecord.vue
  32. 354 0
      pages/weitiandi/device/setting.vue
  33. 183 0
      pages/work/index.vue
  34. 39 0
      permission.js
  35. 60 0
      plugins/auth.js
  36. 14 0
      plugins/index.js
  37. 75 0
      plugins/modal.js
  38. 30 0
      plugins/tab.js
  39. BIN
      static/favicon.ico
  40. 90 0
      static/font/iconfont.css
  41. BIN
      static/font/iconfont.ttf
  42. BIN
      static/images/banner/banner01.jpg
  43. BIN
      static/images/banner/banner02.jpg
  44. BIN
      static/images/banner/banner03.jpg
  45. BIN
      static/images/icons/menu-off.png
  46. BIN
      static/images/icons/menu-on.png
  47. BIN
      static/images/icons/mine-off.png
  48. BIN
      static/images/icons/mine-on.png
  49. BIN
      static/images/profile.jpg
  50. BIN
      static/images/tabbar/home.png
  51. BIN
      static/images/tabbar/home_.png
  52. BIN
      static/images/tabbar/mine.png
  53. BIN
      static/images/tabbar/mine_.png
  54. BIN
      static/images/tabbar/work.png
  55. BIN
      static/images/tabbar/work_.png
  56. 20 0
      static/index.html
  57. BIN
      static/logo.png
  58. BIN
      static/logo200.png
  59. 4804 0
      static/scss/colorui.css
  60. 90 0
      static/scss/global.scss
  61. 6 0
      static/scss/index.scss
  62. 8 0
      store/getters.js
  63. 15 0
      store/index.js
  64. 114 0
      store/modules/user.js
  65. 64 0
      uni.scss
  66. 13 0
      utils/auth.js
  67. 54 0
      utils/common.js
  68. 8 0
      utils/constant.js
  69. 6 0
      utils/errorCode.js
  70. 51 0
      utils/permission.js
  71. 75 0
      utils/request.js
  72. 33 0
      utils/storage.js
  73. 70 0
      utils/upload.js
  74. 27 0
      utils/websocket.js

+ 16 - 0
.gitignore

@@ -0,0 +1,16 @@
+######################################################################
+# Build Tools
+
+/unpackage/*
+/node_modules/*
+
+######################################################################
+# Development Tools
+
+/.idea/*
+/.vscode/*
+/.hbuilderx/*
+
+package-lock.json
+yarn.lock
+

+ 34 - 0
App.vue

@@ -0,0 +1,34 @@
+<script>
+  import config from './config'
+  import store from '@/store'
+  import { getToken } from '@/utils/auth'
+
+  export default {
+    onLaunch: function() {
+      this.initApp()
+    },
+    methods: {
+      // 初始化应用
+      initApp() {
+        // 初始化应用配置
+        this.initConfig()
+        // 检查用户登录状态
+        //#ifdef H5
+        this.checkLogin()
+        //#endif
+      },
+      initConfig() {
+        this.globalData.config = config
+      },
+      checkLogin() {
+        // if (!getToken()) {
+        //   this.$tab.reLaunch('/pages/login')
+        // }
+      }
+    }
+  }
+</script>
+
+<style lang="scss">
+  @import '@/static/scss/index.scss'
+</style>

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 若依
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 52 - 0
README.md

@@ -0,0 +1,52 @@
+<p align="center">
+	<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-43e3941654fa3054c9684bf53d1b1d356a1.png">
+</p>
+<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v1.1.0</h1>
+<h4 align="center">基于UniApp开发的轻量级移动端框架</h4>
+<p align="center">
+	<a href="https://gitee.com/y_project/RuoYi-App/stargazers"><img src="https://gitee.com/y_project/RuoYi-App/badge/star.svg?theme=dark"></a>
+	<a href="https://gitee.com/y_project/RuoYi-App"><img src="https://img.shields.io/badge/RuoYi-v1.1.0-brightgreen.svg"></a>
+	<a href="https://gitee.com/y_project/RuoYi-App/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
+</p>
+
+## 平台简介
+
+RuoYi App 移动解决方案,采用uniapp框架,一份代码多终端适配,同时支持APP、小程序、H5!实现了与[RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue)、[RuoYi-Cloud](https://gitee.com/y_project/RuoYi-Cloud)完美对接的移动解决方案!目前已经实现登录、我的、工作台、编辑资料、头像修改、密码修改、常见问题、关于我们等基础功能。
+
+* 配套后端代码仓库地址[RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue) 或 [RuoYi-Cloud](https://github.com/yangzongzhuan/RuoYi-Cloud) 版本。
+* 应用框架基于[uniapp](https://uniapp.dcloud.net.cn/),支持小程序、H5、Android和IOS。
+* 前端组件采用[uni-ui](https://github.com/dcloudio/uni-ui),全端兼容的高性能UI框架。
+* 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)&nbsp;&nbsp;
+* 阿里云优惠券:[点我领取](https://www.aliyun.com/minisite/goods?userCode=brki8iof&share_source=copy_link),腾讯云优惠券:[点我领取](https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console)&nbsp;&nbsp;
+
+
+## 技术文档
+
+- 官网网站:[http://ruoyi.vip](http://ruoyi.vip)
+- 文档地址:[http://doc.ruoyi.vip](http://doc.ruoyi.vip)
+- H5页体验:[http://h5.ruoyi.vip](http://h5.ruoyi.vip)
+- QQ交流群: ①133713780
+- 小程序体验
+
+<img src="https://oscimg.oschina.net/oscnet/up-26c76dc90b92acdbd9ac8cd5252f07c8ad9.jpg" alt="小程序演示"/>
+ 
+
+## 演示图
+
+<table>
+    <tr>
+        <td><img src="https://oscimg.oschina.net/oscnet/up-3ea20e447ac621a161e395fb53ccc683d84.png"/></td>
+        <td><img src="https://oscimg.oschina.net/oscnet/up-a6f23cf9a371a30165e135eff6d9ae89a9d.png"/></td>
+		<td><img src="https://oscimg.oschina.net/oscnet/up-ff5f62016bf6624c1ff27eee57499dccd44.png"/></td>
+    </tr>
+	<tr>
+        <td><img src="https://oscimg.oschina.net/oscnet/up-b9a582fdb26ec69d407fabd044d2c8494df.png"/></td>
+        <td><img src="https://oscimg.oschina.net/oscnet/up-96427ee08fca29d77934cfc8d1b1a637cef.png"/></td>
+		<td><img src="https://oscimg.oschina.net/oscnet/up-5fdadc582d24cccd7727030d397b63185a3.png"/></td>
+    </tr>
+	<tr>
+        <td><img src="https://oscimg.oschina.net/oscnet/up-0a36797b6bcc50c36d40c3c782665b89efc.png"/></td>
+        <td><img src="https://oscimg.oschina.net/oscnet/up-d77995cc00687cedd00d5ac7d68a07ea276.png"/></td>
+		<td><img src="https://oscimg.oschina.net/oscnet/up-fa8f5ab20becf59b4b38c1b92a9989e7109.png"/></td>
+    </tr>
+</table>

+ 8 - 0
api/device/chargerecord.js

@@ -0,0 +1,8 @@
+import request from '@/utils/request'
+export function listCharge(data) {
+    return request({
+        'url': '/chargeRecord/list?&pageNum='+data.pageNum+"&pageSize="+data.pageSize,
+        'method': 'post',
+        data:data
+    })
+}

+ 56 - 0
api/device/current.js

@@ -0,0 +1,56 @@
+import request from '@/utils/request'
+// 充点电流设置方法
+export function setCurrent(data,current) {
+  return request({
+    'url': '/device/setCurrent?current='+current,
+    'method': 'post',
+      data:data
+  })
+}
+function cloneObj(obj){
+    return JSON.parse(JSON.stringify(obj));
+}
+// 发送获取主板的命令
+export function sendMainboardCmd(deviceInfo) {
+    return request({
+        'url': '/device/sendMainboardCmd',
+        'method': 'post',
+        data:deviceInfo
+    })
+}
+// 获取主板配置方法
+export function getMainbordConfig(deviceInfo,time) {
+    let data = cloneObj(deviceInfo);
+    data.time = time;
+    return request({
+        'url': '/device/getMainboardConfig',
+        'method': 'post',
+        data:data
+    })
+}
+// 获取设备info
+export function getDeviceInfo(deviceID) {
+  return request({
+      url: '/select/'+deviceID,
+      method: 'post'
+  })
+}
+
+
+// 恢复默认配置
+export function reset(deviceInfo) {
+  return request({
+    'url': '/device/resetMainboard',
+    'method': 'post',
+      data:deviceInfo
+  })
+}
+
+// 重启充电桩
+export function restart(deviceInfo) {
+  return request({
+    'url': '/device/restartMainboard',
+    'method': 'post',
+      data:deviceInfo
+  })
+}

+ 56 - 0
api/device/device.js

@@ -0,0 +1,56 @@
+import request from '@/utils/request'
+// 获取设备info
+export function sendPortDetailCmd(deviceInfo) {
+    return request({
+        url: '/device/sendPortDetailCmd',
+        method: 'post',
+        data:deviceInfo
+    })
+}
+function cloneObj(obj){
+    return JSON.parse(JSON.stringify(obj));
+}
+//获取接口状态
+export function getPortDetail(deviceInfo,time) {
+    let data = cloneObj(deviceInfo);
+    data.time = time;
+    return request({
+        url: '/device/getPortDetail',
+        method: 'post',
+        data:data
+    })
+}
+//开始充电
+export function startCharge(deviceInfo) {
+    return request({
+        url: '/device/startCharge',
+        method: 'post',
+        data:deviceInfo
+    })
+}
+//停止充电
+export function stopCharge(deviceInfo){
+    return request({
+        url: '/device/stopCharge',
+        method: 'post',
+        data:deviceInfo
+    })
+}
+//检测是否有端口变动
+export function checkStatusChange(deviceInfo,time){
+    let data = cloneObj(deviceInfo);
+    data.time = time;
+    return request({
+        url: '/device/statusChange?time='+time,
+        method: 'post',
+        data:data
+    })
+}
+//获取设备详情
+export function getDeviceInfoFromQrcode(qrcode){
+    qrcode = encodeURIComponent(qrcode)
+    return request({
+        url: '/device/getDeviceInfo?qrcode='+qrcode,
+        method: 'post',
+    })
+}

+ 20 - 0
api/device/plan.js

@@ -0,0 +1,20 @@
+import request from '@/utils/request'
+export function save(params) {
+	const data = {
+	 params
+	}
+  return request({
+    'url': '/plan/add',
+    'method': 'post',
+     'data': data
+  })
+}
+
+//预约记录
+export function plan(data) {
+	return request({
+		'url': '/plan/list??&pageNum='+data.pageNum+"&pageSize="+data.pageSize,
+		'method': 'post',
+		'data': data
+	})
+}

+ 63 - 0
api/login.js

@@ -0,0 +1,63 @@
+import request from '@/utils/request'
+// 登录方法
+export function login(username, password) {
+  const data = {
+    username,
+    password,
+    // code,
+    // uuid
+  }
+  return request({
+    'url': '/mailLogin',
+    headers: {
+      isToken: false
+    },
+    'method': 'post',
+    'data': data
+  })
+}
+
+// 注册方法
+export function register(username, password) {
+  const data = {
+    username,
+	password
+  }
+  return request({
+    'url': '/mailRegister',
+    headers: {
+      isToken: false
+    },
+    'method': 'post',
+	'data': data
+  })
+}
+
+// 获取用户详细信息
+export function getInfo() {
+  return request({
+    'url': '/getInfo',
+    'method': 'get'
+  })
+}
+
+// 退出方法
+export function logout() {
+  return request({
+    'url': '/logout',
+    'method': 'post'
+  })
+}
+
+
+// // 获取验证码
+// export function getCodeImg() {
+//   return request({
+//     'url': '/captchaImage',
+//     headers: {
+//       isToken: false
+//     },
+//     method: 'get',
+//     timeout: 20000
+//   })
+// }

+ 41 - 0
api/system/user.js

@@ -0,0 +1,41 @@
+import upload from '@/utils/upload'
+import request from '@/utils/request'
+
+// 用户密码重置
+export function updateUserPwd(oldPassword, newPassword) {
+  const data = {
+    oldPassword,
+    newPassword
+  }
+  return request({
+    url: '/system/user/profile/updatePwd',
+    method: 'put',
+    params: data
+  })
+}
+
+// 查询用户个人信息
+export function getUserProfile() {
+  return request({
+    url: '/system/user/profile',
+    method: 'get'
+  })
+}
+
+// 修改用户个人信息
+export function updateUserProfile(data) {
+  return request({
+    url: '/system/user/profile',
+    method: 'put',
+    data: data
+  })
+}
+
+// 用户头像上传
+export function uploadAvatar(data) {
+  return upload({
+    url: '/system/user/profile/avatar',
+    name: data.name,
+    filePath: data.filePath
+  })
+}

+ 167 - 0
components/uni-section/uni-section.vue

@@ -0,0 +1,167 @@
+<template>
+	<view class="uni-section">
+		<view class="uni-section-header" @click="onClick">
+				<view class="uni-section-header__decoration" v-if="type" :class="type" />
+        <slot v-else name="decoration"></slot>
+
+        <view class="uni-section-header__content">
+          <text :style="{'font-size':titleFontSize,'color':titleColor}" class="uni-section__content-title" :class="{'distraction':!subTitle}">{{ title }}</text>
+          <text v-if="subTitle" :style="{'font-size':subTitleFontSize,'color':subTitleColor}" class="uni-section-header__content-sub">{{ subTitle }}</text>
+        </view>
+
+        <view class="uni-section-header__slot-right">
+          <slot name="right"></slot>
+        </view>
+		</view>
+
+		<view class="uni-section-content" :style="{padding: _padding}">
+			<slot />
+		</view>
+	</view>
+</template>
+
+<script>
+
+	/**
+	 * Section 标题栏
+	 * @description 标题栏
+	 * @property {String} type = [line|circle|square] 标题装饰类型
+	 * 	@value line 竖线
+	 * 	@value circle 圆形
+	 * 	@value square 正方形
+	 * @property {String} title 主标题
+	 * @property {String} titleFontSize 主标题字体大小
+	 * @property {String} titleColor 主标题字体颜色
+	 * @property {String} subTitle 副标题
+	 * @property {String} subTitleFontSize 副标题字体大小
+	 * @property {String} subTitleColor 副标题字体颜色
+	 * @property {String} padding 默认插槽 padding
+	 */
+
+	export default {
+		name: 'UniSection',
+    emits:['click'],
+		props: {
+			type: {
+				type: String,
+				default: ''
+			},
+			title: {
+				type: String,
+				required: true,
+				default: ''
+			},
+      titleFontSize: {
+        type: String,
+        default: '14px'
+      },
+			titleColor:{
+				type: String,
+				default: '#333'
+			},
+			subTitle: {
+				type: String,
+				default: ''
+			},
+      subTitleFontSize: {
+        type: String,
+        default: '12px'
+      },
+      subTitleColor: {
+        type: String,
+        default: '#999'
+      },
+			padding: {
+				type: [Boolean, String],
+				default: false
+			}
+		},
+    computed:{
+      _padding(){
+        if(typeof this.padding === 'string'){
+          return this.padding
+        }
+
+        return this.padding?'10px':''
+      }
+    },
+		watch: {
+			title(newVal) {
+				if (uni.report && newVal !== '') {
+					uni.report('title', newVal)
+				}
+			}
+		},
+    methods: {
+			onClick() {
+				this.$emit('click')
+			}
+		}
+	}
+</script>
+<style lang="scss" >
+	$uni-primary: #2979ff !default;
+
+	.uni-section {
+		background-color: #fff;
+    .uni-section-header {
+      position: relative;
+      /* #ifndef APP-NVUE */
+      display: flex;
+      /* #endif */
+      flex-direction: row;
+      align-items: center;
+      padding: 12px 10px;
+      font-weight: normal;
+
+      &__decoration{
+        margin-right: 6px;
+        background-color: $uni-primary;
+        &.line {
+          width: 4px;
+          height: 12px;
+          border-radius: 10px;
+        }
+
+        &.circle {
+          width: 8px;
+          height: 8px;
+          border-top-right-radius: 50px;
+          border-top-left-radius: 50px;
+          border-bottom-left-radius: 50px;
+          border-bottom-right-radius: 50px;
+        }
+
+        &.square {
+          width: 8px;
+          height: 8px;
+        }
+      }
+
+      &__content {
+        /* #ifndef APP-NVUE */
+        display: flex;
+        /* #endif */
+        flex-direction: column;
+        flex: 1;
+        color: #333;
+
+        .distraction {
+          flex-direction: row;
+          align-items: center;
+        }
+        &-sub {
+          margin-top: 2px;
+        }
+      }
+
+      &__slot-right{
+        font-size: 14px;
+      }
+    }
+
+    .uni-section-content{
+      font-size: 14px;
+    }
+	}
+</style>

+ 28 - 0
config.js

@@ -0,0 +1,28 @@
+// 应用全局配置
+module.exports = {
+  //baseUrl: 'https://vue.ruoyi.vip/prod-api',
+  baseUrl: 'http://localhost:8090',
+  socketUrl: 'ws://localhost:8090/websocket',
+  imgUrl:'https://saomawzz.oss-cn-hangzhou.aliyuncs.com/chargerforeign',
+  // 应用信息
+  appInfo: {
+    // 应用名称
+    name: "ruoyi-app",
+    // 应用版本
+    version: "1.1.0",
+    // 应用logo
+    logo: "/static/logo.png",
+    // 官方网站
+    site_url: "http://ruoyi.vip",
+    // 政策协议
+    agreements: [{
+        title: "隐私政策",
+        url: "https://ruoyi.vip/protocol.html"
+      },
+      {
+        title: "用户服务协议",
+        url: "https://ruoyi.vip/protocol.html"
+      }
+    ]
+  }
+}

+ 20 - 0
main.js

@@ -0,0 +1,20 @@
+import Vue from 'vue'
+import App from './App'
+import store from './store' // store
+import plugins from './plugins' // plugins
+import './permission' // permission
+// import axios from "axios";
+
+// Vue.prototype.axios = axios;
+Vue.use(plugins)
+
+Vue.config.productionTip = false
+Vue.prototype.$store = store
+
+App.mpType = 'app'
+
+const app = new Vue({
+  ...App
+})
+
+app.$mount()

+ 69 - 0
manifest.json

@@ -0,0 +1,69 @@
+{
+    "name" : "若依移动端",
+    "appid" : "__UNI__25A9D80",
+    "description" : "",
+    "versionName" : "1.1.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueCompiler" : "uni-app",
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        "modules" : {},
+        "distribute" : {
+            "android" : {
+                "permissions" : [
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ]
+            },
+            "ios" : {},
+            "sdkConfigs" : {}
+        }
+    },
+    "quickapp" : {},
+    "mp-weixin" : {
+        "appid" : "wxccd7e2a0911b3397",
+        "setting" : {
+            "urlCheck" : false,
+            "es6" : false,
+            "minified" : true,
+            "postcss" : true
+        },
+        "optimization" : {
+            "subPackages" : true
+        },
+        "usingComponents" : true
+    },
+    "vueVersion" : "2",
+    "h5" : {
+        "template" : "static/index.html",
+        "devServer" : {
+            "port" : 9090,
+            "https" : false
+        },
+        "title" : "RuoYi-App",
+        "router" : {
+            "mode" : "hash",
+            "base" : "./"
+        }
+    }
+}

+ 132 - 0
pages.json

@@ -0,0 +1,132 @@
+{
+  "pages": [
+
+    {
+      "path": "pages/login",
+      "style": {
+        "navigationBarTitleText": "登录"
+      }
+    },
+     {
+  "path": "pages/weitiandi/device/plan",
+  "style": {
+    "navigationBarTitleText": "预约"
+    }
+  },
+	{
+	"path" : "pages/weitiandi/device/index",
+	"style" : {
+	  "navigationBarTitleText": "设备详情",
+	  "enablePullDownRefresh": false
+    }
+  },
+	{
+    "path": "pages/weitiandi/device/setting",
+    "style": {
+      "navigationBarTitleText": ""
+    }
+  },
+ {
+    "path": "pages/index",
+    "style": {
+      "navigationBarTitleText": "控制台"
+    }
+  }, {
+    "path": "pages/work/index",
+    "style": {
+      "navigationBarTitleText": "工作台"
+    }
+  }, {
+    "path": "pages/mine/index",
+    "style": {
+      "navigationBarTitleText": "我的",
+      "navigationStyle": "custom"
+    }
+  }, {
+    "path": "pages/mine/avatar/index",
+    "style": {
+      "navigationBarTitleText": "修改头像"
+    }
+  }, {
+    "path": "pages/mine/info/index",
+    "style": {
+      "navigationBarTitleText": "个人信息"
+    }
+  }, {
+    "path": "pages/mine/info/edit",
+    "style": {
+      "navigationBarTitleText": "编辑资料"
+    }
+  }, {
+    "path": "pages/mine/pwd/index",
+    "style": {
+      "navigationBarTitleText": "修改密码"
+    }
+  }, {
+    "path": "pages/mine/setting/index",
+    "style": {
+      "navigationBarTitleText": "应用设置"
+    }
+  }, {
+    "path": "pages/mine/help/index",
+    "style": {
+      "navigationBarTitleText": "常见问题"
+    }
+  }, {
+    "path": "pages/mine/about/index",
+    "style": {
+      "navigationBarTitleText": "关于我们"
+    }
+  }, {
+    "path": "pages/common/webview/index",
+    "style": {
+      "navigationBarTitleText": "浏览网页"
+    }
+  }, {
+    "path": "pages/common/textview/index",
+    "style": {
+      "navigationBarTitleText": "浏览文本"
+    }
+  }, {
+      "path" : "pages/weitiandi/device/chargerecord",
+      "style" :
+      {
+        "navigationBarTitleText": "充电记录",
+        "enablePullDownRefresh": false
+      }
+
+    },
+    {
+      "path" : "pages/weitiandi/device/planrecord",
+      "style" :
+      {
+        "navigationBarTitleText": "预约记录",
+        "enablePullDownRefresh": false
+      }
+
+    }
+  ],
+  "tabBar": {
+    "color": "#000000",
+    "selectedColor": "#000000",
+    "borderStyle": "white",
+    "backgroundColor": "#ffffff",
+    "list": [{
+        "pagePath": "pages/index",
+        "iconPath": "static/images/icons/menu-off.png",
+        "selectedIconPath": "static/images/icons/menu-on.png",
+        "text": "首页"
+      }, {
+        "pagePath": "pages/mine/index",
+        "iconPath": "static/images/icons/mine-off.png",
+        "selectedIconPath": "static/images/icons/mine-on.png",
+        "text": "我的"
+      }
+    ]
+  },
+  "globalStyle": {
+    "navigationBarTextStyle": "black",
+    "navigationBarTitleText": "RuoYi",
+    "navigationBarBackgroundColor": "rgb(249,252,255)"
+  }
+}

+ 43 - 0
pages/common/textview/index.vue

@@ -0,0 +1,43 @@
+<template>
+  <view>
+    <uni-card class="view-title" :title="title">
+      <text class="uni-body view-content">{{ content }}</text>
+    </uni-card>
+  </view>
+</template>
+
+<script>
+  export default {
+    data() {
+      return {
+        title: '',
+        content: ''
+      }
+    },
+    onLoad(options) {
+      this.title = options.title
+      this.content = options.content
+      uni.setNavigationBarTitle({
+        title: options.title
+      })
+    }
+  }
+</script>
+
+<style scoped>
+  page {
+    background-color: #ffffff;
+  }
+
+  .view-title {
+    font-weight: bold;
+  }
+
+  .view-content {
+    font-size: 26rpx;
+    padding: 12px 5px 0;
+    color: #333;
+    line-height: 24px;
+    font-weight: normal;
+  }
+</style>

+ 34 - 0
pages/common/webview/index.vue

@@ -0,0 +1,34 @@
+<template>
+  <view v-if="params.url">
+    <web-view :webview-styles="webviewStyles" :src="`${params.url}`"></web-view>
+  </view>
+</template>
+
+<script>
+  export default {
+    data() {
+      return {
+        params: {},
+        webviewStyles: {
+          progress: {
+            color: "#FF3333"
+          }
+        }
+      }
+    },
+    props: {
+      src: {
+        type: [String],
+        default: null
+      }
+    },
+    onLoad(event) {
+      this.params = event
+      if (event.title) {
+        uni.setNavigationBarTitle({
+          title: event.title
+        })
+      }
+    }
+  }
+</script>

+ 133 - 0
pages/index.vue

@@ -0,0 +1,133 @@
+<template>
+  <view class="content">
+    <view class="banner">
+      <uni-swiper-dot class="uni-swiper-dot-box" :info="data" :current="current" field="content">
+        <swiper class="swiper-box" :current="swiperDotIndex" @change="changeSwiper">
+          <swiper-item v-for="(item, index) in data" :key="index">
+            <view class="swiper-item" @click="clickBannerItem(item)">
+              <image :src="imgUrl+item.image" mode="aspectFill" :draggable="false" />
+            </view>
+          </swiper-item>
+        </swiper>
+      </uni-swiper-dot>
+    </view>
+
+    <view class="container">
+      <view class="item">
+        <image :src = "imgUrl+'/index/bluetooth.png'"/>
+      </view>
+
+      <view class="item" @click="scan2">
+        <image :src = "imgUrl+'/index/scan.png'"/>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+  import {getDeviceInfoFromQrcode} from "@/api/device/device";
+
+  export default {
+    data:function(){
+      return {
+        current: 0,
+        swiperDotIndex: 0,
+        data: [{
+          image: '/index/banner-index.png'
+        }
+        ]
+      }
+    },
+    computed: {
+      imgUrl() {
+        return getApp().globalData.config.imgUrl;
+      }
+    },
+    onLoad: function() {
+    },
+    methods:{
+      scan(){
+        uni.scanCode({
+          success: function (res) {
+            console.log('条码类型:' + res.scanType);
+            console.log('条码内容:' + res.result);
+            getDeviceInfoFromQrcode(res.result).then(res=>{
+              if(res.data != null){
+                let imei = res.data.imei;
+                let ccid = res.data.ccid;
+                uni.navigateTo({
+                  url: '/pages/weitiandi/device/index?id='+imei+'&ccid='+ccid
+                });
+              }
+            });
+          }
+        });
+      },
+      scan2(){
+        getDeviceInfoFromQrcode("http://wetiandi.com/app/index.php?i=1&j=1&c=entry&m=wdl_shopping&do=wepay&sn=GD1B342399").then(res=>{
+            if(res.data != null){
+                  let imei = res.data.imei;
+                  let ccid = res.data.ccid;
+                  uni.navigateTo({
+                    url: '/pages/weitiandi/device/index?id='+imei+'&ccid='+ccid
+                  });
+            }
+        });
+      }
+    }
+  }
+</script>
+
+<style>
+  .content {
+    background: rgb(249, 252, 255);
+    inset: 0;
+    position: absolute;
+  }
+
+
+  .swiper {
+    height: 22vh;
+  }
+.banner{
+  height: 22vh;
+  width: 100%;
+  margin-top:1vh
+}
+  .swiper-box {
+    height: 22vh;
+  }
+
+  .swiper-item {
+    /* #ifndef APP-NVUE */
+    display: flex;
+    /* #endif */
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    color: #fff;
+    height: 300rpx;
+    line-height: 300rpx;
+  }
+
+  @media screen and (min-width: 500px) {
+    .uni-swiper-dot-box {
+      width: 400px;
+      /* #ifndef APP-NVUE */
+      margin: 0 auto;
+      /* #endif */
+      margin-top: 8px;
+    }
+
+    .image {
+      width: 100%;
+    }
+  }
+  .item{
+    display: inline-block;
+    width: 50%;
+  }
+  .item image{
+    height: 12vh;
+  }
+</style>

+ 424 - 0
pages/login.vue

@@ -0,0 +1,424 @@
+<template>
+  <view class="normal-login-container">
+
+	<view>
+		<image style="width: 750px;height: 807px;" :src="imgUrl+'/login/backImg2.png'" mode="widthFix">
+		<view class="logo-content align-center justify-center flex">
+		  <text class="title" style="">微天地充电平台</text>
+		</view>
+		<view class="logo-content justify-center flex">
+		  <text class="welcome" style="">欢迎登录</text>
+		</view>
+		<view class="logo-content justify-center flex">
+		  <text class="welcome1" style="">Welcome</text>
+		</view>
+	</view>
+	<!-- 登录表单 -->
+    <view class="login-form-content" style="z-index: 9999;position: relative;"  v-show="isLogin">
+      <view class="input-item flex align-center">
+        <!-- <view class="iconfont icon-user icon"></view> -->
+	   <uni-row>
+	   <uni-col :span="2">
+		<uni-icons class="icon" type="email" size="20" color="#0E9F9B"></uni-icons>
+       </uni-col>
+	   <uni-col :span="22">
+	    <input v-model="loginForm.username" class="input" type="text" placeholder="请输入账号" maxlength="30" />
+	   </uni-col>
+	   </uni-row>
+      </view>
+      <view class="input-item flex align-center">
+        <!-- <view class="iconfont icon-password icon"></view> -->
+		<uni-row>
+		<uni-col :span="2">
+		<uni-icons class="icon" type="locked" size="20" color="#0E9F9B"></uni-icons>
+		</uni-col>
+		<uni-col :span="22">
+        <uni-easyinput style="margin-left:-10px ;" :inputBorder="false" v-model="loginForm.password"  type="password" class="input"  placeholder="请输入密码" maxlength="30" />
+		</uni-col>
+		</uni-row>
+      </view>
+	  <view class="xieyi text-center" style="margin-top: -10px;">
+		  <text class="text-grey">暂无账号,</text>
+		  <text @click="toggleLoginMode" class="text-green">立即注册</text>
+		  <!-- <text @click="handleUserAgrement" class="text-green">立即注册</text> -->
+		  <!-- <text @click="handlePrivacy" class="text-blue">《隐私协议》</text> -->
+		</view>
+
+     <!-- <view class="input-item flex align-center" style="width: 60%;margin: 0px;" v-if="captchaEnabled">
+        <view class="iconfont icon-code icon"></view>
+        <input v-model="loginForm.code" type="number" class="input" placeholder="请输入验证码" maxlength="4" />
+        <view class="login-code"> <image :src="codeUrl" @click="getCode" class="login-code-img"></image></view  >
+      </view> -->
+      <view class="action-btn" style="z-index: 9999;position: relative;">
+        <button @click="handleLogin" class="login-btn cu-btn block bg-blue lg round">立即登录</button>
+      </view>
+    </view>
+    <view class="login-form-content" style="z-index: 9999;position: relative;" v-show="!isLogin" @submit.prevent="handleRegister">
+          <!-- 注册表单 -->
+		  <view>
+		  <view class="input-item flex align-center">
+		   <uni-row>
+		   <uni-col :span="2">
+		  		<uni-icons class="icon" type="email" size="20" color="#0E9F9B"></uni-icons>
+		   </uni-col>
+		   <uni-col :span="22">
+		    <input v-model="register.username" class="input" type="text" placeholder="请输入新账号" maxlength="30" />
+		   </uni-col>
+		   </uni-row>
+		  </view>
+		  <view>
+		  <view class="input-item flex align-center">
+		   <uni-row>
+		   <uni-col :span="2">
+		  		<uni-icons class="icon" type="locked" size="20" color="#0E9F9B"></uni-icons>
+		   </uni-col>
+		   <uni-col :span="22">
+		    <input v-model="register.password" class="input" type="text" placeholder="请输入新密码" maxlength="30" />
+		   </uni-col>
+		   </uni-row>
+		  </view>
+		  </view>
+		  <view class="xieyi text-center" style="margin-top: -10px;">
+		  <p class="text-grey">已有账号?<a href="#" @click="toggleLoginMode" class="text-green">返回登录</a></p>
+		  </view>
+		  <view class="action-btn" style="z-index: 9999;position: relative;">
+          <button @click="handleRegister" type="submit" class="login-btn cu-btn block bg-blue lg round">注册</button>
+		  </view>
+     </view>
+  </view>
+    <view v-show="isLogin">
+    <view class="img">
+	    <image style="width: 31px;" :src="imgUrl+'/login/blueTeeth.png'" mode="widthFix"/>
+	</view>
+	<view class="blue-text">
+	    <text>蓝牙连接</text>
+	</view>
+	</view>
+  </view>
+</template>
+
+<script>
+  import getCodeImg from '@/api/login'
+  import {getToken} from "@/utils/auth";
+  export default {
+    data() {
+      return {
+		// currentImg: "",
+        codeUrl: "",
+        captchaEnabled: true,
+        globalConfig: getApp().globalData.config,
+        loginForm: {
+          username: "admin",
+          password: "admin123",
+          code: "",
+          uuid: ''
+        },
+		register:{
+		username: "",
+		password: "",
+		},
+		isLogin: true, // 是否为登录模式
+       }
+    },
+	 // mounted() {
+	 //        this.addTextEvent()
+	 //    },
+	computed: {
+	  imgUrl: function () {
+	    return getApp().globalData.config.imgUrl;
+	  }
+	},
+    created() {
+      this.checkLogin();
+    },
+    methods: {
+      checkLogin(){
+        let token = getToken()
+        if(token){
+          this.loginSuccess()
+        }else{
+        }
+      },
+      // // 隐私协议
+      // handlePrivacy() {
+      //   let site = this.globalConfig.appInfo.agreements[0]
+      //   this.$tab.navigateTo(`/pages/common/webview/index?title=${site.title}&url=${site.url}`)
+      // },
+      // 用户协议
+      handleUserAgrement() {
+        let site = this.globalConfig.appInfo.agreements[1]
+        this.$tab.navigateTo(`/pages/common/webview/index?title=${site.title}&url=${site.url}`)
+      },
+      // // 获取图形验证码
+      // getCode() {
+      //   getCodeImg().then(res => {
+      //     this.captchaEnabled = res.captchaEnabled === undefined ? true : res.captchaEnabled
+      //     if (this.captchaEnabled) {
+      //       this.codeUrl = 'data:image/gif;base64,' + res.img
+      //       this.loginForm.uuid = res.uuid
+      //     }
+      //   })
+      // },
+      // 登录方法
+      async handleLogin() {
+        if (this.loginForm.username === "") {
+          this.$modal.msgError("请输入您的账号")
+        } else if (this.loginForm.password === "") {
+          this.$modal.msgError("请输入您的密码")
+        }
+		// else if (this.loginForm.code === "" && this.captchaEnabled) {
+  //         this.$modal.msgError("请输入验证码")}
+		  else {
+
+          this.$modal.loading("登录中,请耐心等待...")
+
+          this.pwdLogin()
+        }
+      },
+      // 密码登录
+      async pwdLogin() {
+        this.$store.dispatch('Login', this.loginForm).then(() => {
+          this.$modal.closeLoading()
+          this.loginSuccess()
+        }).catch(() => {
+          if (this.captchaEnabled) {
+            this.getCode()
+          }
+        })
+      },
+      // 登录成功后,处理函数
+      loginSuccess(result) {
+        // 设置用户信息
+        this.$store.dispatch('GetInfo').then(res => {
+          this.$tab.reLaunch('/pages/index')
+        })
+      },
+
+	  // async handleRegister() {
+
+	  //    if (this.register.newUsername === '' || this.register.newPassword === '') {
+	  //      // 如果用户名或密码为空,则弹出提示框
+	  //      uni.showToast({
+	  //        title: '请输入用户名和密码',
+	  //        icon: 'none'
+	  //      })
+	  //      return
+	  //    }
+
+	  //    // 发送注册请求
+	  //    uni.showLoading({
+
+	  //      title: '注册中...'
+	  //    })
+	  //    uni.request({
+	  //      url: 'http://localhost:8080/login/mailRegister',
+	  //      method: 'POST',
+	  //      data: {
+	  //        username: this.register.newUsername,
+	  //        password: this.register.newPassword
+	  //      },
+	  //      success: res => {
+	  //        // uni.hideLoading()
+	  //        if (res.data.success) {
+	  //          // 注册成功,弹出提示框并回到登录模式
+	  //          uni.showToast({
+	  //            title: '注册成功',
+	  //            success: () => {
+	  //              this.isLogin = true
+	  //              this.register.newUsername = ''
+	  //              this.register.newPassword = ''
+	  //            }
+	  //          })
+	  //        } else {
+	  //          // 注册失败,弹出提示框显示错误信息
+	  //          uni.showToast({
+	  //            title: res.data.message || '注册成功',
+	  //            icon: 'none'
+	  //          })
+	  //        }
+	  //      },
+	  //      fail: err => {
+	  //        uni.hideLoading()
+	  //        uni.showToast({
+	  //          title: err.errMsg || '注册失败',
+	  //          icon: 'none'
+	  //        })
+	  //      }
+	  //    })
+	  //  },
+	  // 注册方法
+	      async handleRegister() {
+			  // debugger
+	        if (this.register.username === "") {
+	          this.$modal.msgError("请输入您的注册账号")
+	        } else if (this.register.password === "") {
+	          this.$modal.msgError("请输入您的注册密码")
+	        }
+	  		// else if (this.loginForm.code === "" && this.captchaEnabled) {
+	        // this.$modal.msgError("请输入验证码")}
+	  		  else {
+				 // debugger
+	          this.$modal.loading("注册中,请耐心等待...")
+	          this.pwdRegister()
+	        }
+	     },
+		 // 用户注册
+		 async pwdRegister() {
+
+		   this.$store.dispatch("Register",this.register).then(res => {
+         if (res.msg) {
+           this.$modal.showToast(res.msg)
+           return;
+         }
+         this.loginForm.username = this.register.username;
+         this.loginForm.password = this.register.password;
+         this.$modal.closeLoading()
+         this.registerSuccess()
+       });
+		 },
+           // 注册成功后,处理函数
+      registerSuccess(result){
+         // 设置用户信息
+         this.toggleLoginMode();
+       },
+
+       toggleLoginMode() {
+         // 切换登录/注册模式
+         this.isLogin = !this.isLogin
+         if(this.isLogin){
+           this.register.username = "";
+           this.register.password = "";
+         }
+       }
+
+    }
+  }
+</script>
+
+<style lang="scss">
+  page {
+    background-color: #ffffff;
+  }
+
+  .normal-login-container {
+    width: 100%;
+    font-family: 思源黑体;
+	margin-top: -20px;
+    .logo-content {
+      width: 100%;
+      font-size: 21px;
+      text-align: center;
+      padding-top: 15%;
+
+      .image {
+        border-radius: 4px;
+      }
+
+      .title {
+		width: 244px;
+		font-weight: bold;
+        margin-left: -56px;
+		text-align: left;
+		margin-top: -425px;
+		z-index: 1;
+		white-space: nowrap;
+		// line-height: 51px;
+		text-stroke: 2px #FFFFFF;
+		font-size: 34px;
+		// letter-spacing: 2px;
+		color: #0E9F9B;
+		text-shadow: 0 2px white, 2px 0 white, -2px 0 white, 0 -2px white;
+      }
+
+	  .welcome {
+		  margin-top: -245px;
+		  margin-left: -178px;
+		  z-index: 2;
+		  font-size: 31px;
+		  font-family: Source Han Sans CN;
+		  font-weight: 400;
+	  }
+
+	  .welcome1 {
+		  margin-top: -278px;
+		  margin-left: 8px;
+		  font-family: Alibaba PuHuiTi;
+		  z-index: 2;
+		  font-size: 12px;
+	  }
+
+
+    }
+
+    .login-form-content {
+      text-align: center;
+      margin: -260px 20px 20px 40px;
+      // margin-top: 15%;
+      width: 80%;
+      // margin-top: -200px;
+	  z-index: 1;
+      .input-item {
+        margin: 20px 0 20px 0;
+        height: 45px;
+        border-radius: 8px;
+        background-color: #f0fdfc;
+		border: 1px solid #0E9F9B;
+		opacity: 0.5;
+		color: #0E9F9B;
+		text-indent:1em;
+		.icon {
+			// margin-left: 10px;
+		  font-size: 38rpx;
+          margin-left: 0px;
+		}
+
+        .input {
+          width: 122%;
+          font-size: 14px;
+          line-height: 27px;
+          text-align: left;
+          padding-left: 15px;
+		  color: #0E9F9B;
+		  margin-left: 10;
+        }
+
+      }
+
+      .login-btn {
+        margin-top: 30px;
+        height: 40px;
+		border-radius: 8px;
+		background-color: #0E9F9B;
+      }
+
+      .xieyi {
+        color: #333;
+        margin-top: 100px;
+      }
+
+      .login-code {
+        height: 38px;
+        float: right;
+
+        .login-code-img {
+          height: 38px;
+          position: absolute;
+          margin-left: 10px;
+          width: 200rpx;
+        }
+      }
+    }
+	.text-green {
+			color: #0E9F9B;
+		}
+	.img {
+		margin-top: 25px;
+		text-align:center
+	}
+	.blue-text {
+		color: #666;
+		text-align:center
+	}
+  }
+
+</style>

+ 75 - 0
pages/mine/about/index.vue

@@ -0,0 +1,75 @@
+<template>
+  <view class="about-container">
+    <view class="header-section text-center">
+      <image style="width: 150rpx;height: 150rpx;" src="/static/logo200.png" mode="widthFix">
+      </image>
+      <uni-title type="h2" title="若依移动端"></uni-title>
+    </view>
+
+    <view class="content-section">
+      <view class="menu-list">
+        <view class="list-cell list-cell-arrow">
+          <view class="menu-item-box">
+            <view>版本信息</view>
+            <view class="text-right">v{{version}}</view>
+          </view>
+        </view>
+        <view class="list-cell list-cell-arrow">
+          <view class="menu-item-box">
+            <view>官方邮箱</view>
+            <view class="text-right">ruoyi@xx.com</view>
+          </view>
+        </view>
+        <view class="list-cell list-cell-arrow">
+          <view class="menu-item-box">
+            <view>服务热线</view>
+            <view class="text-right">400-999-9999</view>
+          </view>
+        </view>
+        <view class="list-cell list-cell-arrow">
+          <view class="menu-item-box">
+            <view>公司网站</view>
+            <view class="text-right">
+              <uni-link :href="url" :text="url" showUnderLine="false"></uni-link>
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <view class="copyright">
+      <view>Copyright &copy; 2022 ruoyi.vip All Rights Reserved.</view>
+    </view>
+  </view>
+</template>
+
+<script>
+  export default {
+    data() {
+      return {
+        url: getApp().globalData.config.appInfo.site_url,
+        version: getApp().globalData.config.appInfo.version
+      }
+    }
+  }
+</script>
+
+<style lang="scss">
+  page {
+    background-color: #f8f8f8;
+  }
+
+  .copyright {
+    margin-top: 50rpx;
+    text-align: center;
+    line-height: 60rpx;
+    color: #999;
+  }
+
+  .header-section {
+    display: flex;
+    padding: 30rpx 0 0;
+    flex-direction: column;
+    align-items: center;
+  }
+</style>

+ 631 - 0
pages/mine/avatar/index.vue

@@ -0,0 +1,631 @@
+<template>
+	<view class="container">
+		<view class="page-body uni-content-info">
+			<view class='cropper-content'>
+				<view v-if="isShowImg" class="uni-corpper" :style="'width:'+cropperInitW+'px;height:'+cropperInitH+'px;background:#000'">
+					<view class="uni-corpper-content" :style="'width:'+cropperW+'px;height:'+cropperH+'px;left:'+cropperL+'px;top:'+cropperT+'px'">
+						<image :src="imageSrc" :style="'width:'+cropperW+'px;height:'+cropperH+'px'"></image>
+						<view class="uni-corpper-crop-box" @touchstart.stop="contentStartMove" @touchmove.stop="contentMoveing" @touchend.stop="contentTouchEnd"
+						    :style="'left:'+cutL+'px;top:'+cutT+'px;right:'+cutR+'px;bottom:'+cutB+'px'">
+							<view class="uni-cropper-view-box">
+								<view class="uni-cropper-dashed-h"></view>
+								<view class="uni-cropper-dashed-v"></view>
+								<view class="uni-cropper-line-t" data-drag="top" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
+								<view class="uni-cropper-line-r" data-drag="right" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
+								<view class="uni-cropper-line-b" data-drag="bottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
+								<view class="uni-cropper-line-l" data-drag="left" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
+								<view class="uni-cropper-point point-t" data-drag="top" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
+								<view class="uni-cropper-point point-tr" data-drag="topTight"></view>
+								<view class="uni-cropper-point point-r" data-drag="right" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
+								<view class="uni-cropper-point point-rb" data-drag="rightBottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
+								<view class="uni-cropper-point point-b" data-drag="bottom" @touchstart.stop="dragStart" @touchmove.stop="dragMove" @touchend.stop="dragEnd"></view>
+								<view class="uni-cropper-point point-bl" data-drag="bottomLeft"></view>
+								<view class="uni-cropper-point point-l" data-drag="left" @touchstart.stop="dragStart" @touchmove.stop="dragMove"></view>
+								<view class="uni-cropper-point point-lt" data-drag="leftTop"></view>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view class='cropper-config'>
+				<button type="primary reverse" @click="getImage" style='margin-top: 30rpx;'> 选择头像 </button>
+				<button type="warn" @click="getImageInfo" style='margin-top: 30rpx;'> 提交 </button>
+			</view>
+			<canvas canvas-id="myCanvas" :style="'position:absolute;border: 1px solid red; width:'+imageW+'px;height:'+imageH+'px;top:-9999px;left:-9999px;'"></canvas>
+		</view>
+	</view>
+</template>
+
+<script>
+  import config from '@/config'
+  import store from "@/store"
+  import { uploadAvatar } from "@/api/system/user"
+  
+  const baseUrl = config.baseUrl
+	let sysInfo = uni.getSystemInfoSync()
+	let SCREEN_WIDTH = sysInfo.screenWidth
+	let PAGE_X, // 手按下的x位置
+		PAGE_Y, // 手按下y的位置 
+		PR = sysInfo.pixelRatio, // dpi
+		T_PAGE_X, // 手移动的时候x的位置
+		T_PAGE_Y, // 手移动的时候Y的位置
+		CUT_L, // 初始化拖拽元素的left值
+		CUT_T, // 初始化拖拽元素的top值
+		CUT_R, // 初始化拖拽元素的
+		CUT_B, // 初始化拖拽元素的
+		CUT_W, // 初始化拖拽元素的宽度
+		CUT_H, //  初始化拖拽元素的高度
+		IMG_RATIO, // 图片比例
+		IMG_REAL_W, // 图片实际的宽度
+		IMG_REAL_H, // 图片实际的高度
+		DRAFG_MOVE_RATIO = 1, //移动时候的比例,
+		INIT_DRAG_POSITION = 100, // 初始化屏幕宽度和裁剪区域的宽度之差,用于设置初始化裁剪的宽度
+		DRAW_IMAGE_W = sysInfo.screenWidth // 设置生成的图片宽度
+
+	export default {
+		/**
+		 * 页面的初始数据
+		 */
+		data() {
+			return {
+				imageSrc: store.getters.avatar,
+				isShowImg: false,
+				// 初始化的宽高
+				cropperInitW: SCREEN_WIDTH,
+				cropperInitH: SCREEN_WIDTH,
+				// 动态的宽高
+				cropperW: SCREEN_WIDTH,
+				cropperH: SCREEN_WIDTH,
+				// 动态的left top值
+				cropperL: 0,
+				cropperT: 0,
+
+				transL: 0,
+				transT: 0,
+
+				// 图片缩放值
+				scaleP: 0,
+				imageW: 0,
+				imageH: 0,
+
+				// 裁剪框 宽高
+				cutL: 0,
+				cutT: 0,
+				cutB: SCREEN_WIDTH,
+				cutR: '100%',
+				qualityWidth: DRAW_IMAGE_W,
+				innerAspectRadio: DRAFG_MOVE_RATIO
+			}
+		},
+		/**
+		 * 生命周期函数--监听页面初次渲染完成
+		 */
+		onReady: function () {
+			this.loadImage()
+		},
+		methods: {
+			setData: function (obj) {
+				let that = this
+				Object.keys(obj).forEach(function (key) {
+					that.$set(that.$data, key, obj[key])
+				})
+			},
+			getImage: function () {
+				var _this = this
+				uni.chooseImage({
+					success: function (res) {
+						_this.setData({
+							imageSrc: res.tempFilePaths[0],
+						})
+						_this.loadImage()
+					},
+				})
+			},
+			loadImage: function () {
+				var _this = this
+
+				uni.getImageInfo({
+					src: _this.imageSrc,
+					success: function success(res) {
+						IMG_RATIO = 1 / 1
+						if (IMG_RATIO >= 1) {
+							IMG_REAL_W = SCREEN_WIDTH
+							IMG_REAL_H = SCREEN_WIDTH / IMG_RATIO
+						} else {
+							IMG_REAL_W = SCREEN_WIDTH * IMG_RATIO
+							IMG_REAL_H = SCREEN_WIDTH
+						}
+						let minRange = IMG_REAL_W > IMG_REAL_H ? IMG_REAL_W : IMG_REAL_H
+						INIT_DRAG_POSITION = minRange > INIT_DRAG_POSITION ? INIT_DRAG_POSITION : minRange
+						// 根据图片的宽高显示不同的效果   保证图片可以正常显示
+						if (IMG_RATIO >= 1) {
+							let cutT = Math.ceil((SCREEN_WIDTH / IMG_RATIO - (SCREEN_WIDTH / IMG_RATIO - INIT_DRAG_POSITION)) / 2)
+							let cutB = cutT
+							let cutL = Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH + INIT_DRAG_POSITION) / 2)
+							let cutR = cutL
+							_this.setData({
+								cropperW: SCREEN_WIDTH,
+								cropperH: SCREEN_WIDTH / IMG_RATIO,
+								// 初始化left right
+								cropperL: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH) / 2),
+								cropperT: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH / IMG_RATIO) / 2),
+								cutL: cutL,
+								cutT: cutT,
+								cutR: cutR,
+								cutB: cutB,
+								// 图片缩放值
+								imageW: IMG_REAL_W,
+								imageH: IMG_REAL_H,
+								scaleP: IMG_REAL_W / SCREEN_WIDTH,
+								qualityWidth: DRAW_IMAGE_W,
+								innerAspectRadio: IMG_RATIO
+							})
+						} else {
+							let cutL = Math.ceil((SCREEN_WIDTH * IMG_RATIO - (SCREEN_WIDTH * IMG_RATIO)) / 2)
+							let cutR = cutL
+							let cutT = Math.ceil((SCREEN_WIDTH - INIT_DRAG_POSITION) / 2)
+							let cutB = cutT
+							_this.setData({
+								cropperW: SCREEN_WIDTH * IMG_RATIO,
+								cropperH: SCREEN_WIDTH,
+								// 初始化left right
+								cropperL: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH * IMG_RATIO) / 2),
+								cropperT: Math.ceil((SCREEN_WIDTH - SCREEN_WIDTH) / 2),
+
+								cutL: cutL,
+								cutT: cutT,
+								cutR: cutR,
+								cutB: cutB,
+								// 图片缩放值
+								imageW: IMG_REAL_W,
+								imageH: IMG_REAL_H,
+								scaleP: IMG_REAL_W / SCREEN_WIDTH,
+								qualityWidth: DRAW_IMAGE_W,
+								innerAspectRadio: IMG_RATIO
+							})
+						}
+						_this.setData({
+							isShowImg: true
+						})
+						uni.hideLoading()
+					}
+				})
+			},
+			// 拖动时候触发的touchStart事件
+			contentStartMove(e) {
+				PAGE_X = e.touches[0].pageX
+				PAGE_Y = e.touches[0].pageY
+			},
+
+			// 拖动时候触发的touchMove事件
+			contentMoveing(e) {
+				var _this = this
+				var dragLengthX = (PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
+				var dragLengthY = (PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
+				// 左移
+				if (dragLengthX > 0) {
+					if (this.cutL - dragLengthX < 0) dragLengthX = this.cutL
+				} else {
+					if (this.cutR + dragLengthX < 0) dragLengthX = -this.cutR
+				}
+
+				if (dragLengthY > 0) {
+					if (this.cutT - dragLengthY < 0) dragLengthY = this.cutT
+				} else {
+					if (this.cutB + dragLengthY < 0) dragLengthY = -this.cutB
+				}
+				this.setData({
+					cutL: this.cutL - dragLengthX,
+					cutT: this.cutT - dragLengthY,
+					cutR: this.cutR + dragLengthX,
+					cutB: this.cutB + dragLengthY
+				})
+
+				PAGE_X = e.touches[0].pageX
+				PAGE_Y = e.touches[0].pageY
+			},
+
+			contentTouchEnd() {
+
+			},
+
+			// 获取图片
+			getImageInfo() {
+				var _this = this
+				uni.showLoading({
+					title: '图片生成中...',
+				})
+				// 将图片写入画布
+				const ctx = uni.createCanvasContext('myCanvas')
+				ctx.drawImage(_this.imageSrc, 0, 0, IMG_REAL_W, IMG_REAL_H)
+				ctx.draw(true, () => {
+					// 获取画布要裁剪的位置和宽度   均为百分比 * 画布中图片的宽度    保证了在微信小程序中裁剪的图片模糊  位置不对的问题 canvasT = (_this.cutT / _this.cropperH) * (_this.imageH / pixelRatio)
+					var canvasW = ((_this.cropperW - _this.cutL - _this.cutR) / _this.cropperW) * IMG_REAL_W
+					var canvasH = ((_this.cropperH - _this.cutT - _this.cutB) / _this.cropperH) * IMG_REAL_H
+					var canvasL = (_this.cutL / _this.cropperW) * IMG_REAL_W
+					var canvasT = (_this.cutT / _this.cropperH) * IMG_REAL_H
+					uni.canvasToTempFilePath({
+						x: canvasL,
+						y: canvasT,
+						width: canvasW,
+						height: canvasH,
+						destWidth: canvasW,
+						destHeight: canvasH,
+						quality: 0.5,
+						canvasId: 'myCanvas',
+						success: function (res) {
+							uni.hideLoading()
+							let data = {name: 'avatarfile', filePath: res.tempFilePath}
+							uploadAvatar(data).then(response => {
+								store.commit('SET_AVATAR', baseUrl + response.imgUrl)
+								uni.showToast({ title: "修改成功", icon: 'success' })
+								uni.navigateBack()
+							})
+						}
+					})
+				})
+			},
+			// 设置大小的时候触发的touchStart事件
+			dragStart(e) {
+				T_PAGE_X = e.touches[0].pageX
+				T_PAGE_Y = e.touches[0].pageY
+				CUT_L = this.cutL
+				CUT_R = this.cutR
+				CUT_B = this.cutB
+				CUT_T = this.cutT
+			},
+
+			// 设置大小的时候触发的touchMove事件
+			dragMove(e) {
+				var _this = this
+				var dragType = e.target.dataset.drag
+				switch (dragType) {
+					case 'right':
+						var dragLength = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
+						if (CUT_R + dragLength < 0) dragLength = -CUT_R
+						this.setData({
+							cutR: CUT_R + dragLength
+						})
+						break
+					case 'left':
+						var dragLength = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
+						if (CUT_L - dragLength < 0) dragLength = CUT_L
+						if ((CUT_L - dragLength) > (this.cropperW - this.cutR)) dragLength = CUT_L - (this.cropperW - this.cutR)
+						this.setData({
+							cutL: CUT_L - dragLength
+						})
+						break
+					case 'top':
+						var dragLength = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
+						if (CUT_T - dragLength < 0) dragLength = CUT_T
+						if ((CUT_T - dragLength) > (this.cropperH - this.cutB)) dragLength = CUT_T - (this.cropperH - this.cutB)
+						this.setData({
+							cutT: CUT_T - dragLength
+						})
+						break
+					case 'bottom':
+						var dragLength = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
+						if (CUT_B + dragLength < 0) dragLength = -CUT_B
+						this.setData({
+							cutB: CUT_B + dragLength
+						})
+						break
+					case 'rightBottom':
+						var dragLengthX = (T_PAGE_X - e.touches[0].pageX) * DRAFG_MOVE_RATIO
+						var dragLengthY = (T_PAGE_Y - e.touches[0].pageY) * DRAFG_MOVE_RATIO
+
+						if (CUT_B + dragLengthY < 0) dragLengthY = -CUT_B
+						if (CUT_R + dragLengthX < 0) dragLengthX = -CUT_R
+						let cutB = CUT_B + dragLengthY
+						let cutR = CUT_R + dragLengthX
+
+						this.setData({
+							cutB: cutB,
+							cutR: cutR
+						})
+						break
+					default:
+						break
+				}
+			}
+		}
+	}
+</script>
+
+<style>
+	/* pages/uni-cropper/index.wxss */
+
+	.uni-content-info {
+		/* position: fixed;
+		top: 0;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		display: block;
+		align-items: center;
+		flex-direction: column; */
+	}
+
+	.cropper-config {
+		padding: 20rpx 40rpx;
+	}
+
+	.cropper-content {
+		min-height: 750rpx;
+		width: 100%;
+	}
+
+	.uni-corpper {
+		position: relative;
+		overflow: hidden;
+		-webkit-user-select: none;
+		-moz-user-select: none;
+		-ms-user-select: none;
+		user-select: none;
+		-webkit-tap-highlight-color: transparent;
+		-webkit-touch-callout: none;
+		box-sizing: border-box;
+	}
+
+	.uni-corpper-content {
+		position: relative;
+	}
+
+	.uni-corpper-content image {
+		display: block;
+		width: 100%;
+		min-width: 0 !important;
+		max-width: none !important;
+		height: 100%;
+		min-height: 0 !important;
+		max-height: none !important;
+		image-orientation: 0deg !important;
+		margin: 0 auto;
+	}
+	/* 移动图片效果 */
+
+	.uni-cropper-drag-box {
+		position: absolute;
+		top: 0;
+		right: 0;
+		bottom: 0;
+		left: 0;
+		cursor: move;
+		background: rgba(0, 0, 0, 0.6);
+		z-index: 1;
+	}
+	/* 内部的信息 */
+
+	.uni-corpper-crop-box {
+		position: absolute;
+		background: rgba(255, 255, 255, 0.3);
+		z-index: 2;
+	}
+
+	.uni-corpper-crop-box .uni-cropper-view-box {
+		position: relative;
+		display: block;
+		width: 100%;
+		height: 100%;
+		overflow: visible;
+		outline: 1rpx solid #69f;
+		outline-color: rgba(102, 153, 255, .75)
+	}
+	/* 横向虚线 */
+
+	.uni-cropper-dashed-h {
+		position: absolute;
+		top: 33.33333333%;
+		left: 0;
+		width: 100%;
+		height: 33.33333333%;
+		border-top: 1rpx dashed rgba(255, 255, 255, 0.5);
+		border-bottom: 1rpx dashed rgba(255, 255, 255, 0.5);
+	}
+	/* 纵向虚线 */
+
+	.uni-cropper-dashed-v {
+		position: absolute;
+		left: 33.33333333%;
+		top: 0;
+		width: 33.33333333%;
+		height: 100%;
+		border-left: 1rpx dashed rgba(255, 255, 255, 0.5);
+		border-right: 1rpx dashed rgba(255, 255, 255, 0.5);
+	}
+	/* 四个方向的线  为了之后的拖动事件*/
+
+	.uni-cropper-line-t {
+		position: absolute;
+		display: block;
+		width: 100%;
+		background-color: #69f;
+		top: 0;
+		left: 0;
+		height: 1rpx;
+		opacity: 0.1;
+		cursor: n-resize;
+	}
+
+	.uni-cropper-line-t::before {
+		content: '';
+		position: absolute;
+		top: 50%;
+		right: 0rpx;
+		width: 100%;
+		-webkit-transform: translate3d(0, -50%, 0);
+		transform: translate3d(0, -50%, 0);
+		bottom: 0;
+		height: 41rpx;
+		background: transparent;
+		z-index: 11;
+	}
+
+	.uni-cropper-line-r {
+		position: absolute;
+		display: block;
+		background-color: #69f;
+		top: 0;
+		right: 0rpx;
+		width: 1rpx;
+		opacity: 0.1;
+		height: 100%;
+		cursor: e-resize;
+	}
+
+	.uni-cropper-line-r::before {
+		content: '';
+		position: absolute;
+		top: 0;
+		left: 50%;
+		width: 41rpx;
+		-webkit-transform: translate3d(-50%, 0, 0);
+		transform: translate3d(-50%, 0, 0);
+		bottom: 0;
+		height: 100%;
+		background: transparent;
+		z-index: 11;
+	}
+
+	.uni-cropper-line-b {
+		position: absolute;
+		display: block;
+		width: 100%;
+		background-color: #69f;
+		bottom: 0;
+		left: 0;
+		height: 1rpx;
+		opacity: 0.1;
+		cursor: s-resize;
+	}
+
+	.uni-cropper-line-b::before {
+		content: '';
+		position: absolute;
+		top: 50%;
+		right: 0rpx;
+		width: 100%;
+		-webkit-transform: translate3d(0, -50%, 0);
+		transform: translate3d(0, -50%, 0);
+		bottom: 0;
+		height: 41rpx;
+		background: transparent;
+		z-index: 11;
+	}
+
+	.uni-cropper-line-l {
+		position: absolute;
+		display: block;
+		background-color: #69f;
+		top: 0;
+		left: 0;
+		width: 1rpx;
+		opacity: 0.1;
+		height: 100%;
+		cursor: w-resize;
+	}
+
+	.uni-cropper-line-l::before {
+		content: '';
+		position: absolute;
+		top: 0;
+		left: 50%;
+		width: 41rpx;
+		-webkit-transform: translate3d(-50%, 0, 0);
+		transform: translate3d(-50%, 0, 0);
+		bottom: 0;
+		height: 100%;
+		background: transparent;
+		z-index: 11;
+	}
+
+	.uni-cropper-point {
+		width: 5rpx;
+		height: 5rpx;
+		background-color: #69f;
+		opacity: .75;
+		position: absolute;
+		z-index: 3;
+	}
+
+	.point-t {
+		top: -3rpx;
+		left: 50%;
+		margin-left: -3rpx;
+		cursor: n-resize;
+	}
+
+	.point-tr {
+		top: -3rpx;
+		left: 100%;
+		margin-left: -3rpx;
+		cursor: n-resize;
+	}
+
+	.point-r {
+		top: 50%;
+		left: 100%;
+		margin-left: -3rpx;
+		margin-top: -3rpx;
+		cursor: n-resize;
+	}
+
+	.point-rb {
+		left: 100%;
+		top: 100%;
+		-webkit-transform: translate3d(-50%, -50%, 0);
+		transform: translate3d(-50%, -50%, 0);
+		cursor: n-resize;
+		width: 36rpx;
+		height: 36rpx;
+		background-color: #69f;
+		position: absolute;
+		z-index: 1112;
+		opacity: 1;
+	}
+
+	.point-b {
+		left: 50%;
+		top: 100%;
+		margin-left: -3rpx;
+		margin-top: -3rpx;
+		cursor: n-resize;
+	}
+
+	.point-bl {
+		left: 0%;
+		top: 100%;
+		margin-left: -3rpx;
+		margin-top: -3rpx;
+		cursor: n-resize;
+	}
+
+	.point-l {
+		left: 0%;
+		top: 50%;
+		margin-left: -3rpx;
+		margin-top: -3rpx;
+		cursor: n-resize;
+	}
+
+	.point-lt {
+		left: 0%;
+		top: 0%;
+		margin-left: -3rpx;
+		margin-top: -3rpx;
+		cursor: n-resize;
+	}
+	/* 裁剪框预览内容 */
+
+	.uni-cropper-viewer {
+		position: relative;
+		width: 100%;
+		height: 100%;
+		overflow: hidden;
+	}
+
+	.uni-cropper-viewer image {
+		position: absolute;
+		z-index: 2;
+	}
+</style>

+ 112 - 0
pages/mine/help/index.vue

@@ -0,0 +1,112 @@
+<template>
+  <view class="help-container">
+    <view v-for="(item, findex) in list" :key="findex" :title="item.title" class="list-title">
+      <view class="text-title">
+        <view :class="item.icon"></view>{{ item.title }}
+      </view>
+      <view class="childList">
+        <view v-for="(child, zindex) in item.childList" :key="zindex" class="question" hover-class="hover"
+          @click="handleText(child)">
+          <view class="text-item">{{ child.title }}</view>
+          <view class="line" v-if="zindex !== item.childList.length - 1"></view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+  export default {
+    data() {
+      return {
+        list: [{
+            icon: 'iconfont icon-github',
+            title: '若依问题',
+            childList: [{
+              title: '若依开源吗?',
+              content: '开源'
+            }, {
+              title: '若依可以商用吗?',
+              content: '可以'
+            }, {
+              title: '若依官网地址多少?',
+              content: 'http://ruoyi.vip'
+            }, {
+              title: '若依文档地址多少?',
+              content: 'http://doc.ruoyi.vip'
+            }]
+          },
+          {
+            icon: 'iconfont icon-help',
+            title: '其他问题',
+            childList: [{
+              title: '如何退出登录?',
+              content: '请点击[我的] - [应用设置] - [退出登录]即可退出登录',
+            }, {
+              title: '如何修改用户头像?',
+              content: '请点击[我的] - [选择头像] - [点击提交]即可更换用户头像',
+            }, {
+              title: '如何修改登录密码?',
+              content: '请点击[我的] - [应用设置] - [修改密码]即可修改登录密码',
+            }]
+          }
+        ]
+      }
+    },
+    methods: {
+      handleText(item) {
+        this.$tab.navigateTo(`/pages/common/textview/index?title=${item.title}&content=${item.content}`)
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  page {
+    background-color: #f8f8f8;
+  }
+
+  .help-container {
+    margin-bottom: 100rpx;
+    padding: 30rpx;
+  }
+
+  .list-title {
+    margin-bottom: 30rpx;
+  }
+
+  .childList {
+    background: #ffffff;
+    box-shadow: 0px 0px 10rpx rgba(193, 193, 193, 0.2);
+    border-radius: 16rpx;
+    margin-top: 10rpx;
+  }
+
+  .line {
+    width: 100%;
+    height: 1rpx;
+    background-color: #F5F5F5;
+  }
+
+  .text-title {
+    color: #303133;
+    font-size: 32rpx;
+    font-weight: bold;
+    margin-left: 10rpx;
+
+    .iconfont {
+      font-size: 16px;
+      margin-right: 10rpx;
+    }
+  }
+
+  .text-item {
+    font-size: 28rpx;
+    padding: 24rpx;
+  }
+
+  .question {
+    color: #606266;
+    font-size: 28rpx;
+  }
+</style>

+ 169 - 0
pages/mine/index.vue

@@ -0,0 +1,169 @@
+<template>
+    <view class="mine-container" :style="'background: url('+this.imgUrl+'/index/mingbg.png) no-repeat 100% 100%;height:'+windowHeight+'px;background-position: 0 0;background-size: 100% 100%;'">
+    <!--顶部个人信息栏-->
+    <view class="header-section">
+      <view class="flex padding justify-between">
+        <view class="flex align-center">
+          <view v-if="!avatar" class="cu-avatar xl round bg-white">
+            <view class="iconfont icon-people text-gray icon"></view>
+          </view>
+          <image v-if="avatar" @click="handleToAvatar" :src="avatar" class="cu-avatar xl round" mode="widthFix">
+          </image>
+          <view v-if="!name" @click="handleToLogin" class="login-tip">
+            点击登录
+          </view>
+          <view v-if="name" @click="handleToInfo" class="user-info">
+            <view class="u_title" style="color: #8E8E8E">
+              你好!
+            </view>
+            <view class="u_title">
+              {{ name }}
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+
+
+      <view class="menu-list">
+        <view class="list-cell list-cell-arrow" @click="handleToEditInfo">
+          <view class="menu-item-box">
+            <image :src="this.imgUrl+'/index/settingicon.png'" style="width: 18px;height: 18px;margin-right:5px"></image>
+            <view class="menu-text">账户设置</view>
+          </view>
+        </view>
+        <view class="list-cell list-cell-arrow" @click="handleAbout">
+          <view class="menu-item-box">
+            <image :src="this.imgUrl+'/index/chargeicon.png'" style="width: 18px;height: 18px;margin-right:5px"></image>
+            <view class="menu-text">充电记录</view>
+          </view>
+        </view>
+        <view class="list-cell list-cell-arrow" @click="handleToSetting">
+          <view class="menu-item-box">
+            <image :src="this.imgUrl+'/index/planicon.png'" style="width: 18px;height: 18px;margin-right:5px"></image>
+            <view class="menu-text">预约记录</view>
+          </view>
+        </view>
+      </view>
+
+    </view>
+
+</template>
+
+<script>
+  import storage from '@/utils/storage'
+
+  export default {
+    data() {
+      return {
+        name: this.$store.state.user.name,
+        version: getApp().globalData.config.appInfo.version,
+        imgUrl:getApp().globalData.config.imgUrl
+      }
+    },
+    computed: {
+      avatar() {
+        return getApp().globalData.config.imgUrl+"/index/user.png"
+      },
+      windowHeight() {
+        return uni.getSystemInfoSync().windowHeight - 50
+      }
+    },
+    methods: {
+      handleToEditInfo() {
+        this.$tab.navigateTo('/pages/mine/pwd/index')
+      },
+      handleToSetting() {
+        this.$tab.navigateTo('/pages/weitiandi/device/planrecord')
+      },
+      handleToLogin() {
+        this.$tab.reLaunch('/pages/login')
+      },
+      handleLogout() {
+        this.$modal.confirm('确定注销并退出系统吗?').then(() => {
+          this.$store.dispatch('LogOut').then(() => {
+            this.$tab.reLaunch('/pages/index')
+          })
+        })
+      },
+      handleAbout() {
+        this.$tab.navigateTo('/pages/weitiandi/device/chargerecord')
+      },
+    }
+  }
+</script>
+
+<style lang="scss">
+  page {
+    background-color: #f5f6f7;
+  }
+
+  .mine-container {
+    width: 100%;
+    height: 100%;
+
+
+    .header-section {
+      padding: 15px 15px 45px 15px;
+      color: white;
+
+      .login-tip {
+        font-size: 18px;
+        margin-left: 10px;
+      }
+
+      .cu-avatar {
+        border: 2px solid #eaeaea;
+
+        .icon {
+          font-size: 40px;
+        }
+      }
+
+      .user-info {
+        margin-left: 15px;
+
+        .u_title {
+          font-size: 18px;
+          line-height: 30px;
+          color: #000000;
+        }
+      }
+    }
+
+    .content-section {
+      position: relative;
+      top: -50px;
+
+      .mine-actions {
+        margin: 15px 15px;
+        padding: 20px 0px;
+        border-radius: 8px;
+
+        .action-item {
+          .icon {
+            font-size: 28px;
+          }
+
+          .text {
+            display: block;
+            font-size: 13px;
+            margin: 8px 0px;
+          }
+        }
+      }
+    }
+  }
+  .menu-list{
+
+    box-shadow: 0px 2px 24px 0px rgba(0,0,0,0.06);
+
+    border-radius: 20px;
+  }
+  .list-cell{
+    padding:17px 15px;
+  }
+  .menu-text{
+    color:#555555
+  }
+</style>

+ 127 - 0
pages/mine/info/edit.vue

@@ -0,0 +1,127 @@
+<template>
+  <view class="container">
+    <view class="example">
+      <uni-forms ref="form" :model="user" labelWidth="80px">
+        <uni-forms-item label="用户昵称" name="nickName">
+          <uni-easyinput v-model="user.nickName" placeholder="请输入昵称" />
+        </uni-forms-item>
+        <uni-forms-item label="手机号码" name="phonenumber">
+          <uni-easyinput v-model="user.phonenumber" placeholder="请输入手机号码" />
+        </uni-forms-item>
+        <uni-forms-item label="邮箱" name="email">
+          <uni-easyinput v-model="user.email" placeholder="请输入邮箱" />
+        </uni-forms-item>
+        <uni-forms-item label="性别" name="sex" required>
+          <uni-data-checkbox v-model="user.sex" :localdata="sexs" />
+        </uni-forms-item>
+      </uni-forms>
+      <button type="primary" @click="submit">提交</button>
+    </view>
+  </view>
+</template>
+
+<script>
+  import { getUserProfile } from "@/api/system/user"
+  import { updateUserProfile } from "@/api/system/user"
+
+  export default {
+    data() {
+      return {
+        user: {
+          nickName: "",
+          phonenumber: "",
+          email: "",
+          sex: ""
+        },
+        sexs: [{
+          text: '男',
+          value: "0"
+        }, {
+          text: '女',
+          value: "1"
+        }],
+        rules: {
+          nickName: {
+            rules: [{
+              required: true,
+              errorMessage: '用户昵称不能为空'
+            }]
+          },
+          phonenumber: {
+            rules: [{
+              required: true,
+              errorMessage: '手机号码不能为空'
+            }, {
+              pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
+              errorMessage: '请输入正确的手机号码'
+            }]
+          },
+          email: {
+            rules: [{
+              required: true,
+              errorMessage: '邮箱地址不能为空'
+            }, {
+              format: 'email',
+              errorMessage: '请输入正确的邮箱地址'
+            }]
+          }
+        }
+      }
+    },
+    onLoad() {
+      this.getUser()
+    },
+    onReady() {
+      this.$refs.form.setRules(this.rules)
+    },
+    methods: {
+      getUser() {
+        getUserProfile().then(response => {
+          this.user = response.data
+        })
+      },
+      submit(ref) {
+        this.$refs.form.validate().then(res => {
+          updateUserProfile(this.user).then(response => {
+            this.$modal.msgSuccess("修改成功")
+          })
+        })
+      }
+    }
+  }
+</script>
+
+<style lang="scss">
+  page {
+    background-color: #ffffff;
+  }
+
+  .example {
+    padding: 15px;
+    background-color: #fff;
+  }
+
+  .segmented-control {
+    margin-bottom: 15px;
+  }
+
+  .button-group {
+    margin-top: 15px;
+    display: flex;
+    justify-content: space-around;
+  }
+
+  .form-item {
+    display: flex;
+    align-items: center;
+    flex: 1;
+  }
+
+  .button {
+    display: flex;
+    align-items: center;
+    height: 35px;
+    line-height: 35px;
+    margin-left: 10px;
+  }
+</style>

+ 44 - 0
pages/mine/info/index.vue

@@ -0,0 +1,44 @@
+<template>
+  <view class="container">
+    <uni-list>
+      <uni-list-item showExtraIcon="true" :extraIcon="{type: 'person-filled'}" title="昵称" :rightText="user.nickName" />
+      <uni-list-item showExtraIcon="true" :extraIcon="{type: 'phone-filled'}" title="手机号码" :rightText="user.phonenumber" />
+      <uni-list-item showExtraIcon="true" :extraIcon="{type: 'email-filled'}" title="邮箱" :rightText="user.email" />
+      <uni-list-item showExtraIcon="true" :extraIcon="{type: 'auth-filled'}" title="岗位" :rightText="postGroup" />
+      <uni-list-item showExtraIcon="true" :extraIcon="{type: 'staff-filled'}" title="角色" :rightText="roleGroup" />
+      <uni-list-item showExtraIcon="true" :extraIcon="{type: 'calendar-filled'}" title="创建日期" :rightText="user.createTime" />
+    </uni-list>
+  </view>
+</template>
+
+<script>
+  import { getUserProfile } from "@/api/system/user"
+
+  export default {
+    data() {
+      return {
+        user: {},
+        roleGroup: "",
+        postGroup: ""
+      }
+    },
+    onLoad() {
+      this.getUser()
+    },
+    methods: {
+      getUser() {
+        getUserProfile().then(response => {
+          this.user = response.data
+          this.roleGroup = response.roleGroup
+          this.postGroup = response.postGroup
+        })
+      }
+    }
+  }
+</script>
+
+<style lang="scss">
+  page {
+    background-color: #ffffff;
+  }
+</style>

+ 96 - 0
pages/mine/pwd/index.vue

@@ -0,0 +1,96 @@
+<template>
+  <view class="pwd-retrieve-container">
+    <uni-forms ref="form" :value="user" labelWidth="80px">
+      <uni-forms-item name="oldPassword" label="旧密码">
+        <uni-easyinput type="password" v-model="user.oldPassword" placeholder="请输入旧密码" />
+      </uni-forms-item>
+      <uni-forms-item name="newPassword" label="新密码">
+        <uni-easyinput type="password" v-model="user.newPassword" placeholder="请输入新密码" />
+      </uni-forms-item>
+      <uni-forms-item name="confirmPassword" label="确认密码">
+        <uni-easyinput type="password" v-model="user.confirmPassword" placeholder="请确认新密码" />
+      </uni-forms-item>
+      <button type="primary" style="background: #0E9F9B;color: white" @click="submit">提交</button>
+
+      <view style="bottom: 10px;position: absolute;left: 10px;right:10px">
+        <button type="warn"  @click="" style="background: #0E9F9B;color: white" @click="handleLogout">退出登录</button>
+      </view>
+    </uni-forms>
+  </view>
+</template>
+
+<script>
+  import { updateUserPwd } from "@/api/system/user"
+
+  export default {
+    data() {
+      return {
+        user: {
+          oldPassword: undefined,
+          newPassword: undefined,
+          confirmPassword: undefined
+        },
+        rules: {
+          oldPassword: {
+            rules: [{
+              required: true,
+              errorMessage: '旧密码不能为空'
+            }]
+          },
+          newPassword: {
+            rules: [{
+                required: true,
+                errorMessage: '新密码不能为空',
+              },
+              {
+                minLength: 6,
+                maxLength: 20,
+                errorMessage: '长度在 6 到 20 个字符'
+              }
+            ]
+          },
+          confirmPassword: {
+            rules: [{
+                required: true,
+                errorMessage: '确认密码不能为空'
+              }, {
+                validateFunction: (rule, value, data) => data.newPassword === value,
+                errorMessage: '两次输入的密码不一致'
+              }
+            ]
+          }
+        }
+      }
+    },
+    onReady() {
+      this.$refs.form.setRules(this.rules)
+    },
+    methods: {
+      submit() {
+        this.$refs.form.validate().then(res => {
+          updateUserPwd(this.user.oldPassword, this.user.newPassword).then(response => {
+            this.$modal.msgSuccess("修改成功")
+          })
+        })
+      },
+      handleLogout() {
+        this.$modal.confirm('确定注销并退出系统吗?').then(() => {
+          this.$store.dispatch('LogOut').then(() => {
+            this.$tab.reLaunch('/pages/index')
+          })
+        })
+      }
+    }
+  }
+</script>
+
+<style lang="scss">
+  page {
+    background-color: #ffffff;
+  }
+
+  .pwd-retrieve-container {
+    padding-top: 36rpx;
+    padding: 15px;
+  }
+</style>

+ 78 - 0
pages/mine/setting/index.vue

@@ -0,0 +1,78 @@
+<template>
+  <view class="setting-container" :style="{height: `${windowHeight}px`}">
+    <view class="menu-list">
+      <view class="list-cell list-cell-arrow" @click="handleToPwd">
+        <view class="menu-item-box">
+          <view class="iconfont icon-password menu-icon"></view>
+          <view>修改密码</view>
+        </view>
+      </view>
+      <view class="list-cell list-cell-arrow" @click="handleToUpgrade">
+        <view class="menu-item-box">
+          <view class="iconfont icon-refresh menu-icon"></view>
+          <view>检查更新</view>
+        </view>
+      </view>
+      <view class="list-cell list-cell-arrow" @click="handleCleanTmp">
+        <view class="menu-item-box">
+          <view class="iconfont icon-clean menu-icon"></view>
+          <view>清理缓存</view>
+        </view>
+      </view>
+    </view>
+    <view class="cu-list menu">
+      <view class="cu-item item-box">
+        <view class="content text-center" @click="handleLogout">
+          <text class="text-black">退出登录</text>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+  export default {
+    data() {
+      return {
+        windowHeight: uni.getSystemInfoSync().windowHeight
+      }
+    },
+    methods: {
+      handleToPwd() {
+        this.$tab.navigateTo('/pages/mine/pwd/index')
+      },
+      handleToUpgrade() {
+        this.$modal.showToast('模块建设中~')
+      },
+      handleCleanTmp() {
+        this.$modal.showToast('模块建设中~')
+      },
+      handleLogout() {
+        this.$modal.confirm('确定注销并退出系统吗?').then(() => {
+          this.$store.dispatch('LogOut').then(() => {
+            this.$tab.reLaunch('/pages/index')
+          })
+        })
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .page {
+    background-color: #f8f8f8;
+  }
+
+  .item-box {
+    background-color: #FFFFFF;
+    margin: 30rpx;
+    display: flex;
+    flex-direction: row;
+    justify-content: center;
+    align-items: center;
+    padding: 10rpx;
+    border-radius: 8rpx;
+    color: #303133;
+    font-size: 32rpx;
+  }
+</style>

+ 117 - 0
pages/weitiandi/device/chargerecord.vue

@@ -0,0 +1,117 @@
+<template>
+	<view class="container">
+		<view class="list-item">
+
+			<view class="item" v-for="item in chargeList">
+				<view class="item-header">
+					<view class="item-title">
+						{{item.deviceId}}
+					</view>
+
+					<view class="item-status">
+						<uni-tag type="success" style="background: #0E9F9B" text="充电完成" v-if="item.status == 1"></uni-tag>
+						<uni-tag type="success"  text="正在充电" v-if="item.status == 0"></uni-tag>
+					</view>
+				</view>
+				<view class="item-body">
+					<view class="item-time">充电时间:{{item.createTime}}</view>
+					<view class="item-time" v-if="item.status == 1">结束时间:{{item.endTime}}</view>
+				</view>
+			</view>
+      <uni-load-more @clickLoadMore="getMore" :content-text="contentText" :status="startText"></uni-load-more>
+
+
+    </view>
+	</view>
+</template>
+
+<script>
+  import { listCharge } from '@/api/device/chargerecord.js'
+
+  export default {
+    data() {
+      return {
+        startText:"more",
+        search:{
+          pageNum:1,
+          pageSize:6,
+          reasonable:true,
+        },
+        contentText: {
+          contentdown: '点击查看更多',
+          contentrefresh: '加载中',
+          contentnomore: '没有更多'
+        },
+        chargeList: [],
+      }
+    },
+    methods: {
+      getMore(){
+        this.search.pageNum++;
+        this.chargeRecord();
+      },
+       chargeRecord() {
+          this.startText = "loading"
+         listCharge(this.search).then(res=>{
+           if(res.data.length == 0){
+             this.$modal.showToast("没有更多数据了");
+             this.startText = "no-more"
+           }else{
+             this.startText = "more"
+             this.chargeList =  this.chargeList.concat(res.data);
+
+           }
+
+         })
+      }
+    },
+    created() {
+      this.chargeRecord();
+    }
+  }
+</script>
+
+<style>
+	.container {
+		background: rgb(249, 252, 255);
+		inset: 0;
+		position: absolute;
+	}
+
+	.list-item {
+		margin: 0vw;
+	}
+
+	.item {
+		box-shadow: 0px 5px 27px 0px rgba(195, 195, 195, 0.4);
+		border-radius: 4px;
+		background: #FFFFFF;
+		margin: 4vw;
+		padding: 4vw;
+	}
+
+	.item-title {
+		display: inline-block;
+		float: left;
+	}
+
+	.item-status {
+		display: inline-block;
+		float: right;
+	}
+
+	.item-header {
+		border-bottom: 1px solid lightgray;
+		height: 5vh;
+	}
+
+	.item-body {
+		padding-top: 4vw;
+	}
+
+	.item-time {
+		color: #545454;
+		font-size: 12px;
+		margin-bottom: 4px
+	}
+</style>

+ 443 - 0
pages/weitiandi/device/index.vue

@@ -0,0 +1,443 @@
+<template>
+  <view class="container">
+    <view class="header">
+      <view class="setting" @click="toSet">设备控制</view>
+      <view class="header-status chong-active" v-if="portDetail.port_first_status == 1">
+        充电枪未连接
+      </view>
+
+      <view class="header-status chong-active" v-if="portDetail.port_first_status == 0">
+        正在读取状态
+      </view>
+
+      <view class="header-status chong-active" v-if="portDetail.port_first_status == 5">
+        充电枪已连接
+      </view>
+
+      <view class="header-status chong-active" v-if="portDetail.port_first_status == 2">
+        充电枪已连接
+      </view>
+      <view class="header-img" v-if="portDetail.port_first_status == 2">
+        <image :src="imgUrl+'/chargedetail/chonging.png'">
+
+        </image>
+      </view>
+
+      <view class="header-img" v-if="portDetail.port_first_status != 2">
+        <image :src="imgUrl+'/chargedetail/chongoff.png'">
+
+        </image>
+      </view>
+      <view class="header-status-desc" v-if="portDetail.port_first_status == 2">
+        充电中
+      </view>
+
+      <view class="header-status-desc" style="color: #B2B2B2" v-if="portDetail.port_first_status != 2">
+        未充电
+      </view>
+    </view>
+
+    <view class="info-body">
+      <view class="info-content">
+
+        <view class="info-content-value">{{ portDetail.voltage }}V</view>
+        <view class="info-content-text">
+          充电电压
+        </view>
+      </view>
+
+      <view class="info-content">
+
+        <view class="info-content-value" v-if="portDetail.voltage == 0">0A</view>
+        <view class="info-content-value" v-else>{{portDetail.POWER/portDetail.voltage}}A</view>
+
+        <view class="info-content-text">
+          充电电流
+        </view>
+      </view>
+
+      <view class="info-content">
+
+        <view class="info-content-value">{{portDetail.dev_temper}}℃</view>
+        <view class="info-content-text">
+          设备温度
+        </view>
+      </view>
+
+
+      <view class="info-content">
+
+        <view class="info-content-value">{{portDetail.wire_temper}}℃</view>
+        <view class="info-content-text">
+          线路温度
+        </view>
+      </view>
+
+
+    </view>
+
+    <view class="info-summary">
+      <view class="summary charge-time">
+          <view class="charge-num">{{ portDetail.time }}分钟</view>
+          <view class="charge-title">已冲时间</view>
+
+      </view>
+
+
+      <view class="summary charge-gonglv">
+        <view class="charge-num">{{ portDetail.power }}W</view>
+        <view class="charge-title">充电功率</view>
+
+      </view>
+
+
+      <view class="summary charge-dianliang">
+        <view class="charge-num">{{ portDetail.elec }} 度</view>
+        <view class="charge-title">已冲电量</view>
+
+      </view>
+
+    </view>
+
+
+    <view class="info-bottom">
+
+      <view class="info-bottom-btn" >
+          <view class="btn-area left" @click="getInfo">
+            <image class="btn-image" :src="imgUrl+'/chargedetail/getstatus.png'">
+
+            </image>
+
+          </view>
+        <view class="btn-area right" @click="trigger()">
+          <image class="btn-image" :src="imgUrl+'/chargedetail/stop.png'" v-if="portDetail.port_first_status == 2">
+
+          </image>
+
+          <image class="btn-image" :src="imgUrl+'/chargedetail/start.png'" v-if="portDetail.port_first_status != 2">
+
+          </image>
+
+        </view>
+      </view>
+      <view class="info-plan" @click="toPlan">
+        预约充电
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+import {getDeviceInfo,getPortDetail,startCharge,stopCharge,sendPortDetailCmd,checkStatusChange} from "@/api/device/device";
+import websocket from '@/utils/websocket'
+export default {
+ data() {
+   return {
+     deviceInfo:{},
+     visitTime:"",
+     timer:null,
+     portDetail:{port_first_status:0,POWER:0,voltage:0},
+     statusTimer:"",
+     connected:false,
+     scriptTask:null,
+   }
+ },
+ computed: {
+   imgUrl() {
+     return getApp().globalData.config.imgUrl;
+   }
+ },
+ onLoad(opt) {
+   this.deviceInfo.deviceId = opt.id;
+   this.deviceInfo.ccid = opt.ccid;
+
+ },
+  onShow(){
+    this.getInfo();
+  },
+  onUnload (){
+   this.closeSocket();
+  },
+  methods: {
+    initSocket(key){
+      let self = this;
+      let socketUrl =  getApp().globalData.config.socketUrl
+      this.scriptTask = websocket({
+        url:"/"+key+"/",
+      });
+      let scriptTask = this.scriptTask;
+      scriptTask.onOpen(function (res) {
+        console.log('WebSocket连接已打开!');
+        self.connected = true;
+      });
+      scriptTask.onError(function (res) {
+        self.connected = false;
+        console.log(res);
+      });
+      scriptTask.onMessage(function (res) {
+        let data = JSON.parse(res.data);
+        let type = data.type;
+        let real_data = data.real_data;
+        if(type == 103){
+            self.portDetail = real_data;
+            self.$modal.closeLoading();
+        }
+        if(type == 116){
+          self.$modal.closeLoading();
+          self.getInfo();
+        }
+        if(type == 113){
+          self.$modal.closeLoading();
+          self.getInfo();
+        }
+        if(type == 96){
+          self.mainBoardInfo = real_data;
+          self.formatMainboardData();
+          self.$modal.closeLoading();
+        }
+        console.log('收到服务器内容:' + JSON.stringify(data));
+      });
+      scriptTask.onClose(function (res) {
+        console.log('WebSocket 已关闭!');
+      });
+    },
+    toSet(){
+      this.closeSocket();
+      uni.navigateTo({
+        url: '/pages/weitiandi/device/setting?id='+this.deviceInfo.deviceId+"&ccid="+this.deviceInfo.ccid
+      });
+    },
+    toPlan(){
+      uni.navigateTo({
+        url: '/pages/weitiandi/device/plan?id='+this.deviceInfo.deviceId+"&ccid="+this.deviceInfo.ccid
+      });
+    },
+    trigger(){
+      let port_first_status = this.portDetail.port_first_status;
+      if(port_first_status == 2){//需要停止充电
+        this.$modal.confirm("需要停止充电么?").then(res=>{
+              this.stopCharge();
+        })
+      }else{
+        if(port_first_status == 1){
+          this.$modal.msg("请先将充电枪插入后再点击充电");
+        }
+        if(port_first_status == 1){
+          this.$modal.confirm("确认开始充电么?").then(res=>{
+            this.startCharge();
+          })
+        }
+
+      }
+    },
+   getInfo() {
+    this.$modal.loading("正在获取状态,请稍等...");
+     sendPortDetailCmd(this.deviceInfo).then(res => {
+        this.visitTime = res.msg;
+        if(!this.visitTime){
+          this.$modal.msg("请重新进入页面");
+          return;
+        }
+        if(!this.scriptTask){
+          this.initSocket(this.deviceInfo.deviceId);
+        }
+       // this.getPortInfo()
+    })
+  },
+    stopCharge(){
+      let self = this;
+      stopCharge(this.deviceInfo).then(()=>{
+        self.$modal.loading("停止成功");
+        setTimeout((()=>{
+          self.getInfo();
+        }),1000);
+      })
+    },
+    startCharge(){
+      let self = this;
+      startCharge(this.deviceInfo).then(res=>{
+        self.$modal.loading("启动成功");
+        setTimeout((()=>{
+          self.getInfo();
+        }),1000);
+      })
+    },
+    getPortInfo(){
+      this.startPortDetailTimer();
+    },
+    startPortDetailTimer(){
+     let self = this;
+      this.timer = setTimeout(function (){
+        getPortDetail(self.deviceInfo,self.visitTime).then(res=>{
+         let data = res.data;
+         if(data != null){
+           self.portDetail = data;
+           self.checkStatusCheck();
+           self.$modal.closeLoading();
+         }else{
+           self.startPortDetailTimer();
+         }
+        });
+      },1000);
+    },
+    checkStatusCheck(){
+      this.statusChangeTimer();
+    },
+    checkOnPage(){
+      var pages = getCurrentPages() // 获取栈实例
+      let currentRoute = pages[pages.length - 1].route; // 获取当前页面路由
+      if("pages/weitiandi/device/index" != currentRoute){
+        return false;
+      }
+      return  true;
+    },
+    closeSocket(){
+      this.scriptTask.close();
+      this.scriptTask = null;
+    },
+    statusChangeTimer(){
+      let self = this;
+      this.statusTimer = setTimeout(function(){
+       if(!this.checkOnPage()){
+         return;
+       }
+        checkStatusChange(self.deviceInfo,self.visitTime).then(res=>{
+          let data = res.data;
+          if(data != null){
+            self.getInfo();
+          }else{
+            self.statusChangeTimer();
+          }
+        });
+      },3000);
+    }
+  }
+}
+</script>
+
+<style>
+.container {
+  background: rgb(249, 252, 255);
+  inset: 0;
+  position: absolute;
+}
+
+.header {
+  position: relative;
+}
+
+.header-status-desc {
+  position: absolute;
+  text-align: center;
+  width: 100%;
+  bottom:1vh;
+  font-size: 5vw;
+  font-weight: bold;
+  color: #0E9F9B;
+}
+
+.header-status {
+  font-weight: bold;
+  padding-left: 5vw;
+}
+
+.chong-active {
+  color: #0E9F9B
+}
+
+.header-img {
+  width: 100%;
+  padding: 5% 10%;
+  text-align: center;
+
+}
+
+.header-img image {
+  width: 100%;
+  height: 23vh;
+}
+.info-body{
+  background: #0E9F9B;
+  height: 20vh;
+  margin: 0 10%;
+  border-radius: 1vw;
+  margin-top:2vh;
+  color: #F8FCFF;
+  line-height: 3vh;
+}
+.info-content{
+  display: inline-block;
+  width: 48%;
+  text-align: center;
+  margin: 1%;
+  margin-top:2.5vh;
+}
+.info-content-value{
+  font-weight: bold;
+}
+.info-content-text{
+
+}
+.info-summary{
+  display: flex;
+  flex-direction: row;
+  text-align: center;
+  margin:3vh 0;
+}
+.summary{
+  width: 33%;
+  line-height: 2.5vh;
+}
+.charge-num{
+  color: #0E9F9B;
+  font-weight: bold;
+  font-size: 4.5vw;
+}
+.charge-title{
+  color: #B2B2B2;
+  font-weight: 400;
+}
+.btn-image{
+  width: 90%;
+  height: 100%;
+}
+.info-bottom-btn{
+  display: flex;
+  flex-direction: row;
+  text-align: center;
+  position: relative;
+  margin-top: 10vh
+;
+}
+.btn-area{
+  width: 50%;
+  height: 50px;
+}
+.left{
+  position: relative;
+  left: 10px;
+  text-align: right;
+}
+.right{
+  text-align: left;
+  position: relative;
+  right: 10px;
+}
+.info-plan{
+  text-align: center;
+  color: #0E9F9B;
+  margin-top:1vh;
+  font-weight: 400;
+}
+.setting{
+  position: fixed;
+  right: -1px;
+  top: 10vh;
+  z-index: 999;
+  background: rgb(227,243,245);
+  color: #0E9F9B;
+  font-size: 10px;
+  border-radius: 5px;
+  padding: 3px;
+}
+</style>

+ 305 - 0
pages/weitiandi/device/plan.vue

@@ -0,0 +1,305 @@
+<template>
+  <view class="container">
+
+      <view class="header-image">
+
+        <image style="width: 100%" :src="imgUrl+'/precharge/precharge.png'"/>
+      </view>
+
+      <view class="charge-type">
+
+       <view class="charge-type-container">
+         <view @click="changeType(0)" class="type-left" :class="chargeType == 0?'type-choose':''">单次</view>
+         <view @click="changeType(1)" class="type-right" :class="chargeType == 1?'type-choose':''">自定义</view>
+       </view>
+      </view>
+
+    <view class="time-area"  v-if="chargeType == 0">
+      <!--      <view class="one-date">{{ oneTime.date }}</view>-->
+
+      <view class="one-time">{{ oneTime.time }}</view>
+
+      <view class="one-setting"><uni-datetime-picker @change="changeDate" v-model="oneTime.time" :hide-second	="true">选择时间</uni-datetime-picker></view>
+    </view>
+
+
+    <view class="time-area" style="padding-left:0px;padding-right: 0px"  v-if="chargeType == 1">
+           <view style=";text-align: center;">
+             <view class="one-date" style="display:inline-block;font-size: 12px;padding:0 2px" v-for="item in array">{{ item.name }}</view>
+           </view>
+      <view class="one-setting" @click="showChoose">选择重复日期</view>
+      <view class="one-time">{{time}}</view>
+
+      <picker mode="time" start="00:00" end="23:59" @change="bindTimeChange">
+        <view class="one-setting">选择时间</view>
+      </picker>
+    </view>
+
+    <view style="margin:5vh 7vw;">
+      <button @click="saveConfig"  style="background: #0E9F9B;color: white;" type="default" >确认</button>
+    </view>
+
+    <uni-popup ref="popup" type="bottom">
+
+      <view class="uni-list">
+        <checkbox-group @change="checkboxChange">
+          <label class="uni-list-cell uni-list-cell-pd" v-for="item in items" :key="item.value">
+            <view style="width: 90%">{{item.name}}</view>
+            <view>
+              <checkbox :value="item.value" :checked="item.checked" />
+            </view>
+          </label>
+        </checkbox-group>
+        <view style="text-align: center">
+          <button size="mini" @click="chooseDay" style="background: #1cbbb4;color: white;" type="default" >确认</button>
+        </view>
+      </view>
+
+
+    </uni-popup>
+  </view>
+</template>
+
+<script>
+	import {save} from '@/api/device/plan.js'
+export default {
+	
+  data() {
+    return {
+      deviceId: "",
+      ccid:"",
+	    repeatTime: '',
+      cb:"",
+      chargeType:0,
+      oneTime:{
+        time:"",
+      },
+      time:"",
+      index:0,
+      array: [],
+      items: [{
+        value: '7',
+        name: '周日'
+      },
+        {
+          value: '1',
+          name: '周一',
+        },
+        {
+          value: '2',
+          name: '周二'
+        },
+        {
+          value: '3',
+          name: '周三'
+        },
+        {
+          value: '4',
+          name: '周四'
+        },
+        {
+          value: '5',
+          name: '周五'
+        },
+        {
+          value: '6',
+          name: '周六'
+        }
+      ]
+    }
+  },
+  computed: {
+    imgUrl: function () {
+      return getApp().globalData.config.imgUrl;
+    }
+  },
+  onLoad(opt){
+    this.deviceId = opt.id;
+    this.ccid = opt.ccid;
+  },
+  methods: {
+    chooseDay(){
+      this.array = [];
+      let items = this.items;
+      for (var i = 0, lenI = items.length; i < lenI; ++i) {
+        const item = items[i]
+        if(item.checked){
+          this.array.push(item)
+        }
+      }
+      this.$refs.popup.close();
+    },
+    checkboxChange: function (e) {
+      var items = this.items,
+          values = e.detail.value;
+      for (var i = 0, lenI = items.length; i < lenI; ++i) {
+        const item = items[i]
+        if(values.includes(item.value)){
+          this.$set(item,'checked',true)
+        }else{
+          this.$set(item,'checked',false)
+        }
+      }
+    },
+    showChoose(){
+      this.$refs.popup.open("center");
+      var items = this.items;
+      let values = [];
+      for (let i = 0; i < this.array.length; i++) {
+          let curItem = this.array[i];
+          values.push(curItem.value);
+      }
+      for (var i = 0, lenI = items.length; i < lenI; ++i) {
+        const item = items[i]
+        if(values.includes(item.value)){
+          this.$set(item,'checked',true)
+        }else{
+          this.$set(item,'checked',false)
+        }
+      }
+    },
+    bindTimeChange(e){
+      let time = e.detail;
+      this.time = time.value;
+	    this.repeatTime = time.value;
+    },
+    open(){
+      // 通过组件定义的ref调用uni-popup方法 ,如果传入参数 ,type 属性将失效 ,仅支持 ['top','left','bottom','right','center']
+      this.$refs.popup.open('top')
+    },
+    changeDate(date){
+      let hasMin =  date.split(":")
+      let self = this;
+      if(hasMin.length == 1){
+       setTimeout(function(){
+         date = date.trim();
+         self.oneTime.time = date+" 00:00";
+       },100);
+      }
+    },
+    changeType(type){
+      if(this.chargeType == type){
+        return;
+      }
+      this.chargeType = type;
+      this.array = [];
+      this.time = "";
+      this.oneTime.time = "";
+    },
+     async saveConfig() {
+   		 // debugger
+   // 		 const time = new Date(this.chargeType === 0 ? this.oneTime.time : this.time);
+   // 		 const minute = time.getMinutes();
+   // 		 time.setMinutes(minute + 1, 0, 0); // 将时间的秒数和毫秒数设置为 0,并将分钟数加 1。
+   // 		 const repeatTime = time.toISOString(); // 将时间对象转换成 ISO 格式的字符串。
+		 // const repeatTimeString = repeatTime.substring(0, repeatTime.length - 1); // 去除字符串末尾的 "Z" 字符。
+   		  let runTime = null;
+   		   if (this.chargeType === 0) {
+   		     runTime = this.oneTime.time;
+            if(!runTime){
+              this.$modal.showToast("请设置时间");
+              return;
+            }
+   		   }else{
+            if(!this.repeatTime || this.array.length == 0){
+              this.$modal.showToast("请设置重复时间");
+              return;
+            }
+         }
+             const params = {
+                // createTime: new Date().toLocaleTimeString(),
+                planType: this.chargeType === 0 ? 1 : 2, // 计划类型(单次或自定义)
+                status: 1, // 状态(默认为未完成)
+                runTime: runTime,
+                repeatDays: this.chargeType === 1 ? this.array.map(item => item.value).join(',') : '',
+                repeatTime: this.chargeType === 1 ?  this.repeatTime : '',
+                deviceId: this.deviceId, // 设备 ID
+                ccid:this.ccid
+             };
+   		  
+   		  console.log(params);
+          save(params).then(res => {
+            this.$modal.loading("预约成功");
+            setTimeout(()=>{
+              uni.navigateBack();
+            },1000)
+            })
+          },
+     }
+   }
+</script>
+
+<style>
+
+.container{
+  position: absolute;
+  inset:0;
+  background: rgb(249, 252, 255);
+}
+.header-image{
+  width: 100%;
+}
+.charge-type{
+  text-align: center;
+  width: 100%;
+  margin-top:2vh;
+}
+.charge-type-container{
+  margin:0 20vw;
+  border:1px solid #0E9F9B;
+  border-radius: 2px;
+}
+.charge-type-container view{
+  display: inline-block;
+  padding:2vw;
+  width: 50%;
+  border-radius: 2px;
+
+}
+.type-left{
+
+}
+.type-choose{
+  background: #0E9F9B;
+  color:white;
+}
+.type-right{
+
+}
+.time-area{
+  border: 1px solid #E6E6E6;
+  border-radius: 10px;
+  margin:0 11vh;
+  margin-top:5vh;
+  height: 25vh;
+  text-align: center;
+  padding: 8vw;
+  line-height: 5vh;
+}
+.one-date{
+  text-align: center;
+  color: #0E9F9B;
+}
+.one-time{
+  font-weight: bold;
+  color: #0E9F9B;
+  font-size: 24px;
+}
+.one-setting{
+  font-weight: 400;
+  color: #B2B2B2;
+}
+.uni-list-cell {
+  justify-content: flex-start
+}
+.uni-list-cell{
+  display: flex;
+  flex-direction: row;
+  padding:10px 10px
+
+}
+.uni-list{
+  background:  rgb(249, 252, 255);
+  width: 300px;
+}
+</style>

+ 134 - 0
pages/weitiandi/device/planrecord.vue

@@ -0,0 +1,134 @@
+<template>
+  <view class="container">
+    <view class="list-item">
+      <view class="item" v-for="item in chargeList" :key="item.id">
+        <view class="item-header">
+          <view class="item-title">
+            设备编号:{{ item.deviceId }}
+          </view>
+          <view class="item-status">
+            <uni-tag type="success" text="激活中" v-if="item.status === 1"></uni-tag>
+            <uni-tag type="error" text="已停用" v-if="item.status === 2"></uni-tag>
+          </view>
+        </view>
+        <view class="item-body">
+          <view style="font-size: 12px">
+            预约类型:
+            <uni-tag type="primary" text="单次" v-if="item.planType === 1"></uni-tag>
+            <uni-tag type="warning" text="重复" v-if="item.planType === 2"></uni-tag>
+          </view>
+          <view style="font-size: 12px; margin: 5px 0">
+            创建时间:{{ item.createTime }}
+          </view>
+          <view v-if="item.planType === 2">
+            <view class="item-time">重复日期:{{ formatRepeatDays(item.repeatDays) }}</view>
+            <view class="item-time">重复时间:{{ item.repeatTime }}</view>
+          </view>
+          <view v-if="item.planType === 1">
+            <view class="item-time">执行时间:{{ item.runTime }}</view>
+          </view>
+        </view>
+      </view>
+    </view>
+    <uni-load-more @clickLoadMore="getMore" :content-text="contentText" :status="startText"></uni-load-more>
+  </view>
+</template>
+
+<script>
+import { plan } from '@/api/device/plan.js';
+
+export default {
+  data() {
+    return {
+      startText:"more",
+      search:{
+        pageNum:1,
+        pageSize:6,
+        reasonable:true,
+      },
+      contentText: {
+        contentdown: '点击查看更多',
+        contentrefresh: '加载中',
+        contentnomore: '没有更多'
+      },
+      chargeList: [],
+    };
+  },
+  methods: {
+    getMore(){
+      this.search.pageNum++;
+      this.planRecord();
+    },
+    async planRecord() {
+      plan(this.search).then(res=>{
+        if(res.data.length == 0){
+          this.$modal.showToast("没有更多数据了");
+          this.startText = "no-more"
+        }else{
+          this.startText = "more"
+          this.chargeList =  this.chargeList.concat(res.data);
+        }
+      })
+    },
+    formatRepeatDays(daysStr) {
+      const days = daysStr.split(',');
+      const weekdays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
+      const result = [];
+      for (const day of days) {
+        const weekday = weekdays[parseInt(day) - 1];
+        result.push(weekday);
+      }
+      return result.join(' ');
+    },
+  },
+  created() {
+    this.planRecord();
+  },
+};
+</script>
+<style>
+	.container {
+		background: rgb(249, 252, 255);
+		inset: 0;
+		position: absolute;
+	}
+
+	.list-item {
+		margin: 0vw;
+	}
+
+	.item {
+		box-shadow: 0px 5px 27px 0px rgba(195, 195, 195, 0.4);
+		border-radius: 4px;
+		background: #FFFFFF;
+		margin: 4vw;
+		padding: 4vw;
+	}
+
+	.item-title {
+		display: inline-block;
+		float: left;
+	}
+
+	.item-status {
+		display: inline-block;
+		float: right;
+	}
+
+	.item-header {
+		border-bottom: 1px solid lightgray;
+		height: 4vh;
+		font-size: 12px;
+	}
+
+	.item-body {
+		padding-top: 4vw;
+		line-height: 3vh;
+	}
+
+	.item-time {
+		color: #545454;
+		font-size: 12px;
+		margin-bottom: 4px
+	}
+</style>

+ 354 - 0
pages/weitiandi/device/setting.vue

@@ -0,0 +1,354 @@
+<template>
+  <view class="container">
+    <view class="setting">
+
+      <view class="prop-item">
+        <view class="prop-title">充电电流</view>
+        <view class="prop-value">
+          <view :style="item.style" class="value-tag" @click="checkItem(current,item)" v-for="item in current.items">
+            {{ item.text }}
+          </view>
+        </view>
+        <view class="prop-input" v-if="current.custom">
+          <input v-model="current.customValue" class="input" placeholder="请输入电流"/>
+          <view style="color: #0E9F9B">A</view>
+        </view>
+      </view>
+
+    </view>
+
+
+    <view class="bottom-area">
+
+      <view class="btn" @click="confirm">确认</view>
+
+
+      <view class="areas">
+        <view class="areas-item" @click="sendMainboardCmd">
+          <view class="areas-img">
+            <image style="width: 50px;height: 50px" :src="imgUrl+'/seting/mainboard.png'"></image>
+
+          </view>
+          <view>
+            <view>获取主板配置</view>
+          </view>
+        </view>
+
+        <view class="areas-item" @click="resetMainboard">
+          <view class="areas-img">
+            <image style="width: 50px;height: 50px" :src="imgUrl+'/seting/reset.png'"></image>
+          </view>
+          <view>
+            <view >恢复默认配置</view>
+          </view>
+        </view>
+
+        <view class="areas-item" @click="restartMainboard">
+          <view class="areas-img">
+            <image style="width: 50px;height: 50px" :src="imgUrl+'/seting/restart.png'"></image>
+          </view>
+          <view>
+            <view>重启充电桩</view>
+          </view>
+        </view>
+
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+import {setCurrent, getMainbordConfig,sendMainboardCmd, getDeviceInfo, reset, restart} from '@/api/device/current.js'
+import websocket from '@/utils/websocket'
+export default {
+  data() {
+    return {
+
+      scriptTask:null,
+      deviceId: "",
+      ccid:"",
+      visitTime: "",
+      mainBoardInfo:{},
+      current: {
+        items: [
+          {text: "8A", style: "", value: 8},
+          {text: "10A", style: "", value: 10},
+          {text: "16A", style: "", value: 16},
+          {text: "32A", style: "", value: 32},
+          {text: "自定义", style: "", value: -1}
+        ],
+        custom: false,
+        customValue: ''
+      }
+
+    }
+
+  },
+  onUnload (){
+    this.closeSocket();
+  },
+  computed: {
+    imgUrl: function () {
+      return getApp().globalData.config.imgUrl;
+    }
+  },
+  onLoad(opt) {
+    this.deviceId = opt.id;
+    this.ccid = opt.ccid;
+    this.sendMainboardCmd();
+  },
+  methods: {
+    closeSocket(){
+      this.scriptTask.close();
+    },
+    initSocket(key){
+      let self = this;
+      let socketUrl =  getApp().globalData.config.socketUrl
+      this.scriptTask = websocket({
+        url:"/"+key+"/",
+      });
+      let scriptTask = this.scriptTask;
+      scriptTask.onOpen(function (res) {
+        console.log('WebSocket连接已打开!');
+        self.connected = true;
+      });
+      scriptTask.onError(function (res) {
+        self.connected = false;
+        console.log(res);
+      });
+      scriptTask.onMessage(function (res) {
+        let data = JSON.parse(res.data);
+        let type = data.type;
+        let real_data = data.real_data;
+        if(type == 103){
+          self.portDetail = real_data;
+          self.$modal.closeLoading();
+        }
+        if(type == 116){
+          self.$modal.closeLoading();
+        }
+        if(type == 96){
+          self.mainBoardInfo = real_data;
+          self.formatMainboardData();
+          self.$modal.closeLoading();
+        }
+        console.log('收到服务器内容:' + JSON.stringify(data));
+      });
+      scriptTask.onClose(function (res) {
+        self.connected = true;
+        console.log('WebSocket 已关闭!');
+      });
+    },
+    formatMainboardData(){
+      let mainBoardInfo = this.mainBoardInfo;
+      let max_current = mainBoardInfo.max_current;
+      max_current = max_current/100;
+      this.setCurrent(max_current);
+    },
+    setCurrent(max_current){
+      this.current.currenValue = max_current;
+      let items = this.current.items;
+      let len = items.length;
+      let hasValue = false;
+      this.current.custom = false;
+      for (let i = 0; i < len; i++) {
+        let dian = items[i];
+        if(dian.value == max_current){
+          hasValue = true;
+          dian.style = ";  background: #0E9F9B;color:white"
+        }else{
+          dian.style = ";  background: rgb(227, 242, 245);color: #0E9F9B;"
+        }
+      }
+      if(!hasValue){
+        this.current.custom = true;
+        items[len-1].style = ";  background: #0E9F9B;color:white"
+      }
+      this.current.customValue = max_current;
+    },
+    async checkItem(obj, item) {
+      let items = obj.items;
+      let len = items.length;
+      for (let i = 0; i < len; i++) {
+        let dian = items[i];
+        dian.style = ";  background: rgb(227, 242, 245);color: #0E9F9B;"
+      }
+      item.style = ";  background: #0E9F9B;color:white"
+      if (item.value == -1) {
+        obj.custom = true;
+        obj.customValue = 0;
+      } else {
+        obj.custom = false;
+        obj.customValue = item.value;
+      }
+    },
+    async confirm() {
+      let currenValue = this.current.customValue;
+      currenValue = currenValue *100;
+      let deviceId = this.deviceId;
+      // 发送到后端
+      let self = this;
+      this.$modal.loading("保存中。。");
+      setCurrent({deviceId: deviceId,ccid:this.ccid},currenValue).then(res => {
+          setTimeout(function(){
+            self.sendMainboardCmd();
+          },1000)
+      })
+    },
+    sendMainboardCmd() {
+      this.$modal.loading("正在获取状态,请稍等...");
+      let deviceId = this.deviceId;
+      // 获取主板配置
+      sendMainboardCmd({deviceId: deviceId,ccid:this.ccid}).then(res => {
+        this.visitTime = res.msg;
+        if(!this.visitTime){
+          this.$modal.msg("请重新进入页面");
+          return;
+        }
+        if(!this.scriptTask){
+          this.initSocket(this.deviceId)
+        }
+      })
+    },
+    getMainboardConfig(){
+      this.startTimer();
+    },
+    startTimer(){
+      let self = this;
+      this.timer = setTimeout(function (){
+        getMainbordConfig({deviceId: self.deviceId,ccid:this.ccid},self.visitTime).then(res=>{
+          let data = res.data;
+          if(data != null){
+            self.mainBoardInfo = data;
+            self.formatMainboardData();
+            self.$modal.closeLoading();
+          }else{
+            self.startTimer();
+          }
+        });
+      },1000);
+    },
+    resetMainboard() {
+      let deviceId = this.deviceId;
+      // 恢复默认配置
+      let self = this;
+      this.$modal.loading("正在重置,请稍等...");
+      reset({deviceId:deviceId,ccid:this.ccid}).then(res => {
+        setTimeout(function(){
+          self.sendMainboardCmd();
+        },1000)
+      })
+    },
+    async restartMainboard() {
+      let deviceId = this.deviceId;
+      this.$modal.loading("正在重启中,请稍等...");
+      // 重启充电桩
+      let self = this;
+      restart({deviceId:deviceId,ccid:this.ccid}).then(res => {
+        setTimeout(function(){
+          self.sendMainboardCmd();
+        },1000)
+      })
+    }
+  }
+}
+</script>
+
+<style>
+
+.container {
+  position: absolute;
+  inset: 0;
+  background: rgb(249, 252, 255);
+}
+
+.prop-item {
+  padding: 2vh;
+}
+
+.prop-value {
+  display: flex;
+  flex-direction: row;
+}
+
+.prop-title {
+  color: #252525;
+  font-weight: bold;
+  margin-bottom: 2.5vh;
+}
+
+.value-tag {
+  background: rgb(227, 242, 245);
+  color: #0E9F9B;
+  width: 14vw;
+  height: 4vh;
+  line-height: 4vh;
+  border-radius: 3vw;
+  text-align: center;
+  margin: 0 1vw;
+  font-weight: bold;
+  font-size: 12px;
+}
+
+.prop-input {
+  display: flex;
+  flex-direction: row;
+  line-height: 4vh;
+  margin: 2vh 0;
+}
+
+.input {
+  background: rgb(227, 242, 245);
+  border-radius: 30px;
+  font-size: 12px;
+  height: 4vh;
+  padding: 0 2vw;
+  margin: 0 1vw;
+  width: 90%;
+}
+
+.bottom-area {
+  position: absolute;
+  bottom: 0px;
+  height: 24vh;
+  left: 0;
+  right: 0;
+}
+
+.btn {
+  background: #0E9F9B;
+  text-align: center;
+  color: white;
+  height: 6vh;
+  line-height: 6vh;
+  margin: 0 5vh;
+  border-radius: 1vh;
+  border-style: none;
+}
+
+.btn1 {
+  background: #f9fcff;
+  /*  text-align: center;
+    color: white;
+    height:6vh;
+    line-height: 6vh;
+    margin:0 5vh;
+    border-radius: 1vh; */
+  border-color: transparent;
+}
+
+.areas {
+  display: flex;
+  flex-direction: row;
+  text-align: center;
+  margin-top: 3vh
+}
+
+.areas-item {
+  width: 33%;
+}
+
+.areas-img {
+  text-align: center;
+}
+</style>

+ 183 - 0
pages/work/index.vue

@@ -0,0 +1,183 @@
+<template>
+  <view class="work-container">
+    <!-- 轮播图 -->
+    <uni-swiper-dot class="uni-swiper-dot-box" :info="data" :current="current" field="content">
+      <swiper class="swiper-box" :current="swiperDotIndex" @change="changeSwiper">
+        <swiper-item v-for="(item, index) in data" :key="index">
+          <view class="swiper-item" @click="clickBannerItem(item)">
+            <image :src="item.image" mode="aspectFill" :draggable="false" />
+          </view>
+        </swiper-item>
+      </swiper>
+    </uni-swiper-dot>
+
+    <!-- 宫格组件 -->
+    <uni-section title="系统管理" type="line"></uni-section>
+    <view class="grid-body">
+      <uni-grid :column="4" :showBorder="false" @change="changeGrid">
+        <uni-grid-item>
+          <view class="grid-item-box">
+            <uni-icons type="person-filled" size="30"></uni-icons>
+            <text class="text">用户管理</text>
+          </view>
+        </uni-grid-item>
+        <uni-grid-item>
+          <view class="grid-item-box">
+            <uni-icons type="staff-filled" size="30"></uni-icons>
+            <text class="text">角色管理</text>
+          </view>
+        </uni-grid-item>
+        <uni-grid-item>
+          <view class="grid-item-box">
+            <uni-icons type="color" size="30"></uni-icons>
+            <text class="text">菜单管理</text>
+          </view>
+        </uni-grid-item>
+        <uni-grid-item>
+          <view class="grid-item-box">
+            <uni-icons type="settings-filled" size="30"></uni-icons>
+            <text class="text">部门管理</text>
+          </view>
+        </uni-grid-item>
+        <uni-grid-item>
+          <view class="grid-item-box">
+            <uni-icons type="heart-filled" size="30"></uni-icons>
+            <text class="text">岗位管理</text>
+          </view>
+        </uni-grid-item>
+        <uni-grid-item>
+          <view class="grid-item-box">
+            <uni-icons type="bars" size="30"></uni-icons>
+            <text class="text">字典管理</text>
+          </view>
+        </uni-grid-item>
+        <uni-grid-item>
+          <view class="grid-item-box">
+            <uni-icons type="gear-filled" size="30"></uni-icons>
+            <text class="text">参数设置</text>
+          </view>
+        </uni-grid-item>
+        <uni-grid-item>
+          <view class="grid-item-box">
+            <uni-icons type="chat-filled" size="30"></uni-icons>
+            <text class="text">通知公告</text>
+          </view>
+        </uni-grid-item>
+        <uni-grid-item>
+          <view class="grid-item-box">
+            <uni-icons type="wallet-filled" size="30"></uni-icons>
+            <text class="text">日志管理</text>
+          </view>
+        </uni-grid-item>
+      </uni-grid>
+    </view>
+  </view>
+</template>
+
+<script>
+  export default {
+    data() {
+      return {
+        current: 0,
+        swiperDotIndex: 0,
+        data: [{
+            image: '/static/images/banner/banner01.jpg'
+          },
+          {
+            image: '/static/images/banner/banner02.jpg'
+          },
+          {
+            image: '/static/images/banner/banner03.jpg'
+          }
+        ]
+      }
+    },
+    methods: {
+      clickBannerItem(item) {
+        console.info(item)
+      },
+      changeSwiper(e) {
+        this.current = e.detail.current
+      },
+      changeGrid(e) {
+        this.$modal.showToast('模块建设中~')
+      }
+    }
+  }
+</script>
+
+<style lang="scss">
+  /* #ifndef APP-NVUE */
+  page {
+    display: flex;
+    flex-direction: column;
+    box-sizing: border-box;
+    background-color: #fff;
+    min-height: 100%;
+    height: auto;
+  }
+
+  view {
+    font-size: 14px;
+    line-height: inherit;
+  }
+
+  /* #endif */
+
+  .text {
+    text-align: center;
+    font-size: 26rpx;
+    margin-top: 10rpx;
+  }
+
+  .grid-item-box {
+    flex: 1;
+    /* #ifndef APP-NVUE */
+    display: flex;
+    /* #endif */
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    padding: 15px 0;
+  }
+
+  .uni-margin-wrap {
+    width: 690rpx;
+    width: 100%;
+    ;
+  }
+
+  .swiper {
+    height: 300rpx;
+  }
+
+  .swiper-box {
+    height: 150px;
+  }
+
+  .swiper-item {
+    /* #ifndef APP-NVUE */
+    display: flex;
+    /* #endif */
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    color: #fff;
+    height: 300rpx;
+    line-height: 300rpx;
+  }
+
+  @media screen and (min-width: 500px) {
+    .uni-swiper-dot-box {
+      width: 400px;
+      /* #ifndef APP-NVUE */
+      margin: 0 auto;
+      /* #endif */
+      margin-top: 8px;
+    }
+
+    .image {
+      width: 100%;
+    }
+  }
+</style>

+ 39 - 0
permission.js

@@ -0,0 +1,39 @@
+import { getToken } from '@/utils/auth'
+
+// 登录页面
+const loginPage = "/pages/login"
+  
+// 页面白名单
+const whiteList = [
+  '/pages/login', '/pages/common/webview/index'
+]
+
+// 检查地址白名单
+function checkWhite(url) {
+  const path = url.split('?')[0]
+  return whiteList.indexOf(path) !== -1
+}
+
+// 页面跳转验证拦截器
+let list = ["navigateTo", "redirectTo", "reLaunch", "switchTab"]
+list.forEach(item => {
+  uni.addInterceptor(item, {
+    invoke(to) {
+      if (getToken()) {
+        if (to.url === loginPage) {
+          uni.reLaunch({ url: "/" })
+        }
+        return true
+      } else {
+        if (checkWhite(to.url)) {
+          return true
+        }
+        uni.reLaunch({ url: loginPage })
+        return false
+      }
+    },
+    fail(err) {
+      console.log(err)
+    }
+  })
+})

+ 60 - 0
plugins/auth.js

@@ -0,0 +1,60 @@
+import store from '@/store'
+
+function authPermission(permission) {
+  const all_permission = "*:*:*"
+  const permissions = store.getters && store.getters.permissions
+  if (permission && permission.length > 0) {
+    return permissions.some(v => {
+      return all_permission === v || v === permission
+    })
+  } else {
+    return false
+  }
+}
+
+function authRole(role) {
+  const super_admin = "admin"
+  const roles = store.getters && store.getters.roles
+  if (role && role.length > 0) {
+    return roles.some(v => {
+      return super_admin === v || v === role
+    })
+  } else {
+    return false
+  }
+}
+
+export default {
+  // 验证用户是否具备某权限
+  hasPermi(permission) {
+    return authPermission(permission)
+  },
+  // 验证用户是否含有指定权限,只需包含其中一个
+  hasPermiOr(permissions) {
+    return permissions.some(item => {
+      return authPermission(item)
+    })
+  },
+  // 验证用户是否含有指定权限,必须全部拥有
+  hasPermiAnd(permissions) {
+    return permissions.every(item => {
+      return authPermission(item)
+    })
+  },
+  // 验证用户是否具备某角色
+  hasRole(role) {
+    return authRole(role)
+  },
+  // 验证用户是否含有指定角色,只需包含其中一个
+  hasRoleOr(roles) {
+    return roles.some(item => {
+      return authRole(item)
+    })
+  },
+  // 验证用户是否含有指定角色,必须全部拥有
+  hasRoleAnd(roles) {
+    return roles.every(item => {
+      return authRole(item)
+    })
+  }
+}

+ 14 - 0
plugins/index.js

@@ -0,0 +1,14 @@
+import tab from './tab'
+import auth from './auth'
+import modal from './modal'
+
+export default {
+  install(Vue) {
+    // 页签操作
+    Vue.prototype.$tab = tab
+    // 认证对象
+    Vue.prototype.$auth = auth
+    // 模态框对象
+    Vue.prototype.$modal = modal
+  }
+}

+ 75 - 0
plugins/modal.js

@@ -0,0 +1,75 @@
+export default {
+  // 消息提示
+  msg(content) {
+    uni.showToast({
+      title: content,
+      icon: 'none'
+    })
+  },
+  // 错误消息
+  msgError(content) {
+    uni.showToast({
+      title: content,
+      icon: 'error'
+    })
+  },
+  // 成功消息
+  msgSuccess(content) {
+    uni.showToast({
+      title: content,
+      icon: 'success'
+    })
+  },
+  // 隐藏消息
+  hideMsg(content) {
+    uni.hideToast()
+  },
+  // 弹出提示
+  alert(content) {
+    uni.showModal({
+      title: '提示',
+      content: content,
+      showCancel: false
+    })
+  },
+  // 确认窗体
+  confirm(content) {
+    return new Promise((resolve, reject) => {
+      uni.showModal({
+        title: '系统提示',
+        content: content,
+        cancelText: '取消',
+        confirmText: '确定',
+        success: function(res) {
+          if (res.confirm) {
+            resolve(res.confirm)
+          }
+        }
+      })
+    })
+  },
+  // 提示信息
+  showToast(option) {
+    if (typeof option === "object") {
+      uni.showToast(option)
+    } else {
+      uni.showToast({
+        title: option,
+        icon: "none",
+        duration: 2500
+      })
+    }
+  },
+  // 打开遮罩层
+  loading(content) {
+    uni.showLoading({
+      title: content,
+      icon: 'none',
+      mask:true
+    })
+  },
+  // 关闭遮罩层
+  closeLoading() {
+    uni.hideLoading()
+  }
+}

+ 30 - 0
plugins/tab.js

@@ -0,0 +1,30 @@
+export default {
+  // 关闭所有页面,打开到应用内的某个页面
+  reLaunch(url) {
+    return uni.reLaunch({
+      url: url
+    })
+  },
+  // 跳转到tabBar页面,并关闭其他所有非tabBar页面
+  switchTab(url) {
+    return uni.switchTab({
+      url: url
+    })
+  },
+  // 关闭当前页面,跳转到应用内的某个页面
+  redirectTo(url) {
+    return uni.redirectTo({
+      url: url
+    })
+  },
+  // 保留当前页面,跳转到应用内的某个页面
+  navigateTo(url) {
+    return uni.navigateTo({
+      url: url
+    })
+  },
+  // 关闭当前页面,返回上一页面或多级页面
+  navigateBack() {
+    return uni.navigateBack()
+  }
+}

BIN
static/favicon.ico


+ 90 - 0
static/font/iconfont.css

@@ -0,0 +1,90 @@
+@font-face {
+  font-family: "iconfont";
+  src: url('@/static/font/iconfont.ttf') format('truetype');
+}
+
+.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  display: inline-block;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-user:before {
+  content: "\e7ae";
+}
+
+.icon-password:before {
+  content: "\e8b2";
+}
+
+.icon-code:before {
+  content: "\e699";
+}
+
+.icon-setting:before {
+  content: "\e6cc";
+}
+
+.icon-share:before {
+  content: "\e739";
+}
+
+.icon-edit:before {
+  content: "\e60c";
+}
+
+.icon-version:before {
+  content: "\e63f";
+}
+
+.icon-service:before {
+  content: "\e6ff";
+}
+
+.icon-friendfill:before {
+  content: "\e726";
+}
+
+.icon-community:before {
+  content: "\e741";
+}
+
+.icon-people:before {
+  content: "\e736";
+}
+
+.icon-dianzan:before {
+  content: "\ec7f";
+}
+
+.icon-right:before {
+  content: "\e7eb";
+}
+
+.icon-logout:before {
+  content: "\e61d";
+}
+
+.icon-help:before {
+  content: "\e616";
+}
+
+.icon-github:before {
+  content: "\e628";
+}
+
+.icon-aixin:before {
+  content: "\e601";
+}
+
+.icon-clean:before {
+  content: "\e607";
+}
+
+.icon-refresh:before {
+  content: "\e604";
+}
+

BIN
static/font/iconfont.ttf


BIN
static/images/banner/banner01.jpg


BIN
static/images/banner/banner02.jpg


BIN
static/images/banner/banner03.jpg


BIN
static/images/icons/menu-off.png


BIN
static/images/icons/menu-on.png


BIN
static/images/icons/mine-off.png


BIN
static/images/icons/mine-on.png


BIN
static/images/profile.jpg


BIN
static/images/tabbar/home.png


BIN
static/images/tabbar/home_.png


BIN
static/images/tabbar/mine.png


BIN
static/images/tabbar/mine_.png


BIN
static/images/tabbar/work.png


BIN
static/images/tabbar/work_.png


+ 20 - 0
static/index.html

@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+	<head>
+		<meta charset="utf-8">
+		<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+		  <meta name="renderer" content="webkit">
+		<title><%= htmlWebpackPlugin.options.title %></title>
+    <link rel="shortcut icon" type="image/x-icon" href="<%= BASE_URL %>static/favicon.ico">
+		<script>
+			var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
+			document.write('<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + (coverSupport ? ', viewport-fit=cover' : '') + '" />')
+		</script>
+		<link rel="stylesheet" href="<%= BASE_URL %>static/index.<%= VUE_APP_INDEX_CSS_HASH %>.css" />
+	</head>
+	<body>
+		<noscript>
+			<strong>本站点必须要开启JavaScript才能运行.</strong>
+		</noscript>
+		<div id="app"></div>
+</html>

BIN
static/logo.png


BIN
static/logo200.png


File diff suppressed because it is too large
+ 4804 - 0
static/scss/colorui.css


+ 90 - 0
static/scss/global.scss

@@ -0,0 +1,90 @@
+.text-center {
+	text-align: center;
+}
+
+.font-13 {
+	font-size: 13px;
+}
+
+.font-12 {
+	font-size: 12px;
+}
+
+.font-11 {
+	font-size: 11px;
+}
+
+.text-grey1 {
+	color: #888;
+}
+.text-grey2 {
+	color: #aaa;
+}
+
+.list-cell-arrow::before {
+    content: ' ';
+    height: 10px;
+    width: 10px;
+    border-width: 2px 2px 0 0;
+    border-color: #c0c0c0;
+    border-style: solid;
+    -webkit-transform: matrix(0.5, 0.5, -0.5, 0.5, 0, 0);
+    transform: matrix(0.5, 0.5, -0.5, 0.5, 0, 0);
+    position: absolute;
+    top: 50%;
+    margin-top: -6px;
+    right: 30rpx;
+  }
+  
+  .list-cell {
+    position: relative;
+    width: 100%;
+    box-sizing: border-box;
+    background-color: #fff;
+    color: #333;
+    padding: 26rpx 30rpx;
+  }
+  
+  .list-cell:first-child {
+    border-radius: 8rpx 8rpx 0 0;
+  }
+  
+  .list-cell:last-child {
+    border-radius: 0 0 8rpx 8rpx;
+  }
+  
+  .list-cell::after {
+    content: '';
+    position: absolute;
+    border-bottom: 1px solid #eaeef1;
+    -webkit-transform: scaleY(0.5) translateZ(0);
+    transform: scaleY(0.5) translateZ(0);
+    transform-origin: 0 100%;
+    bottom: 0;
+    right: 0;
+    left: 0;
+    pointer-events: none;
+  }
+  
+  
+  .menu-list {
+    margin: 15px 15px;
+  
+    .menu-item-box {
+      width: 100%;
+      display: flex;
+      align-items: center;
+  
+      .menu-icon {
+        color: #007AFF;
+        font-size: 16px;
+        margin-right: 5px;
+      }
+      
+      .text-right {
+        margin-left: auto;
+        margin-right: 34rpx;
+        color: #999;
+      }
+    }
+  }

+ 6 - 0
static/scss/index.scss

@@ -0,0 +1,6 @@
+// global
+@import "./global.scss";
+// color-ui
+@import "@/static/scss/colorui.css";
+// iconfont
+@import "@/static/font/iconfont.css";

+ 8 - 0
store/getters.js

@@ -0,0 +1,8 @@
+const getters = {
+  token: state => state.user.token,
+  avatar: state => state.user.avatar,
+  name: state => state.user.name,
+  roles: state => state.user.roles,
+  permissions: state => state.user.permissions
+}
+export default getters

+ 15 - 0
store/index.js

@@ -0,0 +1,15 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+import user from '@/store/modules/user'
+import getters from './getters'
+
+Vue.use(Vuex)
+
+const store = new Vuex.Store({
+  modules: {
+    user
+  },
+  getters
+})
+
+export default store

+ 114 - 0
store/modules/user.js

@@ -0,0 +1,114 @@
+import config from '@/config'
+import storage from '@/utils/storage'
+import constant from '@/utils/constant'
+import { login, logout, getInfo, register} from '@/api/login'
+import { getToken, setToken, removeToken } from '@/utils/auth'
+
+const baseUrl = config.baseUrl
+
+const user = {
+  state: {
+    token: getToken(),
+    name: storage.get(constant.name),
+    avatar: storage.get(constant.avatar),
+    roles: storage.get(constant.roles),
+    permissions: storage.get(constant.permissions)
+  },
+
+  mutations: {
+    SET_TOKEN: (state, token) => {
+      state.token = token
+    },
+    SET_NAME: (state, name) => {
+      state.name = name
+      storage.set(constant.name, name)
+    },
+    SET_AVATAR: (state, avatar) => {
+      state.avatar = avatar
+      storage.set(constant.avatar, avatar)
+    },
+    SET_ROLES: (state, roles) => {
+      state.roles = roles
+      storage.set(constant.roles, roles)
+    },
+    SET_PERMISSIONS: (state, permissions) => {
+      state.permissions = permissions
+      storage.set(constant.permissions, permissions)
+    }
+  },
+
+  actions: {
+    // 登录
+    Login({ commit }, userInfo) {
+      const username = userInfo.username.trim()
+      const password = userInfo.password
+      const code = userInfo.code
+      const uuid = userInfo.uuid
+      return new Promise((resolve, reject) => {
+        login(username, password, code, uuid).then(res => {
+          setToken(res.token)
+          commit('SET_TOKEN', res.token)
+          resolve()
+        }).catch(error => {
+          reject(error)
+        })
+      })
+    },
+
+    // 注册
+    Register({ commit }, userInfo) {
+		// debugger
+      const username = userInfo.username
+      const password = userInfo.password
+      return new Promise((resolve, reject) => {
+        register(username, password).then(res => {
+          // setToken(res.token)
+          // commit('SET_TOKEN', res.token)
+          resolve(res)
+        }).catch(error => {
+          reject(error)
+        })
+      })
+    },
+
+    // 获取用户信息
+    GetInfo({ commit, state }) {
+      return new Promise((resolve, reject) => {
+        getInfo().then(res => {
+          const user = res.user
+          const avatar = (user == null || user.avatar == "" || user.avatar == null) ? require("@/static/images/profile.jpg") : baseUrl + user.avatar
+          const username = (user == null || user.userName == "" || user.userName == null) ? "" : user.userName
+          if (res.roles && res.roles.length > 0) {
+            commit('SET_ROLES', res.roles)
+            commit('SET_PERMISSIONS', res.permissions)
+          } else {
+            commit('SET_ROLES', ['ROLE_DEFAULT'])
+          }
+          commit('SET_NAME', username)
+          commit('SET_AVATAR', avatar)
+          resolve(res)
+        }).catch(error => {
+          reject(error)
+        })
+      })
+    },
+
+    // 退出系统
+    LogOut({ commit, state }) {
+      return new Promise((resolve, reject) => {
+        logout(state.token).then(() => {
+          commit('SET_TOKEN', '')
+          commit('SET_ROLES', [])
+          commit('SET_PERMISSIONS', [])
+          removeToken()
+          storage.clean()
+          resolve()
+        }).catch(error => {
+          reject(error)
+        })
+      })
+    }
+  }
+}
+
+export default user

+ 64 - 0
uni.scss

@@ -0,0 +1,64 @@
+/**
+ * uni-app内置的常用样式变量
+ */
+
+/* 行为相关颜色 */
+$uni-color-primary: #007aff;
+$uni-color-success: #4cd964;
+$uni-color-warning: #f0ad4e;
+$uni-color-error: #dd524d;
+
+/* 文字基本颜色 */
+$uni-text-color:#333;//基本色
+$uni-text-color-inverse:#fff;//反色
+$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
+$uni-text-color-placeholder: #808080;
+$uni-text-color-disable:#c0c0c0;
+
+/* 背景颜色 */
+$uni-bg-color:#ffffff;
+$uni-bg-color-grey:#f8f8f8;
+$uni-bg-color-hover:#f1f1f1;//点击状态颜色
+$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
+
+/* 边框颜色 */
+$uni-border-color:#e5e5e5;
+
+/* 尺寸变量 */
+
+/* 文字尺寸 */
+$uni-font-size-sm:12px;
+$uni-font-size-base:14px;
+$uni-font-size-lg:16px;
+
+/* 图片尺寸 */
+$uni-img-size-sm:20px;
+$uni-img-size-base:26px;
+$uni-img-size-lg:40px;
+
+/* Border Radius */
+$uni-border-radius-sm: 2px;
+$uni-border-radius-base: 3px;
+$uni-border-radius-lg: 6px;
+$uni-border-radius-circle: 50%;
+
+/* 水平间距 */
+$uni-spacing-row-sm: 5px;
+$uni-spacing-row-base: 10px;
+$uni-spacing-row-lg: 15px;
+
+/* 垂直间距 */
+$uni-spacing-col-sm: 4px;
+$uni-spacing-col-base: 8px;
+$uni-spacing-col-lg: 12px;
+
+/* 透明度 */
+$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
+
+/* 文章场景相关 */
+$uni-color-title: #2C405A; // 文章标题颜色
+$uni-font-size-title:20px;
+$uni-color-subtitle: #555555; // 二级标题颜色
+$uni-font-size-subtitle:26px;
+$uni-color-paragraph: #3F536E; // 文章段落颜色
+$uni-font-size-paragraph:15px;

+ 13 - 0
utils/auth.js

@@ -0,0 +1,13 @@
+const TokenKey = 'App-Token'
+
+export function getToken() {
+  return uni.getStorageSync(TokenKey)
+}
+
+export function setToken(token) {
+  return uni.setStorageSync(TokenKey, token)
+}
+
+export function removeToken() {
+  return uni.removeStorageSync(TokenKey)
+}

+ 54 - 0
utils/common.js

@@ -0,0 +1,54 @@
+/**
+* 显示消息提示框
+* @param content 提示的标题
+*/
+export function toast(content) {
+  uni.showToast({
+    icon: 'none',
+    title: content
+  })
+}
+
+/**
+* 显示模态弹窗
+* @param content 提示的标题
+*/
+export function showConfirm(content) {
+  return new Promise((resolve, reject) => {
+    uni.showModal({
+      title: '提示',
+      content: content,
+      cancelText: '取消',
+      confirmText: '确定',
+      success: function(res) {
+        resolve(res)
+      }
+    })
+  })
+}
+
+/**
+* 参数处理
+* @param params 参数
+*/
+export function tansParams(params) {
+  let result = ''
+  for (const propName of Object.keys(params)) {
+    const value = params[propName]
+    var part = encodeURIComponent(propName) + "="
+    if (value !== null && value !== "" && typeof (value) !== "undefined") {
+      if (typeof value === 'object') {
+        for (const key of Object.keys(value)) {
+          if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
+            let params = propName + '[' + key + ']'
+            var subPart = encodeURIComponent(params) + "="
+            result += subPart + encodeURIComponent(value[key]) + "&"
+          }
+        }
+      } else {
+        result += part + encodeURIComponent(value) + "&"
+      }
+    }
+  }
+  return result
+}

+ 8 - 0
utils/constant.js

@@ -0,0 +1,8 @@
+const constant = {
+   avatar: 'vuex_avatar',
+   name: 'vuex_name',
+   roles: 'vuex_roles',
+   permissions: 'vuex_permissions'
+ }
+
+ export default constant

+ 6 - 0
utils/errorCode.js

@@ -0,0 +1,6 @@
+export default {
+  '401': '认证失败,无法访问系统资源',
+  '403': '当前操作没有权限',
+  '404': '访问资源不存在',
+  'default': '系统未知错误,请反馈给管理员'
+}

+ 51 - 0
utils/permission.js

@@ -0,0 +1,51 @@
+import store from '@/store'
+
+/**
+ * 字符权限校验
+ * @param {Array} value 校验值
+ * @returns {Boolean}
+ */
+export function checkPermi(value) {
+  if (value && value instanceof Array && value.length > 0) {
+    const permissions = store.getters && store.getters.permissions
+    const permissionDatas = value
+    const all_permission = "*:*:*"
+
+    const hasPermission = permissions.some(permission => {
+      return all_permission === permission || permissionDatas.includes(permission)
+    })
+
+    if (!hasPermission) {
+      return false
+    }
+    return true
+  } else {
+    console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`)
+    return false
+  }
+}
+
+/**
+ * 角色权限校验
+ * @param {Array} value 校验值
+ * @returns {Boolean}
+ */
+export function checkRole(value) {
+  if (value && value instanceof Array && value.length > 0) {
+    const roles = store.getters && store.getters.roles
+    const permissionRoles = value
+    const super_admin = "admin"
+
+    const hasRole = roles.some(role => {
+      return super_admin === role || permissionRoles.includes(role)
+    })
+
+    if (!hasRole) {
+      return false
+    }
+    return true
+  } else {
+    console.error(`need roles! Like checkRole="['admin','editor']"`)
+    return false
+  }
+}

+ 75 - 0
utils/request.js

@@ -0,0 +1,75 @@
+import store from '@/store'
+import config from '@/config'
+import { getToken } from '@/utils/auth'
+import errorCode from '@/utils/errorCode'
+import { toast, showConfirm, tansParams } from '@/utils/common'
+
+let timeout = 10000
+const baseUrl = config.baseUrl
+
+const request = config => {
+  // 是否需要设置 token
+  const isToken = (config.headers || {}).isToken === false
+  config.header = config.header || {}
+  if (getToken() && !isToken) {
+    config.header['Authorization'] = 'Bearer ' + getToken()
+  }
+  // get请求映射params参数
+  if (config.params) {
+    let url = config.url + '?' + tansParams(config.params)
+    url = url.slice(0, -1)
+    config.url = url
+  }
+  return new Promise((resolve, reject) => {
+    uni.showLoading();
+    uni.request({
+        method: config.method || 'get',
+        timeout: config.timeout ||  timeout,
+        url: config.baseUrl || baseUrl + config.url,
+        data: config.data,
+        header: config.header,
+        dataType: 'json'
+      }).then(response => {
+      uni.hideLoading();
+        let [error, res] = response
+        if (error) {
+          toast('后端接口连接异常')
+          reject('后端接口连接异常')
+          return
+        }
+        const code = res.data.code || 200
+        const msg = errorCode[code] || res.data.msg || errorCode['default']
+        if (code === 401) {
+          showConfirm('登录状态已过期,您可以继续留在该页面,或者重新登录?').then(res => {
+            if (res.confirm) {
+              store.dispatch('LogOut').then(res => {
+                uni.reLaunch({ url: '/pages/login' })
+              })
+            }
+          })
+          reject('无效的会话,或者会话已过期,请重新登录。')
+        } else if (code === 500) {
+          toast(msg)
+          reject('500')
+        } else if (code !== 200) {
+          toast(msg)
+          reject(code)
+        }
+        resolve(res.data)
+      })
+      .catch(error => {
+        let { message } = error
+        if (message === 'Network Error') {
+          message = '后端接口连接异常'
+        } else if (message.includes('timeout')) {
+          message = '系统接口请求超时'
+        } else if (message.includes('Request failed with status code')) {
+          message = '系统接口' + message.substr(message.length - 3) + '异常'
+        }
+        toast(message)
+        reject(error)
+      })
+  })
+}
+
+export default request

+ 33 - 0
utils/storage.js

@@ -0,0 +1,33 @@
+import constant from './constant'
+
+// 存储变量名
+let storageKey = 'storage_data'
+
+// 存储节点变量名
+let storageNodeKeys = [constant.avatar, constant.name, constant.roles, constant.permissions]
+
+// 存储的数据
+let storageData = uni.getStorageSync(storageKey) || {}
+
+const storage = {
+  set: function(key, value) {
+    if (storageNodeKeys.indexOf(key) != -1) {
+      let tmp = uni.getStorageSync(storageKey)
+      tmp = tmp ? tmp : {}
+      tmp[key] = value
+      uni.setStorageSync(storageKey, tmp)
+    }
+  },
+  get: function(key) {
+    return storageData[key] || ""
+  },
+  remove: function(key) {
+    delete storageData[key]
+    uni.setStorageSync(storageKey, storageData)
+  },
+  clean: function() {
+    uni.removeStorageSync(storageKey)
+  }
+}
+
+export default storage

+ 70 - 0
utils/upload.js

@@ -0,0 +1,70 @@
+import store from '@/store'
+import config from '@/config'
+import { getToken } from '@/utils/auth'
+import errorCode from '@/utils/errorCode'
+import { toast, showConfirm, tansParams } from '@/utils/common'
+
+let timeout = 10000
+const baseUrl = config.baseUrl
+
+const upload = config => {
+  // 是否需要设置 token
+  const isToken = (config.headers || {}).isToken === false
+  config.header = config.header || {}
+  if (getToken() && !isToken) {
+    config.header['Authorization'] = 'Bearer ' + getToken()
+  }
+  // get请求映射params参数
+  if (config.params) {
+    let url = config.url + '?' + tansParams(config.params)
+    url = url.slice(0, -1)
+    config.url = url
+  }
+  return new Promise((resolve, reject) => {
+      uni.uploadFile({
+        timeout: config.timeout || timeout,
+        url: baseUrl + config.url,
+        filePath: config.filePath,
+        name: config.name || 'file',
+        header: config.header,
+        formData: config.formData,
+        success: (res) => {
+          let result = JSON.parse(res.data)
+          const code = result.code || 200
+          const msg = errorCode[code] || result.msg || errorCode['default']
+          if (code === 200) {
+            resolve(result)
+          } else if (code == 401) {
+            showConfirm("登录状态已过期,您可以继续留在该页面,或者重新登录?").then(res => {
+              if (res.confirm) {
+                store.dispatch('LogOut').then(res => {
+                  uni.reLaunch({ url: '/pages/login/login' })
+                })
+              }
+            })
+            reject('无效的会话,或者会话已过期,请重新登录。')
+          } else if (code === 500) {
+            toast(msg)
+            reject('500')
+          } else if (code !== 200) {
+            toast(msg)
+            reject(code)
+          }
+        },
+        fail: (error) => {
+          let { message } = error
+          if (message == 'Network Error') {
+            message = '后端接口连接异常'
+          } else if (message.includes('timeout')) {
+            message = '系统接口请求超时'
+          } else if (message.includes('Request failed with status code')) {
+            message = '系统接口' + message.substr(message.length - 3) + '异常'
+          }
+          toast(message)
+          reject(error)
+        }
+      })
+  })
+}
+
+export default upload

+ 27 - 0
utils/websocket.js

@@ -0,0 +1,27 @@
+import store from '@/store'
+import config from '@/config'
+import { getToken } from '@/utils/auth'
+import errorCode from '@/utils/errorCode'
+import { toast, showConfirm, tansParams } from '@/utils/common'
+
+const baseUrl = config.socketUrl
+
+const websocket = config => {
+    // 是否需要设置 token
+    const isToken = (config.headers || {}).isToken === false
+    config.header = config.header || {}
+    let token = "";
+    if (getToken() && !isToken) {
+        token =  getToken()
+    }
+       let SocketTask = uni.connectSocket({
+            url: config.baseUrl || baseUrl + config.url+""+token,
+            success:function(res){
+            },
+            fail:function (res){
+            }
+        })
+    return SocketTask;
+}
+
+export default websocket