浏览代码

'初始化'

hzh 11 月之前
当前提交
d755a86697
共有 100 个文件被更改,包括 18867 次插入0 次删除
  1. 5 0
      .gitignore
  2. 20 0
      .hbuilderx/launch.json
  3. 313 0
      App.vue
  4. 7 0
      README.md
  5. 381 0
      alarm/blindSpot/detail.vue
  6. 274 0
      alarm/blindSpot/index.vue
  7. 383 0
      alarm/driverBehavior/detail.vue
  8. 278 0
      alarm/driverBehavior/index.vue
  9. 380 0
      alarm/proactiveSecurity/detail.vue
  10. 279 0
      alarm/proactiveSecurity/index.vue
  11. 19 0
      api/accidentStudy/accidentStudy.js
  12. 38 0
      api/alarm/blindSpot.js
  13. 38 0
      api/alarm/driverBehavior.js
  14. 38 0
      api/alarm/proactiveSecurity.js
  15. 93 0
      api/common/system.js
  16. 39 0
      api/common/user.js
  17. 11 0
      api/common/vehicle.js
  18. 19 0
      api/core/sendOrder.js
  19. 10 0
      api/dict/data.js
  20. 19 0
      api/formTemplate.js
  21. 29 0
      api/iconConfig/iconConfig.js
  22. 93 0
      api/learnMobile/index.js
  23. 76 0
      api/login.js
  24. 26 0
      api/mine/driverTask.js
  25. 36 0
      api/monitor.js
  26. 56 0
      api/pastRoute.js
  27. 69 0
      api/realtimeWatch.js
  28. 64 0
      api/system/driverManage.js
  29. 19 0
      api/system/event.js
  30. 45 0
      api/system/material.js
  31. 19 0
      api/system/sysMessage.js
  32. 149 0
      api/system/vehicle.js
  33. 38 0
      api/system/video.js
  34. 47 0
      api/system/workPlan.js
  35. 28 0
      api/vehDispatch/oilApply.js
  36. 22 0
      api/vehDispatch/taskQrCode.js
  37. 37 0
      api/vehDispatch/vehInspection.js
  38. 11 0
      api/vehicle/common.js
  39. 87 0
      api/wfApi/index.js
  40. 二进制
      app.keystore
  41. 二进制
      assets/fonts/MaoKenTangYuan.ttf
  42. 二进制
      assets/fonts/PMZDCT.ttf
  43. 二进制
      assets/fonts/YSBTH.ttf
  44. 12 0
      assets/styles/commom.scss
  45. 7 0
      assets/styles/uview-ui.scss
  46. 19 0
      common/css/icon.css
  47. 410 0
      components/Intercom/index.vue
  48. 28 0
      components/MapContainer/components/pastRoute.vue
  49. 28 0
      components/MapContainer/components/vehicleInfo.vue
  50. 476 0
      components/MapContainer/controller/createApi.js
  51. 590 0
      components/MapContainer/index.vue
  52. 421 0
      components/playBack/index.vue
  53. 216 0
      components/sidebar/index.vue
  54. 86 0
      components/sidebar/module/sidebarItem.vue
  55. 175 0
      components/sidebardyn/index.vue
  56. 72 0
      components/sidebardyn/module/sidebarItem.vue
  57. 86 0
      components/tq-area-coverage/index.vue
  58. 81 0
      components/tq-calendar/index.vue
  59. 401 0
      components/tq-car-user/index.vue
  60. 402 0
      components/tq-car-user2/index.vue
  61. 133 0
      components/tq-date-time/index.vue
  62. 286 0
      components/tq-dept/index.vue
  63. 59 0
      components/tq-dict-select/index.vue
  64. 935 0
      components/tq-face/index copy.vue
  65. 969 0
      components/tq-face/index.vue
  66. 89 0
      components/tq-maintenance-type/index.vue
  67. 113 0
      components/tq-rate-popup/index.vue
  68. 341 0
      components/tq-search-select/index.vue
  69. 96 0
      components/tq-select/index.vue
  70. 75 0
      components/tq-tab/index.vue
  71. 257 0
      components/tq-upload-img/index.vue
  72. 88 0
      components/tq-work-type/index.vue
  73. 89 0
      components/uploadImg/index.vue
  74. 9 0
      env.js
  75. 23 0
      index.html
  76. 157 0
      learnMobile/materialList/details.vue
  77. 392 0
      learnMobile/materialList/materialList.vue
  78. 403 0
      learnMobile/paperList/paperList.vue
  79. 503 0
      learnMobile/paperList/paperQuestionInfo.vue
  80. 370 0
      learnMobile/record/record.vue
  81. 405 0
      learnMobile/userPaperRecordList/userPaperRecordList.vue
  82. 42 0
      main.js
  83. 134 0
      manifest.json
  84. 74 0
      modifyManifest.js
  85. 734 0
      package-lock.json
  86. 24 0
      package.json
  87. 489 0
      pages.json
  88. 658 0
      pages/driverTask/index.vue
  89. 62 0
      pages/flow/diboot.vue
  90. 64 0
      pages/flow/flow.vue
  91. 21 0
      pages/flow/webview.js
  92. 73 0
      pages/flowPc/flowPc.vue
  93. 21 0
      pages/flowPc/webview.js
  94. 117 0
      pages/formTemplate/formTemplate.vue
  95. 1005 0
      pages/index/index - 副本.vue
  96. 660 0
      pages/index/index.vue
  97. 814 0
      pages/login/login.vue
  98. 208 0
      pages/realtimeWatch/module/appMap.vue
  99. 563 0
      pages/realtimeWatch/module/eleChoose.vue
  100. 207 0
      pages/realtimeWatch/module/h5Map.vue

+ 5 - 0
.gitignore

@@ -0,0 +1,5 @@
+/node_modules
+debug.log
+/unpackage
+.DS_Store
+yarn.lock

+ 20 - 0
.hbuilderx/launch.json

@@ -0,0 +1,20 @@
+{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
+  // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
+    "version": "0.0",
+    "configurations": [{
+     	"app-plus" : 
+     	{
+     		"launchtype" : "local"
+     	},
+     	"default" : 
+     	{
+     		"launchtype" : "local"
+     	},
+     	"mp-weixin" : 
+     	{
+     		"launchtype" : "local"
+     	},
+     	"type" : "uniCloud"
+     }
+    ]
+}

+ 313 - 0
App.vue

@@ -0,0 +1,313 @@
+<!--
+ * @Author: yangpeiqin
+ * @Date: 2023-12-11 10:13:55
+ * @LastEditors: yangpeiqin
+ * @LastEditTime: 2023-12-15 15:35:36
+ * @FilePath: \gdtq_admin_app移动端\App.vue
+-->
+<script>
+import { getToken } from '@/utils/auth'
+import * as dd from 'dingtalk-jsapi'; // 此方式为整体加载,也可按需进行加载
+import Vue from 'vue'
+import {getThirdH5Auth,h5GetToken} from '@/api/login.js'
+
+export default {
+    onLaunch: function ({query}) {
+        console.log('App Launch')
+        this.$store.dispatch('appConfig/ConfigList');
+		
+		if(Vue.prototype.hasHostPlat(["dingH5"]) && Vue.prototype.hasPlat(["enterprises"])) {
+			this.handleDingLogin(query)
+		} else if(Vue.prototype.hasHostPlat(["dingH5Ins"]) && Vue.prototype.hasPlat(["enterprises"])) {
+			this.handleDingInsLogin(query)
+		} else {
+			this.otherLogin(query)
+		}
+    },
+    onShow: function () {
+        console.log('App Show')
+		// this.checkTabPer()
+		
+    },
+    onHide: function () {
+        console.log('App Hide')
+    },
+    methods: {
+		checkTabPer() {
+			if(Vue.prototype.hasPlat(["enterprises"])) {
+			
+				if(Vue.prototype.$hasPermi('mobile:index:base:driverTask')) {
+					// 司机任务tab
+					uni.setTabBarItem({
+						index:1, //从左到右 0开始
+						visible:true ,//默认true false隐藏
+					})
+				} else {
+					// 司机任务tab
+					uni.setTabBarItem({
+						index:1, //从左到右 0开始
+						visible:false ,//默认true false隐藏
+					})
+				}
+			} else {
+				// 司机任务tab
+				uni.setTabBarItem({
+					index:1, //从左到右 0开始
+					visible:false ,//默认true false隐藏
+				})
+			}
+			
+		},
+		otherLogin(query) {
+			if (query.token) {
+				this.$store.dispatch('setTokenData', {
+					token: query.token
+				}).then(() => {
+					this.getUserInfo()
+				})
+				return
+			}
+			
+			if (getToken()) {
+				this.getUserInfo()
+			}
+		},
+		//钉钉第三方应用登录
+		handleDingLogin(query={}) {
+			
+			if(dd.env.platform == "notInDingTalk") return
+			
+			if (query.token) {
+				this.$store.dispatch('setTokenData', {
+					token: query.token
+				}).then(() => {
+					this.getUserInfo()
+				})
+				return
+			}
+			
+			if(query && query.corpId) {
+				uni.setStorageSync("corpId", query.corpId);
+				this.dingLogin(query.corpId)
+				
+				return
+			} else if (getToken()) {
+				this.getUserInfo()
+			}
+			
+		},
+		//钉钉内部应用登录
+		handleDingInsLogin(query={}) {
+			
+			if(dd.env.platform == "notInDingTalk") return
+			
+			if (query.token) {
+				this.$store.dispatch('setTokenData', {
+					token: query.token
+				}).then(() => {
+					this.getUserInfo()
+				})
+				return
+			}
+			
+			if(query && query.corpId) {
+				uni.setStorageSync("corpId", query.corpId);
+				this.dingInsLogin(query.corpId)
+				
+				return
+			} else if (getToken()) {
+				this.getUserInfo()
+			}
+			
+		},
+		getUserInfo() {
+			
+			this.$store.dispatch('GetInfo').then(res => {
+		
+				const {path} = this.$route
+				if(path =='/') {
+					uni.switchTab({
+						url: '/pages/index/index'
+					});
+				}
+				this.checkTabPer()
+				
+			})
+		},
+		// 第三方应用获取token
+		dingLogin(corpId) {
+
+		  dd.getAuthCode({
+		    corpId: corpId,
+		    // corpId: 'ding7ea3eb9200c0101c35c2f4657eb6378f',
+		    success: (res) => {
+				
+		      const { code } = res;
+		 
+		      if(code) {
+				  
+		        getThirdH5Auth({code,corpId}).then((res) => {
+					
+					this.$store.state.user.isModel = false
+					this.$store.state.user.cacheToken = null
+					
+					this.$store.dispatch('setTokenData', {
+						token: res.data
+					}).then(() => {
+						this.getUserInfo()
+					})
+		        }).catch((err) => {
+					
+					uni.showModal({
+						title: '提示',
+						content: `${err}`,
+						showCancel:false,
+						confirmText: '确定',
+						success: function(res) {
+							if (res.confirm) {
+								
+							}
+						}
+					})
+					
+				})
+		      }
+		    },
+		    fail: (res) => {
+
+				uni.showModal({
+					title: '提示',
+					content: `${res.errorMessage}`,
+					showCancel:false,
+					confirmText: '确定',
+					success: function(res) {
+						if (res.confirm) {
+							
+						}
+					}
+				})
+			},
+		    complete: () => {},
+		  });
+		},
+		// 内部应用获取token
+		dingInsLogin(corpId) {
+
+		  dd.getAuthCode({
+		    corpId: corpId,
+		    // corpId: 'ding7ea3eb9200c0101c35c2f4657eb6378f',
+		    success: (res) => {
+				
+		      const { code } = res;
+		 
+		      if(code) {
+				  
+		        h5GetToken({code,corpId}).then((res) => {
+					
+					this.$store.state.user.isModel = false
+					this.$store.state.user.cacheToken = null
+					
+					this.$store.dispatch('setTokenData', {
+						token: res.data
+					}).then(() => {
+						this.getUserInfo()
+					})
+		        }).catch((err) => {
+					
+					uni.showModal({
+						title: '提示',
+						content: `${err}`,
+						showCancel:false,
+						confirmText: '确定',
+						success: function(res) {
+							if (res.confirm) {
+								
+							}
+						}
+					})
+					
+				})
+		      }
+		    },
+		    fail: (res) => {
+
+				uni.showModal({
+					title: '提示',
+					content: `${res.errorMessage}`,
+					showCancel:false,
+					confirmText: '确定',
+					success: function(res) {
+						if (res.confirm) {
+							
+						}
+					}
+				})
+			},
+		    complete: () => {},
+		  });
+		},
+		
+		
+        //下载
+        doUpData(appUrl) {
+            uni.showLoading({
+                title: '更新中……'
+            })
+
+            uni.downloadFile({//执行下载
+                url: appUrl, //下载地址
+                success: downloadResult => {//下载成功
+                    uni.hideLoading();
+                    if (downloadResult.statusCode == 200) {
+                        uni.showModal({
+                            title: '',
+                            content: '更新成功,确定现在重启吗?',
+                            confirmText: '重启',
+                            confirmColor: '#EE8F57',
+                            success: function (res) {
+                                if (res.confirm == true) {
+                                    plus.runtime.install(//安装
+                                        downloadResult.tempFilePath, {
+                                        force: true
+                                    },
+                                        function (res) {
+                                            utils.showToast('更新成功,重启中');
+                                            plus.runtime.restart();
+                                        }
+                                    );
+                                }
+                            }
+                        });
+                    }
+                }
+            });
+        },
+    }
+}
+</script>
+
+<style lang="scss">
+@import '@/common/css/icon.css';
+@import "uview-ui/index.scss";
+@import "@/assets/styles/uview-ui.scss";
+
+// 默认背景颜色
+page {
+    background: #f3f4f8;
+    width: 100%;
+    height: 100%;
+}
+
+view {
+    width: auto;
+	word-wrap: break-word;
+	white-space: normal;
+	word-break: break-all;
+}
+
+.u-cell {
+    width: 100%;
+}
+
+/*每个页面公共css */
+</style>

+ 7 - 0
README.md

@@ -0,0 +1,7 @@
+##配置文件 新增env.js 区分行业
+
+##平台类型区分指令 v-if="hasPlat(['enterprises'])"
+
+##包名 com.tianqing.app
+##证书别名 appalias
+##私钥密码 

+ 381 - 0
alarm/blindSpot/detail.vue

@@ -0,0 +1,381 @@
+<template>
+	<view class="driver_class">
+		<view style="width: 100%;">
+			<video style="width: 100%;" :src="videoUrl"></video>
+		</view>
+		<view class="image" v-if="images.length">
+			<view v-for="(item, index) in images" :key="item">
+				<image style="width: 220rpx;" @click="previewImage(item, index)" :src="item" mode="widthFix"></image>
+			</view>
+		</view>
+		<view class="alarm-type">
+			<view class="alarm-type-title">
+				报警类型
+			</view>
+			<view class="alarm-type-item">
+				<view class="label">
+					类型:
+				</view>
+				<view class="" style="color: #FF3140;">
+					{{ info.alarmType }}
+				</view>
+			</view>
+			<view class="alarm-type-item">
+				<view class="label">
+					等级:
+				</view>
+				<view style="color: #F7B500;">
+					{{ info.level }}
+				</view>
+			</view>
+		</view>
+		<view class="alarm-detail">
+			<view class="alarm-detail-title">
+				详细信息
+			</view>
+			<view class="alarm-detail-name">
+				<view class="alarm-detail-image">
+					<image :src="$getImages('/assetsMobile/images/alarm/touxiang.png')" style="width: 120rpx;height: 120rpx;"
+						mode=""></image>
+				</view>
+				<view class="image-right">
+					<view class="alarm-detail-form">
+						<view class="label">
+							车牌号:
+						</view>
+						<view class="val">
+							{{ info.plate }}
+						</view>
+					</view>
+					<view class="alarm-detail-form">
+						<view class="label">
+							驾驶员名称:
+						</view>
+						<view class="val">
+							{{ info.driverName || '暂无信息' }}
+						</view>
+					</view>
+				</view>
+			</view>
+			<view class="alarm-detail-form top">
+				<view class="label">
+					司机电话:
+				</view>
+				<view class="val">
+					{{ info.driverPhone }}
+				</view>
+			</view>
+			<view class="alarm-detail-form top">
+				<view class="label">
+					企业名称:
+				</view>
+				<view class="val">
+					{{ info.deptName }}
+				</view>
+			</view>
+			<view class="alarm-detail-form top">
+				<view class="label">
+					通信号码:
+				</view>
+				<view class="val">
+					{{ info.phone }}
+				</view>
+			</view>
+			<view class="alarm-detail-form top">
+				<view class="label">
+					报警类型:
+				</view>
+				<view class="val">
+					{{ info.alarmType }}
+				</view>
+			</view>
+			<view class="alarm-detail-form top">
+				<view class="label">
+					报警时间:
+				</view>
+				<view class="val">
+					{{ info.alarmTime }}
+				</view>
+			</view>
+			<view class="alarm-detail-form top">
+				<view class="label">
+					车速:
+				</view>
+				<view class="val">
+					{{ info.speed }}km/h
+				</view>
+			</view>
+			<view class="alarm-detail-form top">
+				<view class="label">
+					经度:
+				</view>
+				<view class="val">
+					{{ info.lng }}
+				</view>
+			</view>
+			<view class="alarm-detail-form top">
+				<view class="label">
+					纬度:
+				</view>
+				<view class="val">
+					{{ info.lat }}
+				</view>
+			</view>
+			<!-- 			<view class="alarm-detail-form top">
+				<view class="label">
+					车辆状态:
+				</view>
+				<view class="val">
+					{{info.phone}}
+				</view>
+			</view> -->
+			<view class="alarm-detail-form top">
+				<view class="label" style="width: 170rpx;">
+					报警地址:
+				</view>
+				<view class="val" style="color: #4573FC;text-decoration:underline;width: calc(100% - 170rpx);"
+					@click="goHistory(info)">
+					{{ info.address }}>>
+				</view>
+			</view>
+		</view>
+		<view style="height: 28rpx;">
+
+		</view>
+		<!-- 		<view class="exports" @click="exportZip">
+			<image src="../../static/vehicle/icon_evidence_derive_24.png" style="width: 48rpx;height: 48rpx;margin-right: 16rpx;" mode=""></image>
+			证据导出
+		</view> -->
+	</view>
+</template>
+
+<script>
+import { selectByFileuuid, getAlarmBlindSpotDetailByFileuuid, exportAlarm } from "@/api/alarm/blindSpot.js"
+import { getToken } from '@/utils/auth'
+export default {
+	components: {},
+	props: {},
+	data() {
+		return {
+			images: [], // 图片数组
+			vehSpeed: 0,
+			fid: '',
+			page: '',
+			videoUrl: '',
+			info: {},
+		};
+	},
+	onLoad(option) {
+		this.fid = this.$route.query.fid;
+		this.page = this.$route.query.type;
+	},
+	created() { },
+	mounted() {
+		this.$nextTick(() => {
+			this.queryVideoImages();
+			this.queryBasicInfo();
+		});
+	},
+	watch: {},
+	computed: {},
+	methods: {
+		previewImage(item, index) {
+			uni.previewImage({
+				current: item,
+				urls: this.images,
+				indicator: index
+			})
+		},
+		async queryVideoImages() {
+			// 查询视频和图片
+			let videoList;
+			this.images = [];
+			let res = await selectByFileuuid(this.fid);
+			if (res.code == "0") {
+				for (let i = 0; i < res.data.length; i++) {
+					if (res.data[i].fileType == "02") {
+						videoList = res.data[i].fileName.replace(".h264", ".mp4");
+						this.videoUrl = this.$getImages("/img/alarmvideo/" + videoList)
+						console.log(this.url)
+					}
+					if (res.data[i].fileType == "00") {
+						this.images.push(
+							this.$getImages("/img/alarmvideo/" + res.data[i].fileName)
+						);
+					}
+				}
+			}
+		},
+		checkData(data) {
+			// 检测数据
+			return data == null || data == undefined ? "" : data;
+		},
+		async queryBasicInfo() {
+			// 查询视频和图片
+			let videoList;
+			this.images = [];
+			let res = await getAlarmBlindSpotDetailByFileuuid(this.fid);
+			if (res.code == "0") {
+				this.info = {
+					...res.data,
+					deptName: this.checkData(res.data.deptName),
+					driverName: this.checkData(res.data.driverName),
+					driverPhone: this.checkData(res.data.driverPhone),
+					plate: this.checkData(res.data.plate),
+					alarmType: this.checkData(res.data.alarmType),
+					alarmTime: this.checkData(res.data.startTime),
+					speed: this.checkData(res.data.speed),
+					lng: this.checkData(res.data.lng),
+					lat: this.checkData(res.data.lat),
+					level: this.checkData(res.data.alarmGrade),
+					vehicleId: this.checkData(res.data.vehicleId),
+					address: this.checkData(res.data.location),
+				}
+			}
+		},
+
+		// 跳到历史轨迹
+		goHistory(item) {
+			let params = {
+				tabStyleType: 'vehicle'
+			}
+			params.vehicleId = item.vehicleId
+
+			let unixTime = this.dayjs(item.startTime).unix()
+
+			let s_time = this.dayjs(item.startTime).format('YYYY-MM-DD HH:mm')
+
+			let e_time = this.dayjs.unix(unixTime + 60).format('YYYY-MM-DD HH:mm')
+			let startTime = s_time;
+			let endTime = e_time;
+			params.time = [startTime, endTime]
+			this.$store.dispatch('setParams', params)
+			uni.navigateTo({
+				url: '/pagesMap/pastRoute/pastRoute',
+			});
+
+		},
+	},
+
+
+
+};
+</script>
+
+<style lang="scss" scoped>
+.driver_class {
+	width: 100%;
+	height: 100%;
+}
+
+.image {
+	width: 100%;
+	// padding: 24rpx 24rpx 0 24rpx;
+	display: flex;
+	justify-content: space-around;
+	margin-top: 15rpx;
+}
+
+.alarm-type {
+	width: calc(100% - 110rpx);
+	margin: 16rpx 20rpx;
+	border-radius: 16rpx;
+	padding: 36rpx;
+	display: flex;
+	flex-direction: column;
+	background-color: #fff;
+	margin-top: 16rpx;
+}
+
+.alarm-type-title {
+	color: #4573FC;
+	font-size: 36rpx;
+	font-weight: bold;
+}
+
+.alarm-type-item {
+	display: flex;
+	justify-content: space-between;
+	font-size: 32rpx;
+	margin-top: 28rpx;
+
+	.label {
+		color: #999999;
+	}
+}
+
+.alarm-detail {
+	width: calc(100% - 116rpx);
+	padding: 38rpx;
+	display: flex;
+	flex-direction: column;
+	border-radius: 8rpx;
+	background-color: #fff;
+	margin: 16rpx 20rpx;
+	font-size: 32rpx;
+	margin-top: 20rpx;
+
+	.alarm-detail-title {
+		color: #4573FC;
+		font-size: 36rpx;
+		font-weight: bold;
+	}
+
+	.alarm-detail-name {
+		display: flex;
+		align-items: center;
+		margin-top: 18rpx;
+
+		.alarm-detail-image {
+			width: 120rpx;
+			height: 120rpx;
+			overflow: hidden;
+			border-radius: 50%;
+			background-color: #C3C7CC;
+		}
+	}
+
+	.image-right {
+		flex: 1;
+		height: 120rpx;
+		margin-left: 24rpx;
+		display: flex;
+		flex-direction: column;
+		justify-content: space-between;
+	}
+
+	.alarm-detail-form {
+		display: flex;
+		justify-content: space-between;
+		width: 100%;
+
+		.label {
+			color: #999999;
+		}
+
+		.val {
+			color: #666666;
+		}
+	}
+
+	.top {
+		margin-top: 28rpx;
+	}
+
+
+}
+
+.exports {
+	width: calc(100% - 48rpx);
+	margin: 24rpx;
+	padding: 24rpx 0;
+	margin-bottom: 24rpx;
+	background-color: #4573FC;
+	border-radius: 20rpx;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	color: #fff;
+	font-size: 36rpx;
+}
+</style>

+ 274 - 0
alarm/blindSpot/index.vue

@@ -0,0 +1,274 @@
+<template>
+  <view class="pageContainer">
+	  <view class="search">
+		<u-cell title="报警时间" :value="calendarStart + ' 至 ' + calendarEnd" style="height: 86rpx"
+		  @click="$refs.calendarRef.show()">
+
+		</u-cell>
+		<tq-calendar ref="calendarRef" type="daterange" @confirm="(e) => {
+		  calendarStart = e[0]
+		  calendarEnd = e[1]
+		  handleTableList()
+		}">
+		</tq-calendar>
+
+		<u-cell title="车辆" :border="true" :isLink="true" @click="$refs.carChooseRef.show()">
+		  <view slot="value">
+			<!-- <u-form-item label="" prop="vehicleId"> -->
+			  {{ plate }}
+			<!-- </u-form-item> -->
+		  </view>
+		  <u-icon v-if="plate == ''" slot="right-icon" size="16" name="arrow-right"></u-icon>
+		  <view v-else slot="right-icon" @click.stop="resetData" style="margin-left: 20rpx;">
+			<u-icon size="16" name="close-circle"></u-icon>
+		  </view>
+		</u-cell>
+		<tq-car-user ref="carChooseRef" :isGetIcon="true" @confirm="(data) => {
+		  vehicleId = data.id
+		  plate = data.label
+		  handleTableList()
+		}">
+		</tq-car-user>
+	</view>
+    <view class="body">
+
+      <view class="list" v-for="item in tableList" :key="item.id">
+        <view style="
+					  display: flex;
+					  margin-bottom: 20rpx;
+					  margin-top: 20rpx;">
+          <view class="head" style="font-size: 26rpx">
+            <text style="font-weight: bold;">车辆:{{ item.plate }}</text>
+          </view>
+          <view class="head" style="margin-left: 73rpx">
+            <text style="font-weight: bold;">
+              <text>{{ item.deptName }}</text>
+            </text>
+          </view>
+        </view>
+        <view style="
+					  margin-bottom: 20rpx;
+					    display: flex;
+					">
+          <view class="head" style="font-size: 26rpx">
+            <text>司机:
+              <text>
+                {{ item.driverName }}
+              </text>
+            </text>
+          </view>
+          <view class="head" style="margin-left: 26rpx">
+            <text>{{ item.driverPhone }}</text>
+          </view>
+        </view>
+        <view>
+          <view class="head" style="font-size: 26rpx;margin-bottom: 20rpx;">
+            <text style="font-size: 30rpx; ">报警开始时间:{{ item.startTime }}</text>
+          </view>
+          <view class="head" style="font-size: 26rpx;margin-bottom: 20rpx;">
+            <text style="font-size: 30rpx;">报警结束时间:{{ item.startTime }}</text>
+          </view>
+        </view>
+        <view style="margin-bottom: 20rpx;">
+          <view class="head" style="font-size: 26rpx">
+            <view style="width: 20%;align-items: flex-start;">
+              报警位置:
+            </view>
+            <text style="font-size: 30rpx;flex: 1;">{{ item.location }}</text>
+          </view>
+        </view>
+        <view style="height: 90rpx;">
+          <u-button type="primary" @click="seeAlarmDetails(item)">查看详情</u-button>
+        </view>
+
+      </view>
+      <view class="nodata-warp" v-if="!tableList.length">
+        <image :src="$getImages('/assetsMobile/images/no-data.png')" class="nodata-image"></image>
+      </view>
+		<u-loadmore :status="loadStatus" height="30"/>
+    </view>
+
+    <u-calendar :show="calendarShow" mode="range" :defaultDate="defaultDate" :maxDate="maxDate" minDate="2023-10-01"
+      monthNum="24" @confirm="calendarChange" @close="onClose"></u-calendar>
+
+  </view>
+</template>
+
+<script>
+import { getBlindSpotList } from "@/api/alarm/blindSpot.js"
+export default {
+  components: {
+  },
+  props: {},
+  data() {
+    return {
+      vehicleId: '',
+      plate: '',
+      tableList: [],
+      calendarShow: false, // 日历
+      calendarStart: this.dayjs().subtract(6, 'day').format('YYYY-MM-DD'),// 开始时间
+      calendarEnd: this.dayjs().subtract(0, 'day').format('YYYY-MM-DD'), // 结束时间
+      defaultDate: [this.dayjs().subtract(6, 'day').format('YYYY-MM-DD'), this.dayjs().subtract(0, 'day').format('YYYY-MM-DD')],
+      page: 1,
+      size: 10,
+      total: 0,
+      maxDate: this.dayjs().subtract(0, 'day').format('YYYY-MM-DD'),
+	  loadStatus: 'nomore', //loading nomore loadmore
+    };
+  },
+  mounted() {
+    this.handleTableList();
+  },
+  onReachBottom() {
+    // 滚动到底部
+    if (this.page * this.size < this.total) {
+      this.page += 1;
+      this.handleTableList();
+    }
+  },
+  methods: {
+    checkTime() {
+      this.calendarShow = true
+    },
+    resetData() {
+      this.vehicleId = ''
+      this.plate = ''
+      this.page = 1;
+      this.tableList = [];
+      this.handleTableList();
+    },
+    handleDataDetection(data) {
+      // 数据检测
+      return data == null || data == undefined ? "" : data;
+    },
+    async handleTableList() {
+      // 获取列表
+      // this.tableList = [];
+	  this.loadStatus = 'loading'
+	  
+      let obj = {
+        startTime: this.calendarStart == "" || this.calendarStart == null ?
+          undefined : this.calendarStart + ' 00:00:00',
+        endTime: this.calendarEnd == "" || this.calendarEnd == null ?
+          undefined : this.calendarEnd + ' 23:59:59',
+        plate: this.plate ? this.plate : undefined,
+        vehicleId: this.vehicleId ? this.vehicleId : undefined,
+        driverAdvanceType: 'BSD',
+        page: this.page,
+        size: 10
+      };
+      let res = await getBlindSpotList(obj);
+     if (res.code == "0" && res.data.list) {
+     		  
+     		  res.data.list.map((e) => {
+     		  	this.tableList.push({
+     		  	    ...e,
+     		  	});
+     		  })
+     		  
+       // this.tableList = res.data.list
+     		
+       this.total = res.data.total;
+       if (this.page * 10 >= this.total)  {
+       	this.loadStatus = 'nomore'
+       }
+     }  else {
+     			this.loadStatus = 'nomore'
+     		}
+    },
+    calendarChange(e) {
+      this.page = 1;
+      this.tableList = [];
+      // 修改日期
+      this.calendarStart = e[0]; // 开始时间
+      this.calendarEnd = e[1]; // 结束时间
+      this.calendarShow = false;
+      this.handleTableList();
+    },
+    onClose() {
+      this.calendarShow = false;
+      this.handleTableList();
+    },
+    seeAlarmDetails(item) {
+      // 报警详情
+      uni.navigateTo({
+        url: `/alarm/blindSpot/detail?fid=${item.fileuuid}`
+      })
+    },
+  },
+};
+</script>
+<style lang="scss" scoped>
+.pageContainer {
+  overflow: unset;
+  // background-color: #fff;
+}
+
+.search {
+  position: relative;
+  padding: 0 0rpx;
+  width: 100%;
+  height: 166rpx;
+  // display: flex;
+  // align-items: center;
+  background-color: #fff;
+}
+
+.head {
+  height: 100%;
+  font-size: 28rpx;
+  display: flex;
+  align-items: center;
+  justify-content: flex-start;
+  color: #333333;
+  
+}
+
+.content {
+  height: 100%;
+  font-size: 28rpx;
+  display: flex;
+  align-items: center;
+  justify-content: flex-start;
+  color: #333333;
+}
+
+.body {
+	margin-top: 20rpx;
+  position: relative;
+  padding: 10rpx;
+  width: 100%;
+  height: calc(100% - 172rpx);
+  
+}
+
+.body .total {
+  width: 100%;
+  height: 65rpx;
+  line-height: 65rpx;
+  font-size: 26rpx;
+  color: #999999;
+}
+
+.list {
+	background-color: #fff;
+  width: calc(100% - 90rpx);
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  padding: 32rpx;
+  background-color: white;
+  border-radius: 20rpx;
+  margin-bottom: 30rpx;
+}
+.nodata-warp{
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 100rpx 0;
+  .nodata-image{
+    width: 218rpx;
+    height: 150rpx;
+  }
+}
+</style>

+ 383 - 0
alarm/driverBehavior/detail.vue

@@ -0,0 +1,383 @@
+<template>
+	<view class="driver_class">
+		<view style="width: 100%;">
+			<video style="width: 100%;" :src="videoUrl"></video>
+		</view>
+		<view class="image" v-if="images.length">
+			<view v-for="(item, index) in images" :key="item">
+				<image style="width: 220rpx;" @click="previewImage(item, index)" :src="item" mode="widthFix"></image>
+			</view>
+		</view>
+		<view class="alarm-type">
+			<view class="alarm-type-title">
+				报警类型
+			</view>
+			<view class="alarm-type-item">
+				<view class="label">
+					类型:
+				</view>
+				<view class="" style="color: #FF3140;">
+					{{ info.alarmType }}
+				</view>
+			</view>
+			<view class="alarm-type-item">
+				<view class="label">
+					等级:
+				</view>
+				<view style="color: #F7B500;">
+					{{ info.level }}
+				</view>
+			</view>
+		</view>
+		<view class="alarm-detail">
+			<view class="alarm-detail-title">
+				详细信息
+			</view>
+			<view class="alarm-detail-name">
+				<view class="alarm-detail-image">
+					<image :src="$getImages('/assetsMobile/images/alarm/touxiang.png')" style="width: 120rpx;height: 120rpx;"
+						mode=""></image>
+				</view>
+				<view class="image-right">
+					<view class="alarm-detail-form">
+						<view class="label">
+							车牌号:
+						</view>
+						<view class="val">
+							{{ info.plate }}
+						</view>
+					</view>
+					<view class="alarm-detail-form">
+						<view class="label">
+							驾驶员名称:
+						</view>
+						<view class="val">
+							{{ info.driverName || '暂无信息' }}
+						</view>
+					</view>
+				</view>
+			</view>
+			<view class="alarm-detail-form top">
+				<view class="label">
+					司机电话:
+				</view>
+				<view class="val">
+					{{ info.driverPhone }}
+				</view>
+			</view>
+			<view class="alarm-detail-form top">
+				<view class="label">
+					企业名称:
+				</view>
+				<view class="val">
+					{{ info.deptName }}
+				</view>
+			</view>
+			<!-- <view class="alarm-detail-form top">
+				<view class="label">
+					通信号码:
+				</view>
+				<view class="val">
+					{{ info.phone }}
+				</view>
+			</view> -->
+			<view class="alarm-detail-form top">
+				<view class="label">
+					报警类型:
+				</view>
+				<view class="val">
+					{{ info.alarmType }}
+				</view>
+			</view>
+			<view class="alarm-detail-form top">
+				<view class="label">
+					报警时间:
+				</view>
+				<view class="val">
+					{{ info.alarmTime }}
+				</view>
+			</view>
+			<view class="alarm-detail-form top">
+				<view class="label">
+					车速:
+				</view>
+				<view class="val">
+					{{ info.speed }}km/h
+				</view>
+			</view>
+			<view class="alarm-detail-form top">
+				<view class="label">
+					经度:
+				</view>
+				<view class="val">
+					{{ info.lng }}
+				</view>
+			</view>
+			<view class="alarm-detail-form top">
+				<view class="label">
+					纬度:
+				</view>
+				<view class="val">
+					{{ info.lat }}
+				</view>
+			</view>
+			<!-- 			<view class="alarm-detail-form top">
+				<view class="label">
+					车辆状态:
+				</view>
+				<view class="val">
+					{{info.phone}}
+				</view>
+			</view> -->
+			<view class="alarm-detail-form top">
+				<view class="label" style="width: 170rpx;">
+					报警地址:
+				</view>
+				<view class="val" style="color: #4573FC;text-decoration:underline;width: calc(100% - 170rpx);"
+					@click="goHistory(info)">
+					{{ info.address }}>>
+				</view>
+			</view>
+		</view>
+		<view style="height: 28rpx;">
+
+		</view>
+		<!-- 		<view class="exports" @click="exportZip">
+			<image src="../../static/vehicle/icon_evidence_derive_24.png" style="width: 48rpx;height: 48rpx;margin-right: 16rpx;" mode=""></image>
+			证据导出
+		</view> -->
+	</view>
+</template>
+
+<script>
+import { selectByFileuuid, getAlarmDriverDetailByFileuuid, exportAlarm } from "@/api/alarm/driverBehavior.js"
+import { getToken } from '@/utils/auth'
+export default {
+	components: {},
+	props: {},
+	data() {
+		return {
+			images: [], // 图片数组
+			vehSpeed: 0,
+			fid: '',
+			page: '',
+			videoUrl: '',
+			info: {},
+		};
+	},
+	onLoad(option) {
+		this.fid = this.$route.query.fid;
+		this.page = this.$route.query.type;
+	},
+	created() { },
+	mounted() {
+		this.$nextTick(() => {
+			this.queryVideoImages();
+			this.queryBasicInfo();
+		});
+	},
+	watch: {},
+	computed: {},
+	methods: {
+		previewImage(item, index) {
+			uni.previewImage({
+				current: item,
+				urls: this.images,
+				indicator: index
+			})
+		},
+		async queryVideoImages() {
+			// 查询视频和图片
+			let videoList;
+			this.images = [];
+			let res = await selectByFileuuid(this.fid);
+			if (res.code == "0") {
+				for (let i = 0; i < res.data.length; i++) {
+					if (res.data[i].fileType == "02") {
+						videoList = res.data[i].fileName.replace(".h264", ".mp4");
+						this.videoUrl = this.$getImages("/img/alarmvideo/" + videoList)
+						console.log(this.url)
+					}
+					if (res.data[i].fileType == "00") {
+						this.images.push(
+							this.$getImages("/img/alarmvideo/" + res.data[i].fileName)
+						);
+					}
+				}
+			}
+		},
+		checkData(data) {
+			// 检测数据
+			return data == null || data == undefined ? "" : data;
+		},
+		async queryBasicInfo() {
+			// 查询视频和图片
+			let videoList;
+			this.images = [];
+			let res = await getAlarmDriverDetailByFileuuid(this.fid);
+			if (res.code == "0") {
+				this.info = {
+					...res.data,
+					deptName: this.checkData(res.data.deptName),
+					driverName: this.checkData(res.data.driverName),
+					driverPhone: this.checkData(res.data.driverPhone),
+					plate: this.checkData(res.data.plate),
+					alarmType: this.checkData(res.data.alarmType),
+					alarmTime: this.checkData(res.data.startTime),
+					speed: this.checkData(res.data.speed),
+					lng: this.checkData(res.data.lng),
+					lat: this.checkData(res.data.lat),
+					level: this.checkData(res.data.alarmGrade),
+					vehicleId: this.checkData(res.data.vehicleId),
+					address: this.checkData(res.data.location),
+
+				}
+
+			}
+		},
+
+		// 跳到历史轨迹
+		goHistory(item) {
+			let params = {
+				tabStyleType: 'vehicle'
+			}
+			params.vehicleId = item.vehicleId
+
+			let unixTime = this.dayjs(item.startTime).unix()
+
+			let s_time = this.dayjs(item.startTime).format('YYYY-MM-DD HH:mm')
+
+			let e_time = this.dayjs.unix(unixTime + 60).format('YYYY-MM-DD HH:mm')
+			let startTime = s_time;
+			let endTime = e_time;
+			params.time = [startTime, endTime]
+			console.info(params.time)
+			this.$store.dispatch('setParams', params)
+			uni.navigateTo({
+				url: '/pagesMap/pastRoute/pastRoute',
+			});
+
+		},
+	},
+
+
+
+};
+</script>
+<style lang="scss" scoped>
+.driver_class {
+	width: 100%;
+	height: 100%;
+}
+
+.image {
+	width: 100%;
+	// padding: 24rpx 24rpx 0 24rpx;
+	display: flex;
+	justify-content: space-around;
+	margin-top: 15rpx;
+}
+
+.alarm-type {
+	width: calc(100% - 40rpx);
+	margin: 4rpx;
+	border-radius: 16rpx;
+	padding: 16rpx;
+	display: flex;
+	flex-direction: column;
+	background-color: #fff;
+	margin-top: 15rpx;
+}
+
+.alarm-type-title {
+	color: #4573FC;
+	font-size: 36rpx;
+	font-weight: bold;
+}
+
+.alarm-type-item {
+	display: flex;
+	justify-content: space-between;
+	font-size: 32rpx;
+	margin-top: 28rpx;
+
+	.label {
+		color: #999999;
+	}
+}
+
+.alarm-detail {
+	width: calc(100% - 50rpx);
+	padding: 20rpx;
+	display: flex;
+	flex-direction: column;
+	border-radius: 8rpx;
+	background-color: #fff;
+	margin: 6rpx;
+	font-size: 32rpx;
+	margin-top: 20rpx;
+
+	.alarm-detail-title {
+		color: #4573FC;
+		font-size: 36rpx;
+		font-weight: bold;
+	}
+
+	.alarm-detail-name {
+		display: flex;
+		align-items: center;
+		margin-top: 18rpx;
+
+		.alarm-detail-image {
+			width: 120rpx;
+			height: 120rpx;
+			overflow: hidden;
+			border-radius: 50%;
+			background-color: #C3C7CC;
+		}
+	}
+
+	.image-right {
+		flex: 1;
+		height: 120rpx;
+		margin-left: 24rpx;
+		display: flex;
+		flex-direction: column;
+		justify-content: space-between;
+	}
+
+	.alarm-detail-form {
+		display: flex;
+		justify-content: space-between;
+		width: 100%;
+
+		.label {
+			color: #999999;
+		}
+
+		.val {
+			color: #666666;
+		}
+	}
+
+	.top {
+		margin-top: 28rpx;
+	}
+
+
+}
+
+.exports {
+	width: calc(100% - 48rpx);
+	margin: 24rpx;
+	padding: 24rpx 0;
+	margin-bottom: 24rpx;
+	background-color: #4573FC;
+	border-radius: 20rpx;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	color: #fff;
+	font-size: 36rpx;
+}
+</style>

+ 278 - 0
alarm/driverBehavior/index.vue

@@ -0,0 +1,278 @@
+<template>
+  <view class="pageContainer">
+	  <view class="search">
+    <u-cell title="报警时间" :value="calendarStart + ' 至 ' + calendarEnd" style="height: 86rpx"
+      @click="$refs.calendarRef.show()">
+
+    </u-cell>
+    <tq-calendar ref="calendarRef" type="daterange" @confirm="(e) => {
+      calendarStart = e[0]
+      calendarEnd = e[1]
+	  page = 1;
+	  tableList = [];
+      handleTableList()
+    }">
+    </tq-calendar>
+
+    <u-cell title="车辆" :border="true" :isLink="true" @click="$refs.carChooseRef.show()">
+      <view slot="value">
+        <!-- <u-form-item label="" prop="vehicleId"> -->
+          {{ plate }}
+        <!-- </u-form-item> -->
+      </view>
+      <u-icon v-if="plate == ''" slot="right-icon" size="16" name="arrow-right"></u-icon>
+      <view v-else slot="right-icon" @click.stop="resetData" style="margin-left: 20rpx;">
+        <u-icon size="16" name="close-circle"></u-icon>
+      </view>
+    </u-cell>
+    <tq-car-user ref="carChooseRef" :isGetIcon="true" @confirm="(data) => {
+      vehicleId = data.id
+      plate = data.label
+	  page = 1;
+	  tableList = [];
+      handleTableList()
+    }">
+    </tq-car-user>
+	</view>
+    <view class="body">
+
+      <view class="list" v-for="item in tableList" :key="item.id">
+        <view style="
+					  display: flex;
+					  margin-bottom: 20rpx;
+					  margin-top: 20rpx;">
+          <view class="head" style="font-size: 26rpx">
+            <text style="font-weight: bold;">车辆:{{ item.plate }}</text>
+          </view>
+          <view class="head" style="margin-left: 73rpx">
+            <text style="font-weight: bold;">
+              <text>{{ item.deptName }}</text>
+            </text>
+          </view>
+        </view>
+        <view style="
+					  margin-bottom: 20rpx;
+					    display: flex;
+					">
+          <view class="head" style="font-size: 26rpx">
+            <text>司机:
+              <text>
+                {{ item.driverName }}
+              </text>
+            </text>
+          </view>
+          <view class="head" style="margin-left: 26rpx">
+            <text>{{ item.driverPhone }}</text>
+          </view>
+        </view>
+        <view>
+          <view class="head" style="font-size: 26rpx;margin-bottom: 20rpx;">
+            <text style="font-size: 30rpx; ">报警开始时间:{{ item.startTime }}</text>
+          </view>
+          <view class="head" style="font-size: 26rpx;margin-bottom: 20rpx;">
+            <text style="font-size: 30rpx;">报警结束时间:{{ item.startTime }}</text>
+          </view>
+        </view>
+        <view style="margin-bottom: 20rpx;">
+          <view class="head" style="font-size: 26rpx">
+            <view style="width: 20%;align-items: flex-start;">
+              报警位置:
+            </view>
+            <text style="font-size: 30rpx;flex: 1;">{{ item.location }}</text>
+          </view>
+        </view>
+        <view style="height: 90rpx;">
+          <u-button type="primary" @click="seeAlarmDetails(item)">查看详情</u-button>
+        </view>
+
+      </view>
+      <view class="nodata-warp" v-if="!tableList.length">
+        <image :src="$getImages('/assetsMobile/images/no-data.png')" class="nodata-image"></image>
+      </view>
+		<u-loadmore :status="loadStatus" height="30"/>
+    </view>
+
+    <u-calendar :show="calendarShow" mode="range" :defaultDate="defaultDate" :maxDate="maxDate" minDate="2023-10-01"
+      monthNum="24" @confirm="calendarChange" @close="onClose"></u-calendar>
+
+  </view>
+</template>
+
+<script>
+import { getAlarmDriverList } from "@/api/alarm/driverBehavior.js"
+export default {
+  components: {
+  },
+  props: {},
+  data() {
+    return {
+      vehicleId: '',
+      plate: '',
+      tableList: [],
+      calendarShow: false, // 日历
+      calendarStart: this.dayjs().subtract(6, 'day').format('YYYY-MM-DD'),// 开始时间
+      calendarEnd: this.dayjs().subtract(0, 'day').format('YYYY-MM-DD'), // 结束时间
+      defaultDate: [this.dayjs().subtract(6, 'day').format('YYYY-MM-DD'), this.dayjs().subtract(0, 'day').format('YYYY-MM-DD')],
+      page: 1,
+      size: 10,
+      total: 0,
+      maxDate: this.dayjs().subtract(0, 'day').format('YYYY-MM-DD'),
+	  loadStatus: 'nomore', //loading nomore loadmore
+    };
+  },
+  mounted() {
+    this.handleTableList();
+  },
+  onReachBottom() {
+    // 滚动到底部
+    if (this.page * this.size < this.total) {
+      this.page += 1;
+      this.handleTableList();
+    }
+  },
+  methods: {
+    checkTime() {
+      this.calendarShow = true
+    },
+    resetData() {
+      this.vehicleId = ''
+      this.plate = ''
+      this.page = 1;
+      this.tableList = [];
+      this.handleTableList();
+    },
+    handleDataDetection(data) {
+      // 数据检测
+      return data == null || data == undefined ? "" : data;
+    },
+    async handleTableList() {
+		this.loadStatus = 'loading'
+      // 获取列表
+      // this.tableList = [];
+      let obj = {
+        startTime: this.calendarStart == "" || this.calendarStart == null ?
+          undefined : this.calendarStart + ' 00:00:00',
+        endTime: this.calendarEnd == "" || this.calendarEnd == null ?
+          undefined : this.calendarEnd + ' 23:59:59',
+        plate: this.plate ? this.plate : undefined,
+        vehicleId: this.vehicleId ? this.vehicleId : undefined,
+        driverAdvanceType: 'DSM',
+        page: this.page,
+        size: 10
+      };
+      let res = await getAlarmDriverList(obj);
+      if (res.code == "0" && res.data.list) {
+		  
+		  res.data.list.map((e) => {
+		  	this.tableList.push({
+		  	    ...e,
+		  	});
+		  })
+		  
+        // this.tableList = res.data.list
+		
+        this.total = res.data.total;
+        if (this.page * 10 >= this.total)  {
+        	this.loadStatus = 'nomore'
+        }
+      }  else {
+			this.loadStatus = 'nomore'
+		}
+    },
+    calendarChange(e) {
+
+      this.page = 1;
+      this.tableList = [];
+      // 修改日期
+      this.calendarStart = e[0]; // 开始时间
+      this.calendarEnd = e[1]; // 结束时间
+      this.calendarShow = false;
+      this.handleTableList();
+    },
+    onClose() {
+      this.calendarShow = false;
+      this.handleTableList();
+    },
+    seeAlarmDetails(item) {
+      // 报警详情
+      uni.navigateTo({
+        url: `/alarm/driverBehavior/detail?fid=${item.fileuuid}`
+      })
+    },
+  },
+};
+</script>
+<style lang="scss" scoped>
+.pageContainer {
+  overflow: unset;
+  // background-color: #fff;
+}
+
+.search {
+  position: relative;
+  padding: 0 0rpx;
+  width: 100%;
+  height: 166rpx;
+  // display: flex;
+  // align-items: center;
+  background-color: #fff;
+}
+
+.head {
+  height: 100%;
+  font-size: 28rpx;
+  display: flex;
+  align-items: center;
+  justify-content: flex-start;
+  color: #333333;
+  
+}
+
+.content {
+  height: 100%;
+  font-size: 28rpx;
+  display: flex;
+  align-items: center;
+  justify-content: flex-start;
+  color: #333333;
+}
+
+.body {
+	margin-top: 20rpx;
+  position: relative;
+  padding: 10rpx;
+  width: 100%;
+  height: calc(100% - 172rpx);
+  
+}
+
+.body .total {
+  width: 100%;
+  height: 65rpx;
+  line-height: 65rpx;
+  font-size: 26rpx;
+  color: #999999;
+}
+
+.list {
+	background-color: #fff;
+  width: calc(100% - 90rpx);
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  padding: 32rpx;
+  background-color: white;
+  border-radius: 20rpx;
+  margin-bottom: 30rpx;
+}
+.nodata-warp{
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 100rpx 0;
+  .nodata-image{
+    width: 218rpx;
+    height: 150rpx;
+  }
+}
+</style>

+ 380 - 0
alarm/proactiveSecurity/detail.vue

@@ -0,0 +1,380 @@
+<template>
+	<view class="driver_class">
+		<view style="width: 100%;">
+			<video style="width: 100%;" :src="videoUrl"></video>
+		</view>
+		<view class="image" v-if="images.length">
+			<view v-for="(item, index) in images" :key="item">
+				<image style="width: 220rpx;" @click="previewImage(item, index)" :src="item" mode="widthFix"></image>
+			</view>
+		</view>
+		<view class="alarm-type">
+			<view class="alarm-type-title">
+				报警类型
+			</view>
+			<view class="alarm-type-item">
+				<view class="label">
+					类型:
+				</view>
+				<view class="" style="color: #FF3140;">
+					{{ info.alarmType }}
+				</view>
+			</view>
+			<view class="alarm-type-item">
+				<view class="label">
+					等级:
+				</view>
+				<view style="color: #F7B500;">
+					{{ info.level }}
+				</view>
+			</view>
+		</view>
+		<view class="alarm-detail">
+			<view class="alarm-detail-title">
+				详细信息
+			</view>
+			<view class="alarm-detail-name">
+				<view class="alarm-detail-image">
+					<image :src="$getImages('/assetsMobile/images/alarm/touxiang.png')" style="width: 120rpx;height: 120rpx;"
+						mode=""></image>
+				</view>
+				<view class="image-right">
+					<view class="alarm-detail-form">
+						<view class="label">
+							车牌号:
+						</view>
+						<view class="val">
+							{{ info.plate }}
+						</view>
+					</view>
+					<view class="alarm-detail-form">
+						<view class="label">
+							驾驶员名称:
+						</view>
+						<view class="val">
+							{{ info.driverName || '暂无信息' }}
+						</view>
+					</view>
+				</view>
+			</view>
+			<view class="alarm-detail-form top">
+				<view class="label">
+					司机电话:
+				</view>
+				<view class="val">
+					{{ info.driverPhone }}
+				</view>
+			</view>
+			<view class="alarm-detail-form top">
+				<view class="label">
+					企业名称:
+				</view>
+				<view class="val">
+					{{ info.deptName }}
+				</view>
+			</view>
+			<!-- <view class="alarm-detail-form top">
+				<view class="label">
+					通信号码:
+				</view>
+				<view class="val">
+					{{ info.phone }}
+				</view>
+			</view> -->
+			<view class="alarm-detail-form top">
+				<view class="label">
+					报警类型:
+				</view>
+				<view class="val">
+					{{ info.alarmType }}
+				</view>
+			</view>
+			<view class="alarm-detail-form top">
+				<view class="label">
+					报警时间:
+				</view>
+				<view class="val">
+					{{ info.alarmTime }}
+				</view>
+			</view>
+			<view class="alarm-detail-form top">
+				<view class="label">
+					车速:
+				</view>
+				<view class="val">
+					{{ info.speed }}km/h
+				</view>
+			</view>
+			<view class="alarm-detail-form top">
+				<view class="label">
+					经度:
+				</view>
+				<view class="val">
+					{{ info.lng }}
+				</view>
+			</view>
+			<view class="alarm-detail-form top">
+				<view class="label">
+					纬度:
+				</view>
+				<view class="val">
+					{{ info.lat }}
+				</view>
+			</view>
+			<!-- 			<view class="alarm-detail-form top">
+				<view class="label">
+					车辆状态:
+				</view>
+				<view class="val">
+					{{info.phone}}
+				</view>
+			</view> -->
+			<view class="alarm-detail-form top">
+				<view class="label" style="width: 170rpx;">
+					报警地址:
+				</view>
+				<view class="val" style="color: #4573FC;text-decoration:underline;width: calc(100% - 170rpx);"
+					@click="goHistory(info)">
+					{{ info.address }}>>
+				</view>
+			</view>
+		</view>
+		<view style="height: 28rpx;">
+
+		</view>
+		<!-- 		<view class="exports" @click="exportZip">
+			<image src="../../static/vehicle/icon_evidence_derive_24.png" style="width: 48rpx;height: 48rpx;margin-right: 16rpx;" mode=""></image>
+			证据导出
+		</view> -->
+	</view>
+</template>
+
+<script>
+import { selectByFileuuid, getAlarmSafetyDetailByFileuuid, exportAlarm } from "@/api/alarm/proactiveSecurity.js"
+import { getToken } from '@/utils/auth'
+export default {
+	components: {},
+	props: {},
+	data() {
+		return {
+			images: [], // 图片数组
+			vehSpeed: 0,
+			fid: '',
+			page: '',
+			videoUrl: '',
+			info: {},
+		};
+	},
+	onLoad(option) {
+		this.fid = this.$route.query.fid;
+		this.page = this.$route.query.type;
+	},
+	created() { },
+	mounted() {
+		this.$nextTick(() => {
+			this.queryVideoImages();
+			this.queryBasicInfo();
+		});
+	},
+	watch: {},
+	computed: {},
+	methods: {
+		previewImage(item, index) {
+			uni.previewImage({
+				current: item,
+				urls: this.images,
+				indicator: index
+			})
+		},
+		async queryVideoImages() {
+			// 查询视频和图片
+			let videoList;
+			this.images = [];
+			let res = await selectByFileuuid(this.fid);
+			if (res.code == "0") {
+				for (let i = 0; i < res.data.length; i++) {
+					if (res.data[i].fileType == "02") {
+						videoList = res.data[i].fileName.replace(".h264", ".mp4");
+						this.videoUrl = this.$getImages("/img/alarmvideo/" + videoList)
+						console.log(this.url)
+					}
+					if (res.data[i].fileType == "00") {
+						this.images.push(
+							this.$getImages("/img/alarmvideo/" + res.data[i].fileName)
+						);
+					}
+				}
+			}
+		},
+		checkData(data) {
+			// 检测数据
+			return data == null || data == undefined ? "" : data;
+		},
+		async queryBasicInfo() {
+			// 查询视频和图片
+			let videoList;
+			this.images = [];
+			let res = await getAlarmSafetyDetailByFileuuid(this.fid);
+			if (res.code == "0") {
+				this.info = {
+					...res.data,
+					deptName: this.checkData(res.data.deptName),
+					driverName: this.checkData(res.data.driverName),
+					driverPhone: this.checkData(res.data.driverPhone),
+					plate: this.checkData(res.data.plate),
+					alarmType: this.checkData(res.data.alarmType),
+					alarmTime: this.checkData(res.data.startTime),
+					speed: this.checkData(res.data.speed),
+					lng: this.checkData(res.data.lng),
+					lat: this.checkData(res.data.lat),
+					level: this.checkData(res.data.alarmGrade),
+					vehicleId: this.checkData(res.data.vehicleId),
+					address: this.checkData(res.data.location),
+				}
+			}
+		},
+
+		// 跳到历史轨迹
+		goHistory(item) {
+			let params = {
+				tabStyleType: 'vehicle'
+			}
+			params.vehicleId = item.vehicleId
+			let unixTime = this.dayjs(item.startTime).unix()
+
+			let s_time = this.dayjs(item.startTime).format('YYYY-MM-DD HH:mm')
+
+			let e_time = this.dayjs.unix(unixTime + 60).format('YYYY-MM-DD HH:mm')
+			let startTime = s_time;
+			let endTime = e_time;
+			params.time = [startTime, endTime]
+			this.$store.dispatch('setParams', params)
+			uni.navigateTo({
+				url: '/pagesMap/pastRoute/pastRoute',
+			});
+
+		},
+	},
+
+
+
+};
+</script>
+
+<style lang="scss" scoped>
+.driver_class {
+	width: 100%;
+	height: 100%;
+}
+
+.image {
+	width: 100%;
+	// padding: 24rpx 24rpx 0 24rpx;
+	display: flex;
+	justify-content: space-around;
+	margin-top: 15rpx;
+}
+
+.alarm-type {
+	width: calc(100% - 110rpx);
+	margin: 16rpx 20rpx;
+	border-radius: 16rpx;
+	padding: 36rpx;
+	display: flex;
+	flex-direction: column;
+	background-color: #fff;
+	margin-top: 16rpx;
+}
+
+.alarm-type-title {
+	color: #4573FC;
+	font-size: 36rpx;
+	font-weight: bold;
+}
+
+.alarm-type-item {
+	display: flex;
+	justify-content: space-between;
+	font-size: 32rpx;
+	margin-top: 28rpx;
+
+	.label {
+		color: #999999;
+	}
+}
+
+.alarm-detail {
+	width: calc(100% - 116rpx);
+	padding: 38rpx;
+	display: flex;
+	flex-direction: column;
+	border-radius: 8rpx;
+	background-color: #fff;
+	margin: 16rpx 20rpx;
+	font-size: 32rpx;
+	margin-top: 20rpx;
+
+	.alarm-detail-title {
+		color: #4573FC;
+		font-size: 36rpx;
+		font-weight: bold;
+	}
+
+	.alarm-detail-name {
+		display: flex;
+		align-items: center;
+		margin-top: 18rpx;
+
+		.alarm-detail-image {
+			width: 120rpx;
+			height: 120rpx;
+			overflow: hidden;
+			border-radius: 50%;
+			background-color: #C3C7CC;
+		}
+	}
+
+	.image-right {
+		flex: 1;
+		height: 120rpx;
+		margin-left: 24rpx;
+		display: flex;
+		flex-direction: column;
+		justify-content: space-between;
+	}
+
+	.alarm-detail-form {
+		display: flex;
+		justify-content: space-between;
+		width: 100%;
+
+		.label {
+			color: #999999;
+		}
+
+		.val {
+			color: #666666;
+		}
+	}
+
+	.top {
+		margin-top: 28rpx;
+	}
+
+
+}
+
+.exports {
+	width: calc(100% - 48rpx);
+	margin: 24rpx;
+	padding: 24rpx 0;
+	margin-bottom: 24rpx;
+	background-color: #4573FC;
+	border-radius: 20rpx;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	color: #fff;
+	font-size: 36rpx;
+}
+</style>

+ 279 - 0
alarm/proactiveSecurity/index.vue

@@ -0,0 +1,279 @@
+<template>
+	<view class="pageContainer">
+		 <view class="search">
+		<u-cell title="报警时间" :value="calendarStart + ' 至 ' + calendarEnd" style="height: 86rpx"
+			@click="$refs.calendarRef.show()">
+
+		</u-cell>
+		<tq-calendar ref="calendarRef" type="daterange" @confirm="(e) => {
+			calendarStart = e[0]
+			calendarEnd = e[1]
+			page = 1;
+			tableList = [];
+			handleTableList()
+		}">
+		</tq-calendar>
+
+		<u-cell title="车辆" :border="true" :isLink="true" @click="$refs.carChooseRef.show()">
+			<view slot="value">
+				<!-- <u-form-item label="" prop="vehicleId"> -->
+					{{ plate }}
+				<!-- </u-form-item> -->
+			</view>
+			<u-icon v-if="plate == ''" slot="right-icon" size="16" name="arrow-right"></u-icon>
+			<view v-else slot="right-icon" @click.stop="resetData" style="margin-left: 20rpx;">
+				<u-icon size="16" name="close-circle"></u-icon>
+			</view>
+		</u-cell>
+		<tq-car-user ref="carChooseRef" :isGetIcon="true" @confirm="(data) => {
+			vehicleId = data.id
+			plate = data.label
+			page = 1;
+			tableList = [];
+			handleTableList()
+		}">
+		</tq-car-user>
+		</view>
+		<view class="body">
+
+			<view class="list" v-for="item in tableList" :key="item.id">
+				<view style="
+					  display: flex;
+					  margin-bottom: 20rpx;
+					  margin-top: 20rpx;">
+					<view class="head" style="font-size: 26rpx">
+						<text style="font-weight: bold;">车辆:{{ item.plate }}</text>
+					</view>
+					<view class="head" style="margin-left: 73rpx">
+						<text style="font-weight: bold;">
+							<text>{{ item.deptName }}</text>
+						</text>
+					</view>
+				</view>
+				<view style="
+					  margin-bottom: 20rpx;
+					    display: flex;
+					">
+					<view class="head" style="font-size: 26rpx">
+						<text>司机:
+							<text>
+								{{ item.driverName }}
+							</text>
+						</text>
+					</view>
+					<view class="head" style="margin-left: 26rpx">
+						<text>{{ item.driverPhone }}</text>
+					</view>
+				</view>
+				<view>
+					<view class="head" style="font-size: 26rpx;margin-bottom: 20rpx;">
+						<text style="font-size: 30rpx; ">报警开始时间:{{ item.startTime }}</text>
+					</view>
+					<view class="head" style="font-size: 26rpx;margin-bottom: 20rpx;">
+						<text style="font-size: 30rpx;">报警结束时间:{{ item.startTime }}</text>
+					</view>
+				</view>
+				<view style="margin-bottom: 20rpx;">
+					<view class="head" style="font-size: 26rpx">
+						<view style="width: 20%;align-items: flex-start;">
+							报警位置:
+						</view>
+						<text style="font-size: 30rpx;flex: 1;">{{ item.location }}</text>
+					</view>
+				</view>
+				<view style="height: 90rpx;">
+					<u-button type="primary" @click="seeAlarmDetails(item)">查看详情</u-button>
+				</view>
+
+			</view>
+      <view class="nodata-warp" v-if="!tableList.length">
+        <image :src="$getImages('/assetsMobile/images/no-data.png')" class="nodata-image"></image>
+      </view>
+		<u-loadmore :status="loadStatus" height="30"/>
+		</view>
+
+		<u-calendar :show="calendarShow" mode="range" :defaultDate="defaultDate" :maxDate="maxDate" minDate="2023-10-01"
+			monthNum="24" @confirm="calendarChange" @close="onClose"></u-calendar>
+
+	</view>
+</template>
+
+<script>
+import { getSafetyDriverList } from "@/api/alarm/proactiveSecurity.js"
+export default {
+	components: {
+	},
+	props: {},
+	data() {
+		return {
+			vehicleId: '',
+			plate: '',
+			tableList: [],
+			calendarShow: false, // 日历
+			calendarStart: this.dayjs().subtract(6, 'day').format('YYYY-MM-DD'),// 开始时间
+			calendarEnd: this.dayjs().subtract(0, 'day').format('YYYY-MM-DD'), // 结束时间
+			defaultDate: [this.dayjs().subtract(6, 'day').format('YYYY-MM-DD'), this.dayjs().subtract(0, 'day').format('YYYY-MM-DD')],
+			page: 1,
+			size: 10,
+			total: 0,
+			maxDate: this.dayjs().subtract(0, 'day').format('YYYY-MM-DD'),
+			 loadStatus: 'nomore', //loading nomore loadmore
+		};
+	},
+	mounted() {
+		this.handleTableList();
+	},
+	onReachBottom() {
+		// 滚动到底部
+		if (this.page * this.size < this.total) {
+			this.page += 1;
+			this.handleTableList();
+		}
+	},
+	methods: {
+		checkTime() {
+			this.calendarShow = true
+		},
+		resetData() {
+			this.vehicleId = ''
+			this.plate = ''
+			this.page = 1;
+			this.tableList = [];
+			this.handleTableList();
+		},
+		handleDataDetection(data) {
+			// 数据检测
+			return data == null || data == undefined ? "" : data;
+		},
+
+		async handleTableList() {
+			this.loadStatus = 'loading'
+			// 获取列表
+			// this.tableList = [];
+			let obj = {
+				startTime: this.calendarStart == "" || this.calendarStart == null ?
+					undefined : this.calendarStart + ' 00:00:00',
+				endTime: this.calendarEnd == "" || this.calendarEnd == null ?
+					undefined : this.calendarEnd + ' 23:59:59',
+				plate: this.plate ? this.plate : undefined,
+				vehicleId: this.vehicleId ? this.vehicleId : undefined,
+				driverAdvanceType: 'ADAS',
+				page: this.page,
+				size: 10
+			};
+			let res = await getSafetyDriverList(obj);
+			if (res.code == "0" && res.data.list) {
+					  
+					  res.data.list.map((e) => {
+					  	this.tableList.push({
+					  	    ...e,
+					  	});
+					  })
+					  
+			  // this.tableList = res.data.list
+					
+			  this.total = res.data.total;
+			  if (this.page * 10 >= this.total)  {
+			  	this.loadStatus = 'nomore'
+			  }
+			}  else {
+						this.loadStatus = 'nomore'
+					}
+		},
+
+		calendarChange(e) {
+			this.page = 1;
+			this.tableList = [];
+			// 修改日期
+			this.calendarStart = e[0]; // 开始时间
+			this.calendarEnd = e[1]; // 结束时间
+			this.calendarShow = false;
+			this.handleTableList();
+		},
+		onClose() {
+			this.calendarShow = false;
+			this.handleTableList();
+		},
+		seeAlarmDetails(item) {
+			// 报警详情
+			uni.navigateTo({
+				url: `/alarm/proactiveSecurity/detail?fid=${item.fileuuid}`
+			})
+		},
+	},
+};
+</script>
+<style lang="scss" scoped>
+.pageContainer {
+  overflow: unset;
+  // background-color: #fff;
+}
+
+.search {
+  position: relative;
+  padding: 0 0rpx;
+  width: 100%;
+  height: 166rpx;
+  // display: flex;
+  // align-items: center;
+  background-color: #fff;
+}
+
+.head {
+  height: 100%;
+  font-size: 28rpx;
+  display: flex;
+  align-items: center;
+  justify-content: flex-start;
+  color: #333333;
+  
+}
+
+.content {
+  height: 100%;
+  font-size: 28rpx;
+  display: flex;
+  align-items: center;
+  justify-content: flex-start;
+  color: #333333;
+}
+
+.body {
+	margin-top: 20rpx;
+  position: relative;
+  padding: 10rpx;
+  width: 100%;
+  height: calc(100% - 172rpx);
+  
+}
+
+.body .total {
+  width: 100%;
+  height: 65rpx;
+  line-height: 65rpx;
+  font-size: 26rpx;
+  color: #999999;
+}
+
+.list {
+	background-color: #fff;
+  width: calc(100% - 90rpx);
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  padding: 32rpx;
+  background-color: white;
+  border-radius: 20rpx;
+  margin-bottom: 30rpx;
+}
+.nodata-warp{
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 100rpx 0;
+  .nodata-image{
+    width: 218rpx;
+    height: 150rpx;
+  }
+}
+</style>

+ 19 - 0
api/accidentStudy/accidentStudy.js

@@ -0,0 +1,19 @@
+import request from '@/utils/request.js'
+
+// 查询培训资料分页
+export function getList(data) {
+    return request({
+        url: '/system/accidentStudy/queryAccidentStudyPaging',
+        method: 'POST',
+        data: data
+    })
+}
+
+// 记录点击次数
+export function addClickNum(query) {
+    return request({
+        url: '/system/accidentStudy/addClickNum',
+        method: 'get',
+        params: query
+    })
+}

+ 38 - 0
api/alarm/blindSpot.js

@@ -0,0 +1,38 @@
+import request from '@/utils/request.js'
+
+// 获取车辆盲点报警详情
+export function getBlindSpotList(params) {
+	return request({
+	  url: '/alarm/alarmDriver/getBlindSpotList',
+	  method: 'post',
+	  params
+	})
+}
+
+// 获取车辆盲点报警详情
+export function getAlarmBlindSpotDetailByFileuuid(params) {
+	return request({
+	  url: '/alarm/alarmDriver/getAlarmBlindSpotDetailByFileuuid',
+	  method: 'post',
+	  params
+	})
+}
+
+
+// 获取报警消息文件
+export function selectByFileuuid(params) {
+	return request({
+	  url: '/alarm/alarmDriver/selectByFileuuid',
+	  method: 'post',
+	  params
+	})
+}
+
+// 导出证据文件
+export function exportAlarm(params) {
+	return request({
+	  url: '/alarm/alarmDriver/export',
+	  method: 'post',
+	  params
+	})
+  }

+ 38 - 0
api/alarm/driverBehavior.js

@@ -0,0 +1,38 @@
+import request from '@/utils/request.js'
+
+// 获取驾驶行为报警详情
+export function getAlarmDriverList(params) {
+	return request({
+	  url: '/alarm/alarmDriver/getAlarmDriverList',
+	  method: 'post',
+	  params
+	})
+}
+
+// 获取驾驶行为报警详情
+export function getAlarmDriverDetailByFileuuid(params) {
+	return request({
+	  url: '/alarm/alarmDriver/getAlarmDriverDetailByFileuuid',
+	  method: 'post',
+	  params
+	})
+}
+
+
+// 获取驾驶行为文件
+export function selectByFileuuid(params) {
+	return request({
+	  url: '/alarm/alarmDriver/selectByFileuuid',
+	  method: 'post',
+	  params
+	})
+}
+
+// 导出证据文件
+export function exportAlarm(params) {
+	return request({
+	  url: '/alarm/alarmDriver/export',
+	  method: 'post',
+	  params
+	})
+  }

+ 38 - 0
api/alarm/proactiveSecurity.js

@@ -0,0 +1,38 @@
+import request from '@/utils/request.js'
+
+// 获取主动安全报警详情
+export function getSafetyDriverList(params) {
+	return request({
+	  url: '/alarm/alarmDriver/getSafetyDriverList',
+	  method: 'post',
+	  params
+	})
+}
+
+// 获取主动安全报警详情
+export function getAlarmSafetyDetailByFileuuid(params) {
+	return request({
+	  url: '/alarm/alarmDriver/getAlarmSafetyDetailByFileuuid',
+	  method: 'post',
+	  params
+	})
+}
+
+
+// 获取报警消息文件
+export function selectByFileuuid(params) {
+	return request({
+	  url: '/alarm/alarmDriver/selectByFileuuid',
+	  method: 'post',
+	  params
+	})
+}
+
+// 导出证据文件
+export function exportAlarm(params) {
+	return request({
+	  url: '/alarm/alarmDriver/export',
+	  method: 'post',
+	  params
+	})
+  }

+ 93 - 0
api/common/system.js

@@ -0,0 +1,93 @@
+import request from '@/utils/request.js'
+import {server_url} from '@/utils/config.js'
+
+//  文件上传
+export const uploadUrl = `${server_url}/system/common/upload`
+
+// 获取路线详细信息
+export function routeDetail(params) {
+	return request({
+		url: '/system/system/route/detail',
+		method: 'GET', 
+		params
+	})
+}
+
+// 查询全部部门
+export function commonDeptAll(query) {
+  return request({
+    url: '/system/dept/common/deptAll',
+    method: 'get',
+    params: query,
+    throttle: false
+  })
+}
+
+// 根据参数键名查询参数值
+export function getConfigKey(configKey) {
+  return request({
+    url: '/system/system/config/configKey/' + configKey,
+    method: 'get'
+  })
+}
+
+// 通过父级code查询code下所有树结构区域
+export function getCodeAreaTree(query) {
+  return request({
+    url: '/system/system/area/areaTree',
+    method: 'get',
+    params: query
+  })
+}
+
+// 车辆作业类型下拉列表(全部)
+export function dropDownList(query) {
+    return request({
+        url: '/system/vehicle/common/workType/dropDown/list',
+        method: 'get',
+        params: query
+    })
+}
+
+// 获取手机路由
+export function getMobileRouters(query) {
+    return request({
+        url: '/system/getMobileRouters',
+        method: 'POST',
+        params: query
+    })
+}
+
+// 钉钉内购
+export function getH5SkuUrl(query) {
+    return request({
+        url: '/system/ding/getH5SkuUrl',
+        method: 'get',
+        params: query
+    })
+}
+
+// 基础数据
+export function boardDetail() {
+  return request({
+    url: '/system/data/board/detail',
+    method: 'get'
+  })
+}
+
+// 消息数量
+export function getMessageNum(data) {
+  return request({
+    url: '/system/sysMessage/getMessageNum',
+    method: 'post',
+	data
+  })
+}
+// 维保类型
+export function mtTypeTreeList(data) {
+  return request({
+    url: '/wf-api/system/mtType/queryTreeList',
+    method: 'post',
+	data
+  })
+}

+ 39 - 0
api/common/user.js

@@ -0,0 +1,39 @@
+import request from '@/utils/request.js'
+
+
+//  部门用户组件树
+export function deptUserTree(params) {
+	return request({
+		url: '/system/user/common/deptUserTree',
+		method: 'GET', 
+		params
+	})
+}
+
+
+// 用户密码重置
+export function updateUserPwd(params) {
+  return request({
+    url: '/system/system/user/profile/updatePwd',
+    method: 'put',
+    params
+  })
+}
+
+// 用户信息
+export function userProfile(params) {
+  return request({
+    url: '/system/system/user/profile',
+    method: 'get',
+    params
+  })
+}
+
+// 修改用户个人信息
+export function updateUserProfile(data) {
+  return request({
+    url: '/system/system/user/profile/updateUser',
+    method: 'post',
+    data: data
+  })
+}

+ 11 - 0
api/common/vehicle.js

@@ -0,0 +1,11 @@
+import request from '@/utils/request.js'
+
+
+//  部门车辆组件树
+export function deptVehTree(params) {
+	return request({
+		url: '/system/vehicle/common/deptVehTree',
+		method: 'GET', 
+		params
+	})
+}

+ 19 - 0
api/core/sendOrder.js

@@ -0,0 +1,19 @@
+import request from '@/utils/request.js'
+
+// 机械作业-任务列表
+export function videoControl(params) {
+	return request({
+		url: '/core/sendOrder/videoControl',
+		method: 'POST', 
+		params
+	})
+}
+
+// 机械作业-任务列表
+export function videoStart(params) {
+	return request({
+		url: '/core/sendOrder/videoStart',
+		method: 'POST', 
+		params
+	})
+}

+ 10 - 0
api/dict/data.js

@@ -0,0 +1,10 @@
+import request from '@/utils/request.js'
+
+
+// 根据字典类型查询字典数据信息
+export function getDicts(dictType) {
+    return request({
+      url: '/system/system/dict/data/type/' + dictType,
+      method: 'GET'
+   })
+  }

+ 19 - 0
api/formTemplate.js

@@ -0,0 +1,19 @@
+import request from '@/utils/request.js'
+
+// 提交表单
+export function submitForm(params) {
+	return request({
+		url: '/system/submitForm',
+		method: 'POST', 
+		params
+	})
+}
+
+// 登录
+export function login(params) {
+	return request({
+		url: '/system/login',
+		method: 'POST',
+		params
+	})
+}

+ 29 - 0
api/iconConfig/iconConfig.js

@@ -0,0 +1,29 @@
+import request from '@/utils/request.js'
+
+// 查询车辆图标
+export function fetchVehIcon(params) {
+	return request({
+		url: '/system/vehIcon/query',
+		method: 'POST', 
+		params
+	})
+}
+
+// 查询人员类型图标
+export function fetchUserIcon(params) {
+	return request({
+		url: '/system/userIcon/query',
+		method: 'POST', 
+		params
+	})
+}
+
+// 查询设施类型图标
+export function fetchFacIcon(params) {
+	return request({
+		url: '/system/facIcon/query',
+		method: 'POST', 
+		params
+	})
+}
+

+ 93 - 0
api/learnMobile/index.js

@@ -0,0 +1,93 @@
+import request from '@/utils/request.js'
+
+// 课程学习列表
+export function materialList(params) {
+	return request({
+		url: '/system/learnMobile/materialList',
+		method: 'POST', 
+		params
+	})
+}
+
+
+// 课程学习详细信息
+export function materialDetail(params) {
+	return request({
+		url: '/system/learnMobile/materialDetail',
+		method: 'POST', 
+		params
+	})
+}
+
+// 保存课程学习
+export function saveUserMaterial(params) {
+	return request({
+		url: '/system/learnMobile/saveUserMaterial',
+		method: 'POST', 
+		params
+	})
+}
+
+// 查询用户学习记录分页列表
+export function userMaterialRecord(params) {
+	return request({
+		url: '/system/learnMobile/userMaterialRecord',
+		method: 'POST', 
+		params
+	})
+}
+
+
+// 我的试卷列表
+export function paperList(params) {
+	return request({
+		url: '/system/learnMobile/paperList',
+		method: 'POST', 
+		params
+	})
+}
+
+// 试卷题目信息(考试试卷)
+export function paperQuestionInfo(params) {
+	return request({
+		url: '/system/learnMobile/paperQuestionInfo',
+		method: 'POST', 
+		params
+	})
+}
+
+// 保存考试
+export function saveUserPaper(params) {
+	return request({
+		url: '/system/learnMobile/saveUserPaper',
+		method: 'POST', 
+		params
+	})
+}
+
+// 查看考试记录信息(试题解析)
+export function userExaminationRecord(params) {
+	return request({
+		url: '/system/learnMobile/userExaminationRecord',
+		method: 'POST', 
+		params
+	})
+}
+
+// 查看最新考试详情
+export function recordByPaperId(params) {
+	return request({
+		url: '/system/learnMobile/recordByPaperId',
+		method: 'GET', 
+		params
+	})
+}
+
+// 查看考试记录列表
+export function userPaperRecordList(params) {
+	return request({
+		url: '/system/learnMobile/userPaperRecordList',
+		method: 'POST', 
+		params
+	})
+}

+ 76 - 0
api/login.js

@@ -0,0 +1,76 @@
+import request from '@/utils/request.js'
+
+
+// 登录方法
+export function login(params) {
+	return request({
+	  	url: '/system/login',
+		method: 'POST', 
+		params
+	})
+} 
+
+
+// 获取用户详细信息
+export function getInfo(params) {
+	return request({
+		url: '/system/getInfo',
+		method: 'GET', 
+		params
+	})
+}
+
+// 退出方法
+export function logout(params) {
+	return request({
+		url: '/system/logout',
+		method: 'POST', 
+		params
+	})
+}
+
+// 获取验证码
+export function getCodeImg(params) {
+	return request({
+		url: '/system/captchaImage',
+		method: 'GET', 
+		params
+	})
+}
+
+
+// 获取配置信息
+export function configList(params) {
+	return request({
+        url: '/system/system/title/config/list',
+		method: 'POST', 
+		params
+	})
+}
+
+
+// 钉钉第三方 code 换token
+export function getThirdH5Auth(params) {
+	return request({
+        url: '/system/ding/getThirdH5Auth',
+		method: 'POST', 
+		params
+	})
+}
+
+// 钉钉内部应用 code 换token
+export function h5GetToken(params) {
+	return request({
+      url: '/system/ding/h5GetToken',
+      method: 'get', 
+      params
+	})
+}
+// 判断用户当日是否首次登录
+export function checkUserFirstLogin(params) {
+	return request({
+      url: '/system/monitor/logininfor/checkUserFirstLogin',
+      method: 'get', 
+      params
+	})
+}

+ 26 - 0
api/mine/driverTask.js

@@ -0,0 +1,26 @@
+import request from '@/utils/request.js'
+
+// 司机获取我的任务
+export function myTaskList(params) {
+	return request({
+		url: '/system/task/myTaskList',
+		method: 'POST', 
+		params
+	})
+}
+// 查询任务管理分页列表
+export function driverTaskList(params) {
+	return request({
+		url: '/system/task/list',
+		method: 'POST', 
+		params
+	})
+}
+// 司机执行任务(开始/结束)
+export function driverExTask(params) {
+	return request({
+		url: '/system/task/driverExTask',
+		method: 'POST', 
+		params
+	})
+}

+ 36 - 0
api/monitor.js

@@ -0,0 +1,36 @@
+import request from '@/utils/request.js'
+
+// 实时定位-视频-根据车辆ID查询所有设备和通道号
+export function vehVideo(params) {
+	return request({
+		url: '/system/vehicle/common/vehVideo',
+		method: 'POST', 
+		params
+	})
+}
+
+
+// 打开音视频
+export function openVideo(data) {
+  return request({
+    url: '/core/real/video/open/av',
+    method: 'POST',
+    params: data
+  })
+}
+// 关闭音视频
+export function closeVideo(data) {
+  return request({
+    url: '/core/real/video/close/av',
+    method: 'POST',
+    params: data
+  })
+}
+// 暂停音视频
+export function pauseVideo(data) {
+  return request({
+    url: '/core/real/video/pause/av',
+    method: 'POST',
+    params: data
+  })
+}

+ 56 - 0
api/pastRoute.js

@@ -0,0 +1,56 @@
+import request from '@/utils/request.js'
+
+
+// 车辆报警
+export function getAlarmVehicleHisList(params) {
+	return request({
+        url: '/alarm/alarmManage/getAlarmVehicleHisList',
+		method: 'POST', 
+		params
+	})
+}
+
+// 查询停车报表列表
+export function vehStopQueryList(params) {
+	return request({
+        url: '/alarm/alarm/vehStop/queryList',
+		method: 'POST', 
+		params
+	})
+}
+
+// 车辆历史轨迹
+export function trackRecord(params) {
+	return request({
+		url: '/metadata/vehHistory/trackRecord',
+		method: 'POST', 
+		params
+	})
+}
+
+// 月份查询轨迹记录
+export function monthHistory(params) {
+	return request({
+		url: '/metadata/vehHistory/monthHistory',
+		method: 'POST', 
+		params
+	})
+}
+
+// 人员历史轨迹
+export function userTrackRecord(params) {
+	return request({
+		url: '/metadata/userHistory/trackRecord',
+		method: 'POST', 
+		params
+	})
+}
+
+// 人员历史轨迹
+export function userMonthHistory(params) {
+	return request({
+		url: '/metadata/userHistory/monthHistory',
+		method: 'POST', 
+		params
+	})
+}

+ 69 - 0
api/realtimeWatch.js

@@ -0,0 +1,69 @@
+import request from '@/utils/request.js'
+
+/* // 实时定位-全部部门车辆信息
+export function deptVehAll(params) {
+	return request({
+		url: '/system/vehicle/common/deptVehAll',
+		method: 'GET', 
+		params
+	})
+}
+
+// 实时定位-全部部门车辆信息(Map)
+export function deptVehMapAll(params) {
+  return request({
+    url: '/system/vehicle/common/deptVehMapAll',
+    method: 'GET',
+    params
+  })
+} */
+
+// 实时列表
+export function realTimeList(params) {
+	return request({
+		url: '/system/largeScreen/realTimeList',
+		method: 'POST', 
+		params
+	})
+}
+
+// 实时更新列表(Map)
+export function realTimeMap(params) {
+	return request({
+		url: '/system/largeScreen/realTimeMap',
+		method: 'POST', 
+		params
+	})
+}
+
+/* 矿山车辆详情 start */
+
+
+// 获取矿山车辆详情
+export function queryRangeVeh(query) {
+  return request({
+    url: '/metadata/geo/queryRangeVeh',
+    method: 'post',
+    data: query
+  })
+}
+
+/* 矿山车辆详情 end */
+
+
+// 修改车辆备注
+export function updateVehRemark(query) {
+  return request({
+    url: '/system/system/vehicle/updateRemark',
+    method: 'post',
+    data: query
+  })
+}
+// 修改保洁备注
+export function updateCleanRemark(query) {
+  return request({
+    url: '/system/sanitation/cleaning/updateRemark',
+    method: 'post',
+    data: query
+  })
+}

+ 64 - 0
api/system/driverManage.js

@@ -0,0 +1,64 @@
+import request from '@/utils/request.js'
+import { parseStrEmpty } from "@/utils/gdtq";
+
+// 查询司机信息列表
+export function listDriverManage(data) {
+  return request({
+    url: '/system/system/driver/list',
+    method: 'post',
+    data: data
+  })
+}
+
+// 新增司机信息
+export function addDriverManage(data) {
+  return request({
+    url: '/system/system/driver/add',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改司机信息
+export function updateDriverManage(data) {
+  return request({
+    url: '/system/system/driver/update',
+    method: 'post',
+    data: data
+  })
+}
+
+// 批量删除司机信息
+export function removeDriverManage(data) {
+  return request({
+    url: '/system/system/driver/remove',
+    method: 'post',
+    data: data
+  })
+}
+
+// 获取司机信息详细信息
+export function detailDriverManage(query) {
+  return request({
+    url: '/system/system/driver/detail',
+    method: 'get',
+    params: query
+  })
+}
+
+
+// 查询用户详细
+export function getUser(userId) {
+  return request({
+    url: '/system/system/user/' + parseStrEmpty(userId),
+    method: 'get'
+  })
+}
+
+// 查询部门下拉树结构
+export function deptTreeSelect() {
+  return request({
+    url: '/system/system/user/deptTree',
+    method: 'get'
+  })
+}

+ 19 - 0
api/system/event.js

@@ -0,0 +1,19 @@
+import request from '@/utils/request.js'
+
+// PC端-事件上报记录(我的或全部)
+export function eventList(params) {
+	return request({
+		url: '/wf-api/hw/flow/event/list',
+		method: 'POST', 
+		params
+	})
+}
+
+// 撤销
+export function recordtaskOperate(params) {
+	return request({
+		url: '/wf-api/workflow/task/taskOperatePost',
+		method: 'post', 
+		params
+	})
+}

+ 45 - 0
api/system/material.js

@@ -0,0 +1,45 @@
+import request from '@/utils/request.js'
+
+// 物料申领(body传参)
+export function addMaterialApply(params) {
+	return request({
+		url: '/system/materialApply/addMaterialApply',
+		method: 'POST', 
+		params
+	})
+}
+// 申领记录(body传参)
+export function materialApplyRec(params) {
+	return request({
+		url: '/system/materialApply/materialApplyRec',
+		method: 'POST', 
+		params
+	})
+}
+// 根据ID查询物料出库记录
+export function queryOneById(params) {
+	return request({
+		url: '/system/materialApply/queryOneById',
+		method: 'GET', 
+		params
+	})
+}
+
+// 获取物料列表
+export function queryMaterialList(params) {
+	return request({
+		url: '/system/material/queryMaterialList',
+		method: 'POST', 
+		params
+	})
+}
+
+// 归还
+export function materialBack(params) {
+	return request({
+		url: '/system/materialApply/materialBack',
+		method: 'POST', 
+		params
+	})
+}
+

+ 19 - 0
api/system/sysMessage.js

@@ -0,0 +1,19 @@
+import request from '@/utils/request.js'
+
+// 移动端车务消息-分页数据
+export function getMobileMsgList(params) {
+	return request({
+		url: '/system/sysMessage/getMobileMsgList',
+		method: 'POST', 
+		params
+	})
+}
+
+// 移动端车务消息-到期处理
+export function updateObjDueTime(params) {
+	return request({
+		url: '/system/sysMessage/updateObjDueTime',
+		method: 'post', 
+		params
+	})
+}

+ 149 - 0
api/system/vehicle.js

@@ -0,0 +1,149 @@
+import request from '@/utils/request.js'
+
+// 新增车辆基础信息
+export function add(data) {
+    return request({
+        url: '/system/system/vehicle/add',
+        method: 'POST',
+        data: data
+    })
+}
+
+
+// 修改垃圾转运站
+export function update(data) {
+    return request({
+        url: '/system/system/vehicle/update',
+        method: 'POST',
+        data: data
+    })
+}
+
+// 取车辆基础信息详细信息
+export function getDetail(query) {
+    return request({
+        url: '/system/system/vehicle/detail',
+        method: 'get',
+        params: query
+    })
+}
+
+// 车辆基础信息详细信息
+export function queryVehicleBind(query) {
+    return request({
+        url: '/system/system/vehicle/queryVehicleBind',
+        method: 'get',
+        params: query
+    })
+}
+
+// 查询车辆基础信息分页列表
+export function getList(query) {
+    return request({
+        url: '/system/system/vehicle/list',
+        method: 'post',
+        data: query
+    })
+}
+
+// 批量删除车辆基础信息
+export function remove(data) {
+    return request({
+        url: '/system/system/vehicle/remove',
+        method: 'post',
+        data: data
+    })
+}
+
+// 车辆作业类型下拉列表(全部)
+export function dropDownList(query) {
+    return request({
+        url: '/system/vehicle/common/workType/dropDown/list',
+        method: 'get',
+        params: query
+    })
+}
+
+// 车辆类型下拉列表
+export function veDropDown(data) {
+    return request({
+        url: '/system/vehicle/common/dropDown',
+        method: 'POST',
+        data: data
+    })
+}
+
+
+// 部门车辆组件树
+export function deptVehTree(query) {
+  return request({
+      url: '/system/vehicle/common/deptVehTree',
+      method: 'get',
+      params: query
+  })
+}
+
+
+// 获取车辆报警信息详细信息
+export function vehicleAlarmDetail(query) {
+  return request({
+      url: '/system/system/vehicleAlarm/detail',
+      method: 'get',
+      params: query
+  })
+}
+
+
+// 修改车辆报警信息
+export function vehicleAlarmUpdate(data) {
+  return request({
+      url: '/system/system/vehicleAlarm/update',
+      method: 'POST',
+      data: data
+  })
+}
+
+// 车辆作业规则详情
+export function getRulesDetails(data) {
+    return request({
+        url: '/system/vehWork/rules/details',
+        method: 'POST',
+        data: data
+    })
+}
+// 设置车辆作业规则
+export function rulesSet(data) {
+    return request({
+        url: '/system/vehWork/rules/rulesSet',
+        method: 'POST',
+        data: data
+    })
+}
+
+// 批量设置车辆作业规则
+export function rulesAllSet(data) {
+    return request({
+        url: '/system/vehWork/rules/rulesAllSet',
+        method: 'POST',
+        data: data
+    })
+}
+
+// 批量修改车辆基础信息
+export function batchUpdate(data) {
+    return request({
+        url: '/system/system/vehicle/batchUpdate',
+        method: 'POST',
+        data: data
+    })
+}
+
+// 查询所有车辆信息
+export function getAllVeh(data) {
+    return request({
+        url: '/system/system/vehicle/getAllVeh',
+        method: 'POST',
+        data: data
+    })
+}
+

+ 38 - 0
api/system/video.js

@@ -0,0 +1,38 @@
+import request from '@/utils/request.js'
+// 查询设备每日是否存在回放视频资源
+export function dayStat(params) {
+	return request({
+		url: '/system/video/common/his/resource/day/stat',
+		method: 'POST', 
+		params
+	})
+}
+
+//查询时间段并播放
+export function playbackPull(params) {
+	return request({
+		url: '/core/video/playback/pull',
+		method: 'POST', 
+		params
+	})
+}
+
+// 取车辆基础信息详细信息
+export function getDetail(query) {
+    return request({
+      url: '/system/system/vehicle/detail',
+      method: 'get',
+      params: query
+    })
+  }
+  
+
+  // 播放
+export function playbackPlay(params) {
+    return request({
+      url: '/core/video/playback/play',
+      method: 'POST',
+      params
+    })
+  }
+  

+ 47 - 0
api/system/workPlan.js

@@ -0,0 +1,47 @@
+import request from '@/utils/request.js'
+
+// 机械作业-任务列表
+export function machineWorkRecord(params) {
+	return request({
+		url: '/system/workPlan/machineWorkRecord',
+		method: 'POST', 
+		params
+	})
+}
+
+
+// 转运作业-任务列表
+export function transportWorkRecord(params) {
+	return request({
+		url: '/system/workPlan/transportWorkRecord',
+		method: 'POST', 
+		params
+	})
+}
+
+// 收运作业-任务列表
+export function collectionWorkRecord(params) {
+	return request({
+		url: '/system/workPlan/collectionWorkRecord',
+		method: 'POST', 
+		params
+	})
+}
+
+// 车辆作业轨迹-作业情况
+export function trackInfo(params) {
+	return request({
+		url: '/system/work/vehicleTrack/trackInfo',
+		method: 'POST', 
+		params
+	})
+}
+
+// 车辆作业轨迹信息
+export function workTrackInfoRecord(params) {
+	return request({
+		url: '/metadata/flatWork/workTrackInfoRecord',
+		method: 'POST', 
+		params
+	})
+}

+ 28 - 0
api/vehDispatch/oilApply.js

@@ -0,0 +1,28 @@
+import request from '@/utils/request.js'
+
+// 新增加油记录
+export function addItem(data) {
+  return request({
+    url: '/system/ryOil/add',
+    method: 'post',
+    data: data
+  })
+}
+
+// 获取加油记录详细信息
+export function detail(data) {
+  return request({
+    url: '/system/ryOil/detail',
+    method: 'get',
+    data: data
+  })
+}
+// 修改加油记录
+export function updateItem(data) {
+  return request({
+    url: '/system/ryOil/update',
+    method: 'get',
+    data: data
+  })
+}
+

+ 22 - 0
api/vehDispatch/taskQrCode.js

@@ -0,0 +1,22 @@
+import request from '@/utils/request.js'
+
+// 判断用户是否有出车中记录
+export function checkHasOutRec(data) {
+  return request({
+    url: '/system/scanRec/checkHasOutRec',
+    method: 'post',
+    data: data
+  })
+}
+
+// 扫码出车/还车
+export function scanVehCode(data) {
+  return request({
+    url: '/system/scanRec/scanVehCode',
+    method: 'post',
+    data: data
+  })
+}
+
+
+

+ 37 - 0
api/vehDispatch/vehInspection.js

@@ -0,0 +1,37 @@
+import request from '@/utils/request.js'
+
+// 新增
+export function addInspectRec(data) {
+  return request({
+    url: '/system/system/inspectRec/add',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改
+export function updateInspectRec(data) {
+  return request({
+    url: '/system/system/inspectRec/update',
+    method: 'post',
+    data: data
+  })
+}
+
+// ID获取详细信息
+export function queryById(query) {
+  return request({
+    url: '/system/system/inspectRec/detail?id=' + query.id,
+    method: 'get',
+    param: query
+  })
+}
+
+// 获取所有启用列表及启用小类
+export function queryAllEnableList() {
+  return request({
+    url: '/system/system/inspectProject/queryAllEnableList',
+    method: 'get'
+  })
+}
+

+ 11 - 0
api/vehicle/common.js

@@ -0,0 +1,11 @@
+import request from '@/utils/request.js'
+
+
+// 车辆类型下拉列表
+export function dropDown(params) {
+	return request({
+	  	url: '/system/vehicle/common/dropDown',
+		method: 'POST', 
+		params
+	})
+} 

+ 87 - 0
api/wfApi/index.js

@@ -0,0 +1,87 @@
+import request from '@/utils/request.js'
+
+// 司机任务列表
+export function drivertaskList(params) {
+	return request({
+		url: '/wf-api/qsy/system/flow/veh/driver/task/list',
+		method: 'POST', 
+		params
+	})
+}
+
+
+// 司机处理任务
+export function taskOperate(params) {
+	return request({
+		url: '/wf-api/qsy/system/flow/veh/driver/task/taskOperate',
+		method: 'POST', 
+		params
+	})
+}
+
+
+// 用车记录
+export function vehList(params) {
+	return request({
+		url: '/wf-api/qsy/system/flow/veh/user/apply/list',
+		method: 'POST', 
+		params
+	})
+}
+
+
+// 费用记录
+export function recordlist(params) {
+	return request({
+		url: '/wf-api/qsy/system/flow/cost/record/list',
+		method: 'POST', 
+		params
+	})
+}
+
+
+// 费用记录撤销
+export function recordtaskOperate(params) {
+	return request({
+		url: '/wf-api/workflow/task/taskOperatePost',
+		method: 'post', 
+		params
+	})
+}
+
+// 评价
+export function evaluate(params) {
+	return request({
+		url: '/wf-api/qsy/system/flow/veh/evaluate',
+		method: 'POST', 
+		params
+	})
+}
+
+
+// 移动端-车辆维修记录(我的或全部)
+export function repairRecordList(params) {
+	return request({
+		url: '/wf-api/qsy/system/flow/repair/my/record/list',
+		method: 'POST', 
+		params
+	})
+}
+
+// 移动端-车辆保养记录(我的或全部)
+export function maintenanceRecordList(params) {
+	return request({
+		url: '/wf-api/qsy/system/flow/maintenance/my/record/list',
+		method: 'POST', 
+		params
+	})
+}
+
+// 工作流消息数据
+export function taskCount(params) {
+	return request({
+		url: '/wf-api/workflow/task/count',
+		method: 'GET', 
+		params
+	})
+}

二进制
app.keystore


二进制
assets/fonts/MaoKenTangYuan.ttf


二进制
assets/fonts/PMZDCT.ttf


二进制
assets/fonts/YSBTH.ttf


+ 12 - 0
assets/styles/commom.scss

@@ -0,0 +1,12 @@
+@font-face {
+  font-family: 'PangMenZhengDao';
+  /* 自定义的字体名称 */
+  src: url('@/assets/fonts/PMZDCT.ttf') format('truetype');
+  /* 字体文件路径及格式 */
+}
+@font-face {
+  font-family: 'YSBTH';
+  /* 自定义的字体名称 */
+  src: url('@/assets/fonts/YSBTH.ttf') format('truetype');
+  /* 字体文件路径及格式 */
+}

+ 7 - 0
assets/styles/uview-ui.scss

@@ -0,0 +1,7 @@
+
+.u-row {
+    width: 100%;
+}
+.u-toolbar__wrapper__confirm{
+  color: #4573FC !important;
+}

+ 19 - 0
common/css/icon.css

@@ -0,0 +1,19 @@
+@font-face {
+    font-family: 'iconfont';  /* Project id 2685108 */
+    src: url('//at.alicdn.com/t/font_2685108_cunb39ou4p.woff2?t=1626684182445') format('woff2'),
+         url('//at.alicdn.com/t/font_2685108_cunb39ou4p.woff?t=1626684182445') format('woff'),
+         url('//at.alicdn.com/t/font_2685108_cunb39ou4p.ttf?t=1626684182445') format('truetype');
+  }
+  
+  .iconfont {
+      font-family: "iconfont" !important;
+      font-size: 40upx;
+      font-style: normal;
+      -webkit-font-smoothing: antialiased;
+      -moz-osx-font-smoothing: grayscale;
+  }
+  
+  .icon-you:before {
+    content: "\e632";
+  }
+  

+ 410 - 0
components/Intercom/index.vue

@@ -0,0 +1,410 @@
+<template>
+	<view>
+		<u-toast ref="uToast" />
+	</view>
+</template>
+<script>
+import { videoStart,videoControl  } from "@/api/core/sendOrder";
+//录音对象
+var Recorder = function (stream) {
+  var sampleBits = 16; //输出采样数位 8, 16
+  var sampleRate = 8000; //输出采样率
+  var context = new AudioContext();
+  var audioInput = context.createMediaStreamSource(stream);
+  var recorder = context.createScriptProcessor(4096, 1, 1);
+  var audioData = {
+    size: 0, //录音文件长度
+    buffer: [], //录音缓存
+    inputSampleRate: 48000, //输入采样率
+    inputSampleBits: 16, //输入采样数位 8, 16
+    outputSampleRate: sampleRate,
+    oututSampleBits: sampleBits,
+    clear: function () {
+      this.buffer = [];
+      this.size = 0;
+    },
+    input: function (data) {
+      this.buffer.push(new Float32Array(data));
+      this.size += data.length;
+    },
+    compress: function () {
+      //合并压缩
+      //合并
+      var data = new Float32Array(this.size);
+      var offset = 0;
+      for (var i = 0; i < this.buffer.length; i++) {
+        data.set(this.buffer[i], offset);
+        offset += this.buffer[i].length;
+      }
+      //压缩
+      var compression = parseInt(this.inputSampleRate / this.outputSampleRate);
+      var length = data.length / compression;
+      var result = new Float32Array(length);
+      var index = 0,
+        j = 0;
+      while (index < length) {
+        result[index] = data[j];
+        j += compression;
+        index++;
+      }
+      return result;
+    },
+    encodePCM: function () {
+      //这里不对采集到的数据进行其他格式处理,如有需要均交给服务器端处理。
+      var sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);
+      var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);
+      var bytes = this.compress();
+      var dataLength = bytes.length * (sampleBits / 8);
+      var buffer = new ArrayBuffer(dataLength);
+      var data = new DataView(buffer);
+      var offset = 0;
+      for (var i = 0; i < bytes.length; i++, offset += 2) {
+        var s = Math.max(-1, Math.min(1, bytes[i]));
+        data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
+      }
+      return new Blob([data]);
+    },
+  };
+  this.start = function () {
+    audioInput.connect(recorder);
+    recorder.connect(context.destination);
+  };
+
+  this.stop = function () {
+    recorder.disconnect();
+  };
+
+  this.getBlob = function () {
+    return audioData.encodePCM();
+  };
+
+  this.clear = function () {
+    audioData.clear();
+  };
+
+  recorder.onaudioprocess = function (e) {
+    audioData.input(e.inputBuffer.getChannelData(0));
+  };
+};
+export default {
+	data() {
+	  return {
+	    ws: null,
+		deviceId:'',
+		wsUrl: "wss://80.5000v.com/websocket", // websocket地址
+		record: null, //多媒体对象,用来处理音频
+		sendingTimes: 0, // 发送设备号的次数
+		timeInte: null, // 定时任务 定时发送
+		audo: new Audio(),
+		openIntercom:false
+	  };
+	},
+	created() {
+		
+	},
+	methods: {
+		init () {
+           navigator.mediaDevices
+            .getUserMedia({	audio: true})
+            .then( (mediaStream)=> {
+                /* 使用这个 stream stream */
+                console.log('使用这个',mediaStream)
+                this.record = new Recorder(mediaStream);
+            })
+            .catch(function (err) {
+                /* 处理 error */
+                console.log('处理 error',err)
+                            uni.showToast({
+                                title: '浏览器不支持音频输入'+err,
+                                duration: 2000,
+                                icon: 'none'
+                            });
+			});
+
+            			// navigator.getUserMedia(
+						//   {
+						// 	audio: true,
+						//   },
+						//   (mediaStream) => {
+                        //     console.log('2使用这个',mediaStream)
+						// 	this.record = new Recorder(mediaStream);
+							
+						//   },
+						//   (error) => {
+						//   });
+
+            // console.log('方法',getUserMedia)
+            // let getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia;
+            // navigator.getUserMedia = getUserMedia
+			// if (!navigator.getUserMedia) {
+            //                 uni.showToast({
+            //                     title: '浏览器不支持音频输入'+getUserMedia,
+            //                     duration: 2000,
+            //                     icon: 'none'
+            //                 });
+			// 			   console.log('浏览器不支持音频输入')
+			// } else {
+			// 			navigator.getUserMedia(
+			// 			  {
+			// 				audio: true,
+			// 			  },
+			// 			  (mediaStream) => {
+			// 				this.record = new Recorder(mediaStream);
+			// 				console.log('2121',this.record)
+			// 			  },
+			// 			  (error) => {
+			// 			  }
+			// 			);
+			// }
+		},
+		async toIntercom(deviceId) {
+			console.log(deviceId,'itemdeviceId')
+			if(this.isNull(deviceId)) {
+				uni.showToast({
+                    title: 'deviceId为空',
+                    duration: 2000,
+                    icon: 'none'
+                });
+				return false
+			}
+			this.init()
+		   this.deviceId = deviceId;
+		   // #ifdef H5
+		   if(this.openIntercom){
+		     //打开中 执行关闭
+		     let obj = {
+		       audioVideoType: 0,
+		       channel: 1,
+		       controll: 4,
+		       dataRate: 0,
+		       deviceId: deviceId, //设备号
+		     };
+             let res = await videoControl(obj);
+		   if(res.code == '0'){
+				 uni.closeSocket();
+			
+				 this.openIntercom = false;
+				 this.ws.close();
+				 this.record.stop();
+				 clearInterval(this.timeInte);
+				 this.ws = null;
+				 this.timeInte = null;
+			 
+			 }else{
+				 uni.showToast({
+                    title:res.desc,
+                    duration: 2000,
+                    icon: 'none'
+                });
+			 }
+		   }else {
+		     // 关闭中 执行打开
+		   let obj = {
+		       channel: 1,
+		       dataRate: 1,
+		       dataType: 2, // 对讲
+		       deviceId: deviceId, // 设备号
+		     };
+             let res = await videoStart(obj);
+		    if(res.code == '0'){
+		       // 成功以后连接socket
+		        // 命令下发成功
+                let wsUrl;
+                if (window.location.hostname != "localhost") {
+                        wsUrl = "wss://" + window.location.host + "/websocket";
+                    } else {
+                        wsUrl = "wss://tq.5000v.cn:443/websocket"
+                    }
+		        this.ws = new WebSocket(wsUrl); // 连接websocket
+		        this.ws.binaryType = "arraybuffer"; //传输的是 ArrayBuffer 类型的数据
+		        this.ws.onopen = (event) => {
+		          // 连接成功后
+		          console.log("websocket连接成功");
+		          // 连接成功后发送设备号
+		          this.websocketSend();
+		        };
+		
+		        this.ws.onclose = (event) => {
+		          // 监听websocket断开
+		          console.log("websocket连接关闭");
+                  this.$emit('oNstate',false)//回传状态
+		        };
+		
+		        this.ws.onmessage = (evt) => {
+		          // 收到websocket传来的信息
+		          console.log('websocket传来的信息',evt.data)
+                  this.$emit('oNstate',true)//回传状态
+		          if(typeof evt.data == 'string'){
+		            if(evt.data.indexOf(':1') > -1){
+		              // :1 代表接通 :0代表未接通
+		              this.record.start(); //开始录音
+		              this.receive();
+		            }else {
+		              if(this.sendingTimes < 3) {
+		                // 重发次数小于3 继续发
+		                // 间隔一秒发
+		                setTimeout(()=>{
+		                  this.websocketSend();
+		                  this.sendingTimes++
+		                }, 1000);
+		              }else{
+		                // 重发3次 提升无法连接 并且下发关闭指令 关闭连接
+		                // this.$message.error('设备无法链接');
+						// this.$refs.uToast.show({
+						//   title: '设备无法链接',
+						//   type: "error",
+						// });
+                        uni.showToast({
+                            title:'设备无法链接',
+                            duration: 2000,
+                            icon: 'none'
+                        });
+		                // this.closeIntercom();
+                        this.toIntercom(this.deviceId);
+		                this.sendingTimes = 0;
+		              }
+		            }
+		          } else {
+		            this.playAudio(evt.data);
+		          }
+		
+		        };
+				
+				this.openIntercom = true;
+		     }else{
+				//  this.$refs.uToast.show({
+				//    title: res.desc,
+				//    type: "error",
+				//  });
+                uni.showToast({
+                    title:res.desc,
+                    duration: 2000,
+                    icon: 'none'
+                });
+			 }
+		    
+		   }
+           this.$emit('oNstate',this.openIntercom)
+		   // #endif
+		},
+		websocketSend() {
+		  this.ws.send(this.deviceId);
+		},
+		receive() {
+			  // 对讲
+			this.timeInte = setInterval(()=>{
+				if(this.ws.readyState==1){ // ws进入连接状态,则每隔500毫秒发送一包数据
+					console.log(this.record.getBlob());
+					this.ws.send(this.record.getBlob()); //发送音频数据
+					this.record.clear();//每次发送完成则清理掉旧数据
+				}
+			},200);  //每隔200ms发送一次,定时器
+		},
+		
+		
+		
+		playAudio(data) {
+		  // 收听
+		  let _this = this;
+		  var buffer = (new Response(data)).arrayBuffer();
+		  buffer.then(function(buf){
+			var audioContext = new ( window.AudioContext || window.webkitAudioContext )();
+			var fileResult = _this.addWavHeader(buf, '8000', '16', '1'); // 解析数据转码wav
+			audioContext.decodeAudioData(fileResult, function(buffer) {
+				_this.visualize(audioContext, buffer); // 播放
+			});
+		  });
+		},
+	
+		visualize(audioContext, buffer) {
+		  var audioBufferSouceNode = audioContext.createBufferSource();
+		  var analyser = audioContext.createAnalyser();
+		  //将信号源连接到分析仪
+		  audioBufferSouceNode.connect(analyser);
+		  //将分析仪连接到目的地(扬声器),否则我们将听不到声音
+		  analyser.connect(audioContext.destination);
+		  //然后将缓冲区分配给缓冲区源节点
+		  audioBufferSouceNode.buffer = buffer;
+		  //发挥作用
+		  if (!audioBufferSouceNode.start) {
+			  audioBufferSouceNode.start = audioBufferSouceNode.noteOn //在旧浏览器中使用noteOn方法
+			  audioBufferSouceNode.stop = audioBufferSouceNode.noteOff //在旧浏览器中使用noteOff方法
+		  };
+		  //如果有的话,停止前一个声音
+		  if (this.animationId !== null) {
+			  cancelAnimationFrame(this.animationId);
+		  }
+		  audioBufferSouceNode.start(0);
+		  this.audo.source = audioBufferSouceNode;
+		  this.audo.audioContext = audioContext;
+		},
+	
+	
+		//处理音频流,转码wav
+		addWavHeader(samples, sampleRateTmp, sampleBits, channelCount) {
+			var dataLength = samples.byteLength;
+			var buffer = new ArrayBuffer(44 + dataLength);
+			var view = new DataView(buffer);
+			function writeString(view, offset, string){
+				for (var i = 0; i < string.length; i++){
+					view.setUint8(offset + i, string.charCodeAt(i));
+				}
+			}
+			var offset = 0;
+			/* 资源交换文件标识符 */
+			writeString(view, offset, 'RIFF'); offset += 4;
+			/* 下个地址开始到文件尾总字节数,即文件大小-8 */
+			view.setUint32(offset, /*32*/ 36 + dataLength, true); offset += 4;
+			/* WAV文件标志 */
+			writeString(view, offset, 'WAVE'); offset += 4;
+			/* 波形格式标志 */
+			writeString(view, offset, 'fmt '); offset += 4;
+			/* 过滤字节,一般为 0x10 = 16 */
+			view.setUint32(offset, 16, true); offset += 4;
+			/* 格式类别 (PCM形式采样数据) */
+			view.setUint16(offset, 1, true); offset += 2;
+			/* 通道数 */
+			view.setUint16(offset, channelCount, true); offset += 2;
+			/* 采样率,每秒样本数,表示每个通道的播放速度 */
+			view.setUint32(offset, sampleRateTmp, true); offset += 4;
+			/* 波形数据传输率 (每秒平均字节数) 通道数×每秒数据位数×每样本数据位/8 */
+			view.setUint32(offset, sampleRateTmp * channelCount * (sampleBits / 8), true); offset +=4;
+			/* 快数据调整数 采样一次占用字节数 通道数×每样本的数据位数/8 */
+			view.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2;
+			/* 每样本数据位数 */
+			view.setUint16(offset, sampleBits, true); offset += 2;
+			/* 数据标识符 */
+			writeString(view, offset, 'data'); offset += 4;
+			/* 采样数据总数,即数据总大小-44 */
+			view.setUint32(offset, dataLength, true); offset += 4;
+			function floatTo32BitPCM(output, offset, input){
+				input = new Int32Array(input);
+				for (var i = 0; i < input.length; i++, offset+=4){
+					output.setInt32(offset,input[i],true);
+				}
+			}
+			function floatTo16BitPCM(output, offset, input){
+				input = new Int16Array(input);
+				for (var i = 0; i < input.length; i++, offset+=2){
+					output.setInt16(offset,input[i],true);
+				}
+			}
+			function floatTo8BitPCM(output, offset, input){
+				input = new Int8Array(input);
+				for (var i = 0; i < input.length; i++, offset++){
+					output.setInt8(offset,input[i],true);
+				}
+			}
+			if(sampleBits == 16){
+				floatTo16BitPCM(view, 44, samples);
+			}else if(sampleBits == 8){
+				floatTo8BitPCM(view, 44, samples);
+			}else{
+				floatTo32BitPCM(view, 44, samples);
+			}
+			return view.buffer;
+		}
+	}
+}
+
+</script>

+ 28 - 0
components/MapContainer/components/pastRoute.vue

@@ -0,0 +1,28 @@
+<template>
+	<view class="">
+	</view>
+</template>
+
+<script>
+export default {
+	components: {
+	},
+	data() {
+		return {
+			
+		}
+	},
+	onReady () {		  
+	},
+	methods: {
+		callBack() {
+			
+		}
+		
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 28 - 0
components/MapContainer/components/vehicleInfo.vue

@@ -0,0 +1,28 @@
+<template>
+	<view class="">
+	</view>
+</template>
+
+<script>
+export default {
+	components: {
+	},
+	data() {
+		return {
+			
+		}
+	},
+	onReady () {		  
+	},
+	methods: {
+		callBack() {
+			
+		}
+		
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 476 - 0
components/MapContainer/controller/createApi.js

@@ -0,0 +1,476 @@
+import _ from "lodash";
+import {getImages} from '@/plugins/images.js'
+import store from '@/store'
+//图标
+
+let cheliangtuli_tingche1 = getImages('/assetsMobile/svg/icon-cheliangtuli_tingche1.svg') //停留报警
+let cheliangtuli_baojing1 = getImages('/assetsMobile/svg/icon-cheliangtuli_baojing1.svg') //违规报警
+let origin = getImages('/assetsMobile/svg/origin.svg') //起点
+let destination = getImages('/assetsMobile/svg/destination.svg') //终点
+// 经过点类型 1.标注点 2.收集点 3.转运站 4.处理厂 5.公厕 6.投放点 7.垃圾桶 途径点不同类型图标
+let vehis1Icon = getImages('/assetsMobile/svg/icon-cheliangtuli_biaozhudian.svg') 
+let vehis2Icon = getImages('/assetsMobile/svg/icon-cheliang_shoujidian.svg') 
+let vehis3Icon = getImages('/assetsMobile/svg/icon-cheliangtuli_zhuanyunzhan.svg')
+let vehis4Icon = getImages('/assetsMobile/svg/icon-cheliang_chulichang.svg')
+let vehis5Icon = getImages('/assetsMobile/svg/icon-cheliang_toufangdian.svg')
+let vehis6Icon = getImages('/assetsMobile/svg/icon-cheliang_toufangdian.svg')
+let vehis7Icon = getImages('/assetsMobile/svg/icon-cheliangtuli_lajitong.svg')
+
+
+/**创建弧线*/
+export function createPulseLinkLayer(map) {
+
+    // 创建 Loca 实例
+    let loca = new Loca.Container({
+        map: map
+    });
+
+    // 弧线
+    var pulseLink = new Loca.PulseLinkLayer({
+        // loca,
+        zIndex: 10,
+        opacity: 1,
+        visible: true,
+        zooms: [2, 22],
+        depth: true,
+    });
+
+    var geo = new Loca.GeoJSONSource({
+        url: 'https://a.amap.com/Loca/static/loca-v2/demos/mock_data/data-line-out.json',
+    });
+
+    pulseLink.setSource(geo);
+    pulseLink.setStyle({
+        unit: 'meter',
+        dash: [40000, 0, 40000, 0],
+        lineWidth: function () {
+            return [10000, 1000];
+        },
+        height: function (index, feat) {
+            return feat.distance / 3 + 10;
+        },
+        // altitude: 1000,
+        smoothSteps: 30,
+        speed: function (index, prop) {
+            return 1000 + Math.random() * 200000;
+        },
+        flowLength: 100000,
+        lineColors: function (index, feat) {
+            return ['rgb(255,228,105)', 'rgb(255,164,105)', 'rgba(1, 34, 249,1)'];
+        },
+        maxHeightScale: 0.3, // 弧顶位置比例
+        headColor: 'rgba(255, 255, 0, 1)',
+        trailColor: 'rgba(255, 255,0,0)',
+    });
+    loca.add(pulseLink);
+    loca.animate.start();
+}
+
+
+/**创建聚合点*/
+export function createMarkerCluster(map, data, callBack) {
+    let { points } = data
+    /**完全自定义点样式*/
+    function _renderMarker(context) {
+        // console.log(context,'context')
+        // var content = `<img style="width: 28px;"  src="${context.data[0].item.img}" />`;
+        var content = `
+                <div style="position: relative;">
+                    <img src="${context.data[0].item.img}" 
+                        style="width: 28px; transform:rotate(${context.data[0].item.direction}deg)"/></br>
+                    <div style="
+                        border:solid 1px #00a0e9; 
+                        background-color:#ffffff;
+                        position: absolute;
+                        padding: 2px;
+                        white-space: nowrap;
+                        left: 50%;
+                        top: 100%;
+						font-size: 12px;
+                        transform: translate(-50%,0%);
+                    "
+                       
+                    >
+                        ${context.data[0].item.label}
+                    </div>
+                </div>
+        `;
+        var offset = new AMap.Pixel(-14, -14);
+        context.marker.setContent(content)
+        context.marker.setOffset(offset)
+        // context.marker.setAngle(context.data[0].item.direction)
+    }
+    let cluster = new AMap.MarkerCluster(map, points, {
+        gridSize: 60, // 设置网格像素大小
+        renderMarker: _renderMarker,
+        clusterByZoomChange: false
+    });
+	
+	let handleClickData = _.throttle((e) => {
+		// console.log('testeeee',e)
+		callBack(e)
+	},500, {leading: true,trailing: false})
+    cluster.on('click', (e) => {
+		handleClickData(e)
+    })
+
+    return cluster
+}
+
+/**创建标注点*/
+let allmarkerArr = [];
+export function createMarker(map, data, callBack) {
+    console.log('参', data)
+    let iconMap = {
+        'vehStop': cheliangtuli_tingche1,
+        'veAlarm': cheliangtuli_baojing1,
+        // 经过点类型 1.标注点 2.收集点 3.转运站 4.处理厂 5.公厕 6.投放点 7.垃圾桶
+        'vehis1': vehis1Icon,
+        'vehis2': vehis2Icon,
+        'vehis3': vehis3Icon,
+        'vehis4': vehis4Icon,
+        'vehis5': vehis5Icon,
+        'vehis6': vehis6Icon,
+        'vehis7': vehis7Icon,
+    }
+    let markerArr = data.list.map(e => {
+        let icontype=data.type
+        if(icontype==='vehis'){ //途径点
+            icontype=`${icontype}${e.type}`
+            e.cloneType = e.type
+        }
+        // 创建一个 Icon
+        let startIcon = new AMap.Icon({
+            // 图标尺寸
+            size: new AMap.Size(31, 40),
+            // 图标的取图地址
+            image: iconMap[icontype],//'//a.amap.com/jsapi_demos/static/demo-center/icons/dir-marker.png',
+            // 图标所用图片大小
+            imageSize: new AMap.Size(31, 40),
+        });
+        let marker
+        if (e.option) {
+            marker = new AMap.Marker({
+                ...e.option
+            });
+        } else {
+            marker = new AMap.Marker({
+                icon: startIcon,
+                position: [e.lng02, e.lat02],
+                offset: new AMap.Pixel(0, 6),
+                anchor: 'bottom-center'
+            });
+        }
+
+        marker.on('click', function (el) {
+            console.log('点击', e)
+            callBack('click', { ...e, type: data.type})
+        })
+        return marker
+    })
+    map.add(markerArr);
+    // 实例方法
+    allmarkerArr = allmarkerArr.concat(markerArr)
+    const removeAllMarkers = () => {
+        for (var i = 0; i < allmarkerArr.length; i++) {
+            allmarkerArr[i].setMap(null); // 从地图上移除标记点
+        }
+        allmarkerArr = []; // 清空存储标记点的数组
+    }
+
+    return { removeAllMarkers };
+}
+
+/**创建轨迹回放 */
+let marker, originMarker, destinationMarker, polyline, passedPolyline, startAnimation, removeShowSpeed;
+const setCenterDebounce = _.throttle((map, e) => {
+    var isVisible = map.getBounds().contains(e.target.getPosition());
+    if (!isVisible) {
+        map.setCenter(e.target.getPosition(), true)//自动聚焦
+    }
+    // map.setCenter(e.target.getPosition(), true)//自动聚焦
+}, 0);
+
+export function createMoveAlong(map, data, callBack) {
+    console.log(marker, 'marker数据', data)
+    let pointsData = data.points
+    let points = pointsData.map(e => {
+        return [Number(e.lng02), Number(e.lat02)]
+    })
+    let resetIdx;
+    //清空已有实例
+    if (marker) {
+        marker.stopMove();
+        marker.off('moving', markerMoving);
+        marker.setMap(null);
+        marker = null;
+    }
+    //清空起点和终点
+    if (originMarker) {
+        originMarker.setMap(null);
+        originMarker = null
+    }
+    if (destinationMarker) {
+        destinationMarker.setMap(null);
+        destinationMarker = null
+    }
+
+    if (polyline) {
+        polyline.setMap(null);
+        polyline = null;
+    }
+    if (passedPolyline) {
+        passedPolyline.setMap(null);
+        passedPolyline = null;
+    }
+    if (removeShowSpeed) {
+        removeShowSpeed();
+    }
+
+    if (pointsData.length <= 0) return; //无数据
+    let duration = pointsData[0].duration //传入的速度
+    let tabStyle = pointsData[0].tabStyle //传入的类型 人员车辆
+
+    // 1、创建图标
+	
+
+    let icons
+	if (store.state.pastRoute.mapImg) {
+		icons = new AMap.Icon({
+		    image: store.state.pastRoute.mapImg, // 图标的图片地址
+		    size: new AMap.Size(28, 28), // 设置图标的大小,这里是宽度和高度均为48像素
+		    imageSize: new AMap.Size(28, 28), // 设置图标的大小,这里是宽度和高度均为48像素
+		})
+	} else {
+		icons = "https://a.amap.com/jsapi_demos/static/demo-center-v2/car.png"
+	}
+	
+
+    marker = new AMap.Marker({
+        map: map,
+        position: points[0], //points[0],
+        icon: icons,
+        zIndex: 18,
+        offset: new AMap.Pixel(-13, -26),
+    });
+    //创建起点终点图标显示
+    let originicons = new AMap.Icon({
+        image: origin, // 图标的图片地址
+        size: new AMap.Size(28, 28), // 设置图标的大小,这里是宽度和高度均为48像素
+        imageSize: new AMap.Size(28, 28), // 设置图标的大小,这里是宽度和高度均为48像素
+    })
+    originMarker = new AMap.Marker({
+        map: map,
+        position: points[0], //points[0],
+        icon: originicons,
+        zIndex: 16,
+        offset: new AMap.Pixel(-13, -26),
+    });
+    let destinationicons = new AMap.Icon({
+        image: destination, // 图标的图片地址
+        size: new AMap.Size(28, 28), // 设置图标的大小,这里是宽度和高度均为48像素
+        imageSize: new AMap.Size(28, 28), // 设置图标的大小,这里是宽度和高度均为48像素
+    })
+    destinationMarker = new AMap.Marker({
+        map: map,
+        position: points[points.length - 1], //points[0],
+        icon: destinationicons,
+        zIndex: 16,
+        offset: new AMap.Pixel(-13, -26),
+    });
+
+    // 2、创建路线
+
+    polyline = new AMap.Polyline({
+        map: map,
+        path: points,
+        zIndex: 10,
+        showDir: true,
+        strokeColor: "#28F",  //线颜色
+        // strokeOpacity: 1,     //线透明度
+        strokeWeight: 6,      //线宽
+        // strokeStyle: "solid"  //线样式
+    });
+    // 3、创建行驶路线
+    passedPolyline = new AMap.Polyline({
+        zIndex: 13,
+        map: map,
+        strokeColor: "#AF5",  //线颜色
+        strokeWeight: 6,      //线宽
+    });
+
+
+    // 4、事件监听
+    marker.on('moving', markerMoving);
+    function markerMoving(e) {
+        console.log('更新轨迹', e)
+        // 更新轨迹
+        let passedPath = e.passedPath
+        if (resetIdx) {
+            e.index = e.index + resetIdx //拖拽
+            passedPath = [...points.slice(0, resetIdx), ...e.passedPath]  //points.slice(0,index+1).concat(e.passedPath)
+        }
+        if (tabStyle === 'vehicle') {
+            marker.setAngle(pointsData[e.index + 1].direction) //手动设置方向
+        }
+        passedPolyline.setPath(passedPath);//聚焦
+        setCenterDebounce(map, e)
+        callBack('moving', e)
+    }
+    marker.on('click', function (e) {
+        if (resetIdx) {
+            e.resetIdx = resetIdx
+        }
+        
+        callBack('click', e)
+    })
+
+    map.setFitView(marker,false,[60,60,160,60],10);
+    // 开始
+    startAnimation = function startAnimation() {
+        let resetPoints;
+        if (resetIdx) { //拖拽
+            resetPoints = points.slice(resetIdx, points.length)
+            if (resetPoints.length < 2) return; //moveAlong必须两点
+        }
+        marker.moveAlong(resetPoints || points, {
+            // 每一段的时长
+            duration: duration || 500,//可根据实际采集时间间隔设置
+            // JSAPI2.0 是否延道路自动设置角度在 moveAlong 里设置
+            autoRotation: false, //设置后setAngle无效BUG 
+        });
+    };
+
+    // 销毁事件
+
+    const pauseAnimation = function () {
+        marker.pauseMove();
+    };
+
+    const resumeAnimation = function () {
+        marker.resumeMove();
+    };
+
+    const stopAnimation = function () {
+        marker.stopMove();
+    };
+
+
+    const moveTo = function ({ lnglat, angle, index }) {
+        stopAnimation()
+        resetIdx = index
+        marker.setPosition(lnglat)
+        // console.log('设置方向', angle)
+        if (tabStyle === 'vehicle') {
+            marker.setAngle(angle)
+        }
+        passedPolyline.setPath(points.slice(0, index + 1));
+        callBack('moveTo', { lnglat, angle, index })
+
+        // marker.moveTo(lnglat) //卡顿BUG
+    }
+
+    const resetMoveAlong = function () {
+        startAnimation()
+    }
+
+    // 显示速度
+    let polylineArr = []
+    const showSpeed = function () {
+        for (var i = 0; i < points.length - 1; i++) {
+            let itempolyline = new AMap.Polyline({
+                path: [points[i], points[i + 1]],
+                showDir: true,
+                strokeColor: speedColor(pointsData[i].speed),
+                strokeOpacity: 1,
+                zIndex: 11,
+                strokeWeight: 6,// 线宽
+                map: map
+            });
+            polylineArr.push(itempolyline)
+        }
+        function speedColor(e) { //速度颜色
+            let speed = Number(e)
+            let itemColor = '#16B92A'
+            if (speed > 60 && speed <= 80) {
+                itemColor = '#0000FE'
+            } else if (speed > 80 && speed <= 100) {
+                itemColor = '#F98202'
+            } else if (speed > 100) {
+                itemColor = '#FE0100'
+            }
+            return itemColor
+        }
+    }
+    removeShowSpeed = () => {
+        for (var i = 0; i < polylineArr.length; i++) {
+            polylineArr[i].setMap(null); // 从地图上移除标记点
+        }
+        polylineArr = []; // 清空存储标记点的数组
+    }
+
+
+    return {
+        startAnimation,
+        pauseAnimation,
+        resumeAnimation,
+        stopAnimation,
+        moveTo,
+        resetMoveAlong,
+        showSpeed, //显示速度
+        removeShowSpeed //关闭速度
+    }
+}
+
+/**创建一条展示路线 */
+let PolylineShow,lineShoworiginMarker,lineShowdestinationMarker;
+export function createPolylineShow(map, data) {
+    if (PolylineShow) {
+        PolylineShow.setMap(null);
+        PolylineShow = null;
+    }
+    if (lineShoworiginMarker) {
+        lineShoworiginMarker.setMap(null);
+        lineShoworiginMarker = null;
+    }
+    if (lineShowdestinationMarker) {
+        lineShowdestinationMarker.setMap(null);
+        lineShowdestinationMarker = null;
+    }
+    let { points } = data
+    let pointsArr = points.map(e => {
+       return [Number(e.lng02), Number(e.lat02)]
+    })
+    PolylineShow = new AMap.Polyline({
+        map: map,
+        path: pointsArr,
+        strokeStyle: "solid",
+        strokeColor: "#027AFF",  //线颜色
+        strokeWeight: 6,      //线宽
+    });
+     //创建起点终点图标显示
+     let originicons = new AMap.Icon({
+        image: origin, // 图标的图片地址
+        size: new AMap.Size(28, 28), // 设置图标的大小,这里是宽度和高度均为48像素
+        imageSize: new AMap.Size(28, 28), // 设置图标的大小,这里是宽度和高度均为48像素
+    })
+    lineShoworiginMarker = new AMap.Marker({
+        map: map,
+        position: pointsArr[0], //points[0],
+        icon: originicons,
+        zIndex: 16,
+        offset: new AMap.Pixel(-13, -26),
+    });
+    let destinationicons = new AMap.Icon({
+        image: destination, // 图标的图片地址
+        size: new AMap.Size(28, 28), // 设置图标的大小,这里是宽度和高度均为48像素
+        imageSize: new AMap.Size(28, 28), // 设置图标的大小,这里是宽度和高度均为48像素
+    })
+    lineShowdestinationMarker = new AMap.Marker({
+        map: map,
+        position: pointsArr[pointsArr.length - 1], //points[0],
+        icon: destinationicons,
+        zIndex: 16,
+        offset: new AMap.Pixel(-13, -26),
+    });
+}

+ 590 - 0
components/MapContainer/index.vue

@@ -0,0 +1,590 @@
+<template>
+    <view class="container-box">
+        <view :id="mapId" class="container"></view>
+
+        <!-- info window弹框窗体 -->
+        <component :is="componentView" ref="componentViewRef" />
+
+        <slot></slot>
+    </view>
+</template>
+<script>
+import Vue from 'vue'
+import AMapLoader from '@amap/amap-jsapi-loader';
+import { createPulseLinkLayer, createMarkerCluster, createMoveAlong, createMarker, createPolylineShow } from "./controller/createApi";
+import { uniqueId,isEmpty } from 'lodash'
+import {getMaxMinBounds,listCrudChange} from '@/utils/gdtq.js'
+
+/**地图展示相关组件*/
+import vehicleInfo from './components/vehicleInfo.vue';//聚合车辆弹框
+import pastRoute from './components/pastRoute.vue'; //轨迹弹框
+
+//组件映射
+const componentsMapDefault = {
+    markerCluster: vehicleInfo,
+    moveAnimation: pastRoute,
+}
+
+
+export default {
+	components: {
+	},
+	props: {
+		//外部重置默认弹窗组件
+		componentsMapProps: {
+		    type: Object,
+		    default: () => {}
+		},
+		mapConfig: {
+		    type: Object,
+		    default: () => { }
+		},
+		pointsData: {
+		    type: Array,
+		    default: () => []
+		},
+		pointsDataConfig: {
+		    type: Object,
+		    default: () => { }
+		},
+		toolMapKey: { //markerCluster moveAnimation(轨迹回放)  
+		    type: String,
+		    default: 'markerCluster'
+		},
+		isCreateInfoWindow: {
+		    type: Boolean,
+		    default: true
+		},
+		infoWindowConfig: {
+		    type: Object,
+		    default: () => {
+		        return {
+		            isCustom: true
+		        }
+		    }
+		},
+		//城市定位
+		enableCityPositon: {
+			type: Boolean,
+			default: true
+		}
+	},
+	watch: {
+		pointsData: function(pointsData, prevPointsData)  {
+			/**对比改变后的数据做业务处理*/
+			// console.log('watch地图数据', pointsData, prevPointsData)
+			if (this.map === null) return;
+			this.createProjects(this.toolMapKey, { points: this.pointsData, prePoints: prevPointsData })
+		}
+	},
+	
+	data() {
+		return {
+			map: null,
+			mapId:  uniqueId('gaode_map_'),
+			
+			/* 聚合相关 */
+			cluster: undefined, //点聚合实例
+			
+			/* 轨迹相关 */
+			 createMoveAlongFn: null,//轨迹回放响应式实例对象
+			 allmarkerPro: undefined,
+			 isMoveAlongWindow: undefined,
+			 
+			 /* 弹窗相关*/
+			  createInfoWindow: undefined,
+			  componentView: undefined,
+			  
+			  satelliteLayer: '', //卫星图
+	
+			trafficLayer: '', //实时路况
+			
+			isMoveAlongWindow: false,
+		}
+	},
+	
+	
+	mounted() {
+		let that = this;
+		//组件映射
+		this.componentsMap = Object.assign(componentsMapDefault,this.componentsMapProps)
+		
+		/* 窗体相关 */
+		this.createInfoWindow = function createInfoWindowFn() {
+			let infoWindow;
+			let cluster2;
+		
+			function add(data) {
+				let { cluster, clusterData, lnglat, offset, componentsMapKey } = data
+				if (clusterData) {
+					cluster2 = clusterData.length > 0 ? clusterData : [cluster.p]
+				}
+		
+				that.componentView = that.componentsMap[componentsMapKey]
+				
+				that.$nextTick(() => {
+					console.log(that.$refs.componentViewRef,'that.that.$refs.componentViewRef')
+					that.$refs.componentViewRef.callBack(data, closeWindow)
+					
+					if (clusterData?.length > 1) return;
+					infoWindow = new AMap.InfoWindow({
+						...that.infoWindowConfig,
+						content: that.$refs.componentViewRef.$el,
+						offset: offset
+					});
+					that.map.on('click', function () {
+						infoWindow.close();
+					});
+					setTimeout(() => {
+						infoWindow.open(that.map, [lnglat.lng, lnglat.lat]);
+						infoWindow.setOffset(new AMap.Pixel(0, -23));
+					}, 0);
+				})
+				
+			}
+		
+			function setPosition(data) {
+				infoWindow.setPosition(data);
+			}
+		
+			function closeWindow() {
+				if (infoWindow) {
+					infoWindow.close();
+				}
+			}
+		
+			function getItem() {
+				return cluster2
+			}
+			
+			function getinfoWindow() {
+				return infoWindow
+			}
+			
+			return {
+				add: add,
+				setPosition: setPosition,
+				closeWindow: closeWindow,
+				getItem: getItem,
+				getinfoWindow: getinfoWindow,
+			};
+		}()
+		
+		
+		this.initMap()
+
+	},
+	
+	methods: {
+		
+		initMap() {
+		    AMapLoader.load({
+		        key: "08d130e013f96dcf4329cf7bac274c8e",  // 申请好的Web端开发者Key,首次调用 load 时必填
+		        version: "2.0",      // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
+		        plugins: [
+		            'AMap.MarkerCluster', 'AMap.Polyline', 'AMap.MoveAnimation', 'AMap.CircleEditor',
+		            'AMap.Driving', 'AMap.AutoComplete', 'AMap.Scale','AMap.MouseTool', 'AMap.Geolocation','AMap.CitySearch',
+		        ],       // 需要使用的的插件列表,如比例尺'AMap.Scale'等
+		        Loca: {                // 是否加载 Loca, 缺省不加载
+		            "version": '2.0.0'  // Loca 版本,缺省 1.3.2
+		        },
+		    }).then((AMap) => {
+				console.log('AMap Load','data')
+		        this.map = new AMap.Map(this.mapId, {  //设置地图容器id
+		            viewMode: "3D",    //是否为3D地图模式
+		            zoom: 3,           //初始化地图级别
+		            center: [105.602725, 37.076636], //初始化地图中心点位置
+		            ...this.mapConfig
+		        });
+		        this.createProjects(this.toolMapKey, { points: this.pointsData, pointsDataConfig: this.pointsDataConfig })
+		
+		        this.map.addControl(new AMap.Scale());
+				
+				if(this.enableCityPositon) {
+					this.citySearch()
+				}
+		        
+		
+		        this.$emit('onload', this.map)
+		    }).catch(e => {
+		        console.log(e);
+		    })
+		},
+		createProjects(key, data, callBack) {
+		    if (!key) return
+		    const toolMapFn = {
+		        pulseLinkLayer: () => {//飞线
+		            createPulseLinkLayer(this.map, data, callBack);
+		        },
+		        markerCluster: () => {//创建聚合点
+
+		            if (!this.cluster) {
+		                this.cluster = createMarkerCluster(this.map, data, (e) => {
+		                    const { clusterData, lnglat } = e
+		                    if (clusterData.length > 1) {
+		                      
+		                        let {southWest,northEast} = getMaxMinBounds(clusterData.map((e) => [e.item.lng,e.item.lat]))
+		                        let bounds = new AMap.Bounds(southWest, northEast)
+		                       
+		                        this.map.setBounds(bounds,true,[80, 80, 40, 40])
+		                    }
+							
+		                    // console.log(e, 'eeeee')
+							if (clusterData?.length > 1) return;
+							this.$emit('handleMarkerClick',e)
+		                    // this.createInfoWindow.closeWindow()
+		                    // this.createInfoWindow.add({ ...e, offset: [0, -14], componentsMapKey: 'markerCluster' })//创建窗体
+		                });
+		            } else {
+		                let { points, prePoints } = data
+		                this.cluster.setData(points)
+		
+		                /*移动窗体 */
+		                /* let activeItem = this.createInfoWindow.getItem()
+		       
+		                if (activeItem) {
+		
+		                    let filterItem = points.filter((item) => {
+		                        return item.item.id == activeItem[0].item.id
+		                    })
+		                    if (!isEmpty(filterItem)) {
+		                        this.createInfoWindow.setPosition([filterItem[0].item.lng, filterItem[0].item.lat])
+		                    } else {
+		                        this.createInfoWindow.closeWindow()
+		                    }
+		
+		                } */
+						
+						
+						let newPoint = points.map(e => e.item)
+						let oldPoint = prePoints.map(e => e.item)
+						let {adds,deletes} = listCrudChange(newPoint,oldPoint)
+						
+						console.log(adds,'addsaddsadds')
+						if( adds.length>=2) {
+							let {southWest,northEast} = getMaxMinBounds(adds.map((e) => [e.lng,e.lat]))
+							let bounds = new AMap.Bounds(southWest, northEast)
+							this.$nextTick(() => {
+								this.map.setBounds(bounds,true,[80, 80,40, 40])
+							})
+							
+							console.log(bounds,'boundsbounds',this.map)
+						} else if (adds.length == 1) {
+							let item = adds[0]
+							this.map.setZoomAndCenter(18, [item.lng,item.lat], true);
+						}
+					
+		
+		            }
+		
+		        },
+		        moveAnimation: () => {//创建轨迹回放
+		            let index = 0;
+		            if (this.createInfoWindow.getinfoWindow()) {
+		                this.createInfoWindow.closeWindow();
+		            }
+		            this.createMoveAlongFn = createMoveAlong(this.map, data, (key, e) => {
+		                if (key === 'moving') {
+		                    index = e.index
+		                    this.$emit('moving', e)
+		                    /* if (this.createInfoWindow.getinfoWindow()) {
+		                        if (this.isMoveAlongWindow) {
+		                            this.createInfoWindow.setPosition([e.target.getPosition().lng, e.target.getPosition().lat])
+		                            if (this.$refs.componentViewRef) {
+		                                this.$refs.componentViewRef.callBack(e, this.createInfoWindow.closeWindow)
+		                            }
+		                        }
+		
+		                    } */
+							this.$emit('handleMarkerClick',e, key)
+		                }
+		                if (key === 'moveTo') {
+		                   /* if (this.createInfoWindow.getinfoWindow()) {
+		                        if (this.isMoveAlongWindow) {
+		                            this.createInfoWindow.setPosition(e.lnglat)
+		                            if (this.$refs.componentViewRef) {
+		                                this.$refs.componentViewRef.callBack(e, this.createInfoWindow.closeWindow)
+		                            }
+		                        }
+		                    } */
+							this.$emit('handleMarkerClick',e, key)
+		                }
+		                if (key === 'click') {
+                            if(e.resetIdx>index){
+                                e.index = e.resetIdx
+                            }else{
+                                e.index = index
+                            }
+							
+							this.$emit('handleMarkerClick',e, key)
+		                   /* this.isMoveAlongWindow = true
+		                    if (!this.isCreateInfoWindow) return;
+		                    this.createInfoWindow.add({ index, lnglat: e.target.getPosition(), offset: [0, -23], componentsMapKey: 'moveAnimation' })//创建窗体 */
+		                }
+		            })
+		        },
+		        createMarker: () => { //创建标注点
+		            this.allmarkerPro = createMarker(this.map, data, (key, e) => { //返回目前地图上所有Mark实例
+		                if (this.createInfoWindow.getinfoWindow()) {
+		                    this.createInfoWindow.closeWindow();
+		                }
+		                console.log('创建标注点', e)
+		                let lng = Number(e.lng02)
+		                let lat = Number(e.lat02)
+		                this.isMoveAlongWindow = false
+		
+		                if (e.hiddenInfoWindow) return
+		                this.createInfoWindow.add({ lnglat: { lng, lat }, offset: [0, 0], componentsMapKey: e.type, info: e })//创建窗体
+		            })
+		        },
+		        removeAllMarkers: () => {//删除所有创建的标注点
+		            if (this.allmarkerPro) {
+		                this.allmarkerPro.removeAllMarkers()
+		            }
+		        },
+		        createPolyline: () => {//创建展示路线
+		            createPolylineShow(this.map, data)
+		        }
+		    }
+		    toolMapFn[key]();
+		},
+		citySearch() {
+		    //实例化城市查询类
+			let that = this;
+		    var citysearch = new AMap.CitySearch();
+		
+		    //自动获取用户IP,返回当前城市
+		    citysearch.getLocalCity(function(status, result) {
+		        if (status === 'complete' && result.info === 'OK') {
+		            if (result && result.city && result.bounds) {
+		                var citybounds = result.bounds;
+		                //地图显示当前城市
+		                that.map.setBounds(citybounds,true);
+		            }
+		        } else {
+		            
+		        }
+		    });
+		},
+		setisMoveAlongWindow(e) {
+		    isMoveAlongWindow = e
+		},
+		
+		/**创建路线 */
+		createPolyline(data, option) {
+		
+		    let polyline
+		    polyline = new AMap.Polyline({
+		        map: this.map,
+		        path: data,
+		        showDir: true,
+		        strokeStyle: "solid",
+		        strokeColor: "#027AFF",  //线颜色
+		        strokeWeight: 6,      //线宽
+		        strokeOpacity: 1,
+		        ...option,
+		    });
+		
+		    return polyline
+		},
+		/**移除覆盖物 */
+		removeOverlays(Overlays) {
+		    this.map.remove(Overlays)
+		},
+		/**创建围栏 */
+		createPolygon(data, option) {
+		
+		    let Polygon
+		    Polygon = new AMap.Polygon({
+		        map: this.map,
+		        path: data,
+		        strokeStyle: "solid",
+		        fillColor: '#00b0ff',
+		        strokeColor: '#80d8ff',
+		        strokeWeight: 2,     //轮廓线宽度
+		        ...option,
+		    });
+		
+		    return Polygon
+		},
+		
+		/**创建圆 */
+		createCircle(center, option) {
+		
+		    let Circle
+		    Circle = new AMap.Circle({
+		        center: center,
+		        radius: 50, //半径
+		        borderWeight: 3,
+		        strokeColor: "#FF33FF",
+		        strokeOpacity: 1,
+		        strokeWeight: 6,
+		        strokeOpacity: 0.2,
+		        fillOpacity: 0.4,
+		        fillColor: '#1791fc',
+		        ...option,
+		    });
+		    this.map.add(Circle)
+		
+		    return Circle
+		},
+		/**批量创建标记点 */
+		batchCreateMarkers(data, option) {
+		
+		    let Markers = []
+		    data.forEach(item => {
+		        Markers.push(
+		            new AMap.Marker({
+		                position: item.position,
+		                content: item.content,
+		                offset: item.offset,
+		                title: item.title,
+		                ...option
+		            })
+		        )
+		    });
+		
+		    this.map.add(Markers)
+		
+		    return Markers
+		},
+	
+		// 坐标集合适配
+		setBoundsPolygon(bounds,immediately){
+		    let polygon = new AMap.Polygon({
+		        path: bounds,
+		    });
+		    let mybounds = polygon.getBounds();
+		    this.map.setBounds(mybounds,immediately);
+		},
+		/**视觉适配 */
+		setFitView(...args) {
+		    this.map.setFitView(...args);
+		},
+		
+		/**设置中心点 */
+		setCenter(...args) {
+		    this.map.setCenter(...args);
+		},
+		/**地图缩放至指定级别并以指定点为地图显示中心点 */
+		setZoomAndCenter(...args) {
+		    this.map.setZoomAndCenter(...args);
+		},
+		
+		/* 设置卫星图 */
+		setStelliteLayer(isShow = true) {
+			if (isShow) {
+				if (!this.satelliteLayer) {
+					this.satelliteLayer = new AMap.TileLayer.Satellite();
+					this.satelliteLayer.setMap(this.map);
+				} else {
+					this.satelliteLayer.show();
+				}
+			} else {
+				 if (this.satelliteLayer) this.satelliteLayer.hide();
+			}
+			
+		},
+		
+	}
+}
+
+
+</script>
+<style scoped lang="scss">
+// 地图
+::v-deep {
+	.amap-logo {
+		display: none !important;
+	}
+	.amap-copyright {
+		 display: none !important;
+	}
+}
+
+
+.container-box {
+    width: 100%;
+    height: 100%;
+    position: relative;
+
+    .showspeed-box {
+        border-radius: 6px;
+        overflow: hidden;
+        position: absolute;
+        right: 12px;
+        top: 11px;
+        height: 33px;
+        color: #fff;
+        display: flex;
+        align-items: center;
+        text-align: center;
+        height: 33px;
+        line-height: 33px;
+        font-size: 12px;
+
+        .flex-al {
+            display: flex;
+            align-items: center;
+            justify-content: center;
+        }
+
+        .checkbox {
+            width: 84px;
+            background: linear-gradient(180deg, #3296FF 0%, #027AFF 100%);
+            border-radius: 6px 0px 0px 6px;
+
+            :deep(.el-checkbox__label) {
+                color: #fff;
+            }
+
+            :deep(.el-checkbox) {
+                height: 33px;
+                line-height: 33px;
+                display: flex;
+                align-items: center;
+                justify-content: center;
+            }
+        }
+
+        .speed-60 {
+            width: 30px;
+            background: #16B92A;
+        }
+
+        .speed-80 {
+            width: 45px;
+            background: #0000FE;
+        }
+
+        .speed-100 {
+            width: 53px;
+            background: #F98202;
+
+        }
+
+        .speed-m100 {
+            width: 71px;
+            background: #FE0100;
+            border-radius: 0px 6px 6px 0px;
+        }
+    }
+
+    .mapToolBox {
+        position: absolute;
+        display: flex;
+        max-width: 500px;
+        overflow: auto;
+        top: 10px;
+        left: 30px;
+
+    }
+}
+
+.container {
+    padding: 0px;
+    margin: 0px;
+    width: 100%;
+    height: 100%;
+}
+</style>

+ 421 - 0
components/playBack/index.vue

@@ -0,0 +1,421 @@
+<template>
+    <view class="content">
+        <view class="link"></view>
+        <view class="itemTime">{{ itemTime }}</view>
+        <scroll-view :scroll-top="scrollTop" @touchstart="closeintervalID" scroll-y="true" class="scroll-Y"
+            @scrolltoupper="upper"
+            @scrolltolower="lower" @scroll="scroll">
+            <view class="interval"></view>
+            <view class="scroll-view-item">
+                <view class="andChannel" v-for="(item, index) in andChannel"
+                    :style="{ top: item.startTop + 'px', height: item.endTop - item.startTop + 'px' }"
+                    :key="'andChannel' + index">
+                </view>
+                <template v-for="(item, key) in timetwoMinuteScaleAll">
+                    <view class="twoMinute" v-if="item.twoMinute" :key="key">
+                        <view class="showMinuteScale" v-if="item.showMinuteScale">{{ toYYMM(item.itemTime) }}</view>
+                    </view>
+                </template>
+            </view>
+            <view class="interval-but"></view>
+        </scroll-view>
+    </view>
+</template>
+
+<script>
+export default {
+    props: {
+        timeSection: {
+            type: Array,
+            default: () => [
+                
+            //     {
+            //       "id": "845947455808901120",
+            //       "key": "vrkey__13306138126_1_1690795730000_1",
+            //       "deviceId": "13306138126",
+            //       "vehicleId": "748032610971848704",
+            //       "fileDate": "2023-07-31 00:00:00",
+            //       "channel": 1,
+            //       "startTime": "2023-07-31 17:28:50",
+            //       "endTime": "2023-07-31 19:24:31",
+            //       "alarmType": "0000000000000000",
+            //       "dataType": 0,
+            //       "dataRate": 1,
+            //       "storageType": 1,
+            //       "fileSize": 1657579203,
+            //       "uploadStatus": "noupload",
+            //       "uploadingTime": null,
+            //       "uploadedTime": null,
+            //       "orderNumber": null,
+            //       "sourceFile": null,
+            //       "targetFile": null
+            //     },
+            //     {
+            //       "id": "845949971443388416",
+            //       "key": "vrkey__13306138126_1_1690802671000_1",
+            //       "deviceId": "13306138126",
+            //       "vehicleId": "748032610971848704",
+            //       "fileDate": "2023-07-31 00:00:00",
+            //       "channel": 1,
+            //       "startTime": "2023-07-31 19:24:31",
+            //       "endTime": "2023-07-31 19:29:03",
+            //       "alarmType": "0000000000000000",
+            //       "dataType": 0,
+            //       "dataRate": 1,
+            //       "storageType": 1,
+            //       "fileSize": 64791937,
+            //       "uploadStatus": "noupload",
+            //       "uploadingTime": null,
+            //       "uploadedTime": null,
+            //       "orderNumber": null,
+            //       "sourceFile": null,
+            //       "targetFile": null
+            //     },
+            //     {
+            //       "id": "845980170583183360",
+            //       "key": "vrkey__13306138126_1_1690802942000_1",
+            //       "deviceId": "13306138126",
+            //       "vehicleId": "748032610971848704",
+            //       "fileDate": "2023-07-31 00:00:00",
+            //       "channel": 1,
+            //       "startTime": "2023-07-31 19:29:02",
+            //       "endTime": "2023-07-31 21:29:17",
+            //       "alarmType": "0000000000000000",
+            //       "dataType": 0,
+            //       "dataRate": 1,
+            //       "storageType": 1,
+            //       "fileSize": 1722336210,
+            //       "uploadStatus": "noupload",
+            //       "uploadingTime": null,
+            //       "uploadedTime": null,
+            //       "orderNumber": null,
+            //       "sourceFile": null,
+            //       "targetFile": null
+            //     },
+            //     {
+            //       "id": "846010369483902976",
+            //       "key": "vrkey__13306138126_1_1690810157000_1",
+            //       "deviceId": "13306138126",
+            //       "vehicleId": "748032610971848704",
+            //       "fileDate": "2023-07-31 00:00:00",
+            //       "channel": 1,
+            //       "startTime": "2023-07-31 21:29:17",
+            //       "endTime": "2023-07-31 23:29:29",
+            //       "alarmType": "0000000000000000",
+            //       "dataType": 0,
+            //       "dataRate": 1,
+            //       "storageType": 1,
+            //       "fileSize": 1722228045,
+            //       "uploadStatus": "noupload",
+            //       "uploadingTime": null,
+            //       "uploadedTime": null,
+            //       "orderNumber": null,
+            //       "sourceFile": null,
+            //       "targetFile": null
+            //     },
+            //     {
+            //       "id": "846017919084302336",
+            //       "key": "vrkey__13306138126_1_1690817368000_1",
+            //       "deviceId": "13306138126",
+            //       "vehicleId": "748032610971848704",
+            //       "fileDate": "2023-07-31 00:00:00",
+            //       "channel": 1,
+            //       "startTime": "2023-07-31 23:29:28",
+            //       "endTime": "2023-07-31 23:59:59",
+            //       "alarmType": "0000000000000000",
+            //       "dataType": 0,
+            //       "dataRate": 1,
+            //       "storageType": 1,
+            //       "fileSize": 437114784,
+            //       "uploadStatus": "noupload",
+            //       "uploadingTime": null,
+            //       "uploadedTime": null,
+            //       "orderNumber": null,
+            //       "sourceFile": null,
+            //       "targetFile": null
+            //     }
+            ]
+        }
+    },
+    data() {
+        return {
+            andChannel: [],
+            timeScaleAll: {},//obj
+            timetwoMinuteScaleAll: {},
+            scrollTop: 0,
+            old: {
+                scrollTop: 0
+            },
+            itemTime: '00:00',
+            timerId: '',
+            intervalID: '',
+            scrollList: []
+        }
+    },
+    watch: {
+        timeSection: {
+            deep: true,
+            immediate: false,
+            handler(newVal) {
+                console.log('更新', newVal)
+                this.getfindResByDateAndChannel()
+            }
+        }
+    },
+    created() {
+        this.initTimeScale()
+        this.getfindResByDateAndChannel()
+    },
+    methods: {
+        /**return obj */
+        initTimeScale() {
+            for (let hour = 0; hour < 24; hour++) {
+                for (let minute = 0; minute < 60; minute++) {
+                    for (let second = 0; second < 60; second++) {
+                        let timestamp = hour * 3600 + minute * 60 + second;
+                        let paddedHour = hour.toString().padStart(2, '0');
+                        let paddedMinute = minute.toString().padStart(2, '0');
+                        let paddedSecond = second.toString().padStart(2, '0');
+                        let itemTime = `${paddedHour}:${paddedMinute}:${paddedSecond}`;
+                        let oneMinute = timestamp % 60 === 0;
+                        let twoMinute = timestamp % 120 === 0;
+                        let showMinuteScale = minute % 10 === 0 && second === 0;
+                        this.timeScaleAll[timestamp] = { timestamp, itemTime, oneMinute, twoMinute, showMinuteScale, isvideo: false }
+                        if (twoMinute) {
+                            this.timetwoMinuteScaleAll[timestamp] = { itemTime, oneMinute, twoMinute, showMinuteScale, isvideo: false }
+                        }
+                    }
+                }
+            }
+            console.log(this.timeScaleAll)
+        },
+        toYYMM(e) {
+            let timeArr = e.split(':')
+            return `${timeArr[0]}:${timeArr[1]}`
+        },
+        upper: function (e) {
+            console.log(e)
+        },
+        lower: function (e) {
+            console.log(e)
+        },
+        scroll: function (e) {
+            console.log('scroll', e)
+            this.scrollList.push(e)
+            this.old.scrollTop = e.detail.scrollTop
+            this.getScrollTiem(e.detail.scrollTop)
+        },
+        closeintervalID: function () {
+            clearInterval(this.intervalID)
+            console.log('关闭定时器')
+        },
+        //设置竖向滚动条位置
+        goTop: function (e) {
+            this.$nextTick(function () {
+                this.scrollTop = e
+                this.getScrollTiem(e)
+            });
+        },
+        /**
+         * 计算滚动到多少分钟
+         * @param {Number} scrollTop 4 === 1分钟
+         * 
+         */
+        getScrollTiem(scrollTop) {
+            let minute = Math.round(scrollTop / 4 * 60)
+            if (this.timeScaleAll[minute]) {
+                this.itemTime = this.timeScaleAll[minute].itemTime
+            } else {
+                this.itemTime = '23:59:59'
+            }
+
+            //手动拖动
+            if (this.timerId) clearTimeout(this.timerId);
+            this.timerId = setTimeout(() => {
+                if (this.scrollList.length > 2) {
+                    clearInterval(this.intervalID)
+                    this.isPlayable(minute)
+                }
+                this.scrollList = []
+            }, 1000);
+        },
+        //重置
+        resetComponents() {
+            this.andChannel = []
+            this.scrollTop = 0
+            this.old = {
+                scrollTop: 0
+            }
+            this.itemTime = '00:00'
+            this.scrollList = []
+            if (this.timerId) clearTimeout(this.timerId);
+            clearInterval(this.intervalID)
+            console.log('进度条重置完成',this.itemTime)
+        },
+        // 显示可回放区域
+        getfindResByDateAndChannel() {
+            this.resetComponents();
+            let data = this.timeSection
+            for (let key in data) {
+                console.log(data[key].startTime, '-', data[key].endTime)
+                let startTimeNum = this.getSeconds(data[key].startTime)
+                let endTimeNum = this.getSeconds(data[key].endTime)
+                let startTop = Math.round(startTimeNum * 4 / 60)
+                let endTop = Math.round(endTimeNum * 4 / 60)
+                this.andChannel.push({ startTop, endTop, startTimeNum, endTimeNum, ...data[key] })
+            }
+            console.log('可视时间段', this.andChannel[0]);
+            // this.$emit('scrollEnd', this.andChannel[0].startTime)
+            this.isPlayable(this.andChannel[0].startTimeNum)
+        },
+        //计时器滚动
+        setScrollTop(seconds) {
+            console.log('重置计时器')
+            this.intervalID = setInterval(() => {
+                let scrollTop = seconds * 4 / 60
+                this.goTop(scrollTop)
+                seconds++
+                // console.log('计时')
+            }, 1000);
+
+        },
+        // 获取秒
+        getSeconds(time) {
+            let dayjsInstance = this.dayjs
+            let startTime = dayjsInstance(time).format("HH:mm:ss");
+            const datetimeString = dayjsInstance().format("YYYY-MM-DD") + " " + startTime;
+            const momentObj = dayjsInstance(datetimeString, "YYYY-MM-DD HH:mm:ss");
+            const secondsOfDay = momentObj.diff(dayjsInstance().startOf('day'), 'second'); // 注意这里使用 'second' 而非 'seconds'
+            return secondsOfDay;
+            //   let startTime = moment(time).format("HH:mm:ss");
+            //   const datetimeString = moment().format("YYYY-MM-DD") + " " + startTime;
+            //   const momentObj = moment(datetimeString, "YYYY-MM-DD HH:mm:ss");
+            //   const secondsOfDay = momentObj.diff(moment().startOf('day'), 'seconds');
+            //   return secondsOfDay
+        },
+        /**
+          * 判断是否在有效视频段内
+          * @param {Number} seconds 秒
+          */
+        isPlayable(seconds, isemit) {
+            console.log('拖动',seconds)
+            // 设置初始进度时间 
+            const andChannel = this.andChannel
+            let allSection = []
+            for (const element of andChannel) {
+                if (element.startTimeNum <= seconds && seconds <= element.endTimeNum) {
+                    
+                    let startTimeHHmmss = this.dayjs(element.startTime).format("HH:mm:ss");
+                    allSection.push({ ...element, itemTime: this.itemTime==='00:00' ? startTimeHHmmss : this.itemTime })
+                }
+            }
+            if (allSection.length <= 0) return;
+            // 重叠的情况下取最后一段
+            if (!isemit) {
+                this.$emit('scrollEnd', allSection[allSection.length - 1])
+            }
+            this.setScrollTop(seconds)
+        }
+    }
+}
+</script>
+
+<style scoped lang="scss">
+.content {
+
+    position: relative;
+
+    .link {
+        position: absolute;
+        width: 100%;
+        height: 4rpx;
+        background: #FF7F00;
+        top: 60rpx;
+        z-index: 4;
+    }
+
+    .itemTime {
+        position: absolute;
+        top: 40rpx;
+        left: 180rpx;
+        z-index: 8;
+        font-size: 20rpx;
+    }
+
+
+    .scroll-Y {
+        height: 528rpx;
+        // border: 2px solid #000000;
+    }
+
+    .scroll-view_H {
+        white-space: nowrap;
+        width: 100%;
+    }
+
+    .interval {
+        width: 20rpx;
+        height: 56rpx;
+        background: #CFE7FF;
+        margin-left: 144rpx;
+
+    }
+
+    .interval-but {
+        width: 20rpx;
+        height: 486rpx;
+        background: #CFE7FF;
+        margin-left: 144rpx;
+        position: relative;
+        top: -10px;
+    }
+
+    .placeholder {
+        height: 400rpx;
+        width: 20rpx;
+    }
+
+    .scroll-view-item {
+        width: 20rpx;
+        margin-left: 144rpx;
+        background: #CFE7FF;
+        position: relative;
+
+        .andChannel {
+            position: absolute;
+            width: 100%;
+            // height: 52px;
+            // top: 10px;
+            background: #3C9CFF;
+        }
+
+        .twoMinute {
+            width: 20rpx;
+            height: 2px;
+            box-sizing: border-box;
+            position: relative;
+            left: -20rpx;
+            background: #CFE7FF;
+            margin-bottom: 6px;
+            border: 0;
+
+            .showMinuteScale {
+                position: absolute;
+                left: -90rpx;
+                width: 66rpx;
+                font-size: 24rpx;
+                zoom: 0.8;
+            }
+        }
+    }
+
+    .scroll-view-item_H {
+        display: inline-block;
+        width: 100%;
+        height: 300rpx;
+        line-height: 300rpx;
+        text-align: center;
+        font-size: 36rpx;
+    }
+}
+</style>

+ 216 - 0
components/sidebar/index.vue

@@ -0,0 +1,216 @@
+<!--
+ * @Author: yangpeiqin
+ * @Date: 2023-12-11 10:33:23
+ * @LastEditors: zhangmudan
+ * @LastEditTime: 2024-07-24 14:26:54
+ * @FilePath: \tqApp\components\sidebar\index.vue
+-->
+<template>
+	<view class="sidebar-container" v-if="$hasPermiOr(allPermission)">
+    <view class="container" v-if="direction=='vertical'">
+      <template v-for="(item,index) in sidebarList">
+          <view :index="index" v-if="$hasPermi(item.permission) && (item.hasOwnProperty('isShow')?item.isShow: true)" class="sidebar-list">
+            <view @click="handleFun(item)" class="sidebar-item">
+              <image :src="item.icon" class="background-image"></image>
+              <view class="info">
+                <view class="label-title">{{ item.label }}</view>
+                <view class="icon-warp">点击进入<image :src="$getImages('/assetsMobile/images/index/icon-in.png')" class="icon-image"></image></view>
+              </view>
+            </view>
+          </view>
+        </template>  
+    </view>
+    <view class="container" v-else-if="direction=='user' || (!hasPlat(['sanitation']) && !hasPlat(['enterprises']))">
+        <view class="sidebar-title" v-if="hasTitle">{{ label }}</view>
+        <u-row customStyle="width: 100%;flex-wrap: wrap;">
+          <template v-for="(item,index) in sidebarList">
+            <u-col :span="span"  :index="index" v-if="$hasPermi(item.permission) && (item.hasOwnProperty('isShow')?item.isShow: true)">
+              <view @click="handleFun(item)">
+                <SidebarItem :info="item" :direction="direction"></SidebarItem>
+              </view>
+              <!-- <navigator v-else :url="item.url">
+                  <SidebarItem :info="item"></SidebarItem>
+              </navigator> -->
+            </u-col>
+          </template>  
+        </u-row >
+    </view>
+    <view class="container" v-else>
+      <view class="sidebar-title" v-if="hasTitle">
+        <image :src="titleIcon" class="title-image" v-if="titleIcon"></image>
+          {{ label }}
+        </view>
+      <u-row class="sidebar-row">
+        <template v-for="(item,index) in sidebarList">
+            <u-col :span="span" :index="index" v-if="$hasPermi(item.permission) && (item.hasOwnProperty('isShow')?item.isShow: true)" class="sidebar-col">
+              <image :src="$getImages(`/assetsMobile/images/index/icon-hbg${item.iconbg}.png`)" class="hbg-image"></image>
+              <view @click="handleFun(item)">
+                <SidebarItem :info="item"></SidebarItem>
+              </view>
+              <!-- <navigator v-else :url="item.url">
+                  <SidebarItem :info="item"></SidebarItem>
+              </navigator> -->
+            </u-col>
+          
+          
+        </template>  
+      </u-row>
+    </view>
+	</view>
+</template>
+
+<script>
+import SidebarItem from './module/sidebarItem.vue'
+export default {
+	components: {
+		SidebarItem,
+	},
+    props: {
+        label: {
+            type: String,
+            default: ''
+        },
+        sidebarList: {
+            type: Array,
+            default: () => {
+                return []
+            }
+        },
+		span: {
+		    type: Number,
+		    default: 3
+		},
+		hasTitle: {
+			type: Boolean,
+			default: true
+		},
+    direction:{
+      type: String,
+			default: 'horizontal'
+    },
+    titleIcon:{
+      type: String,
+			default: ''
+    }
+    },
+	computed: {
+		allPermission() {
+			return this.sidebarList.map((e) => e.permission)
+		}
+	},
+	data() {
+		return {
+			
+		}
+	},
+	onLoad() {
+	},
+	methods: {
+		handleFun(item) {
+			
+			if(item.event) {
+				this.$emit(`${item.event}`)
+			} else if (item.url) {
+				uni.navigateTo({
+					url: item.url,
+				})
+			}else {
+				uni.showToast({
+					title: '功能正在开发',
+					icon: 'none'
+				})
+      }
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.sidebar-container {
+    position: relative;
+    width: 100%;
+    height: auto;
+    .container {
+        position: relative;
+        width: 100%;
+        height: auto;
+        display: flex;
+        flex-flow: column nowrap;
+        align-items: flex-start;
+        .sidebar-title {
+          font-family: PingFangSC, PingFang SC;
+          width: 100%;
+          font-weight: bold;
+          color: #333333;
+          font-size: 16px;
+          margin-bottom: 24rpx;
+          display: flex;
+          align-items: center;
+        }
+        .title-image{
+          width: 40rpx;
+          height: 40rpx;
+          margin-right: 6rpx;
+        }
+        .sidebar-list{
+          width: 100%;
+          height: 144rpx;
+          margin-bottom: 16rpx;
+          .sidebar-item{
+          height: 100%;
+          position: relative;
+          .background-image {
+            position: absolute;
+            bottom: 0;
+            right: 0;
+            width: 100%;
+            height: 100%;
+          }
+          .icon-image{
+            width: 28rpx;
+            height: 28rpx;
+            margin-left: 12rpx;
+          }
+          .info{
+            position: absolute;
+            left: 40rpx;
+            top: 28rpx;
+            color: #fff;
+            font-size: 24rpx;
+            .label-title{
+              font-size: 32rpx;
+            }
+            .icon-warp{
+              display: flex;
+              align-items: center;
+              margin-top: 12rpx;
+            }
+          }
+        }
+        }
+        .hbg-image{
+          position: absolute;
+          width: 146rpx;
+          height: 148rpx;
+        }
+        .sidebar-row{
+          width: 100%;
+          flex-wrap: wrap;
+          // margin-bottom: 20rpx;
+        }
+        .sidebar-col{
+          height: 148rpx;
+          margin-bottom: 20rpx;
+          display: flex;
+          align-items: center !important;
+        }
+        
+        
+    }
+}
+
+::v-deep {
+
+	
+}
+</style>

+ 86 - 0
components/sidebar/module/sidebarItem.vue

@@ -0,0 +1,86 @@
+<!--
+ * @Author: yangpeiqin
+ * @Date: 2023-12-11 10:35:21
+ * @LastEditors: zhangmudan
+ * @LastEditTime: 2024-07-24 14:40:35
+ * @FilePath: \tqApp\components\sidebar\module\sidebarItem.vue
+-->
+
+<template>
+	<view class="sidebar-item-container">
+		<view class="container">
+      <u-badge 
+				numberType="overflow" max="99" 
+				:value="info.hint" shape="circle" 
+				:absolute="true" :offset="(!hasPlat(['sanitation']) && !hasPlat(['enterprises']))?[-5,10]:[-24,-7]"
+				style="z-index: 1000;justify-content: center;overflow: hidden;"
+			>
+			</u-badge>
+			<view class="item-icon">
+				<image :src="info.icon" class="icon-img" :class="direction?'img80':''" />
+			</view>
+			<view class="item-label">{{ info.label }}</view>
+        </view>
+	</view>
+</template>
+
+<script>
+export default {
+    props: {
+        info: {
+            type: Object,
+            default: () => {
+                return {}
+            }
+        },
+        direction:{
+          type: String,
+          default: ''
+        },
+    },
+	data() {
+		return {
+			
+		}
+	},
+	onLoad() {
+	},
+	methods: {
+		
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.sidebar-item-container {
+    position: relative;
+    width: 100%;
+    height: auto;
+	margin-top: 30rpx;
+    .container {
+        position: relative;
+        width: 144rpx;
+        width: 100%;
+        height: auto;
+        display: flex;
+        flex-flow: column nowrap;
+        align-items: center;
+        .item-icon {
+            .icon-img {
+                width: 48rpx;
+                height: 48rpx;
+            }
+            .img80{
+              width: 80rpx;
+                height: 80rpx;
+            }
+        }
+		.item-label {
+			// margin-top: 5px;
+			color: #888;
+			font-size: 22rpx;
+			line-height: 32rpx;
+		}
+    }
+}
+</style>

+ 175 - 0
components/sidebardyn/index.vue

@@ -0,0 +1,175 @@
+<template>
+	<view class="sidebar-container">
+   
+		<view class="container">
+		  <view class="sidebar-title">
+			<image :src="$getImages(`${titleIcon}`)" class="title-image" v-if="titleIcon"></image>
+			  {{ label }}
+			</view>
+		  <u-row class="sidebar-row">
+			<template v-for="(item,index) in children">
+				<u-col :span="span" :index="index"  class="sidebar-col">
+				  <image :src="$getImages(item.backImg)" class="hbg-image"></image>
+				  <view @click="handleFun(item)">
+					<SidebarItem :info="item" :hint="himObj[item.perms]"></SidebarItem>
+				  </view>
+				</u-col>		  
+			</template>  
+		  </u-row>
+		</view>
+	</view>
+</template>
+
+<script>
+import SidebarItem from './module/sidebarItem.vue'
+export default {
+	components: {
+		SidebarItem,
+	},
+    props: {
+        label: {
+            type: String,
+            default: ''
+        },
+        children: {
+            type: Array,
+            default: () => {
+                return []
+            }
+        },
+		span: {
+		    type: Number,
+		    default: 3
+		},
+		titleIcon:{
+			type: String,
+			default: ''
+		},
+		himObj: {
+		    type: Object,
+		    default: () => {
+		        return {}
+		    }
+		},
+    },
+	computed: {
+		allPermission() {
+			return this.children.map((e) => e.perms)
+		}
+	},
+	data() {
+		return {
+			
+		}
+	},
+	onLoad() {
+	},
+	methods: {
+		handleFun(item) {
+			
+			if(item.event) {
+				this.$emit(`${item.event}`)
+			} else if (item.path) {
+				uni.navigateTo({
+					url: item.path,
+				})
+			}else {
+				uni.showToast({
+					title: '功能正在开发',
+					icon: 'none'
+				})
+      }
+		}
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.sidebar-container {
+    position: relative;
+    width: 100%;
+    height: auto;
+    .container {
+        position: relative;
+        width: 100%;
+        height: auto;
+        display: flex;
+        flex-flow: column nowrap;
+        align-items: flex-start;
+        .sidebar-title {
+          font-family: PingFangSC, PingFang SC;
+          width: 100%;
+          font-weight: bold;
+          color: #333333;
+          font-size: 16px;
+          margin-bottom: 24rpx;
+          display: flex;
+          align-items: center;
+        }
+        .title-image{
+          width: 40rpx;
+          height: 40rpx;
+          margin-right: 6rpx;
+        }
+        .sidebar-list{
+          width: 100%;
+          height: 144rpx;
+          margin-bottom: 16rpx;
+          .sidebar-item{
+          height: 100%;
+          position: relative;
+          .background-image {
+            position: absolute;
+            bottom: 0;
+            right: 0;
+            width: 100%;
+            height: 100%;
+          }
+          .icon-image{
+            width: 28rpx;
+            height: 28rpx;
+            margin-left: 12rpx;
+          }
+          .info{
+            position: absolute;
+            left: 40rpx;
+            top: 28rpx;
+            color: #fff;
+            font-size: 24rpx;
+            .label-title{
+              font-size: 32rpx;
+            }
+            .icon-warp{
+              display: flex;
+              align-items: center;
+              margin-top: 12rpx;
+            }
+          }
+        }
+        }
+        .hbg-image{
+          position: absolute;
+          width: 146rpx;
+          height: 148rpx;
+        }
+        .sidebar-row{
+          width: 100%;
+          flex-wrap: wrap;
+          // margin-bottom: 20rpx;
+        }
+        .sidebar-col{
+          height: 148rpx;
+          margin-bottom: 20rpx;
+          display: flex;
+          align-items: center !important;
+        }
+        
+        
+    }
+}
+
+::v-deep {
+
+	
+}
+</style>

+ 72 - 0
components/sidebardyn/module/sidebarItem.vue

@@ -0,0 +1,72 @@
+<template>
+	<view class="sidebar-item-container">
+		<view class="container">
+			<u-badge 
+				numberType="overflow" max="99" 
+				:value="hint" shape="circle" 
+				:absolute="true" :offset="(!hasPlat(['sanitation']) && !hasPlat(['enterprises']))?[-5,10]:[-24,-7]"
+				style="z-index: 1000;justify-content: center;overflow: hidden;"
+			>
+			</u-badge>
+			<view class="item-icon">
+				<image :src="$getImages(`${info.mobileIcon}`)" class="icon-img" />
+			</view>
+			<view class="item-label">{{ info.meta.title }}</view>
+        </view>
+	</view>
+</template>
+
+<script>
+export default {
+    props: {
+        info: {
+            type: Object,
+            default: () => {
+                return {}
+            }
+        },
+		hint: {
+            type: Number,
+            default: 0
+        },
+    },
+	data() {
+		return {
+			
+		}
+	},
+	onLoad() {
+	},
+	methods: {
+		
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.sidebar-item-container {
+    position: relative;
+    width: 100%;
+    height: auto;
+	margin-top: 30rpx;
+    .container {
+        position: relative;
+        width: 144rpx;
+        height: auto;
+        display: flex;
+        flex-flow: column nowrap;
+        align-items: center;
+        .item-icon {
+            .icon-img {
+                width: 48rpx;
+                height: 48rpx;
+            }
+        }
+		.item-label {
+			color: #888;
+			font-size: 22rpx;
+			line-height: 32rpx;
+		}
+    }
+}
+</style>

+ 86 - 0
components/tq-area-coverage/index.vue

@@ -0,0 +1,86 @@
+<template>
+    <xm-cascader 
+		ref="cascaderRef"
+		v-model="value" 
+		:options="list" 
+		:checkStrictly="checkStrictly" 
+		@input="handleInput" 
+		:showAllLevels="true"
+		@confirm="handleConfirm"
+		:props="{
+			value: 'code',
+			label: 'areaName',
+			children: 'child'
+		}"
+	>
+	</xm-cascader>
+</template>
+
+<script>
+import { getCodeAreaTree,getConfigKey} from "@/api/common/system.js";
+
+export default {
+    props: {
+
+        checkStrictly: { //传入此参数columns无效
+            type: Boolean,
+            default: true,
+        },
+    },
+
+    data() {
+        return {
+			value: '',
+            list: [
+				/* {
+                    "code": 1,
+                    "areaName": "Parent 1",
+                    "parentId": 0,
+                    "child": [{
+                            "code": 2,
+                            "areaName": "Child 1.1",
+                            "parentId": 1,
+                            "child": []
+                        },
+                        {
+                            "code": 3,
+                            "areaName": "Child 1.2",
+                            "parentId": 1,
+                            "child": []
+                        }
+                    ]
+                }, */
+			],
+        }
+    },
+    created() {
+       this.initOptions()
+    },
+    methods: {
+		async initOptions() {
+			let { msg } = await getConfigKey('areaCode');
+			let { data } = await getCodeAreaTree({pCode: msg});
+			this.list = data
+		},
+		handleInput(e) {
+			// console.log(e,'eeee')
+		},
+		
+        show() {
+			this.$refs.cascaderRef.openDept()
+        },
+
+        close() {
+			this.$refs.cascaderRef.close()
+        },
+
+        handleConfirm(e) {
+
+            this.$emit('confirm', e)
+        },
+
+    }
+}
+</script>
+
+<style lang="scss" scoped></style>

+ 81 - 0
components/tq-calendar/index.vue

@@ -0,0 +1,81 @@
+<template>
+   <uni-datetime-picker
+		ref="calendarRef"
+		v-model="innerValue"
+		:type="type"
+		@change="confirm"
+		:hide-second="hideSecond"
+   >
+	<text></text>
+   </uni-datetime-picker>
+</template>
+
+<script>
+
+export default {
+	props: {
+		value: {
+			type: String | Number | Array,
+			default: function() {
+				return null
+			}
+		},
+		
+		//展示格式
+		type: {
+			type: String,
+			default: 'datetimerange'
+		},
+		hideSecond: {
+			type: Boolean,
+			default: true
+		},
+		
+	},
+	
+	watch: {
+		value: {
+			handler(newVal) {
+				this.innerValue = newVal
+			}
+		},
+		innerValue: {
+			handler(newVal) {
+				this.$emit('input', newVal)
+			}
+		},
+	},
+	computed: {
+	
+	},
+    data() {
+        return {
+		   innerValue: null,
+        }
+    },
+	onReady() {
+		
+	},
+	
+    methods: {
+		
+		show() {
+			this.$refs.calendarRef.show()
+		},
+		
+		reset() {
+			this.innerValue = null;
+		},
+		
+		confirm(e) {
+			this.$emit('confirm',e)
+		},
+		
+	 
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+	
+</style>

+ 401 - 0
components/tq-car-user/index.vue

@@ -0,0 +1,401 @@
+<template>
+   <u-popup :show="isShow" @close="close" :closeOnClickOverlay="true">	
+		<view class="container">
+			<view class="search">
+				<u-row>
+					<u-col :span="2" v-if="multiple">
+						<view class="all-choosed">
+							<image
+								@click="handleChooseAll"
+								:src="allChecked?'/static/icon/choosed.png':'/static/icon/nochoose.png'" 
+								class="choosed-image">
+							</image>
+							全选
+						</view>
+					</u-col>
+					<u-col :span="!multiple?12:10">
+						<u-search
+							v-model="search.text"
+							placeholder="关键字搜索" 
+							@search="handleSearch"
+							@custom="handleSearch"
+							:actionStyle="{
+								color: '#2979ff'
+							}"
+						>
+						</u-search>
+					</u-col>
+				</u-row>
+
+			</view>
+			<view class="last">
+				<u-button type="primary" :plain="true" :disabled="page==1" @click="handleLast"
+					:custom-style="{
+						borderColor: '#FFF',
+						background: '#F2f2f2',
+					}"
+				>
+					<!-- <u-icon name="arrow-up" color="#2979ff" size="28"></u-icon> -->
+					<image
+						:src="'/static/icon/up.png'" 
+						class="option-image">
+					</image>
+				</u-button>
+			</view>
+			<view class="list">
+				<template v-for="(item,index) in showList">
+					<view class="item" :key="index" 
+						@click="handleClick(item)"
+					>
+						<image 
+							v-if="multiple"
+							:src="item.checked?'/static/icon/choosed.png':'/static/icon/nochoose.png'" 
+							class="choosed-image">
+						</image>
+						<image
+							:src="item.icon" 
+							class="icon-image">
+						</image>
+						<view>
+							<view class="item-value">
+								{{item.label}}<text v-if="item.workStatusName">({{item.workStatusName}})</text>
+							</view>
+							<view class="item-label">
+								{{item.deptName}}
+							</view>
+						</view>
+						
+					</view>
+				</template>
+				
+				<view class="empty" v-if="showList.length==0">暂无数据</view>
+			</view>
+			<view class="next">
+				<u-button type="primary" :plain="true" :disabled="page==totalPage" @click="handleNext"
+					:custom-style="{
+						borderColor: '#FFF',
+						background: '#F2f2f2',
+					}"
+				>
+					<!-- <u-icon name="arrow-down" color="#2979ff" size="28"></u-icon> -->
+					<image
+						:src="'/static/icon/down.png'" 
+						class="option-image">
+					</image>
+				</u-button>
+			</view>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+import {isArray,clone } from 'lodash'
+import {realTimeList} from '@/api/realtimeWatch.js'
+import {getImages} from '@/plugins/images'
+//资源引入
+let car_list_online = getImages('/assetsMobile/images/map/icon/vehicle/car_list_online.png') //行驶在线
+let car_list_engin = getImages('/assetsMobile/images/map/icon/vehicle/car_list_engin.png') //停驶在线 
+let car_list_unline = getImages('/assetsMobile/images/map/icon/vehicle/car_list_unline.png') //离线
+
+let car_online = getImages('/assetsMobile/images/map/icon/vehicle/car_online.png') //行驶在线--地图
+let car_engin = getImages('/assetsMobile/images/map/icon/vehicle/car_engin.png') //停驶在线  -- 地图
+let car_unline = getImages('/assetsMobile/images/map/icon/vehicle/car_unline.png') //离线 --地图
+
+let people_list_online = getImages('/assetsMobile/images/map/icon/people/people_list_online.png') //在线
+let people_list_unline = getImages('/assetsMobile/images/map/icon/people/people_list_unline.png') //离线
+
+let people_online = getImages('/assetsMobile/images/map/icon/people/people_online.png') //在线--地图
+let people_unline = getImages('/assetsMobile/images/map/icon/people/people_unline.png') //离线--地图
+
+
+export default {
+	props: {
+		//单页展示数据量
+		size: {
+			type: Number,
+			default: 30
+		},
+		// 类型 1.人员 2.车辆
+		queryType: {
+			type: Number,
+			default: 2
+		},
+		// 多选
+		multiple: {
+			type: Boolean,
+			default: false
+		},
+		// 是否请求图标
+		isGetIcon: {
+			type: Boolean,
+			default: false
+		}
+	},
+    data() {
+        return {
+			allChecked: false,
+           isShow: false,
+		   loadding: false,
+		   
+		   page: 1,
+		   total: 0,
+		   totalPage: 0,
+		   search: {
+			   text: ''
+		   },
+		   
+		   orginList: [],
+		   resultList: [], //搜索结果集合
+		   showList: [], 
+
+        }
+    },
+	mounted() {
+		if (this.isGetIcon) {
+			Promise.all([this.$store.dispatch('getVehIcon'),this.$store.dispatch('getUserIcon')]).then((data) => {
+				this.getData()
+			})
+		} else {
+			this.getData()
+		}
+	},
+	
+    methods: {
+		
+		show() {
+			this.isShow= true;
+		},
+		handleChooseAll() {
+			this.allChecked = !this.allChecked
+			this.resultList.map(e => {
+				e.checked = this.allChecked
+			}) 
+		},
+		
+		
+		computeCheckedData() {
+			let allLength = this.resultList.length
+			let checedLength = this.resultList.filter((e) => e.checked).length
+			if(allLength == checedLength) {
+				this.allChecked = true
+			} else {
+				this.allChecked = false
+			}
+		},
+			
+		getChoosed() {
+			let data = this.orginList.filter((e) => e.checked)
+			this.$emit('confirm',data)
+		},	
+		
+		handleClick(e) {
+			if(this.multiple) {
+				e.checked=!e.checked
+				this.computeCheckedData()	
+			} else {
+				console.log(e,'e')
+				this.$emit('confirm',e);
+				this.isShow = false;
+			}			
+		},
+		
+		handleSearch() {
+			this.page = 1
+			if(this.search.text) {
+				this.resultList = this.orginList.filter((e) => e.label.includes(this.search.text) || e.deptName.includes(this.search.text))
+			} else {
+				this.resultList = clone(this.orginList)
+			}
+			this.computeCheckedData()
+			this.totalPage = Math.ceil(this.resultList.length / this.size)
+			this.handlePageData()	
+		},
+		//上一页
+		handleLast() {
+			if(this.page != 1) this.page -= 1;
+			this.handlePageData()
+		},
+		//下一页
+		handleNext() {
+			if(this.page != this.totalPage) this.page += 1;	
+			this.handlePageData()
+		},
+		//处理分页数据
+		handlePageData() {
+			let index = (this.page - 1) * this.size
+			this.showList = clone(this.resultList).splice(index,this.size)
+		},
+        getData() {
+			let that = this;
+			this.loadding = true;
+			
+		    realTimeList({
+				type: this.queryType,
+			}).then((res) => {
+			    if(isArray(res.data)) {
+					
+					res.data.forEach((item) => {			
+						let iconData = {}
+						
+						if (this.isGetIcon) {
+							if(item.type == 2) {
+								iconData = this.getVehIcon(item)
+							} else if (item.type == 1) {
+								iconData = this.getUserIcon(item)
+							}
+						}
+						
+						let obj = {
+							...item,
+							...iconData,
+							checked: false,
+						}			
+						this.orginList.push(obj);
+					})				
+					this.$emit('onDataLoad',this.orginList)
+					this.handleSearch()
+					
+				} else {
+					this.orginList = [];
+					this.showList = [];
+					this.page = 1
+					this.totalPage = 0
+				}
+			
+		   }).finally(() => {
+			   this.loadding = false
+		   })
+	   },
+	   
+		close() {
+			if(this.multiple) this.getChoosed()
+		    this.isShow = false
+		},
+		
+		getVehIcon(item) {
+		    //列表图标
+		    let listIcon = this.$store.state.icon.vehIcon[2]
+		     //地图图标
+		     let mapIcon = this.$store.state.icon.vehIcon[1]
+		
+		    let icon = ""
+		    let img = ""
+		    if (item.online == "1" && item.engineStatus == "1") {
+				
+		        icon = listIcon[1][item.vehType]?getImages(listIcon[1][item.vehType]):car_list_online
+		        img = mapIcon[1][item.vehType]? getImages(mapIcon[1][item.vehType]):car_online
+				
+		    } else if (item.online == "1" && item.engineStatus == "0") {
+		        icon = listIcon[2][item.vehType]?getImages(listIcon[2][item.vehType]):car_list_engin
+		        img = mapIcon[2][item.vehType]? getImages(mapIcon[2][item.vehType]):car_engin
+		    } else if (item.online == "0") {
+		        icon = listIcon[3][item.vehType]?getImages(listIcon[3][item.vehType]):car_list_unline
+		        img = mapIcon[3][item.vehType]?getImages(mapIcon[3][item.vehType]):car_unline
+		    } else {
+		        icon = listIcon[3][item.vehType]?getImages(listIcon[3][item.vehType]):car_list_unline
+		        img = mapIcon[3][item.vehType]?getImages(mapIcon[3][item.vehType]):car_unline
+		    }
+		    return {
+		        icon,
+		        img
+		    }
+		},
+		
+		getUserIcon(item) {
+		    let listIcon = this.$store.state.icon.userIcon[2]
+		    //地图图标
+		    let mapIcon = this.$store.state.icon.userIcon[1]
+		
+		    let icon = ""
+		    let img = ""
+		    if (item.online == "1") {
+		       icon = listIcon[1][item.workType]?getImages(listIcon[1][item.workType]):people_list_online
+		       img = mapIcon[1][item.workType]? getImages(mapIcon[1][item.workType]):people_online
+		    } else {
+		        icon = listIcon[3][item.workType]?getImages(listIcon[3][item.workType]):people_list_unline
+		        img = mapIcon[3][item.workType]? getImages(mapIcon[3][item.workType]):people_unline
+		    }   
+		
+		    return {
+		        icon,
+		        img
+		    }
+		},
+
+	 
+    }
+
+}
+</script>
+
+<style lang="scss" scoped>
+	
+	/* 内容satrt */
+	.container {
+		background: #fff;
+		.search {
+			padding: 20rpx 40rpx 10rpx 40rpx;
+			
+			.all-choosed {
+				display: flex;
+				justify-content: flex-start;
+				align-items: center;
+				font-size: 24rpx;
+			}
+		}
+		.list {
+			height: calc(85vh - 400rpx);
+			padding: 0 20rpx;
+			overflow-y: auto;
+			
+			.item {
+				padding: 10rpx 20rpx;
+				display: flex;
+				align-items: center;				
+				 
+				.item-value {
+					font-size: 22rpx;
+				}
+				.item-label {
+					font-size: 22rpx;
+				}
+				// border-bottom: 2rpx solid #ddd;
+			}
+			.empty {
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				height: calc(100vh - 400rpx);
+				color: #909193;
+				min-height: 800rpx;
+			}
+		}
+		
+		.last,.next {
+			margin: 10rpx 40rpx;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+		}
+	
+	}
+	
+	/* 内容end */
+
+	
+	.choosed-image {
+		width: 30rpx;
+		height: 30rpx;
+		margin-right: 10rpx;
+	}
+	.icon-image {
+		width: 30rpx;
+		height: 30rpx;
+		margin-right: 10rpx;
+	}
+	.option-image {
+		width: 40rpx;
+		height: 40rpx;
+		margin-right: 10rpx;
+	}
+</style>

+ 402 - 0
components/tq-car-user2/index.vue

@@ -0,0 +1,402 @@
+<template>
+   <u-popup :show="isShow" @close="close" :closeOnClickOverlay="true">	
+		<view class="container">
+			<view class="search">
+				<u-row>
+					<u-col :span="2" v-if="multiple">
+						<view class="all-choosed">
+							<image
+								@click="handleChooseAll"
+								:src="allChecked?'/static/icon/choosed.png':'/static/icon/nochoose.png'" 
+								class="choosed-image">
+							</image>
+							全选
+						</view>
+					</u-col>
+					<u-col :span="!multiple?12:10">
+						<u-search
+							v-model="search.text"
+							placeholder="关键字搜索" 
+							@search="handleSearch"
+							@custom="handleSearch"
+						>
+						</u-search>
+					</u-col>
+				</u-row>
+
+			</view>
+			<view class="last">
+				<u-button type="primary" :plain="true" :disabled="page==1" @click="handleLast">
+					<u-icon name="arrow-up" color="#2979ff" size="28"></u-icon>
+				</u-button>
+			</view>
+			<view class="list">
+				<template v-for="(item,index) in showList">
+					<view class="item" :key="index" 
+						@click="handleClick(item)"
+					>
+						<image 
+							v-if="multiple"
+							:src="item.checked?'/static/icon/choosed.png':'/static/icon/nochoose.png'" 
+							class="choosed-image">
+						</image>
+						<view>
+							<view class="item-value">
+								{{item.label}}<text v-if="item.workStatusName">({{item.workStatusName}})</text>
+							</view>
+							<view class="item-label">
+								{{item.deptName}}
+							</view>
+						</view>
+						
+					</view>
+				</template>
+				
+				<view class="empty" v-if="showList.length==0">暂无数据</view>
+			</view>
+			<view class="next">
+				<u-button type="primary" :plain="true" :disabled="page==totalPage" @click="handleNext">
+					<u-icon name="arrow-down" color="#2979ff" size="28"></u-icon>
+				</u-button>
+			</view>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+import * as dd from 'dingtalk-jsapi'; // 此方式为整体加载,也可按需进行加载
+	
+import {isArray,clone } from 'lodash'
+import {realTimeList} from '@/api/realtimeWatch.js'
+import {getImages} from '@/plugins/images'
+//资源引入
+let car_list_online = getImages('/assetsMobile/images/map/icon/vehicle/car_list_online.png') //行驶在线
+let car_list_engin = getImages('/assetsMobile/images/map/icon/vehicle/car_list_engin.png') //停驶在线 
+let car_list_unline = getImages('/assetsMobile/images/map/icon/vehicle/car_list_unline.png') //离线
+
+let car_online = getImages('/assetsMobile/images/map/icon/vehicle/car_online.png') //行驶在线--地图
+let car_engin = getImages('/assetsMobile/images/map/icon/vehicle/car_engin.png') //停驶在线  -- 地图
+let car_unline = getImages('/assetsMobile/images/map/icon/vehicle/car_unline.png') //离线 --地图
+
+let people_list_online = getImages('/assetsMobile/images/map/icon/people/people_list_online.png') //在线
+let people_list_unline = getImages('/assetsMobile/images/map/icon/people/people_list_unline.png') //离线
+
+let people_online = getImages('/assetsMobile/images/map/icon/people/people_online.png') //在线--地图
+let people_unline = getImages('/assetsMobile/images/map/icon/people/people_unline.png') //离线--地图
+
+
+export default {
+	props: {
+		//单页展示数据量
+		size: {
+			type: Number,
+			default: 30
+		},
+		// 类型 1.人员 2.车辆
+		queryType: {
+			type: Number,
+			default: 2
+		},
+		// 多选
+		multiple: {
+			type: Boolean,
+			default: false
+		},
+		// 是否请求图标
+		isGetIcon: {
+			type: Boolean,
+			default: false
+		}
+	},
+    data() {
+        return {
+			allChecked: false,
+           isShow: false,
+		   loadding: false,
+		   
+		   page: 1,
+		   total: 0,
+		   totalPage: 0,
+		   search: {
+			   text: ''
+		   },
+		   
+		   orginList: [],
+		   resultList: [], //搜索结果集合
+		   showList: [], 
+
+        }
+    },
+	mounted() {
+		if(this.hasHostPlat(["dingH5"]) && this.hasPlat(["enterprises"]) && dd.env.platform != "notInDingTalk") {
+			
+		} else {
+			if (this.isGetIcon) {
+				Promise.all([this.$store.dispatch('getVehIcon'),this.$store.dispatch('getUserIcon')]).then((data) => {
+					this.getData()
+				})
+			} else {
+				this.getData()
+			}
+		}
+		
+	},
+	
+    methods: {
+		
+		show() {
+			if(this.hasHostPlat(["dingH5"]) && this.hasPlat(["enterprises"]) && dd.env.platform != "notInDingTalk") {
+				dd.complexChoose({
+					corpId: uni.getStorageSync('corpId'),
+				  multiple: this.multiple,
+				  responseUserOnly: true,
+				  success: (res) => {
+				    const { users, departments, selectedCount } = res;
+					
+					if (this.multiple) {
+						
+					} else {
+						let obj = {
+							id: users[0].emplId,
+							label: users[0].name,
+							deptId: users[0].selectDeptId,
+							deptName: users[0].selectDeptName,
+						}
+						this.$emit('confirm',obj);
+					}
+					
+					
+					console.log(res,'resresresresr')
+				  },
+				  fail: () => {},
+				  complete: () => {},
+				});
+			} else {
+				this.isShow= true;
+			}
+			
+		},
+		handleChooseAll() {
+			this.allChecked = !this.allChecked
+			this.resultList.map(e => {
+				e.checked = this.allChecked
+			}) 
+		},
+		
+		
+		computeCheckedData() {
+			let allLength = this.resultList.length
+			let checedLength = this.resultList.filter((e) => e.checked).length
+			if(allLength == checedLength) {
+				this.allChecked = true
+			} else {
+				this.allChecked = false
+			}
+		},
+			
+		getChoosed() {
+			let data = this.orginList.filter((e) => e.checked)
+			this.$emit('confirm',data)
+		},	
+		
+		handleClick(e) {
+			if(this.multiple) {
+				e.checked=!e.checked
+				this.computeCheckedData()	
+			} else {
+				console.log(e,'e')
+				this.$emit('confirm',e);
+				this.isShow = false;
+			}			
+		},
+		
+		handleSearch() {
+			this.page = 1
+			if(this.search.text) {
+				this.resultList = this.orginList.filter((e) => e.label.includes(this.search.text) || e.deptName.includes(this.search.text))
+			} else {
+				this.resultList = clone(this.orginList)
+			}
+			this.computeCheckedData()
+			this.totalPage = Math.ceil(this.resultList.length / this.size)
+			this.handlePageData()	
+		},
+		//上一页
+		handleLast() {
+			if(this.page != 1) this.page -= 1;
+			this.handlePageData()
+		},
+		//下一页
+		handleNext() {
+			if(this.page != this.totalPage) this.page += 1;	
+			this.handlePageData()
+		},
+		//处理分页数据
+		handlePageData() {
+			let index = (this.page - 1) * this.size
+			this.showList = clone(this.resultList).splice(index,this.size)
+		},
+        getData() {
+			let that = this;
+			this.loadding = true;
+			
+		    realTimeList({
+				type: this.queryType,
+			}).then((res) => {
+			    if(isArray(res.data)) {
+					
+					res.data.forEach((item) => {			
+						let iconData = {}
+						
+						if (this.isGetIcon) {
+							if(item.type == 2) {
+								iconData = this.getVehIcon(item)
+							} else if (item.type == 1) {
+								iconData = this.getUserIcon(item)
+							}
+						}
+						
+						let obj = {
+							...item,
+							...iconData,
+							checked: false,
+						}			
+						this.orginList.push(obj);
+					})				
+					this.$emit('onDataLoad',this.orginList)
+					this.handleSearch()
+					
+				} else {
+					this.orginList = [];
+					this.showList = [];
+					this.page = 1
+					this.totalPage = 0
+				}
+			
+		   }).finally(() => {
+			   this.loadding = false
+		   })
+	   },
+	   
+		close() {
+			if(this.multiple) this.getChoosed()
+		    this.isShow = false
+		},
+		
+		getVehIcon(item) {
+		    //列表图标
+		    let listIcon = this.$store.state.icon.vehIcon[2]
+		     //地图图标
+		     let mapIcon = this.$store.state.icon.vehIcon[1]
+		
+		    let icon = ""
+		    let img = ""
+		    if (item.online == "1" && item.engineStatus == "1") {
+				
+		        icon = listIcon[1][item.vehType]?getImages(listIcon[1][item.vehType]):car_list_online
+		        img = mapIcon[1][item.vehType]? getImages(mapIcon[1][item.vehType]):car_online
+				
+		    } else if (item.online == "1" && item.engineStatus == "0") {
+		        icon = listIcon[2][item.vehType]?getImages(listIcon[2][item.vehType]):car_list_engin
+		        img = mapIcon[2][item.vehType]? getImages(mapIcon[2][item.vehType]):car_engin
+		    } else if (item.online == "0") {
+		        icon = listIcon[3][item.vehType]?getImages(listIcon[3][item.vehType]):car_list_unline
+		        img = mapIcon[3][item.vehType]?getImages(mapIcon[3][item.vehType]):car_unline
+		    } else {
+		        icon = listIcon[3][item.vehType]?getImages(listIcon[3][item.vehType]):car_list_unline
+		        img = mapIcon[3][item.vehType]?getImages(mapIcon[3][item.vehType]):car_unline
+		    }
+		    return {
+		        icon,
+		        img
+		    }
+		},
+		
+		getUserIcon(item) {
+		    let listIcon = this.$store.state.icon.userIcon[2]
+		    //地图图标
+		    let mapIcon = this.$store.state.icon.userIcon[1]
+		
+		    let icon = ""
+		    let img = ""
+		    if (item.online == "1") {
+		       icon = listIcon[1][item.workType]?getImages(listIcon[1][item.workType]):people_list_online
+		       img = mapIcon[1][item.workType]? getImages(mapIcon[1][item.workType]):people_online
+		    } else {
+		        icon = listIcon[3][item.workType]?getImages(listIcon[3][item.workType]):people_list_unline
+		        img = mapIcon[3][item.workType]? getImages(mapIcon[3][item.workType]):people_unline
+		    }   
+		
+		    return {
+		        icon,
+		        img
+		    }
+		},
+
+	 
+    }
+
+}
+</script>
+
+<style lang="scss" scoped>
+	
+	/* 内容satrt */
+	.container {
+		background: #fff;
+		.search {
+			padding: 20rpx 40rpx 20rpx 40rpx;
+			
+			.all-choosed {
+				display: flex;
+				justify-content: flex-start;
+				align-items: center;
+				font-size: 24rpx;
+			}
+		}
+		.list {
+			height: calc(100vh - 400rpx);
+			padding: 0 20rpx;
+			overflow-y: auto;
+			
+			.item {
+				padding: 10rpx 20rpx;
+				display: flex;
+				align-items: center;				
+				 
+				.item-value {
+					font-size: 22rpx;
+				}
+				.item-label {
+					font-size: 22rpx;
+				}
+				// border-bottom: 2rpx solid #ddd;
+			}
+			.empty {
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				height: calc(100vh - 400rpx);
+				color: #909193;
+				min-height: 800rpx;
+			}
+		}
+		
+		.last,.next {
+			margin: 10rpx 40rpx;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+		}
+	
+	}
+	
+	/* 内容end */
+
+	
+	.choosed-image {
+		width: 40rpx;
+		height: 40rpx;
+		margin-right: 10rpx;
+	}
+</style>

+ 133 - 0
components/tq-date-time/index.vue

@@ -0,0 +1,133 @@
+<template>
+   <u-datetime-picker
+		ref="datetimePickerRef"
+		:show="isShow"
+		v-model="innerValue"
+		:mode="mode"
+		@confirm="confirm"
+		@cancel="close"
+		:formatter="formatter"
+		:maxDate="maxDate"
+		:minDate="minDate"
+   ></u-datetime-picker>
+</template>
+
+<script>
+/* https://uviewui.com/components/datetimePicker.html */
+
+export default {
+	props: {
+		value: {
+			type: String | Number,
+			default: function() {
+				return null
+			}
+		},
+		
+		//展示格式
+		mode: {
+			type: String,
+			default: 'datetime'
+		},
+		format: {
+			type: String,
+			default: 'YYYY-MM-DD HH:mm'
+		},
+		
+		disStartDate: {
+			type: String,
+			default: ''
+		},
+		disEndDate: {
+			type: String,
+			default: ''
+		}
+	},
+	
+	watch: {
+		value: {
+			handler(newVal) {
+				this.innerValue = newVal
+			}
+		},
+		innerValue: {
+			handler(newVal) {
+				this.$emit('input', newVal)
+			}
+		},
+	},
+	computed: {
+		maxDate() {
+			let res
+			if(this.disEndDate) {
+				res = this.dayjs(this.disEndDate).valueOf() - 60000
+			} else {
+				res = this.dayjs().add(20, 'year').valueOf() 
+			}
+			return res
+		},
+		minDate() {
+			
+			let res
+			if(this.disStartDate) {
+				res = this.dayjs(this.disStartDate).valueOf() + 60000
+			} else {
+				res = this.dayjs().subtract(20, 'year').valueOf()
+			}
+			return res
+		},
+	},
+    data() {
+        return {
+           isShow: false,
+		   innerValue: Number(new Date()),
+        }
+    },
+	onReady() {
+		// 微信小程序需要用此写法
+		this.$refs.datetimePickerRef.setFormatter(this.formatter)
+	},
+	
+    methods: {
+		
+		show() {
+			this.isShow= true;
+		},
+		
+		close() {
+		    this.isShow = false
+		},
+		
+		confirm(e) {
+			
+			let time = this.dayjs(e.value).format(this.format)
+			this.$emit('confirm',time)
+			this.close()
+		},
+		
+		 formatter(type, value) {
+			if (type === 'year') {
+				return `${value}年` 
+			}
+			if (type === 'month') {
+				return `${value}月`
+			}
+			if (type === 'day') {
+				return `${value}日`
+			}
+			if (type === 'hour') {
+				return `${value}时`
+			}
+			if (type === 'minute') {
+				return `${value}分`
+			}
+			return value
+		},
+	 
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+	
+</style>

+ 286 - 0
components/tq-dept/index.vue

@@ -0,0 +1,286 @@
+<template>
+   <u-popup :show="isShow" @close="close" :closeOnClickOverlay="true">	
+		<view class="container">
+			<view class="search">
+				<u-row>
+					<u-col :span="2" v-if="multiple">
+						<view class="all-choosed">
+							<image
+								@click="handleChooseAll"
+								:src="allChecked?'/static/icon/choosed.png':'/static/icon/nochoose.png'" 
+								class="choosed-image">
+							</image>
+							全选
+						</view>
+					</u-col>
+					<u-col :span="!multiple?12:10">
+						<u-search
+							v-model="search.text"
+							placeholder="关键字搜索" 
+							@search="handleSearch"
+							@custom="handleSearch"
+						>
+						</u-search>
+					</u-col>
+				</u-row>
+
+			</view>
+			<view class="last">
+				<u-button type="primary" :plain="true" :disabled="page==1" @click="handleLast">
+					<u-icon name="arrow-up" color="#2979ff" size="28"></u-icon>
+				</u-button>
+			</view>
+			<view class="list">
+				<template v-for="(item,index) in showList">
+					<view class="item" :key="index" 
+						@click="handleClick(item)"
+					>
+						<image 
+							v-if="multiple"
+							:src="item.checked?'/static/icon/choosed.png':'/static/icon/nochoose.png'" 
+							class="choosed-image">
+						</image>
+						<image 
+							v-if="item.icon"
+							:src="item.icon" 
+							class="icon-image">
+						</image>
+						<view>
+							<view class="item-value">
+								{{item.deptName}}
+							</view>
+						</view>
+						
+					</view>
+				</template>
+				
+				<view class="empty" v-if="showList.length==0">暂无数据</view>
+			</view>
+			<view class="next">
+				<u-button type="primary" :plain="true" :disabled="page==totalPage" @click="handleNext">
+					<u-icon name="arrow-down" color="#2979ff" size="28"></u-icon>
+				</u-button>
+			</view>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+import {isArray,clone } from 'lodash'
+import {commonDeptAll} from '@/api/common/system.js'
+import {getImages} from '@/plugins/images'
+//资源引入
+let shujiegou = getImages('/assetsMobile/images/common/shujiegou.png') //行驶在线
+
+
+
+export default {
+	props: {
+		//单页展示数据量
+		size: {
+			type: Number,
+			default: 30
+		},
+		// 多选
+		multiple: {
+			type: Boolean,
+			default: false
+		},
+	},
+    data() {
+        return {
+			allChecked: false,
+           isShow: false,
+		   loadding: false,
+		   
+		   page: 1,
+		   total: 0,
+		   totalPage: 0,
+		   search: {
+			   text: ''
+		   },
+		   
+		   orginList: [],
+		   resultList: [], //搜索结果集合
+		   showList: [], 
+
+        }
+    },
+	mounted() {
+		this.getData()
+	},
+	
+    methods: {
+		
+		show() {
+			this.isShow= true;
+		},
+		handleChooseAll() {
+			this.allChecked = !this.allChecked
+			this.resultList.map(e => {
+				e.checked = this.allChecked
+			}) 
+		},
+		
+		
+		computeCheckedData() {
+			let allLength = this.resultList.length
+			let checedLength = this.resultList.filter((e) => e.checked).length
+			if(allLength == checedLength) {
+				this.allChecked = true
+			} else {
+				this.allChecked = false
+			}
+		},
+			
+		getChoosed() {
+			let data = this.orginList.filter((e) => e.checked)
+			this.$emit('confirm',data)
+		},	
+		
+		handleClick(e) {
+			if(this.multiple) {
+				e.checked=!e.checked
+				this.computeCheckedData()	
+			} else {
+				console.log(e,'e')
+				this.$emit('confirm',e);
+				this.isShow = false;
+			}			
+		},
+		
+		handleSearch() {
+			this.page = 1
+			if(this.search.text) {
+				this.resultList = this.orginList.filter((e) => e.deptName.includes(this.search.text) || e.deptName.includes(this.search.text))
+			} else {
+				this.resultList = clone(this.orginList)
+			}
+			this.computeCheckedData()
+			this.totalPage = Math.ceil(this.resultList.length / this.size)
+			this.handlePageData()	
+		},
+		//上一页
+		handleLast() {
+			if(this.page != 1) this.page -= 1;
+			this.handlePageData()
+		},
+		//下一页
+		handleNext() {
+			if(this.page != this.totalPage) this.page += 1;	
+			this.handlePageData()
+		},
+		//处理分页数据
+		handlePageData() {
+			let index = (this.page - 1) * this.size
+			this.showList = clone(this.resultList).splice(index,this.size)
+		},
+        getData() {
+			let that = this;
+			this.loadding = true;
+			
+		    commonDeptAll({
+			}).then((res) => {
+			    if(isArray(res.data)) {
+					
+					res.data.forEach((item) => {			
+					
+						let obj = {
+							...item,
+							icon: shujiegou,
+							checked: false,
+						}			
+						this.orginList.push(obj);
+					})				
+					this.$emit('onDataLoad',this.orginList)
+					this.handleSearch()
+					
+				} else {
+					this.orginList = [];
+					this.showList = [];
+					this.page = 1
+					this.totalPage = 0
+				}
+			
+		   }).finally(() => {
+			   this.loadding = false
+		   })
+	   },
+	   
+		close() {
+			if(this.multiple) this.getChoosed()
+		    this.isShow = false
+		},
+
+	 
+    }
+
+}
+</script>
+
+<style lang="scss" scoped>
+	
+	/* 内容satrt */
+	.container {
+		background: #fff;
+		.search {
+			padding: 20rpx 40rpx 20rpx 40rpx;
+			
+			.all-choosed {
+				display: flex;
+				justify-content: flex-start;
+				align-items: center;
+				font-size: 24rpx;
+			}
+		}
+		.list {
+			height: calc(100vh - 400rpx);
+			padding: 0 20rpx;
+			overflow-y: auto;
+			
+			.item {
+				padding: 16rpx 20rpx;
+				display: flex;
+				align-items: center;				
+				 
+				.item-value {
+					font-size: 28rpx;
+				}
+				.item-label {
+					font-size: 28rpx;
+				}
+				// border-bottom: 2rpx solid #ddd;
+			}
+			.empty {
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				height: calc(100vh - 400rpx);
+				color: #909193;
+				min-height: 800rpx;
+			}
+		}
+		
+		.last,.next {
+			margin: 10rpx 40rpx;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+		}
+	
+	}
+	
+	/* 内容end */
+
+	
+	.choosed-image {
+		width: 40rpx;
+		height: 40rpx;
+		margin-right: 10rpx;
+	}
+	.icon-image {
+		width: 30rpx;
+		height: 30rpx;
+		margin-right: 10rpx;
+	}
+</style>

+ 59 - 0
components/tq-dict-select/index.vue

@@ -0,0 +1,59 @@
+<!-- 字典 -->
+<template>
+    <u-picker
+        ref="pickerRef"
+        :keyName="'dictLabel'"
+        :show="isShow"
+        :columns="[columns]"
+        @confirm="confirm"
+        @cancel="close"></u-picker>
+</template>
+
+<script>
+/* https://uviewui.com/components/picker.html */
+import { getDicts } from "@/api/dict/data";
+export default {
+    props: {
+        dictType: { //字典类型
+            type: String,
+            default: ''
+        },
+    },
+
+    data() {
+        return {
+            columns: [],
+            isShow: false,
+        }
+    },
+    mounted() {
+        this.init()
+    },
+    methods: {
+        // 获取字典数据
+        init() {
+            getDicts(this.dictType).then((res) => {
+                this.columns = res.data
+            })
+        },
+        show() {
+            this.isShow = true;
+        },
+
+        close() {
+            this.isShow = false
+        },
+
+        confirm(e) {
+            console.log('确定', e)
+            const { value } = e
+            this.$emit('confirm', value[0])
+            this.close()
+        },
+
+
+    }
+}
+</script>
+
+<style lang="scss" scoped></style>

+ 935 - 0
components/tq-face/index copy.vue

@@ -0,0 +1,935 @@
+<template>
+	<u-popup :show="isShow" @close="close" :closeOnClickOverlay="false">
+		<view class="page-container">
+			<view class="errortext" v-if="!startVideoStatus">{{ tip }}</view>
+			<view class="errortext"><text v-if="errortext != ''">{{ errortext }}</text></view>
+			<view id="show-content" :class="openCamera == true ? '' : 'show-content-hide'">
+				<view class="home">
+					<view :class="bgImgClass">
+						<img src="../../static/neuralNets/img4.png" />
+					</view>
+				</view>
+				<!-- <view class="uer-camera" @tap="changeUserCamera('hou')" v-if="useCamera == 'qian'">
+					<img src="../../static/neuralNets/change_camera.png" /> 
+				</view>
+				<view class="uer-camera" @tap="changeUserCamera('qian')" v-if="useCamera == 'hou'">
+					<img src="../../static/neuralNets/change_camera.png" /> 
+				</view> -->
+				<video
+					src=""
+					id="video"
+					:style="{ width: vwidth + 'px', height: vheight + 'px', display: showVideo }"
+					playsinline
+					webkit-playsinline autoplay=true muted=true loop=true controls=false show-center-play-btn=false
+					object-fit=fill>
+				</video>
+			</view>
+
+
+			<view class="main_bottom">
+				确认为<text style="color: red;padding: 6rpx;"
+					v-if="userName">{{ userName }}</text>本人照片<br>保持正脸在取景框中根据屏幕指示完成
+				<view class="bottom_main">
+					<view class="bottom_main_list">
+						<img src="../../static/neuralNets/img3.png" />
+						<p>正对手机</p>
+					</view>
+					<view class="bottom_main_list">
+						<img src="../../static/neuralNets/img2.png" />
+						<p>光线充足</p>
+					</view>
+					<view class="bottom_main_list">
+						<img src="../../static/neuralNets/img1.png" />
+						<p>脸无遮挡</p>
+					</view>
+				</view>
+			</view>
+
+			<view class="button-view" :style="{ display: (openCamera == true ? 'flex' : 'none') }">
+
+				<view class="click-button">
+					<u-button type="primary" shape="circle" @click="startVideo"
+						:disabled="startVideoStatus">{{ !startVideoStatus ? '开始刷脸' : '检测中...' }}</u-button>
+					<u-button type="error"
+						shape="circle" @click="close"
+						:customStyle="{
+							marginTop: '30rpx'
+						}">退出</u-button>
+
+				</view>
+
+			</view>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+import $ from "@/static/collect_face/jquery2.3.js";
+import faceFilter from "@/static/neuralNets/jeelizFaceFilter.module.js";
+import JeelizCanvas2DHelper from "@/static/neuralNets/JeelizCanvas2DHelper.js";
+// 引入uni.webview.1.5.2.js
+import '@/static/js/uni.webview.1.5.4.js'
+import { compareFace } from '@/api/common/system.js'
+import { getToken } from '@/utils/auth'
+
+let VIDEOELEMENT = null;
+var CVD = null; // return of Canvas2DDisplay
+
+
+export default {
+	props: {
+
+		// 用户名
+		userName: {
+			type: String,
+			default: null
+		},
+		tip: {
+			type: String,
+			default: '需进行人脸验证后再继续学习'
+		},
+		querParams: {
+			type: Object,
+			default: () => {
+				return {}
+			}
+		},
+	},
+	data() {
+		return {
+			isShow: false,
+
+			isTest: false,
+			againCount: 0,
+			openCamera: false,
+			screenSize: {
+				width: window.screen.width,
+				height: window.screen.height,
+			},
+			vwidth: 240,
+			vheight: 240,
+			scanTip: '',
+			errortext: '点击开始刷脸按钮开始拍摄',
+			uploadHeadImg: '',
+			imgUrl: '',
+			pauseStatus: false,
+			startCut: false,
+			faceCoo: null,
+			detectState: null,
+			showVideo: 'block',
+			appendCss: false,
+			bgImgClass: 'home_wai',
+			bgImgClass2: 'home_nei',
+			mediaStreamTrack: null,
+			startVideoStatus: false,
+			useCamera: 'qian', //qian代表前置摄像头 hou代表后置摄像头
+
+		}
+	},
+	mounted() { },
+
+	methods: {
+
+		show() {
+			this.isShow = true;
+
+			if (faceFilter && typeof faceFilter.toggle_pause == 'function') {
+				faceFilter.destroy();
+			}
+			var that = this; //a
+			setTimeout(function () {
+				//初始化的时候给追加上canvas用作载体
+				if ($('#jeeFaceFilterCanvas').html() == undefined) {
+					$('#show-content').append('<canvas class="appendcanvas" width="' + that.vwidth +
+						'" height="' + that.vheight + '" id="jeeFaceFilterCanvas"></canvas>');
+				}
+				if ($('#jeeFaceFilterCanvas2').html() == undefined) {
+					$('#show-content').append('<canvas class="appendcanvas appendcanvas2"  width="' + that
+						.vwidth + '" height="' + that.vheight + '" id="jeeFaceFilterCanvas2"></canvas>');
+				}
+				if (that.appendCss == false) {
+					$('uni-page-body').append(`<style>
+					#jeeFaceFilterCanvas{
+						display:none;
+					}
+					#jeeFaceFilterCanvas2{
+						display:none;
+						border-radius:50%
+					}
+					.uni-video-bar{
+						display:none !important; //去除底部菜单栏
+					}
+					.uni-video-cover{
+						display:none !important; //去除中间播放按钮
+					}
+					video{
+						transform: rotateY(180deg);
+						-webkit-transform: rotateY(180deg);    /* Safari 和 Chrome */
+						-moz-transform: rotateY(180deg);
+						border-radius:50% !important;
+					}
+					.uni-video-container{border-radius:50% !important;}
+					.uni-video-video{border-radius:50% !important;}
+					</style>`);
+					that.appendCss = true;
+				}
+				//uniapp编译过后无法获取video的dom,此处追加id
+				$('#video').find('video').prop('id', 'myVideo');
+				that.showVideo = 'block';
+				if (that.isTest == true) {
+					//是否是测试环境 测试环境直接调用视频
+					that.openCamera = true;
+					that.main();
+				} else {
+					that.openCamera = true;
+					that.bgImgClass = 'home_wai remove_animation';
+					that.bgImgClass2 = 'home_nei remove_animation';
+					that.errortext = '点击开始刷脸按钮开始拍摄';
+				}
+			}, 100)
+
+		},
+
+
+		close() {
+			this.startVideoStatus = false;
+			this.closeVideo();
+			this.isShow = false
+			this.$emit('close')
+		},
+
+		closeVideo() {
+			try {
+				//页面卸载的时候关闭视频播放
+				if (this.mediaStreamTrack) {
+					this.mediaStreamTrack.getTracks()[0].stop();
+				}
+			} catch {
+				console.log('close mediaStreamTrack error')
+			}
+			try {
+				const video = document.getElementsByClassName('uni-video-video')[0];
+				video.srcObject = null;
+			} catch {
+				console.log('close video.srcObject error')
+			}
+			try {
+				faceFilter.toggle_pause(true);
+				faceFilter.destroy(); //销毁注册对象
+			} catch {
+				console.log('close faceFilter toggle_pause error')
+			}
+		},
+		changeUserCamera(type) {
+			this.useCamera = type;
+			if (type == 'hou') {
+				$('uni-page-body').append(`<style>
+					video{
+						transform: rotateY(0deg);
+						-webkit-transform: rotateY(0deg);    /* Safari 和 Chrome */
+						-moz-transform: rotateY(0deg);
+						border-radius:50% !important;
+					}
+					</style>`);
+			} else {
+				$('uni-page-body').append(`<style>
+					video{
+						transform: rotateY(180deg);
+						-webkit-transform: rotateY(180deg);    /* Safari 和 Chrome */
+						-moz-transform: rotateY(180deg);
+						border-radius:50% !important;
+					}
+					</style>`);
+			}
+			this.playVideo();
+		},
+		startVideo() {
+			if (this.startVideoStatus == false) {
+				this.playVideo();
+			} else {
+				if (this.pauseStatus == false) {
+					this.$api.msg('当前正在拍照');
+					return;
+				}
+				this.savePause(false)
+			}
+		},
+		savePause(status) {
+			this.uploadHeadImg = '';
+			this.imgUrl = '';
+			this.pauseStatus = status;
+			if (status == false) {
+				this.startCut = false;
+				this.bgImgClass = 'home_wai';
+				this.bgImgClass2 = 'home_nei';
+				this.showVideo = 'none';
+				$('#jeeFaceFilterCanvas2').hide();
+			} else {
+				this.showVideo = 'block';
+				$('#jeeFaceFilterCanvas2').hide();
+			}
+			//console.log(status)
+			//true 暂停  false 继续执行
+			faceFilter.toggle_pause(status);
+		},
+		playVideo() {
+
+			var that = this;
+			that.uploadHeadImg = '';
+			that.imgUrl = '';
+			that.startVideoStatus = true;
+			that.startCut = false;
+			var thatUseCamera = that.useCamera;
+			if (thatUseCamera == 'qian') {
+				var facingMode = 'user';
+			} else if (thatUseCamera == 'hou') {
+				var facingMode = {
+					exact: "environment"
+				};
+			}
+			if (faceFilter && typeof faceFilter.toggle_pause == 'function') {
+				faceFilter.destroy();
+			}
+			setTimeout(function () {
+				that.getUserMedia({
+					//摄像头拍摄的区域
+					video: {
+						width: 500,
+						height: 500,
+						facingMode: facingMode,
+					},
+					/* 前置优先 */
+				},
+					that.success,
+					that.error
+				);
+			}, 100)
+
+		},
+		// 访问用户媒体设备
+		getUserMedia(constrains, success, error) {
+
+			if (navigator.mediaDevices.getUserMedia) {
+				navigator.mediaDevices.getUserMedia(constrains).then(success).catch(error); //最新标准API
+			} else if (navigator.webkitGetUserMedia) {
+				navigator.webkitGetUserMedia(constrains).then(success).catch(error); //webkit内核浏览器
+			} else if (navigator.mozGetUserMedia) {
+				navagator.mozGetUserMedia(constrains).then(success).catch(error); //Firefox浏览器
+			} else if (navigator.getUserMedia) {
+				navigator.getUserMedia(constrains).then(success).catch(error); //旧版API
+			} else {
+				this.errortext = "你的浏览器不支持访问用户媒体设备";
+			}
+		},
+		success(stream) {
+			var that = this;
+			that.bgImgClass = 'home_wai';
+			that.bgImgClass2 = 'home_nei';
+			that.openCamera = true;
+			that.errortext = '请把人脸放在圆圈内拍摄脸部';
+			that.mediaStreamTrack = stream;
+			const video = document.getElementsByClassName('uni-video-video')[0];
+			try {
+				video.srcObject = stream;
+			} catch {
+				this.URL = window.URL || window.webkitURL;
+				video.src = this.URL.createObjectURL(stream);
+			}
+			// webkit内核浏览器
+			//苹果手机的系统弹框会阻止js的线程的继续执行 手动0.1秒之后自动执行代码
+			setTimeout(() => {
+				try {
+					video.play();
+				} catch {
+					that.errortext = '视频流播放失败';
+				}
+				that.main();
+			}, 100);
+
+		},
+		error(e) {
+			var that = this;
+			that.startVideoStatus = false;
+			that.bgImgClass = 'home_wai remove_animation';
+			that.bgImgClass2 = 'home_nei remove_animation';
+			if (this.useCamera == 'hou') {
+				that.errortext = "调用后置摄像头失败:" + (e.name ? e.name : '') + (e.message ? e.message : '');
+			} else {
+				that.errortext = "调用前置摄像头失败:" + (e.name ? e.name : '') + (e.message ? e.message : '');
+			}
+
+		},
+		main() { // entry point
+			var that = this;
+			VIDEOELEMENT = document.getElementById('myVideo');
+
+			if (VIDEOELEMENT['currentTime'] && VIDEOELEMENT['videoWidth'] && VIDEOELEMENT['videoHeight']) {
+
+				that.start();
+			} else {
+				setTimeout(function () {
+					that.main();
+				}, 100);
+
+			}
+		},
+		start() { // launched when the video is loaded
+
+			var that = this;
+			faceFilter.init({
+				canvasId: 'jeeFaceFilterCanvas',
+				videoSettings: {
+					videoElement: VIDEOELEMENT
+				},
+				rotate: -90,
+				NNCPath: '../../static/neuralNets/', // root of NN_DEFAULT.json file
+				callbackReady: function (errCode, spec) {
+					if (errCode) {
+						//
+						that.errortext = '人脸初始化出错' + errCode;
+						that.startVideoStatus = false;
+						console.log('AN ERROR HAPPENS. SORRY BRO :( . ERR =', errCode);
+						return;
+					}
+					CVD = JeelizCanvas2DHelper(spec);
+					CVD.ctx.strokeStyle = 'yellow';
+				},
+
+				callbackTrack: function (detectState) {
+					//console.log(detectState.detected)
+					that.detectState = '';
+					that.faceCoo = '';
+
+					if (detectState.detected > 0.9) {
+
+						that.detectState = detectState; 
+						that.showVideo = 'block';
+						$('#jeeFaceFilterCanvas2').hide();
+						var faceCoo = CVD.getCoordinates(detectState);
+						that.faceCoo = faceCoo;
+						CVD.ctx.clearRect(0, 0, CVD.canvas.width, CVD.canvas.height);
+						CVD.update_canvasTexture();
+						CVD.draw();
+						var check = that.checkTrue();
+						if (check) {
+							that.errortext = '请保持不动,即将拍照';
+							if (that.startCut == false) {
+
+								that.startCut = true;
+								setTimeout(function () {
+									//console.log('setTimeout')
+									that.savePause(true);
+									that.cutPhoto();
+								}, 1000)
+							}
+						}
+					} else {
+						that.showVideo = 'block';
+						$('#jeeFaceFilterCanvas2').hide();
+						that.errortext = '请将完整脸部对准屏幕中央';
+					}
+
+				}
+			}); //end JEELIZFACEFILTER.init call
+		},
+
+		checkTrue(dump) {
+
+			var that = this;
+			var faceCoo = that.faceCoo;
+			var faceCooStr = JSON.stringify(faceCoo);
+			var detectState = that.detectState;
+			if (!faceCoo || !detectState || detectState == '' || faceCoo == '') {
+				return false;
+			}
+			var x = parseFloat(faceCoo.x).toFixed(2);
+			var y = parseFloat(faceCoo.y).toFixed(2);
+			var w = parseFloat(faceCoo.w).toFixed(2);
+			var h = parseFloat(faceCoo.h).toFixed(2);
+			var cw = parseFloat(CVD.canvas.width).toFixed(2);
+			var ch = parseFloat(CVD.canvas.height).toFixed(2);
+			var xcw = x / cw;
+			var ycw = y / ch;
+			if (xcw < 0.25 || xcw > 0.5 || ycw > 0.45 || ycw < 0.1) {
+				that.errortext = '请将完整脸部对准屏幕中央';
+			} else {
+				that.errortext = '';
+			}
+			if (that.errortext && that.errortext != '') {
+				return false;
+			}
+			var faceMj = w * h;
+			var allMj = cw * ch;
+			var zhanbi = faceMj / allMj;
+			var mianjiObj = {
+				faceMj: faceMj,
+				zhanbi: zhanbi
+			}
+			var mianjiobj2 = {
+				cw: cw,
+				ch: ch,
+				allMj: allMj,
+			}
+			var mianjiStr = JSON.stringify(mianjiObj);
+			var mianjiStr2 = JSON.stringify(mianjiobj2);
+			if (zhanbi < 0.18) {
+				that.errortext = '请离脸部近一点'
+			} else if (zhanbi > 0.28) {
+				that.errortext = '请离脸部远一点'
+			} else {
+				return true;
+			}
+			return false;
+		},
+
+		cutPhoto(rectparams) {
+
+			var that = this;
+			var check = that.checkTrue(true);
+			if (check == false) {
+				that.savePause(false);
+				return;
+			}
+			that.errortext = '拍照成功,请稍后';
+			that.bgImgClass = 'home_wai remove_animation';
+			that.bgImgClass2 = 'home_nei remove_animation';
+
+			that.showVideo = 'none';
+			$('#jeeFaceFilterCanvas2').show();
+			CVD.ctx.clearRect(0, 0, CVD.canvas.width, CVD.canvas.height);
+			CVD.draw();
+			var clip = $('#jeeFaceFilterCanvas2')[0];
+			var context_clip = clip.getContext('2d');
+
+			context_clip.drawImage(
+				$('#jeeFaceFilterCanvas')[0], //规定要使用的图像、画布或视频。
+				0, 0, //开始剪切的 x 坐标位置。
+				CVD.canvas.width, CVD.canvas.height, //被剪切图像的高度。
+				0, 0, //在画布上放置图像的 x 、y坐标位置。
+				this.vwidth, this.vwidth //要使用的图像的宽度、高度
+			);
+
+			//位移来做镜像翻转
+			if (that.useCamera == 'qian') {
+				//使用前置摄像头需要反转镜头
+				context_clip.translate(this.vwidth, 0);
+				context_clip.scale(-1, 1); //左右镜像翻转
+				context_clip.drawImage($('#jeeFaceFilterCanvas2')[0], 0, 0, this.vwidth, this.vwidth);
+			}
+
+			this.imgUrl = clip.toDataURL("image/jpeg");
+			/* clip.toBlob((blob) => {
+			    if (blob) {
+					this.imgUrl = createObjectURL(blob)
+			    } else {
+			        reject(new Error('Failed to create blob from canvas'));
+			    }
+			}, 'image/png'); */
+			this.useImg();
+		},
+		useImg() {
+			this.imgSize();
+		},
+		imgSize() {
+			var that = this;
+			if (this.imgUrl) {
+				// 获取base64图片byte大小
+				const equalIndex = this.imgUrl.indexOf("="); // 获取=号下标
+				let size;
+				if (equalIndex > 0) {
+					const str = this.imgUrl.substring(0, equalIndex); // 去除=号
+					const strLength = str.length;
+					const fileLength = strLength - (strLength / 8) * 2; // 真实的图片byte大小
+					size = Math.floor(fileLength / 1024); // 向下取整
+				} else {
+					const strLength = this.imgUrl.length;
+					const fileLength = strLength - (strLength / 8) * 2;
+					size = Math.floor(fileLength / 1024); // 向下取整
+				}
+				if (size >= 800) {
+					// 图片超过800k 重新再次压缩
+					this.imgUrl = $('#jeeFaceFilterCanvas2')[0].toDataURL("image/jpeg", 0.8);
+					this.imgSize();
+				} else {
+
+					that.faceOpeImg();
+				}
+			}
+		},
+		async faceOpeImg() {
+
+			var that = this;
+			uni.webView.postMessage({
+				data: {
+					photo: this.imgUrl
+				}
+			});
+			
+			console.log(that.imgUrl,'that.imgUrlthat.imgUrl')
+			return false
+			
+			$('#jeeFaceFilterCanvas2')[0].toBlob(async (blob) => {
+			    if (blob) {
+					let newBlobUrl = that.createObjectURL(blob)
+			       
+					const {code,data} = await this.uploadFilePromise(newBlobUrl)
+					if(code == 0) {
+						// console.log(data,'datadata')
+						if(data.data == false) {
+							that.errortext = '不是有效的人脸,请重新拍照';
+							that.startVideoStatus = false;
+						
+							return;
+						} else {
+							that.errortext = '人脸识别成功';
+							that.startVideoStatus = false;
+							this.$emit('success', data, newBlobUrl, that.imgUrl)
+							 this.close()
+							 
+							return
+						}
+					} else {
+						that.errortext = '比对人脸失败';
+						that.startVideoStatus = false;
+						return; 
+					}
+					
+			    } else {
+			        reject(new Error('Failed to create blob from canvas'));
+			    }
+			}, 'image/png');
+			
+			
+		},
+		
+		/**
+		* blob转url临时访问地址
+		* @param String blob 对象
+		*/
+		
+		createObjectURL(blob){
+			return URL.createObjectURL(blob);
+		},
+		
+		uploadFilePromise(url) {
+		    return new Promise((resolve, reject) => {
+				// console.log(url,'urlurl')
+		        let a = uni.uploadFile({
+		            url: compareFace, // 仅为示例,非真实的接口地址
+					header: {
+						'Authorization': 'Bearer ' + getToken()
+					},
+		            filePath: url,
+		            name: 'file',
+		            formData: {
+		                ...this.querParams,
+		            },
+		            success: (res) => {			
+						let data = JSON.parse(res.data)
+						resolve(data)
+		            },
+					fail: (err) => {
+						resolve({
+							code: -1,
+							msg: '上传失败'
+						})
+					}
+		        });
+		    })
+		},
+
+	},
+
+}
+</script>
+
+<style lang="scss" scoped>
+.page-container {
+	min-width: 600rpx;
+	font-size: 28rpx;
+	height: 100vh;
+	position: relative;
+
+	.contentp1 {
+		width: 100px;
+		height: 30px;
+		background: #EEEEEE;
+		color: #28A745;
+		line-height: 30px;
+		margin-top: 30px;
+		text-align: center;
+	}
+
+	.contentp {
+		height: 120px;
+	}
+
+	.errortext {
+		width: 100%;
+		height: 40rpx;
+		line-height: 40rpx;
+		color: #e03030;
+		// color: #333333;
+		text-align: center;
+		margin-top: 30rpx;
+		font-size: 30rpx;
+	}
+
+	.img-face {
+		display: -webkit-box;
+		/* 老版本语法: Safari, iOS, Android browser, older WebKit browsers. */
+		display: -moz-box;
+		/* 老版本语法: Firefox (buggy) */
+		display: -ms-flexbox;
+		/* 混合版本语法: IE 10 */
+
+		display: -webkit-flex;
+		/* 新版本语法: Chrome 21+ */
+		display: flex;
+
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+
+		.imgurl {
+			/* position: fixed;
+			 top: 117.5px;
+			 width: 266px;
+			 height: 266px;
+			 border-radius: 230rpx; */
+		}
+	}
+
+	#show-content {
+		width: 100%;
+		margin-top: 30px;
+		height: 260px;
+		display: -webkit-box;
+		/* 老版本语法: Safari, iOS, Android browser, older WebKit browsers. */
+		display: -moz-box;
+		/* 老版本语法: Firefox (buggy) */
+		display: -ms-flexbox;
+		/* 混合版本语法: IE 10 */
+
+		display: -webkit-flex;
+		/* 新版本语法: Chrome 21+ */
+		display: flex;
+		justify-content: center;
+		align-items: center;
+	}
+
+	.show-content-hide {
+		display: none !important;
+	}
+
+	.button-view {
+		width: 100%;
+		margin-top: 60rpx;
+		height: 80rpx;
+		display: -webkit-box;
+		/* 老版本语法: Safari, iOS, Android browser, older WebKit browsers. */
+		display: -moz-box;
+		/* 老版本语法: Firefox (buggy) */
+		display: -ms-flexbox;
+		/* 混合版本语法: IE 10 */
+
+		display: -webkit-flex;
+		/* 新版本语法: Chrome 21+ */
+		display: flex;
+		justify-content: center;
+		align-items: center;
+
+		.click-button {
+			width: 80%;
+			height: 100%;
+
+		}
+	}
+
+	.not-zhuanimg {
+		width: 17.225rem;
+		overflow: hidden;
+		z-index: 10;
+		position: absolute;
+	}
+
+	.home {
+		overflow: hidden;
+		text-align: center;
+		width: 240px;
+		height: 240px;
+		position: absolute;
+		z-index: 10;
+	}
+
+	.home_wai img {
+		width: 100%;
+		overflow: hidden;
+		animation: myfirst 8s infinite linear;
+	}
+
+	@keyframes myfirst {
+		0% {
+			transform: rotate(0deg) scale(1);
+		}
+
+		50% {
+			transform: rotate(360deg) scale(1);
+		}
+
+		75% {
+			transform: rotate(540deg) scale(0.9);
+		}
+
+		100% {
+			transform: rotate(720deg) scale(1);
+		}
+	}
+
+	.home_nei {
+		overflow: hidden;
+		width: 12.575rem;
+		margin: 3.69133rem auto;
+	}
+
+	.home_nei img {
+		width: 100%;
+		overflow: hidden;
+		animation: mysecond 8s infinite linear;
+	}
+
+	.remove_animation img {
+		animation: none;
+	}
+
+	@keyframes mysecond {
+		0% {
+			transform: rotate(0deg) scale(1);
+		}
+
+		50% {
+			transform: rotate(-360deg) scale(1);
+		}
+
+		75% {
+			transform: rotate(-540deg) scale(0.9);
+		}
+
+		100% {
+			transform: rotate(-720deg) scale(1);
+		}
+	}
+
+
+	.title {
+		background: #FFFFFF;
+		padding: 0.9rem 0;
+		text-align: center;
+		box-shadow: 0 0 10px #e5e5e5;
+		font-size: 0.9rem;
+		font-weight: bold;
+		color: #000;
+	}
+
+	.main_top {
+		padding-top: 2.5rem;
+		text-align: center;
+		color: #757575;
+		font-size: 0.9rem;
+		font-weight: bold;
+	}
+
+	.main_top_cricle {
+		margin: 1.25rem auto;
+		width: 9.2rem;
+		height: 9.2rem;
+		position: relative;
+		overflow: hidden;
+	}
+
+	.main_top_cricle img {
+		width: 98%;
+	}
+
+	.main_cricle {
+		position: absolute;
+		top: 0;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		overflow: hidden;
+		z-index: 5;
+		animation: myfirst 4s infinite linear;
+	}
+
+	@keyframes myfirst {
+		0% {
+			transform: rotate(0deg);
+		}
+
+		50% {
+			transform: rotate(360deg);
+		}
+
+		75% {
+			transform: rotate(540deg);
+		}
+
+		100% {
+			transform: rotate(720deg);
+		}
+	}
+
+	.main_cricle img {
+		width: 100%;
+		height: 100%;
+	}
+
+	.main_bottom {
+		margin-top: 30rpx;
+		overflow: hidden;
+		text-align: center;
+		font-size: 28rpx;
+		color: #333;
+	}
+
+	.bottom_main {
+		margin-top: 1rem;
+		display: flex;
+		overflow: hidden;
+	}
+
+	.bottom_main_list {
+		text-align: center;
+		flex-grow: 1;
+		font-size: 28rpx;
+		color: #333;
+	}
+
+	.bottom_main_list img {
+		width: 2.5rem;
+		margin-bottom: 10rpx;
+	}
+
+
+
+	.uer-camera {
+		position: fixed;
+		top: 200rpx;
+		right: 42%;
+		color: #fff;
+		text-align: center;
+		font-size: 24rpx;
+		padding: 6rpx 8rpx 8rpx 6rpx;
+		border-radius: 24rpx;
+		height: 88rpx;
+		width: 88rpx;
+		overflow: hidden;
+	}
+
+	.uer-camera img {
+		width: 100%;
+		height: 100%;
+	}
+}
+</style>

+ 969 - 0
components/tq-face/index.vue

@@ -0,0 +1,969 @@
+<template>
+	<u-popup :show="isShow" @close="close" :closeOnClickOverlay="false">
+		<view class="page-face-container">
+			<view class="errortext" v-if="!startVideoStatus">{{ tip }}</view>
+			<view class="errortext"><text v-if="errortext != ''">{{ errortext }}</text></view>
+			<view id="show-content" :class="openCamera == true ? '' : 'show-content-hide'">
+				<view class="home">
+					<view :class="bgImgClass">
+						<img src="../../static/neuralNets/img4.png" />
+					</view>
+				</view>
+				<!-- <view class="uer-camera" @tap="changeUserCamera('hou')" v-if="useCamera == 'qian'">
+					<img src="../../static/neuralNets/change_camera.png" /> 
+				</view>
+				<view class="uer-camera" @tap="changeUserCamera('qian')" v-if="useCamera == 'hou'">
+					<img src="../../static/neuralNets/change_camera.png" /> 
+				</view> -->
+				<video
+					src=""
+					id="video"
+					:style="{ width: vwidth + 'px', height: vheight + 'px', display: showVideo }"
+					playsinline
+					webkit-playsinline autoplay=true muted=true loop=true controls=false show-center-play-btn=false
+					object-fit=fill>
+				</video>
+			</view>
+
+
+			<view class="main_bottom">
+				确认为<text style="color: red;padding: 6rpx;"
+					v-if="userName">{{ userName }}</text>本人照片<br>保持正脸在取景框中根据屏幕指示完成
+				<view class="bottom_main">
+					<view class="bottom_main_list">
+						<img src="../../static/neuralNets/img3.png" />
+						<p>正对手机</p>
+					</view>
+					<view class="bottom_main_list">
+						<img src="../../static/neuralNets/img2.png" />
+						<p>光线充足</p>
+					</view>
+					<view class="bottom_main_list">
+						<img src="../../static/neuralNets/img1.png" />
+						<p>脸无遮挡</p>
+					</view>
+				</view>
+			</view>
+
+			<view class="button-view" :style="{ display: (openCamera == true ? 'flex' : 'none') }">
+
+				<view class="click-button">
+					<u-button type="primary" shape="circle" @click="startVideo" v-if="!isMaxVlide"
+						:disabled="startVideoStatus">{{ !startVideoStatus ? '开始刷脸' : '检测中...' }}</u-button>
+					<u-button type="error"
+						shape="circle" @click="close"
+						:customStyle="{
+							marginTop: '30rpx'
+						}">退出</u-button>
+
+				</view>
+
+			</view>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+import $ from "@/static/collect_face/jquery2.3.js";
+import faceFilter from "@/static/neuralNets/jeelizFaceFilter.module.js";
+import JeelizCanvas2DHelper from "@/static/neuralNets/JeelizCanvas2DHelper.js";
+// 引入uni.webview.1.5.2.js
+import '@/static/js/uni.webview.1.5.4.js'
+import { compareFace } from '@/api/common/system.js'
+import { getToken } from '@/utils/auth'
+
+let VIDEOELEMENT = null;
+var CVD = null; // return of Canvas2DDisplay
+
+
+export default {
+	props: {
+
+		// 用户名
+		userName: {
+			type: String,
+			default: null
+		},
+		tip: {
+			type: String,
+			default: '需进行人脸验证后再继续学习'
+		},
+		querParams: {
+			type: Object,
+			default: () => {
+				return {}
+			}
+		},
+		maxValidNum: {
+			type: Number,
+			default: 10000
+		}
+	},
+	data() {
+		return {
+			isShow: false,
+
+			isTest: false,
+			againCount: 0,
+			openCamera: false,
+			screenSize: {
+				width: window.screen.width,
+				height: window.screen.height,
+			},
+			vwidth: 240,
+			vheight: 240,
+			scanTip: '',
+			errortext: '点击开始刷脸按钮开始拍摄',
+			uploadHeadImg: '',
+			imgUrl: '',
+			pauseStatus: false,
+			startCut: false,
+			faceCoo: null,
+			detectState: null,
+			showVideo: 'block',
+			appendCss: false,
+			bgImgClass: 'home_wai',
+			bgImgClass2: 'home_nei',
+			mediaStreamTrack: null,
+			startVideoStatus: false,
+			useCamera: 'qian', //qian代表前置摄像头 hou代表后置摄像头
+
+			valideNum: 0, //失败次数
+			isMaxVlide: false, //是否超过最大次数
+		}
+	},
+	mounted() { },
+
+	methods: {
+
+		show() {
+			this.valideNum = 0;
+			this.isMaxVlide = false;
+			if (origin.indexOf('https') === -1 && window.location.hostname != "localhost") {
+				uni.showModal({
+						title: '提示',
+						content: `请在 https 环境中使用人脸验证`,
+						showCancel:false,
+						confirmText: '确定',
+						success: function(res) {
+							if (res.confirm) {
+								uni.navigateBack()
+							}
+						}
+					})
+				return false;
+			}
+			
+			this.isShow = true;
+
+			if (faceFilter && typeof faceFilter.toggle_pause == 'function') {
+				faceFilter.destroy();
+			}
+			var that = this; //a
+			setTimeout(function () {
+				//初始化的时候给追加上canvas用作载体
+				if ($('#jeeFaceFilterCanvas').html() == undefined) {
+					$('#show-content').append('<canvas class="appendcanvas" width="' + that.vwidth +
+						'" height="' + that.vheight + '" id="jeeFaceFilterCanvas"></canvas>');
+				}
+				if ($('#jeeFaceFilterCanvas2').html() == undefined) {
+					$('#show-content').append('<canvas class="appendcanvas appendcanvas2"  width="' + that
+						.vwidth + '" height="' + that.vheight + '" id="jeeFaceFilterCanvas2"></canvas>');
+				}
+				if (that.appendCss == false) {
+					$('uni-page-body').append(`<style>
+					#jeeFaceFilterCanvas{
+						display:none;
+					}
+					#jeeFaceFilterCanvas2{
+						display:none;
+						border-radius:50%
+					}
+					#video .uni-video-bar{
+						display:none !important; //去除底部菜单栏
+					}
+					#video .uni-video-cover{
+						display:none !important; //去除中间播放按钮
+					}
+					#video video{
+						transform: rotateY(180deg);
+						-webkit-transform: rotateY(180deg);    /* Safari 和 Chrome */
+						-moz-transform: rotateY(180deg);
+						border-radius:50% !important;
+					}
+					#video .uni-video-container{border-radius:50% !important;}
+					#video .uni-video-video{border-radius:50% !important;}
+					</style>`);
+					that.appendCss = true;
+				}
+				//uniapp编译过后无法获取video的dom,此处追加id
+				$('#video').find('video').prop('id', 'myVideo');
+				that.showVideo = 'block';
+				if (that.isTest == true) {
+					//是否是测试环境 测试环境直接调用视频
+					that.openCamera = true;
+					that.main();
+				} else {
+					that.openCamera = true;
+					that.bgImgClass = 'home_wai remove_animation';
+					that.bgImgClass2 = 'home_nei remove_animation';
+					that.errortext = '点击开始刷脸按钮开始拍摄';
+				}
+			}, 100)
+
+		},
+
+
+		close(type) {
+			this.valideNum = 0;
+			this.isMaxVlide = false;
+			this.startVideoStatus = false;
+			this.closeVideo();
+			this.isShow = false
+			if(type!='confrim') {
+				this.$emit('close')
+			}
+			
+		},
+
+		closeVideo() {
+			try {
+				//页面卸载的时候关闭视频播放
+				if (this.mediaStreamTrack) {
+					this.mediaStreamTrack.getTracks()[0].stop();
+				}
+			} catch {
+				console.log('close mediaStreamTrack error')
+			}
+			try {
+				// const video = document.getElementsByClassName('uni-video-video')[0];
+				const video = document.getElementById('myVideo');
+				video.srcObject = null;
+			} catch {
+				console.log('close video.srcObject error')
+			}
+			try {
+				faceFilter.toggle_pause(true);
+				faceFilter.destroy(); //销毁注册对象
+			} catch {
+				console.log('close faceFilter toggle_pause error')
+			}
+		},
+		changeUserCamera(type) {
+			this.useCamera = type;
+			if (type == 'hou') {
+				$('uni-page-body').append(`<style>
+					#video video{
+						transform: rotateY(0deg);
+						-webkit-transform: rotateY(0deg);    /* Safari 和 Chrome */
+						-moz-transform: rotateY(0deg);
+						border-radius:50% !important;
+					}
+					</style>`);
+			} else {
+				$('uni-page-body').append(`<style>
+					#video video{
+						transform: rotateY(180deg);
+						-webkit-transform: rotateY(180deg);    /* Safari 和 Chrome */
+						-moz-transform: rotateY(180deg);
+						border-radius:50% !important;
+					}
+					</style>`);
+			}
+			this.playVideo();
+		},
+		startVideo() {
+			if (this.startVideoStatus == false) {
+				this.playVideo();
+			} else {
+				if (this.pauseStatus == false) {
+					this.$api.msg('当前正在拍照');
+					return;
+				}
+				this.savePause(false)
+			}
+		},
+		savePause(status) {
+			this.uploadHeadImg = '';
+			this.imgUrl = '';
+			this.pauseStatus = status;
+			if (status == false) {
+				this.startCut = false;
+				this.bgImgClass = 'home_wai';
+				this.bgImgClass2 = 'home_nei';
+				this.showVideo = 'none';
+				$('#jeeFaceFilterCanvas2').hide();
+			} else {
+				this.showVideo = 'block';
+				$('#jeeFaceFilterCanvas2').hide();
+			}
+			//console.log(status)
+			//true 暂停  false 继续执行
+			faceFilter.toggle_pause(status);
+		},
+		playVideo() {
+
+			var that = this;
+			that.uploadHeadImg = '';
+			that.imgUrl = '';
+			that.startVideoStatus = true;
+			that.startCut = false;
+			var thatUseCamera = that.useCamera;
+			if (thatUseCamera == 'qian') {
+				var facingMode = 'user';
+			} else if (thatUseCamera == 'hou') {
+				var facingMode = {
+					exact: "environment"
+				};
+			}
+			if (faceFilter && typeof faceFilter.toggle_pause == 'function') {
+				faceFilter.destroy();
+			}
+			setTimeout(function () {
+				that.getUserMedia({
+					//摄像头拍摄的区域
+					video: {
+						width: 500,
+						height: 500,
+						facingMode: facingMode,
+					},
+					/* 前置优先 */
+				},
+					that.success,
+					that.error
+				);
+			}, 100)
+
+		},
+		// 访问用户媒体设备
+		getUserMedia(constrains, success, error) {
+
+			if (navigator.mediaDevices.getUserMedia) {
+				navigator.mediaDevices.getUserMedia(constrains).then(success).catch(error); //最新标准API
+			} else if (navigator.webkitGetUserMedia) {
+				navigator.webkitGetUserMedia(constrains).then(success).catch(error); //webkit内核浏览器
+			} else if (navigator.mozGetUserMedia) {
+				navagator.mozGetUserMedia(constrains).then(success).catch(error); //Firefox浏览器
+			} else if (navigator.getUserMedia) {
+				navigator.getUserMedia(constrains).then(success).catch(error); //旧版API
+			} else {
+				this.errortext = "你的浏览器不支持访问用户媒体设备";
+			}
+		},
+		success(stream) {
+			var that = this;
+			that.bgImgClass = 'home_wai';
+			that.bgImgClass2 = 'home_nei';
+			that.openCamera = true;
+			that.errortext = '请把人脸放在圆圈内拍摄脸部';
+			that.mediaStreamTrack = stream;
+			// const video = document.getElementsByClassName('uni-video-video')[0];
+			const video = document.getElementById('myVideo');
+			try {
+				video.srcObject = stream;
+			} catch {
+				this.URL = window.URL || window.webkitURL;
+				video.src = this.URL.createObjectURL(stream);
+			}
+			// webkit内核浏览器
+			//苹果手机的系统弹框会阻止js的线程的继续执行 手动0.1秒之后自动执行代码
+			setTimeout(() => {
+				try {
+					video.play();
+				} catch {
+					that.errortext = '视频流播放失败';
+				}
+				that.main();
+			}, 100);
+
+		},
+		error(e) {
+			var that = this;
+			that.startVideoStatus = false;
+			that.bgImgClass = 'home_wai remove_animation';
+			that.bgImgClass2 = 'home_nei remove_animation';
+			if (this.useCamera == 'hou') {
+				that.errortext = "调用后置摄像头失败:" + (e.name ? e.name : '') + (e.message ? e.message : '');
+			} else {
+				that.errortext = "调用前置摄像头失败:" + (e.name ? e.name : '') + (e.message ? e.message : '');
+			}
+
+		},
+		main() { // entry point
+			var that = this;
+			VIDEOELEMENT = document.getElementById('myVideo');
+
+			if (VIDEOELEMENT['currentTime'] && VIDEOELEMENT['videoWidth'] && VIDEOELEMENT['videoHeight']) {
+
+				that.start();
+			} else {
+				setTimeout(function () {
+					that.main();
+				}, 100);
+
+			}
+		},
+		start() { // launched when the video is loaded
+
+			var that = this;
+			faceFilter.init({
+				canvasId: 'jeeFaceFilterCanvas',
+				videoSettings: {
+					videoElement: VIDEOELEMENT
+				},
+				rotate: -90,
+				NNCPath: '../../static/neuralNets/', // root of NN_DEFAULT.json file
+				callbackReady: function (errCode, spec) {
+					if (errCode) {
+						//
+						that.errortext = '人脸初始化出错' + errCode;
+						that.startVideoStatus = false;
+						console.log('AN ERROR HAPPENS. SORRY BRO :( . ERR =', errCode);
+						return;
+					}
+					CVD = JeelizCanvas2DHelper(spec);
+					CVD.ctx.strokeStyle = 'yellow';
+				},
+
+				callbackTrack: function (detectState) {
+					//console.log(detectState.detected)
+					that.detectState = '';
+					that.faceCoo = '';
+
+					if (detectState.detected > 0.9) {
+
+						that.detectState = detectState; 
+						that.showVideo = 'block';
+						$('#jeeFaceFilterCanvas2').hide();
+						var faceCoo = CVD.getCoordinates(detectState);
+						that.faceCoo = faceCoo;
+						CVD.ctx.clearRect(0, 0, CVD.canvas.width, CVD.canvas.height);
+						CVD.update_canvasTexture();
+						CVD.draw();
+						var check = that.checkTrue();
+						if (check) {
+							that.errortext = '请保持不动,即将拍照';
+							if (that.startCut == false) {
+
+								that.startCut = true;
+								setTimeout(function () {
+									//console.log('setTimeout')
+									that.savePause(true);
+									that.cutPhoto();
+								}, 1000)
+							}
+						}
+					} else {
+						that.showVideo = 'block';
+						$('#jeeFaceFilterCanvas2').hide();
+						that.errortext = '请将完整脸部对准屏幕中央';
+					}
+
+				}
+			}); //end JEELIZFACEFILTER.init call
+		},
+
+		checkTrue(dump) {
+
+			var that = this;
+			var faceCoo = that.faceCoo;
+			var faceCooStr = JSON.stringify(faceCoo);
+			var detectState = that.detectState;
+			if (!faceCoo || !detectState || detectState == '' || faceCoo == '') {
+				return false;
+			}
+			var x = parseFloat(faceCoo.x).toFixed(2);
+			var y = parseFloat(faceCoo.y).toFixed(2);
+			var w = parseFloat(faceCoo.w).toFixed(2);
+			var h = parseFloat(faceCoo.h).toFixed(2);
+			var cw = parseFloat(CVD.canvas.width).toFixed(2);
+			var ch = parseFloat(CVD.canvas.height).toFixed(2);
+			var xcw = x / cw;
+			var ycw = y / ch;
+			if (xcw < 0.25 || xcw > 0.5 || ycw > 0.45 || ycw < 0.1) {
+				that.errortext = '请将完整脸部对准屏幕中央';
+			} else {
+				that.errortext = '';
+			}
+			if (that.errortext && that.errortext != '') {
+				return false;
+			}
+			var faceMj = w * h;
+			var allMj = cw * ch;
+			var zhanbi = faceMj / allMj;
+			var mianjiObj = {
+				faceMj: faceMj,
+				zhanbi: zhanbi
+			}
+			var mianjiobj2 = {
+				cw: cw,
+				ch: ch,
+				allMj: allMj,
+			}
+			var mianjiStr = JSON.stringify(mianjiObj);
+			var mianjiStr2 = JSON.stringify(mianjiobj2);
+			if (zhanbi < 0.18) {
+				that.errortext = '请离脸部近一点'
+			} else if (zhanbi > 0.28) {
+				that.errortext = '请离脸部远一点'
+			} else {
+				return true;
+			}
+			return false;
+		},
+
+		cutPhoto(rectparams) {
+
+			var that = this;
+			var check = that.checkTrue(true);
+			if (check == false) {
+				that.savePause(false);
+				return;
+			}
+			that.errortext = '拍照成功,请稍后';
+			that.bgImgClass = 'home_wai remove_animation';
+			that.bgImgClass2 = 'home_nei remove_animation';
+
+			that.showVideo = 'none';
+			$('#jeeFaceFilterCanvas2').show();
+			CVD.ctx.clearRect(0, 0, CVD.canvas.width, CVD.canvas.height);
+			CVD.draw();
+			var clip = $('#jeeFaceFilterCanvas2')[0];
+			var context_clip = clip.getContext('2d');
+
+			context_clip.drawImage(
+				$('#jeeFaceFilterCanvas')[0], //规定要使用的图像、画布或视频。
+				0, 0, //开始剪切的 x 坐标位置。
+				CVD.canvas.width, CVD.canvas.height, //被剪切图像的高度。
+				0, 0, //在画布上放置图像的 x 、y坐标位置。
+				this.vwidth, this.vwidth //要使用的图像的宽度、高度
+			);
+
+			//位移来做镜像翻转
+			if (that.useCamera == 'qian') {
+				//使用前置摄像头需要反转镜头
+				context_clip.translate(this.vwidth, 0);
+				context_clip.scale(-1, 1); //左右镜像翻转
+				context_clip.drawImage($('#jeeFaceFilterCanvas2')[0], 0, 0, this.vwidth, this.vwidth);
+			}
+
+			this.imgUrl = clip.toDataURL("image/jpeg");
+			/* clip.toBlob((blob) => {
+			    if (blob) {
+					this.imgUrl = createObjectURL(blob)
+			    } else {
+			        reject(new Error('Failed to create blob from canvas'));
+			    }
+			}, 'image/png'); */
+			this.useImg();
+		},
+		useImg() {
+			this.imgSize();
+		},
+		imgSize() {
+			var that = this;
+			if (this.imgUrl) {
+				// 获取base64图片byte大小
+				const equalIndex = this.imgUrl.indexOf("="); // 获取=号下标
+				let size;
+				if (equalIndex > 0) {
+					const str = this.imgUrl.substring(0, equalIndex); // 去除=号
+					const strLength = str.length;
+					const fileLength = strLength - (strLength / 8) * 2; // 真实的图片byte大小
+					size = Math.floor(fileLength / 1024); // 向下取整
+				} else {
+					const strLength = this.imgUrl.length;
+					const fileLength = strLength - (strLength / 8) * 2;
+					size = Math.floor(fileLength / 1024); // 向下取整
+				}
+				if (size >= 800) {
+					// 图片超过800k 重新再次压缩
+					this.imgUrl = $('#jeeFaceFilterCanvas2')[0].toDataURL("image/jpeg", 0.8);
+					this.imgSize();
+				} else {
+
+					that.faceOpeImg();
+				}
+			}
+		},
+		async faceOpeImg() {
+
+			var that = this;
+			uni.webView.postMessage({
+				data: {
+					photo: this.imgUrl
+				}
+			});
+			
+			// console.log(that.imgUrl,'that.imgUrlthat.imgUrl')
+			// return false
+			
+			$('#jeeFaceFilterCanvas2')[0].toBlob(async (blob) => {
+			    if (blob) {
+					let newBlobUrl = that.createObjectURL(blob)
+			       
+					const {code,data} = await this.uploadFilePromise(newBlobUrl)
+					if(code == 0) {
+						// console.log(data,'datadata')
+						if(data.data == false) {
+							that.errortext = '不是有效的人脸,请重新拍照';
+							this.valideNum += 1;
+							if(this.valideNum >= this.maxValidNum) {
+								this.isMaxVlide = true
+							}
+							that.startVideoStatus = false;
+							
+							return;
+						} else {
+							that.errortext = '人脸识别成功';
+							that.startVideoStatus = false;
+							this.$emit('success', data, newBlobUrl, that.imgUrl)
+							 this.close('confrim')
+							 
+							return
+						}
+					} else {
+						that.errortext = '比对人脸失败';
+						that.startVideoStatus = false;
+						return; 
+					}
+					
+			    } else {
+			        reject(new Error('Failed to create blob from canvas'));
+			    }
+			}, 'image/png');
+			
+			
+		},
+		
+		/**
+		* blob转url临时访问地址
+		* @param String blob 对象
+		*/
+		
+		createObjectURL(blob){
+			return URL.createObjectURL(blob);
+		},
+		
+		uploadFilePromise(url) {
+		    return new Promise((resolve, reject) => {
+				// console.log(url,'urlurl')
+		        let a = uni.uploadFile({
+		            url: compareFace, // 仅为示例,非真实的接口地址
+					header: {
+						'Authorization': 'Bearer ' + getToken()
+					},
+		            filePath: url,
+		            name: 'file',
+		            formData: {
+		                ...this.querParams,
+		            },
+		            success: (res) => {			
+						let data = JSON.parse(res.data)
+						resolve(data)
+		            },
+					fail: (err) => {
+						resolve({
+							code: -1,
+							msg: '上传失败'
+						})
+					}
+		        });
+		    })
+		},
+
+	},
+
+}
+</script>
+
+<style lang="scss" scoped>
+.page-face-container {
+	min-width: 600rpx;
+	font-size: 28rpx;
+	height: 100vh;
+	position: relative;
+
+	.contentp1 {
+		width: 100px;
+		height: 30px;
+		background: #EEEEEE;
+		color: #28A745;
+		line-height: 30px;
+		margin-top: 30px;
+		text-align: center;
+	}
+
+	.contentp {
+		height: 120px;
+	}
+
+	.errortext {
+		width: 100%;
+		height: 40rpx;
+		line-height: 40rpx;
+		color: #e03030;
+		// color: #333333;
+		text-align: center;
+		margin-top: 30rpx;
+		font-size: 30rpx;
+	}
+
+	.img-face {
+		display: -webkit-box;
+		/* 老版本语法: Safari, iOS, Android browser, older WebKit browsers. */
+		display: -moz-box;
+		/* 老版本语法: Firefox (buggy) */
+		display: -ms-flexbox;
+		/* 混合版本语法: IE 10 */
+
+		display: -webkit-flex;
+		/* 新版本语法: Chrome 21+ */
+		display: flex;
+
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+
+		.imgurl {
+			/* position: fixed;
+			 top: 117.5px;
+			 width: 266px;
+			 height: 266px;
+			 border-radius: 230rpx; */
+		}
+	}
+
+	#show-content {
+		width: 100%;
+		margin-top: 30px;
+		height: 260px;
+		display: -webkit-box;
+		/* 老版本语法: Safari, iOS, Android browser, older WebKit browsers. */
+		display: -moz-box;
+		/* 老版本语法: Firefox (buggy) */
+		display: -ms-flexbox;
+		/* 混合版本语法: IE 10 */
+
+		display: -webkit-flex;
+		/* 新版本语法: Chrome 21+ */
+		display: flex;
+		justify-content: center;
+		align-items: center;
+	}
+
+	.show-content-hide {
+		display: none !important;
+	}
+
+	.button-view {
+		width: 100%;
+		margin-top: 60rpx;
+		height: 80rpx;
+		display: -webkit-box;
+		/* 老版本语法: Safari, iOS, Android browser, older WebKit browsers. */
+		display: -moz-box;
+		/* 老版本语法: Firefox (buggy) */
+		display: -ms-flexbox;
+		/* 混合版本语法: IE 10 */
+
+		display: -webkit-flex;
+		/* 新版本语法: Chrome 21+ */
+		display: flex;
+		justify-content: center;
+		align-items: center;
+
+		.click-button {
+			width: 80%;
+			height: 100%;
+
+		}
+	}
+
+	.not-zhuanimg {
+		width: 17.225rem;
+		overflow: hidden;
+		z-index: 10;
+		position: absolute;
+	}
+
+	.home {
+		overflow: hidden;
+		text-align: center;
+		width: 240px;
+		height: 240px;
+		position: absolute;
+		z-index: 10;
+	}
+
+	.home_wai img {
+		width: 100%;
+		overflow: hidden;
+		animation: myfirst 8s infinite linear;
+	}
+
+	@keyframes myfirst {
+		0% {
+			transform: rotate(0deg) scale(1);
+		}
+
+		50% {
+			transform: rotate(360deg) scale(1);
+		}
+
+		75% {
+			transform: rotate(540deg) scale(0.9);
+		}
+
+		100% {
+			transform: rotate(720deg) scale(1);
+		}
+	}
+
+	.home_nei {
+		overflow: hidden;
+		width: 12.575rem;
+		margin: 3.69133rem auto;
+	}
+
+	.home_nei img {
+		width: 100%;
+		overflow: hidden;
+		animation: mysecond 8s infinite linear;
+	}
+
+	.remove_animation img {
+		animation: none;
+	}
+
+	@keyframes mysecond {
+		0% {
+			transform: rotate(0deg) scale(1);
+		}
+
+		50% {
+			transform: rotate(-360deg) scale(1);
+		}
+
+		75% {
+			transform: rotate(-540deg) scale(0.9);
+		}
+
+		100% {
+			transform: rotate(-720deg) scale(1);
+		}
+	}
+
+
+	.title {
+		background: #FFFFFF;
+		padding: 0.9rem 0;
+		text-align: center;
+		box-shadow: 0 0 10px #e5e5e5;
+		font-size: 0.9rem;
+		font-weight: bold;
+		color: #000;
+	}
+
+	.main_top {
+		padding-top: 2.5rem;
+		text-align: center;
+		color: #757575;
+		font-size: 0.9rem;
+		font-weight: bold;
+	}
+
+	.main_top_cricle {
+		margin: 1.25rem auto;
+		width: 9.2rem;
+		height: 9.2rem;
+		position: relative;
+		overflow: hidden;
+	}
+
+	.main_top_cricle img {
+		width: 98%;
+	}
+
+	.main_cricle {
+		position: absolute;
+		top: 0;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		overflow: hidden;
+		z-index: 5;
+		animation: myfirst 4s infinite linear;
+	}
+
+	@keyframes myfirst {
+		0% {
+			transform: rotate(0deg);
+		}
+
+		50% {
+			transform: rotate(360deg);
+		}
+
+		75% {
+			transform: rotate(540deg);
+		}
+
+		100% {
+			transform: rotate(720deg);
+		}
+	}
+
+	.main_cricle img {
+		width: 100%;
+		height: 100%;
+	}
+
+	.main_bottom {
+		margin-top: 30rpx;
+		overflow: hidden;
+		text-align: center;
+		font-size: 28rpx;
+		color: #333;
+	}
+
+	.bottom_main {
+		margin-top: 1rem;
+		display: flex;
+		overflow: hidden;
+	}
+
+	.bottom_main_list {
+		text-align: center;
+		flex-grow: 1;
+		font-size: 28rpx;
+		color: #333;
+	}
+
+	.bottom_main_list img {
+		width: 2.5rem;
+		margin-bottom: 10rpx;
+	}
+
+
+
+	.uer-camera {
+		position: fixed;
+		top: 200rpx;
+		right: 42%;
+		color: #fff;
+		text-align: center;
+		font-size: 24rpx;
+		padding: 6rpx 8rpx 8rpx 6rpx;
+		border-radius: 24rpx;
+		height: 88rpx;
+		width: 88rpx;
+		overflow: hidden;
+	}
+
+	.uer-camera img {
+		width: 100%;
+		height: 100%;
+	}
+}
+</style>

+ 89 - 0
components/tq-maintenance-type/index.vue

@@ -0,0 +1,89 @@
+<template>
+    <xm-cascader 
+		ref="cascaderRef"
+		v-model="value" 
+		:options="list" 
+		:checkStrictly="checkStrictly" 
+		@input="handleInput" 
+		:showAllLevels="true"
+		@confirm="handleConfirm"
+		:props="{
+			value: 'id',
+			label: 'mtTypeName',
+			children: 'children'
+		}"
+	>
+	</xm-cascader>
+</template>
+
+<script>
+import { mtTypeTreeList} from "@/api/common/system.js";
+import {clearProps,copyProps,toTree} from '@/utils/gdtq.js'
+
+export default {
+    props: {
+
+        checkStrictly: { //传入此参数columns无效
+            type: Boolean,
+            default: true,
+        },
+		
+    },
+
+    data() {
+        return {
+			value: '',
+            list: [
+				/* {
+                    "code": 1,
+                    "areaName": "Parent 1",
+                    "parentId": 0,
+                    "child": [{
+                            "code": 2,
+                            "areaName": "Child 1.1",
+                            "parentId": 1,
+                            "child": []
+                        },
+                        {
+                            "code": 3,
+                            "areaName": "Child 1.2",
+                            "parentId": 1,
+                            "child": []
+                        }
+                    ]
+                }, */
+			],
+        }
+    },
+	
+    created() {
+       this.initOptions()
+    },
+    methods: {
+		async initOptions() {
+			
+			let { data } = await mtTypeTreeList({});
+			this.list = data
+			 // this.list = toTree(data, "id", "parentId");
+		},
+		handleInput(e) {
+			// console.log(e,'eeee')
+		},
+		
+        show() {
+			this.$refs.cascaderRef.openDept()
+        },
+
+        close() {
+			this.$refs.cascaderRef.close()
+        },
+
+        handleConfirm(e) {
+            this.$emit('confirm', e)
+        },
+
+    }
+}
+</script>
+
+<style lang="scss" scoped></style>

+ 113 - 0
components/tq-rate-popup/index.vue

@@ -0,0 +1,113 @@
+<template>
+    <u-popup :show="isShow" @close="close" @open="open">
+        <view class="rate-con">
+            <view class="title">{{ title }}</view>
+            <view class="rate-item">
+                <view>评分:</view>
+                <u-rate :activeColor="activeColor" :count="count" v-model="rateNum"></u-rate>
+            </view>
+            <view class="textarea-box">
+                <u--textarea v-model="textarea" :placeholder="placeholder"></u--textarea>
+            </view>
+            <view class="operate-box">
+                <u-button text="取消" @click="close"></u-button>
+                <u-button type="primary" @click="confirm" text="确定"></u-button>
+            </view>
+        </view>
+    </u-popup>
+</template>
+
+<script>
+
+export default {
+    props: {
+        title: { //标题
+            type: String,
+            default: '评语'
+        },
+        activeColor: { //星星颜色
+            type: String,
+            default: '#FA3534'
+        },
+        count: { //最多星星数
+            type: String | Number,
+            default: 5
+        },
+        rateNumProp: { //默认分数
+            type: String | Number,
+            default: 0
+        },
+        placeholder: {
+            type: String,
+            default: '请输入您的评价!'
+        },
+        columns: {
+            type: Array,
+            default: () => []
+        },
+    },
+
+    data() {
+        return {
+            isShow: false,
+            rateNum: 0,
+            textarea: '',
+            item: {}
+        }
+    },
+
+    methods: {
+        open() {
+            console.log('open');
+        },
+        show(item) {
+            this.rateNum = this.rateNumProp
+            this.textarea = ''
+            this.item = item
+            this.isShow = true;
+        },
+        close() {
+            this.isShow = false
+        },
+        confirm() {
+            let { rateNum, textarea } = this
+            this.$emit('confirm', {
+                rateNum, textarea, item: this.item
+            })
+            this.close()
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+.rate-con {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    padding: 10rpx 20rpx 20rpx 20rpx;
+
+    .title {
+        padding: 10rpx 0;
+    }
+
+    .rate-item {
+        margin-top: 20rpx;
+        width: 100%;
+        padding-left: 20rpx;
+        display: flex;
+    }
+
+    .textarea-box {
+        width: 100%;
+        margin-top: 10rpx;
+        padding: 10rpx;
+        box-sizing: border-box;
+    }
+
+    .operate-box {
+        width: 100%;
+        display: flex;
+    }
+}
+</style>

+ 341 - 0
components/tq-search-select/index.vue

@@ -0,0 +1,341 @@
+<template>
+   <u-popup :show="isShow" @close="close" :closeOnClickOverlay="false">	
+		<view class="container">
+			<view class="search">
+				<u-row>
+					<u-col :span="2" v-if="multiple">
+						<view class="all-choosed">
+							<image
+								@click="handleChooseAll"
+								:src="allChecked?'/static/icon/choosed.png':'/static/icon/nochoose.png'" 
+								class="choosed-image">
+							</image>
+							全选
+						</view>
+					</u-col>
+					<u-col :span="!multiple?12:10">
+						<u-search
+							v-model="search.text"
+							placeholder="关键字搜索" 
+							@search="handleSearch"
+							@custom="handleSearch"
+						>
+						</u-search>
+					</u-col>
+				</u-row>
+
+			</view>
+			<view class="last">
+				<u-button type="primary" :plain="true" :disabled="page==1" @click="handleLast">
+					<u-icon name="arrow-up" color="#2979ff" size="28"></u-icon>
+				</u-button>
+			</view>
+			<view class="list">
+				<template v-for="(item,index) in showList">
+					<view class="item" :key="index" 
+						@click="handleClick(item)"
+					>
+						<image 
+							v-if="multiple"
+							:src="item.checked?'/static/icon/choosed.png':'/static/icon/nochoose.png'" 
+							class="choosed-image">
+						</image>
+						<view>
+							<view class="item-value"
+								:class="{
+									'is-disabled': renderItem(item,'disabled')
+								}"
+							>
+								{{item[objMap.label]}}<text v-if="hasTip && !isNull(renderItem(item,'tip'))">{{renderItem(item,'tip')}}</text>
+							</view>
+						</view>
+						
+					</view>
+				</template>
+				
+				<view class="empty" v-if="showList.length==0">暂无数据</view>
+			</view>
+			<view class="next">
+				<u-button type="primary" :plain="true" :disabled="page==totalPage" @click="handleNext">
+					<u-icon name="arrow-down" color="#2979ff" size="28"></u-icon>
+				</u-button>
+			</view>
+			
+			<view class="page-footer" v-if="multiple">
+				<u-row style="">
+					<u-col :span="6">
+						<u-button type="info" shape="" :hairline="false" :custom-style="{
+								width: '100%',
+								borderRadius: '0',
+								background: '#FFF',
+								height: '100rpx',
+								fontSize: '32rpx',
+								fontWeight: '500',
+							  }" @click="handleClose">取消
+						</u-button>
+					</u-col>	
+					<u-col :span="6">
+						<u-button type="primary" shape="" :hairline="false" :custom-style="{
+								width: '100%',
+								borderRadius: '0',
+								height: '100rpx',
+								fontSize: '32rpx',
+								fontWeight: '500',
+								background:'linear-gradient(to right, #41B5FF, #4573FC)',
+							  }" @click="handleSubmit">确认
+						</u-button>
+					</u-col>
+				</u-row>
+				
+			</view>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+import {isArray,clone } from 'lodash'
+import {getImages} from '@/plugins/images'
+
+export default {
+	props: {
+		//单页展示数据量
+		size: {
+			type: Number,
+			default: 30
+		},
+		// 多选
+		multiple: {
+			type: Boolean,
+			default: false
+		},
+		dataList: {
+			type: Array,
+			default: () => []
+		},
+		objMap: {
+			type: Object,
+			default: () => {
+				return {
+					label: 'label',
+					value: 'value',
+				}
+			}
+		},
+		hasTip: {
+			type: Boolean,
+			default: false
+		},
+	},
+    data() {
+        return {
+			allChecked: false,
+           isShow: false,
+		   loadding: false,
+		   
+		   page: 1,
+		   total: 0,
+		   totalPage: 0,
+		   search: {
+			   text: ''
+		   },
+		   
+		   orginList: [],
+		   resultList: [], //搜索结果集合
+		   showList: [], 
+
+        }
+    },
+	mounted() {
+		
+	},
+	
+	watch: {
+		dataList: {
+			immediate: true,
+			deep: true,
+			handler(newVal) {
+				this.getData()
+			}
+		}
+	},
+	
+    methods: {
+		
+		show() {
+			this.isShow= true;
+		},
+		handleChooseAll() {
+			this.allChecked = !this.allChecked
+			this.resultList.map(e => {
+				if(!this.renderItem(e,'disabled')) e.checked = this.allChecked
+				
+			}) 
+		},
+		
+		
+		computeCheckedData() {
+			let allLength = this.resultList.filter((e) => !this.renderItem(e,'disabled')).length
+			let checedLength = this.resultList.filter((e) => e.checked).length
+			if(allLength == checedLength) {
+				this.allChecked = true
+			} else {
+				this.allChecked = false
+			}
+		},
+			
+		getChoosed() {
+			let data = this.orginList.filter((e) => e.checked)
+			this.$emit('confirm',data)
+		},	
+		
+		handleClick(e) {
+			if(this.renderItem(e,'disabled')) return
+			if(this.multiple) {
+				e.checked=!e.checked
+				this.computeCheckedData()	
+			} else {
+				console.log(e,'e')
+				this.$emit('confirm',e);
+				this.isShow = false;
+			}			
+		},
+		
+		handleSearch() {
+			this.page = 1
+			if(this.search.text) {
+				this.resultList = this.orginList.filter((e) => e[this.objMap.label].includes(this.search.text))
+			} else {
+				this.resultList = clone(this.orginList)
+			}
+			this.computeCheckedData()
+			this.totalPage = Math.ceil(this.resultList.length / this.size)
+			this.handlePageData()	
+		},
+		//上一页
+		handleLast() {
+			if(this.page != 1) this.page -= 1;
+			this.handlePageData()
+		},
+		//下一页
+		handleNext() {
+			if(this.page != this.totalPage) this.page += 1;	
+			this.handlePageData()
+		},
+		//处理分页数据
+		handlePageData() {
+			let index = (this.page - 1) * this.size
+			this.showList = clone(this.resultList).splice(index,this.size)
+		},
+        getData() {
+			
+			let that = this;
+			if(isArray(this.dataList)) {
+				this.dataList.forEach((item) => {			
+					let obj = {
+						...item,
+						checked: false,
+					}			
+					this.orginList.push(obj);
+				})				
+				this.$emit('onDataLoad',this.orginList)
+				this.handleSearch()
+				
+			} else {
+				this.orginList = [];
+				this.showList = [];
+				this.page = 1
+				this.totalPage = 0
+			}
+			
+	   },
+	   
+		close() {
+			this.isShow = false
+			this.allChecked = true
+			this.handleChooseAll()	
+		},
+		
+		handleClose() {
+			 this.close()	
+		},
+		handleSubmit() {
+			if(this.multiple) this.getChoosed()
+			this.close()
+		},
+
+    }
+
+}
+</script>
+
+<style lang="scss" scoped>
+	
+	/* 内容satrt */
+	.container {
+		background: #fff;
+		.search {
+			padding: 20rpx 40rpx 20rpx 40rpx;
+			
+			.all-choosed {
+				display: flex;
+				justify-content: flex-start;
+				align-items: center;
+				font-size: 24rpx;
+			}
+		}
+		.list {
+			height: calc(100vh - 540rpx);
+			padding: 0 20rpx;
+			overflow-y: auto;
+			
+			.item {
+				padding: 20rpx 20rpx;
+				display: flex;
+				align-items: center;				
+				 
+				.item-value {
+					font-size: 24rpx;
+				}
+				.item-label {
+					font-size: 24rpx;
+				}
+				.is-disabled {
+					color: #909193;
+				}
+				// border-bottom: 2rpx solid #ddd;
+			}
+			.empty {
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				height: calc(100vh - 400rpx);
+				color: #909193;
+				min-height: 800rpx;
+			}
+		}
+		
+		.last,.next {
+			margin: 10rpx 40rpx;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+		}
+		
+		.page-footer {
+			// position: absolute;
+			width: 100%;
+			left: 0;
+			bottom: 0;
+		}
+	
+	}
+	
+	/* 内容end */
+
+	
+	.choosed-image {
+		width: 40rpx;
+		height: 40rpx;
+		margin-right: 10rpx;
+	}
+</style>

+ 96 - 0
components/tq-select/index.vue

@@ -0,0 +1,96 @@
+<template>
+    <u-picker
+        ref="pickerRef"
+        :keyName="keyName"
+        :show="isShow"
+        :columns="[xhColumns]"
+        @confirm="confirm"
+        @cancel="close"></u-picker>
+</template>
+
+<script>
+import { dropDown } from "@/api/vehicle/common";
+/* https://uviewui.com/components/picker.html */
+
+export default {
+    props: {
+
+        //自定义需要展示的text属性键名
+        keyName: {
+            type: String,
+            default: 'label'
+        },
+        columns: {
+            type: Array,
+            default: () => []
+        },
+        api: { //传入此参数columns无效
+            type: String,
+            default: ''
+        },
+		workTypeId: { //
+            type: String,
+            default: ''
+        },
+    },
+	
+	watch: {
+		workTypeId: {
+			immediate: false,
+			deep: true,
+			handler(newVal) {
+				if(this.api == 'dropDown') this.getData()
+			}
+		}
+	},
+
+    data() {
+        return {
+            isShow: false,
+            xhColumns: []
+        }
+    },
+    created() {
+        if (this.api) {
+            this.getData()
+        } else {
+            this.xhColumns = this.columns
+        }
+    },
+    methods: {
+        getData() {
+            let getApiMap = {
+                'dropDown': async () => {
+                    let { data } = await dropDown({workTypeId: this.workTypeId})
+					
+                    this.xhColumns = data.map(e => {
+                        return {
+                            label: e.vehTypeName,
+                            value: e.vehTypeId
+                        }
+                    })
+					// console.log( this.xhColumns,' this.xhColumns')
+                },
+            }
+            getApiMap[this.api]()
+        },
+        show() {
+            this.isShow = true;
+        },
+
+        close() {
+            this.isShow = false
+        },
+
+        confirm(e) {
+            const { value } = e
+            this.$emit('confirm', value[0])
+            this.close()
+        },
+
+
+    }
+}
+</script>
+
+<style lang="scss" scoped></style>

+ 75 - 0
components/tq-tab/index.vue

@@ -0,0 +1,75 @@
+<template>
+    <view class="tq-tab-contaienr" :class="classType">
+		<slot></slot>
+	</view>
+</template>
+
+<script>
+
+export default {
+    props: {
+        //样式类型
+        classType: {
+            type: Object | String,
+            default: function () {
+				return {}
+			}
+        },  
+    },
+	
+
+    data() {
+        return {
+            
+        }
+    },
+    created() {
+       
+    },
+    methods: {
+        
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+	
+.tq-tab-contaienr {
+	height: 40rpx;
+	min-width: 100rpx;
+	display: inline-block;
+	
+	font-family: PingFangSC, PingFang SC;
+	border-radius: 4rpx;
+	overflow: hidden;
+	line-height: 40rpx;
+	font-style: normal;
+	text-align: center;
+	font-size: 22rpx;
+	padding: 2rpx 10rpx;
+}
+
+.blue {
+	color: #4573FC;
+	background-color: rgba(69, 115, 252, .2);
+}
+.green {
+	color: #38B42F;
+	background-color: rgba(56, 180, 47, .2);
+}
+
+.red {
+	color: #E44C4C;
+	background-color: rgba(228, 76, 76, .2);
+}
+.orange {
+	color: #F7A254;
+	background-color: rgba(247, 162, 84, .2);
+}
+
+</style>
+<style lang="scss" >
+	.tq-tab-contaienr + .tq-tab-contaienr {
+		margin-right: 10rpx;
+	}
+</style>

+ 257 - 0
components/tq-upload-img/index.vue

@@ -0,0 +1,257 @@
+<template>
+    <view>
+        <u-upload
+            ref="uploadRef"
+            name="Img"
+            :multiple="true"
+            :previewFullImage="true"
+            :maxCount="maxCount"
+            :fileList="fileListImg"
+            :sizeType="['compressed']"
+            :width="width"
+            :height="height"
+            :capture="capture"
+            :uploadText="uploadText"
+            @beforeRead="beforeRead"
+            @afterRead="afterRead"
+            @delete="deletePic"></u-upload>
+    </view>
+</template>
+
+<script>
+/* https://uviewui.com/components/upload.html */
+import { isEmpty,isArray,isString } from 'lodash';
+import * as imageConversion from 'image-conversion'
+import { getToken } from '@/utils/auth'
+import { uploadUrl } from '@/api/common/system.js'
+import {server_url} from '@/utils/config.js'
+import {createWaterMark} from '@/utils/watermark'
+/**
+* blob转url临时访问地址
+* @param String blob 对象
+*/
+
+function createObjectURL(blob){
+	return URL.createObjectURL(blob);
+}
+
+
+
+export default {
+	props: {
+		value: {
+			type: String,
+			default: ''
+		},
+		maxCount: {
+			type: String | Number,
+			default: 6
+		},
+		sizeType: { //original 原图,compressed 压缩图,H5无效
+			type: Array,
+			default: () => ['compressed'],
+		},
+		//开启h5压缩
+		h5Compressed: {
+			type: Boolean,
+			default: true
+		},
+		//0.01-1压缩质量 1-N 压缩大小
+		h5CompressedSize: {
+			type: Number,
+			default: 0.8
+		},
+		width: {
+			type: String | Number,
+			default: 68,
+		},
+		height: {
+			type: String | Number,
+			default: 68,
+		},
+		capture: {
+			type: String | Array,
+			default: () => ['album', 'camera'],
+		},
+		uploadText: {
+			type: String,
+			default: '请上传图片'
+		},
+		querParams: {
+			type: Object,
+			default: () => {
+				return {}
+			}
+		},
+		
+		//开启用户信息水印
+		userInfoWaterMark: {
+			type: Boolean,
+			default: false
+		},
+		
+	},
+    data() {
+        return {
+            fileListImg: [],
+        }
+    },
+	computed: {
+		userName: function() {
+		      return this.$store.getters.name
+		}
+	},
+	watch: {
+		value: {
+			immediate: true,
+			deep: true,
+			handler(newVal) {
+					console.log(newVal,'newVal')
+				let list = []
+				if (isString(newVal) && !isEmpty(newVal)) {
+					list = newVal.split(',').map((url) => {
+						let thumb = this.$getImages(url)
+						
+						// console.log(thumb,'thumb--url')
+						return {
+							status: 'success',
+							message: '上传成功',
+							url: thumb,
+							thumb: thumb,
+							value: url,
+						}
+					 
+					})
+					
+				} 
+			
+				this.fileListImg = list
+			},
+		},
+		fileListImg: {
+			immediate: false,
+			deep: true,
+			handler(newVal) {
+				// console.log(newVal,'newVal-fileListImg')
+				let newUrl = newVal.map(e => e.value).join(',')
+				this.$emit('input',newUrl)
+				this.$emit('confirm',newUrl)
+			}
+		}
+	},
+    methods: {
+        // 删除图片
+        deletePic(event) {
+            this[`fileList${event.name}`].splice(event.index, 1)
+        },
+		
+        // 读取后的处理函数
+        async afterRead(event) {
+			
+			let time = this.dayjs().format('YYYY-MM-DD HH:mm:ss')
+			
+			let watermarks = [time,this.userName]
+			
+            // 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
+            let lists = [].concat(event.file)
+            let fileListLen = this[`fileList${event.name}`].length
+            lists.map((item) => {
+                this[`fileList${event.name}`].push({
+                    ...item,
+                    status: 'uploading',
+                    message: '上传中'
+                })
+            })
+
+			
+            for (let i = 0; i < lists.length; i++) {
+				
+				// #ifdef H5
+				
+				if(this.h5Compressed) {
+					//图片压缩
+					if(lists[i].type == 'image') {
+						
+						let blob = await imageConversion.urltoBlob(lists[i].url)	
+						console.log(blob,'压缩前blobd对象')
+						if(blob) {
+							let res
+							//压缩大小
+							if (this.h5CompressedSize>1) {
+								res = await imageConversion.compressAccurately(blob,this.h5CompressedSize)
+							} else { //压缩质量
+								res = await imageConversion.compress(blob,this.h5CompressedSize)
+							}
+							if(res) {
+								console.log(res,'压缩后blob对象')	
+								let newBlobUrl = createObjectURL(res)
+								lists[i].thumb = lists[i].url = newBlobUrl
+								lists[i].size = res.size
+								
+							}
+						}
+					}
+					
+				}
+				
+				// #endif
+				if(lists[i].type == 'image') {
+					if(this.userInfoWaterMark) {
+						
+						lists[i].thumb = lists[i].url = await createWaterMark(lists[i].url,watermarks)
+					}
+				}
+				
+				
+               const res = await this.uploadFilePromise(lists[i].url)
+				let item = this[`fileList${event.name}`][fileListLen]
+				
+				if (res.code == 0) {			
+					this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
+					    status: 'success',
+					    message: '上传成功',
+					    value: res.fileName,
+					}))
+					fileListLen++
+
+				} else {
+					this[`fileList${event.name}`].splice(fileListLen, 1,)
+				}             
+            }
+			
+        },
+		
+        uploadFilePromise(url) {
+            return new Promise((resolve, reject) => {
+				// console.log(url,'urlurl')
+                let a = uni.uploadFile({
+                    url: uploadUrl, // 仅为示例,非真实的接口地址
+					header: {
+						'Authorization': 'Bearer ' + getToken()
+					},
+                    filePath: url,
+                    name: 'file',
+                    formData: {
+                        ...this.querParams,
+                    },
+                    success: (res) => {			
+						let data = JSON.parse(res.data)
+						resolve(data)
+                    },
+					fail: (err) => {
+						resolve({
+							code: -1,
+							msg: '上传失败'
+						})
+					}
+                });
+            })
+        },
+    }
+
+}
+</script>
+
+<style lang="scss" scoped>
+	
+</style>

+ 88 - 0
components/tq-work-type/index.vue

@@ -0,0 +1,88 @@
+<template>
+    <xm-cascader 
+		ref="cascaderRef"
+		v-model="value" 
+		:options="list" 
+		:checkStrictly="checkStrictly" 
+		@input="handleInput" 
+		:showAllLevels="true"
+		@confirm="handleConfirm"
+		:props="{
+			value: 'id',
+			label: 'name',
+			children: 'children'
+		}"
+	>
+	</xm-cascader>
+</template>
+
+<script>
+import { dropDownList} from "@/api/common/system.js";
+import {clearProps,copyProps,toTree} from '@/utils/gdtq.js'
+
+export default {
+    props: {
+
+        checkStrictly: { //传入此参数columns无效
+            type: Boolean,
+            default: true,
+        },
+		
+    },
+
+    data() {
+        return {
+			value: '',
+            list: [
+				/* {
+                    "code": 1,
+                    "areaName": "Parent 1",
+                    "parentId": 0,
+                    "child": [{
+                            "code": 2,
+                            "areaName": "Child 1.1",
+                            "parentId": 1,
+                            "child": []
+                        },
+                        {
+                            "code": 3,
+                            "areaName": "Child 1.2",
+                            "parentId": 1,
+                            "child": []
+                        }
+                    ]
+                }, */
+			],
+        }
+    },
+	
+    created() {
+       this.initOptions()
+    },
+    methods: {
+		async initOptions() {
+			
+			let { data } = await dropDownList({});
+			 this.list = toTree(data, "id", "parentId");
+		},
+		handleInput(e) {
+			// console.log(e,'eeee')
+		},
+		
+        show() {
+			this.$refs.cascaderRef.openDept()
+        },
+
+        close() {
+			this.$refs.cascaderRef.close()
+        },
+
+        handleConfirm(e) {
+            this.$emit('confirm', e)
+        },
+
+    }
+}
+</script>
+
+<style lang="scss" scoped></style>

+ 89 - 0
components/uploadImg/index.vue

@@ -0,0 +1,89 @@
+<!--
+ * @Author: yangpeiqin
+ * @Date: 2023-12-15 16:57:35
+ * @LastEditors: yangpeiqin
+ * @LastEditTime: 2023-12-15 17:30:29
+ * @FilePath: \gdtq_admin_app移动端\components\uploadImg\index.vue
+-->
+<template>
+    <view>
+        <u-upload
+            ref="uploadFile"
+            :name="name"
+            :multiple="true"
+            :maxCount="maxCount"
+            :fileList="fileList1"
+            :sizeType="['compressed']"
+            :width="width"
+            :height="height"
+            :capture="capture"
+            :uploadText="uploadText"
+            @afterRead="afterRead"
+            @delete="deletePic"></u-upload>
+    </view>
+</template>
+
+<script>
+import { isEmpty } from 'lodash';
+import * as imageConversion from 'image-conversion';
+import { getToken } from '@/utils/auth'
+
+export default {
+    data() {
+        return {
+            fileList1: [],
+        }
+    },
+    methods: {
+        // 删除图片
+        deletePic(event) {
+            this[`fileList${event.name}`].splice(event.index, 1)
+        },
+        // 新增图片
+        async afterRead(event) {
+            // 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
+            let lists = [].concat(event.file)
+            let fileListLen = this[`fileList${event.name}`].length
+            lists.map((item) => {
+                this[`fileList${event.name}`].push({
+                    ...item,
+                    status: 'uploading',
+                    message: '上传中'
+                })
+            })
+            for (let i = 0; i < lists.length; i++) {
+                const result = await this.uploadFilePromise(lists[i].url)
+                let item = this[`fileList${event.name}`][fileListLen]
+                this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
+                    status: 'success',
+                    message: '',
+                    url: result
+                }))
+                fileListLen++
+            }
+        },
+        uploadFilePromise(url) {
+            return new Promise((resolve, reject) => {
+                let a = uni.uploadFile({
+                    url: 'http://192.168.2.21:7001/upload', // 仅为示例,非真实的接口地址
+                    filePath: url,
+                    name: 'file',
+                    formData: {
+                        user: 'test'
+                    },
+                    success: (res) => {
+                        setTimeout(() => {
+                            resolve(res.data.data)
+                        }, 1000)
+                    }
+                });
+            })
+        },
+    }
+
+}
+</script>
+
+<style lang="scss" scoped>
+	
+</style>

+ 9 - 0
env.js

@@ -0,0 +1,9 @@
+let env = {
+	// 平台类型 enterprises-企事业   sanitation--环卫 mine--矿山 gfys--固废运输
+	"PLATFORM": "enterprises",
+	// 托管平台类型 cus-独立部署   dingH5--钉钉第三方应用h5平台 dingH5Ins--钉钉H5内部应用 wxH5--微信公众号h5平台 wxworkH5--企业微信h5平台
+	"HOSTPLATFORM": "cus", 
+}
+
+// export default env
+module.exports = env

+ 23 - 0
index.html

@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+	<head>
+		<meta charset="utf-8">
+		<meta http-equiv="X-UA-Compatible" content="IE=edge">
+		<title>
+			<%= htmlWebpackPlugin.options.title %>
+		</title>
+		
+		<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>Please enable JavaScript to continue.</strong>
+		</noscript>
+		<div id="app"></div>
+		<!-- built files will be auto injected -->
+	</body>
+</html>

+ 157 - 0
learnMobile/materialList/details.vue

@@ -0,0 +1,157 @@
+<template>
+    <view class="pageContainer">
+        <ss-preview :fileUrl="fileUrl" v-if="fileUrl" :fileType="fileType" :imageList="imageList"></ss-preview>
+        <view class="countDownTime" v-show="countDownTime">剩余时间:{{ countDownTime }}s</view>
+		
+		<!-- 人脸校验 -->
+		<tq-face ref="faceRef" 
+			@success="faceSuccess"
+			@close="faceClose"
+		></tq-face>
+    </view>
+</template>
+<script>
+import ssPreview from '@/uni_modules/ss-preview/components/ss-preview/ss-preview.vue'
+
+import { materialDetail, saveUserMaterial } from "@/api/learnMobile/index";
+let deepSetInterval;
+let countDownInterval;
+export default {
+    components: {
+        ssPreview
+    },
+    data() {
+        return {
+            fileType: '2', //类型(1.预览图片,2.预览文件,3.预览视频)
+            showPreview: false,
+            id: '',
+            fileUrl: '',
+            imageList: [],
+            startTime: '',
+            endTime: '',
+            countDownTime: '',
+            recordId: '',
+			
+			isValidFace: false, //是否检验人脸
+			lastTime: '', //人脸校验前剩余时间
+			rdTime: '',//触发人脸时间点
+        }
+    },
+    onLoad(option) {
+        this.id = option.id
+        console.log('参数', option)
+        this.startTime = this.dayjs().format('YYYY-MM-DD HH:mm:ss')
+        this.getData();
+    },
+    //页面销毁事件
+    onUnload() {
+        console.log('销毁')
+        clearInterval(deepSetInterval)
+        clearInterval(countDownInterval)
+    },
+    methods: {
+		
+		getRandomInteger(min, max) {
+		  return Math.floor(Math.random() * (max - min + 1)) + min;
+		},
+		
+		randomTime(time) {
+			let max = time-1
+			this.rdTime = this.getRandomInteger(1,max)
+		},
+		
+        async getData() {
+            let { data, code } = await materialDetail({
+                id: this.id
+            })
+            if (code == '0') {
+                if (data.type === 1) {
+                    this.fileType = '2'
+                }
+                if (data.type === 2) {
+                    this.fileType = '3'
+                }
+				this.randomTime(data.learnTime)
+                this.countDown(data.learnTime)
+                this.fileUrl = this.$getImages(data.annexUrl)
+            }
+        },
+        /**倒计时 */
+        countDown(time) {
+            countDownInterval = setInterval(() => {
+				if(time == this.rdTime && !this.isValidFace) {
+					this.lastTime = time
+					clearInterval(countDownInterval)
+					this.openFace()
+					return
+				}
+                time--
+                this.countDownTime = time
+                if (time <= 0) {
+                    clearInterval(countDownInterval)
+                    this.countDownTime = 0
+                    this.endTime = this.dayjs().format('YYYY-MM-DD HH:mm:ss')
+                    this.saveLearn()
+                }
+            }, 1000)
+        },
+        /**保存学习记录 */
+        async saveLearn() {
+            let { code, data } = await saveUserMaterial({
+                startTime: this.startTime,
+                endTime: this.dayjs().format('YYYY-MM-DD HH:mm:ss'),//this.endTime,
+                materialId: this.id,
+                recordId: this.recordId
+            })
+            if (code == '0') {
+                // uni.showToast({
+                //     title: '学习成功'
+                // })
+                this.recordId = data.recordId
+                if (!deepSetInterval) {
+                    this.deepSaveLearn()
+                }
+            }
+        },
+        deepSaveLearn() {
+            if (deepSetInterval) clearInterval(deepSetInterval);
+            deepSetInterval = setInterval(e => {
+                this.saveLearn()
+            }, 5000)
+        },
+		openFace() {
+			this.$refs.faceRef.show()
+		},
+		faceClose() {
+			uni.navigateBack()
+		},
+		faceSuccess() {
+			this.isValidFace = true
+			this.countDown(this.lastTime)
+		}
+    },
+}
+
+</script>
+<style lang="scss" scoped>
+.pageContainer {
+    height: 100%;
+
+    .nodata-image {
+        height: 500px;
+        width: 100%;
+    }
+
+    .countDownTime {
+        position: fixed;
+        z-index: 999;
+        // width: 400rpx;
+        bottom: 0;
+        left: 0;
+        background: rgba(255, 255, 255, .4);
+        color: rgba(0, 0, 0, .6);
+        border-radius: 4rpx;
+        padding: 4rpx 18rpx;
+    }
+}
+</style>

+ 392 - 0
learnMobile/materialList/materialList.vue

@@ -0,0 +1,392 @@
+<template>
+    <view class="registrationRecord-box">
+        <!-- 头部筛选 -->
+        <view class="page-head">
+
+            <u-tabs :list="tablist"
+                :itemStyle="{
+                    width: '330rpx',
+                    height: '80rpx'
+                }"
+                :activeStyle="{
+                    color: '#4573FC'
+                }"
+                lineWidth="40"
+                lineColor="#4573FC"
+                @click="({ index }) => {
+                    curNow = index
+                    anewQueryMyList()
+                }">
+            </u-tabs>
+
+
+            <u-row>
+                <u-col :span="12" style="padding: 16rpx 24rpx;">
+                    <view>
+                        <view class="input-wapper">
+                            <u--input
+                                v-model="form.queryParam"
+                                :disabled="false"
+                                :clearable="true"
+                                placeholder="请输入名称"
+                                prefixIcon="search"
+                                shape="circle"
+                                prefixIconStyle="font-size: 22px;color: #909399"
+                                inputAlign="center"
+                                fontSize="28rpx"
+                                disabledColor="#F2F2F2"
+                                border="none"
+                                :customStyle="{
+                                    height: '62rpx',
+                                    padding: '4rpx 28rpx',
+                                    background: '#F2F2F2'
+                                }"
+                                @confirm="anewQueryMyList">
+                            </u--input>
+                        </view>
+                    </view>
+                </u-col>
+
+            </u-row>
+        </view>
+        <view class="box-container" >
+            <!-- @click="handleDetaisl(item)" -->
+            <view class="box-body" @click="handleDetaisl(item)" v-for="(item, index) in queryMyListdata" :key="index">
+                <u-row customStyle="margin-bottom: 10px">
+                    <u-col span="8">
+                        <view class="left">
+                            <view class="name">{{item.name}}</view>
+                            <view class="status">
+                                <u-tag text="未学习" plain size="mini" v-if="item.learnStatus===0" type="error"></u-tag>
+                                <u-tag text="已学习" plain size="mini" v-if="item.learnStatus===1" type="success"></u-tag>
+                                <view class="time">{{item.updateTime}}</view>
+                            </view>
+                        </view>
+                    </u-col>
+                    <u-col span="4">
+                        <image class="nodata-image" mode="aspectFit" :src="$getImages(item.coverUrl)"  ></image>
+                    </u-col>
+                </u-row>
+            </view>
+        </view>
+        <view class="nodata-warp" v-if="!queryMyListdata.length">
+					  <image :src="$getImages('/assetsMobile/images/no-data.png')" class="nodata-image"></image>
+					</view>
+        <u-loadmore :status="status" />
+        <u-toast ref="uToast" />
+        <u-calendar :show="calendarShow" mode="range" :defaultDate="defaultDate" :maxDate="maxDate" minDate="2023-10-01"
+            monthNum="24" @confirm="calendarChange" @close="calendarShow = false"></u-calendar>
+        <!-- 评价 -->
+        <!-- <tq-rate-popup ref="ratePopupRef" @confirm="(e) => { postEvaluate(e) }"></tq-rate-popup> -->
+    </view>
+</template>
+<script>
+import { materialList } from "@/api/learnMobile/index";
+
+export default {
+    data() {
+        return {
+            calendarShow: false, // 日历
+            calendarStart: this.dayjs().subtract(6, 'day').format('YYYY-MM-DD'),// 开始时间
+            calendarEnd: this.dayjs().subtract(0, 'day').format('YYYY-MM-DD'), // 结束时间
+            defaultDate: [this.dayjs().subtract(6, 'day').format('YYYY-MM-DD'), this.dayjs().subtract(0, 'day').format('YYYY-MM-DD')],
+            maxDate: this.dayjs().add(365, 'day').format('YYYY-MM-DD'),
+            tablist: [
+                { name: '未学习' },
+                { name: '已学习' },
+            ],
+            curNow: 0,
+            queryMyListdata: [],
+            form: {
+                queryParam: '',
+                // createTimeBegin: '',//开始时间
+                // createTimeEnd: '', //结束时间
+                // applyType: '',//用车类型(字典编码:matter_type)
+                // applyTypeName: '',
+                // flowStatus: '',//总经办部门领导审核状态:1.审核通过、2.审核不通过
+                // flowStatusName: '',
+            },
+            pageIndex: 1,
+            total: 0,
+            status: 'nomore' //loading nomore loadmore
+        }
+    },
+    onShow(option) {
+        this.queryMyListdata = []
+        this.pageIndex = 1
+        this.queryMyList();
+    },
+    methods: {
+        // http://tq.5000v.com:8025/workflow/mobile/dealTask?definitionId=system_cost:3:eb004fdf-4f0d-11ef-86fb-005056bb2bc2&category=undefined&taskId=&withDealTask=false&processInstanceId=61aef549-4f18-11ef-86fb-005056bb2bc2&fromMobile=true&authtoken=eyJhbGciOiJIUzUxMiJ9.eyJjbGllbnQiOiJUUS1IVy1NT0JJTEUiLCJsb2dpbl91c2VyX2tleSI6IjFkMTY1OGYzLThhOWMtNDM5Yy04Yzk3LTYwMjQyMDg2MzUyOCJ9.C0QYkgPxsjTPLOFTwrK6xjlzLLGMInzChQ7M1Rm6IupwLEI8HA1EK0HKm6y1IfWekmiybOyc3WSx838sRem-aQ
+        handleDetaisl({id}) {
+            uni.navigateTo({
+                url: `/learnMobile/materialList/details?id=`+id,
+            })
+        },
+        handleRestartWorkflow(instance) {
+            // http://localhost:9529/workflow/mobile/startFlow?id=system_cost%3A1%3A0cc5fb50-3dd3-11ef-80a8-000c29425992&processInstanceId=7455d6f9-4018-11ef-80a8-000c29425992&defaultTitle=%E8%B4%B9%E7%94%A8%E6%B5%81%E7%A8%8B_admin_20240712&name=%E9%87%8D%E6%96%B0%E5%8F%91%E8%B5%B7%E3%80%90%E8%B4%B9%E7%94%A8%E6%B5%81%E7%A8%8B%E3%80%91&restart=true
+            // instance = {
+            //     "procDefId": "system_cost:1:0cc5fb50-3dd3-11ef-80a8-000c29425992",
+            //     "companyName": "重新发起【费用流程】",
+            //     "id": "7455d6f9-4018-11ef-80a8-000c29425992"
+            // }
+            uni.navigateTo({
+                url: `/pages/flow/diboot?routes=/workflow/mobile/startFlow&id=${instance.procDefId}&processInstanceId=${instance.processInstanceId}&defaultTitle=${instance.companyName}&name=${instance.companyName}&restart=true`,
+            })
+        },
+        // handleTakeBack({ processInstanceId }) { //撤销
+        //     uni.showModal({
+        //         title: '',
+        //         content: `确认撤销?`,
+        //         showCancel: true,
+        //         confirmText: '确定',
+        //         success: async (res) => {
+        //             if (res.confirm) {
+        //                 let { code } = await recordtaskOperate({
+        //                     processInstanceId,
+        //                     taskOperate: 'cancel',
+        //                     validTask: false,
+        //                 })
+        //                 if (code == 0) {
+        //                     uni.showToast({
+        //                         title: '取消成功',
+        //                         icon: 'none',
+        //                     })
+        //                     this.anewQueryMyList()
+        //                 }
+        //             }
+        //         }
+        //     })
+        // },
+        calendarChange(e) {
+            console.info('选择的时间', e)
+
+            // 修改日期
+            this.form.createTimeBegin = e[0]; // 开始时间
+            this.form.createTimeEnd = e[e.length - 1]; // 结束时间
+            this.defaultDate = [e[0], e[e.length - 1]]
+            this.calendarShow = false;
+            this.anewQueryMyList();
+        },
+        anewQueryMyList() {
+            this.pageIndex = 1;
+            this.queryMyListdata = [];
+            this.queryMyList();
+        },
+        phoneCall(e) {
+            uni.makePhoneCall({
+                phoneNumber: e //仅为示例
+            });
+        },
+        pointAddrParse(e) {
+            let arr = JSON.parse(e)
+            let arrStg = ''
+            for (let item of arr) {
+                arrStg += item.address + '-'
+            }
+            return arrStg.slice(0, -1);
+        },
+        sectionChange(index) {
+            this.curNow = index;
+            this.anewQueryMyList()
+        },
+        toRealMap(item) {
+            let obj = {
+                routeId: item.routeId,
+                classId: item.classId,
+                startAndEnd: item.startAndEnd
+            }
+            let objst = encodeURIComponent(JSON.stringify(obj))
+            uni.navigateTo({
+                url: '/newdriversub/setClassPlan/details?item=' + objst
+            });
+        },
+        async queryMyList() {
+            this.loading = 'loading'
+            let { createTimeBegin, createTimeEnd } = this.form
+            let obj = {
+                ...this.form,
+                page: this.pageIndex,
+                size: 8,
+                // createTimeBegin: createTimeBegin ? createTimeBegin + ' 00:00:00' : '',
+                // createTimeEnd: createTimeEnd ? createTimeEnd + ' 23:59:59' : '',
+                learnStatus: this.curNow //查询类型:学习状态:0.未学习 1.已学习
+
+
+            }
+            for (let key in obj) {
+                if (obj[key] === '') delete obj[key]
+            }
+            let { data, code } = await materialList(obj)
+            if (code == "0") {
+
+                let {list,total} = data
+                this.queryMyListdata = this.queryMyListdata.concat(list)
+                this.total = Number(total)
+                this.loading = 'loadmore'
+                if (this.queryMyListdata.length >= this.total) {
+                    this.status = 'nomore'
+                }
+                console.log('数据', this.queryMyListdata)
+            }
+        },
+        // 操作
+        // async postEvaluate(e) {
+        //     let { data, code, msg } = await evaluate(
+        //         {
+        //             evaluate: e.textarea,
+        //             evaluateScore: e.rateNum,
+        //             systemFlowVehId: e.item.id, //任务id
+        //         })
+        //     if (code == "0") {
+        //         uni.showToast({
+        //             title: msg || '评价成功',
+        //             duration: 2000
+        //         });
+        //         this.anewQueryMyList();
+        //     }
+        // }
+
+    },
+    // 触底
+    onReachBottom() {
+        console.log('当前条数', this.queryMyListdata.length, '总条数', this.total)
+        if (this.queryMyListdata.length >= this.total) return;
+        this.pageIndex++
+        this.queryMyList();
+    }
+}
+</script>
+<style lang="scss" scoped>
+.registrationRecord-box {
+
+    padding: 0 0 10rpx 0;
+    overflow: unset;
+
+    .page-head {
+        background: #fff;
+    }
+
+    .bg-purple-light {
+        word-wrap: break-word;
+    }
+
+    .list-box {
+        border-radius: 10rpx;
+        background: #fff;
+        // padding: 20rpx;
+        position: sticky;
+
+        // align-items: center;
+        // display: flex;
+        top: 0px;
+        width: 100%;
+        z-index: 999;
+
+        /deep/ {
+
+            .u-form-item__body {
+                padding: 0;
+            }
+        }
+
+    }
+
+    .item-list-box {
+        padding: 10rpx 0 0 0;
+        // background: #fff;
+        position: relative;
+
+
+        .info-box {
+            margin-bottom: 20rpx;
+            border: none;
+            width: 100%;
+            // border-radius: 20rpx;
+            box-shadow: 2rpx 2rpx 5rpx rgba(169, 169, 169, 0.34901960784313724);
+            // height: 150rpx;
+            background-color: rgba(255, 255, 255, 1);
+            display: flex;
+            align-items: center;
+            flex-wrap: wrap;
+            padding: 20rpx;
+            box-sizing: border-box;
+            position: relative;
+
+            font-size: 24rpx;
+
+
+
+
+        }
+
+
+    }
+
+
+}
+
+.box-container {
+    background: #fff;
+    margin: 20rpx 10rpx;
+    border-radius: 10rpx;
+    font-size: 24rpx;
+
+    .box-head {
+        border-bottom: 1px solid #ddd;
+        padding: 20rpx;
+        font-weight: 700;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+    }
+
+    .box-body {
+        padding: 20rpx;
+        display: flex;
+        align-items: center;
+        position: relative;
+        // height: 180rpx;
+        .left {
+            display: flex;
+            flex-direction: column;
+            height: 140rpx;
+            justify-content: space-between;
+
+            .name {
+                font-size: 32rpx;
+            }
+            .status{
+                display: flex;
+                align-items: center;
+                .time{
+                    margin-left: 8rpx;
+                }
+            }
+        }
+        .nodata-image{
+            width: 100%;
+            height: 140rpx;
+        }
+    }
+
+    .box-footer {
+        border-top: 1px solid #ddd;
+        // padding: 20rpx;
+    }
+}
+
+.primary {
+    color: $uni-color-primary;
+}
+.nodata-warp{
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 100rpx 0;
+  .nodata-image{
+    width: 218rpx;
+    height: 150rpx;
+  }
+}
+</style>

+ 403 - 0
learnMobile/paperList/paperList.vue

@@ -0,0 +1,403 @@
+<template>
+    <view class="registrationRecord-box">
+        <!-- 头部筛选 -->
+        <view class="page-head">
+
+            <u-tabs :list="tablist"
+                :itemStyle="{
+                    width: '330rpx',
+                    height: '80rpx'
+                }"
+                :activeStyle="{
+                    color: '#4573FC'
+                }"
+                lineWidth="40"
+                lineColor="#4573FC"
+                @click="({ index }) => {
+                    curNow = index
+                    anewQueryMyList()
+                }">
+            </u-tabs>
+
+
+            <u-row>
+                <u-col :span="12" style="padding: 16rpx 24rpx;">
+                    <view>
+                        <view class="input-wapper">
+                            <u--input
+                                v-model="form.queryParam"
+                                :disabled="false"
+                                :clearable="true"
+                                placeholder="请输入名称"
+                                prefixIcon="search"
+                                shape="circle"
+                                prefixIconStyle="font-size: 22px;color: #909399"
+                                inputAlign="center"
+                                fontSize="28rpx"
+                                disabledColor="#F2F2F2"
+                                border="none"
+                                :customStyle="{
+                                    height: '62rpx',
+                                    padding: '4rpx 28rpx',
+                                    background: '#F2F2F2'
+                                }"
+                                @confirm="anewQueryMyList">
+                            </u--input>
+                        </view>
+                    </view>
+                </u-col>
+
+            </u-row>
+        </view>
+        <view class="box-container">
+            <!-- @click="handleDetaisl(item)" -->
+            <view class="box-body" @click="handleDetaisl(item)" v-for="(item, index) in queryMyListdata" :key="index">
+                <u-row customStyle="margin-bottom: 10px">
+                    <u-col span="10">
+                        <view class="left">
+                            <view class="name">{{ item.name }}</view>
+                            <view class="status">
+                                <u-tag text="未作答" plainFill size="mini" v-if="item.paperStatus === 0" type="error"></u-tag>
+                                <u-tag text="已作答" plainFill size="mini" v-if="item.paperStatus === 1" type="success"></u-tag>
+                                <view class="time">{{ item.updateTime }}</view>
+                            </view>
+                        </view>
+                    </u-col>
+                    <u-icon class="arrow-right" name="arrow-right" color="#909399" size="22"></u-icon>
+                 
+                </u-row>
+            </view>
+        </view>
+        <view class="nodata-warp" v-if="!queryMyListdata.length">
+					  <image :src="$getImages('/assetsMobile/images/no-data.png')" class="nodata-image"></image>
+					</view>
+        <u-loadmore :status="status" />
+        <u-toast ref="uToast" />
+        <u-calendar :show="calendarShow" mode="range" :defaultDate="defaultDate" :maxDate="maxDate" minDate="2023-10-01"
+            monthNum="24" @confirm="calendarChange" @close="calendarShow = false"></u-calendar>
+        <!-- 评价 -->
+        <!-- <tq-rate-popup ref="ratePopupRef" @confirm="(e) => { postEvaluate(e) }"></tq-rate-popup> -->
+    </view>
+</template>
+<script>
+import { paperList } from "@/api/learnMobile/index";
+
+export default {
+
+    data() {
+        return {
+            calendarShow: false, // 日历
+            calendarStart: this.dayjs().subtract(6, 'day').format('YYYY-MM-DD'),// 开始时间
+            calendarEnd: this.dayjs().subtract(0, 'day').format('YYYY-MM-DD'), // 结束时间
+            defaultDate: [this.dayjs().subtract(6, 'day').format('YYYY-MM-DD'), this.dayjs().subtract(0, 'day').format('YYYY-MM-DD')],
+            maxDate: this.dayjs().add(365, 'day').format('YYYY-MM-DD'),
+            tablist: [
+                { name: '未作答' },
+                { name: '已作答' },
+            ],
+            curNow: 0,
+            queryMyListdata: [],
+            form: {
+                queryParam: '',
+                // createTimeBegin: '',//开始时间
+                // createTimeEnd: '', //结束时间
+                // applyType: '',//用车类型(字典编码:matter_type)
+                // applyTypeName: '',
+                // flowStatus: '',//总经办部门领导审核状态:1.审核通过、2.审核不通过
+                // flowStatusName: '',
+            },
+            pageIndex: 1,
+            total: 0,
+            status: 'nomore' //loading nomore loadmore
+        }
+    },
+    onShow(option) {
+        this.queryMyListdata = []
+        this.pageIndex = 1
+        this.queryMyList();
+    },
+    methods: {
+        // http://tq.5000v.com:8025/workflow/mobile/dealTask?definitionId=system_cost:3:eb004fdf-4f0d-11ef-86fb-005056bb2bc2&category=undefined&taskId=&withDealTask=false&processInstanceId=61aef549-4f18-11ef-86fb-005056bb2bc2&fromMobile=true&authtoken=eyJhbGciOiJIUzUxMiJ9.eyJjbGllbnQiOiJUUS1IVy1NT0JJTEUiLCJsb2dpbl91c2VyX2tleSI6IjFkMTY1OGYzLThhOWMtNDM5Yy04Yzk3LTYwMjQyMDg2MzUyOCJ9.C0QYkgPxsjTPLOFTwrK6xjlzLLGMInzChQ7M1Rm6IupwLEI8HA1EK0HKm6y1IfWekmiybOyc3WSx838sRem-aQ
+        handleDetaisl({ id,recordId,paperStatus }) {
+            uni.navigateTo({
+                url: `/learnMobile/paperList/paperQuestionInfo?id=${id}&recordId=${recordId}&paperStatus=${paperStatus}`,
+            })
+        },
+        handleRestartWorkflow(instance) {
+            // http://localhost:9529/workflow/mobile/startFlow?id=system_cost%3A1%3A0cc5fb50-3dd3-11ef-80a8-000c29425992&processInstanceId=7455d6f9-4018-11ef-80a8-000c29425992&defaultTitle=%E8%B4%B9%E7%94%A8%E6%B5%81%E7%A8%8B_admin_20240712&name=%E9%87%8D%E6%96%B0%E5%8F%91%E8%B5%B7%E3%80%90%E8%B4%B9%E7%94%A8%E6%B5%81%E7%A8%8B%E3%80%91&restart=true
+            // instance = {
+            //     "procDefId": "system_cost:1:0cc5fb50-3dd3-11ef-80a8-000c29425992",
+            //     "companyName": "重新发起【费用流程】",
+            //     "id": "7455d6f9-4018-11ef-80a8-000c29425992"
+            // }
+            uni.navigateTo({
+                url: `/pages/flow/diboot?routes=/workflow/mobile/startFlow&id=${instance.procDefId}&processInstanceId=${instance.processInstanceId}&defaultTitle=${instance.companyName}&name=${instance.companyName}&restart=true`,
+            })
+        },
+        // handleTakeBack({ processInstanceId }) { //撤销
+        //     uni.showModal({
+        //         title: '',
+        //         content: `确认撤销?`,
+        //         showCancel: true,
+        //         confirmText: '确定',
+        //         success: async (res) => {
+        //             if (res.confirm) {
+        //                 let { code } = await recordtaskOperate({
+        //                     processInstanceId,
+        //                     taskOperate: 'cancel',
+        //                     validTask: false,
+        //                 })
+        //                 if (code == 0) {
+        //                     uni.showToast({
+        //                         title: '取消成功',
+        //                         icon: 'none',
+        //                     })
+        //                     this.anewQueryMyList()
+        //                 }
+        //             }
+        //         }
+        //     })
+        // },
+        calendarChange(e) {
+            console.info('选择的时间', e)
+
+            // 修改日期
+            this.form.createTimeBegin = e[0]; // 开始时间
+            this.form.createTimeEnd = e[e.length - 1]; // 结束时间
+            this.defaultDate = [e[0], e[e.length - 1]]
+            this.calendarShow = false;
+            this.anewQueryMyList();
+        },
+        anewQueryMyList() {
+            this.pageIndex = 1;
+            this.queryMyListdata = [];
+            this.queryMyList();
+        },
+        phoneCall(e) {
+            uni.makePhoneCall({
+                phoneNumber: e //仅为示例
+            });
+        },
+        pointAddrParse(e) {
+            let arr = JSON.parse(e)
+            let arrStg = ''
+            for (let item of arr) {
+                arrStg += item.address + '-'
+            }
+            return arrStg.slice(0, -1);
+        },
+        sectionChange(index) {
+            this.curNow = index;
+            this.anewQueryMyList()
+        },
+        toRealMap(item) {
+            let obj = {
+                routeId: item.routeId,
+                classId: item.classId,
+                startAndEnd: item.startAndEnd
+            }
+            let objst = encodeURIComponent(JSON.stringify(obj))
+            uni.navigateTo({
+                url: '/newdriversub/setClassPlan/details?item=' + objst
+            });
+        },
+        async queryMyList() {
+            
+            this.loading = 'loading'
+            let { createTimeBegin, createTimeEnd } = this.form
+            let obj = {
+                ...this.form,
+                page: this.pageIndex,
+                size: 8,
+                // createTimeBegin: createTimeBegin ? createTimeBegin + ' 00:00:00' : '',
+                // createTimeEnd: createTimeEnd ? createTimeEnd + ' 23:59:59' : '',
+                paperStatus: this.curNow //查询类型:作答状态:0.未作答 1.已作答
+            }
+            for (let key in obj) {
+                if (obj[key] === '') delete obj[key]
+            }
+            let { data, code } = await paperList(obj)
+            if (code == "0") {
+
+                let { list, total } = data
+                this.queryMyListdata = this.queryMyListdata.concat(list)
+                this.total = Number(total)
+                this.loading = 'loadmore'
+                if (this.queryMyListdata.length >= this.total) {
+                    this.status = 'nomore'
+                }
+                console.log('数据', this.queryMyListdata)
+            }
+        },
+        // 操作
+        // async postEvaluate(e) {
+        //     let { data, code, msg } = await evaluate(
+        //         {
+        //             evaluate: e.textarea,
+        //             evaluateScore: e.rateNum,
+        //             systemFlowVehId: e.item.id, //任务id
+        //         })
+        //     if (code == "0") {
+        //         uni.showToast({
+        //             title: msg || '评价成功',
+        //             duration: 2000
+        //         });
+        //         this.anewQueryMyList();
+        //     }
+        // }
+
+    },
+    // 触底
+    onReachBottom() {
+        console.log('当前条数', this.queryMyListdata.length, '总条数', this.total)
+        if (this.queryMyListdata.length >= this.total) return;
+        this.pageIndex++
+        this.queryMyList();
+    }
+}
+</script>
+<style lang="scss" scoped>
+.registrationRecord-box {
+
+    padding: 0 0 10rpx 0;
+    overflow: unset;
+
+    .page-head {
+        background: #fff;
+    }
+
+    .bg-purple-light {
+        word-wrap: break-word;
+    }
+
+    .list-box {
+        border-radius: 10rpx;
+        background: #fff;
+        // padding: 20rpx;
+        position: sticky;
+
+        // align-items: center;
+        // display: flex;
+        top: 0px;
+        width: 100%;
+        z-index: 999;
+
+        /deep/ {
+
+            .u-form-item__body {
+                padding: 0;
+            }
+        }
+
+    }
+
+    .item-list-box {
+        padding: 10rpx 0 0 0;
+        // background: #fff;
+        position: relative;
+
+
+        .info-box {
+            margin-bottom: 20rpx;
+            border: none;
+            width: 100%;
+            // border-radius: 20rpx;
+            box-shadow: 2rpx 2rpx 5rpx rgba(169, 169, 169, 0.34901960784313724);
+            // height: 150rpx;
+            background-color: rgba(255, 255, 255, 1);
+            display: flex;
+            align-items: center;
+            flex-wrap: wrap;
+            padding: 20rpx;
+            box-sizing: border-box;
+            position: relative;
+
+            font-size: 24rpx;
+
+
+
+
+        }
+
+
+    }
+
+
+}
+
+.box-container {
+    // background: #fff;
+    margin: 20rpx 10rpx;
+    border-radius: 10rpx;
+    font-size: 24rpx;
+
+    .box-head {
+        border-bottom: 1px solid #ddd;
+        padding: 20rpx;
+        font-weight: 700;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+    }
+
+    .box-body {
+        background: #fff;
+        padding: 20rpx;
+        display: flex;
+        align-items: center;
+        position: relative;
+        margin-bottom: 20rpx;
+        // height: 180rpx;
+        .left {
+            display: flex;
+            flex-direction: column;
+            height: 140rpx;
+            justify-content: space-between;
+
+            .name {
+                font-size: 32rpx;
+                font-weight: bold;
+            }
+
+            .status {
+                display: flex;
+                align-items: center;
+
+                .time {
+                    margin-left: 16rpx;
+                    color: #333;
+                }
+            }
+        }
+        .arrow-right{
+            position: absolute;
+            right: 10rpx;
+            opacity: 0.6;
+          
+        }
+        .nodata-image {
+            width: 100%;
+            height: 140rpx;
+        }
+    }
+
+    .box-footer {
+        border-top: 1px solid #ddd;
+        // padding: 20rpx;
+    }
+}
+
+.primary {
+    color: $uni-color-primary;
+}
+.nodata-warp{
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 100rpx 0;
+  .nodata-image{
+    width: 218rpx;
+    height: 150rpx;
+  }
+}
+</style>

+ 503 - 0
learnMobile/paperList/paperQuestionInfo.vue

@@ -0,0 +1,503 @@
+<template>
+    <view class="paperQuestionInfo-container">
+        <template v-if="currentQuestion.questionId">
+            <view class="box-body">
+
+                <view style=" background: #fff; padding: 30rpx 32rpx;">
+                    <view class="head">
+                        <view class="tit">{{ currentQuestion.typeName }}({{ currentQuestion.questionScore }}分)</view>
+                        <view class="count">{{ currentIndex + 1 }}/{{ questionList.length }}</view>
+                    </view>
+                    <view class="item-content">
+                        {{ currentQuestion.name }}
+                    </view>
+
+                    <view class="option">
+                        <!-- 复选 -->
+                        <u-checkbox-group
+                            v-model="currentQuestion.useAnswer"
+                            v-if="currentQuestion.type === 2"
+                            :disabled="isExplain"
+                            placement="column"
+                            @change="checkboxChange">
+                            <u-checkbox
+                                :customStyle="{ marginBottom: '16px' }"
+                                v-for="(item, index) in currentQuestionOption"
+                                :key="index"
+                                :label="item.label"
+                                :name="item.value">
+                            </u-checkbox>
+                        </u-checkbox-group>
+                        <!-- 单选 -->
+                        <u-radio-group
+                            v-model="currentQuestion.useAnswer"
+                            v-else
+                            placement="column"
+                            :disabled="isExplain"
+                            @change="checkboxChange">
+                            <u-radio
+                                :customStyle="{ marginBottom: '16px' }"
+                                v-for="(item, index) in currentQuestionOption"
+                                :key="index"
+                                :label="item.label"
+                                :name="item.value">
+                            </u-radio>
+                        </u-radio-group>
+
+                    </view>
+                </view>
+                <view style=" background: #fff; padding: 30rpx 32rpx;margin-top: 20rpx;" v-if="isExplain">
+                    <view class="head">
+                        <view class="tit">答题解析</view>
+                    </view>
+                    <!-- 解析 -->
+                    <view class="explain">
+                        <!-- <view class="explain-content">你的选择:{{ getanswer(currentQuestion.useAnswer) }}</view> -->
+                        <view class="explain-title">正确答案:{{ getanswer(currentQuestion.answer) }}</view>
+                        <view class="explain-content">得分:{{ currentQuestion.score }}分</view>
+                    </view>
+
+                </view>
+
+            </view>
+
+
+
+            <!-- 底部 -->
+            <view class="operation">
+                <u-button text="上一题" :disabled="currentIndex === 0" @click="previousQuestion"></u-button>
+                <u-button text="答题卡" @click="show = true"></u-button>
+                <u-button text="下一题" @click="nextQuestion"
+                    v-if="currentIndex < questionList.length - 1"></u-button>
+                <u-button type="primary" @click="saveUserPaper"
+                    v-if="currentIndex === questionList.length - 1 && !isExplain" text="立即交卷"></u-button>
+            </view>
+        </template>
+        <!-- 答题卡 -->
+        <u-popup :show="show" @close="close" @open="open" v-if="!isExplain">
+            <view class="answer-cardsheet">
+                <view class="item-box" v-for="(item, index) in questionList">
+                    <view class="item" @click="toCurrentIndex(index)"
+                        :class="{ 'item1': item.useAnswer, 'item2': index === currentIndex }" :key="index">{{ index + 1
+                        }}
+                    </view>
+                </view>
+                <view class="button-box">
+                    <u-button text="继续答题" @click="close"></u-button>
+                    <u-button type="primary" @click="saveUserPaper" text="立即交卷"></u-button>
+                </view>
+            </view>
+        </u-popup>
+
+        <!-- 解析答题卡 -->
+        <u-popup :show="show" @close="close" @open="open" v-if="isExplain">
+            <view class="answer-cardsheet">
+                <view class="item-box" v-for="(item, index) in questionList">
+                    <view class="item" @click="toCurrentIndex(index)"
+                        :class="{ 'item1': item.status === 1, 'item3': item.status === 0 }" :key="index">{{ index + 1
+                        }}
+                    </view>
+                </view>
+                <view class="button-box">
+                    <u-button type="primary" @click="close" text="关闭"></u-button>
+                </view>
+            </view>
+        </u-popup>
+        <!-- 确认提示框 -->
+        <u-popup :show="warnShow" @close="warnShow = false" @open="warnShow = true">
+            <view class="answer-cardsheet">
+                <view class="is-sub">
+                    <u-icon type="warn" class="error-circle-fill" name="error-circle-fill" size="24"
+                        color="#fb6547"></u-icon>
+                    <view class="warn">
+                        <span v-if="notReach">您有{{ notReach }}道题未答,确定提交吗?</span>
+                        <span v-else>试卷提交后不可更改,确定提交吗?</span>
+                    </view>
+                </view>
+                <view class="button-box">
+                    <u-button text="取消" @click="warnShow = false"></u-button>
+                    <u-button type="primary" @click="saveUserPaper(true)" text="确定"></u-button>
+                </view>
+            </view>
+        </u-popup>
+        <!-- 结果 -->
+        <u-popup :show="resultShow" mode="center" :closeOnClickOverlay="false" @close="resultShow = false"
+            @open="resultShow = true">
+            <view class="answer-cardsheet">
+                <view class="is-sub">
+                    <!-- 通过 -->
+                    <template v-if="resultData.viaStatus === 1">
+                        <image
+                            class="result-img"
+                            src="@/static/icon/pass.png"
+                            style="width: 300rpx;height: 300px;"
+                            mode="widthFix"></image>
+                        <view style="font-size: 48rpx;color: red;">
+                            恭喜您通过考试!
+                        </view>
+                    </template>
+                    <!-- 不通过 -->
+                    <template v-if="resultData.viaStatus === 0">
+                        <image
+                            class="result-img"
+                            src="@/static/icon/not.png"
+                            style="width: 300rpx;height: 300px;"
+                            mode="widthFix"></image>
+                        <view style="font-size: 48rpx;color: red;">
+                            未通过考试!
+                        </view>
+                    </template>
+
+                    <view style="font-size: 48rpx;color: brown;margin-top: 10rpx;">
+                        {{ resultData.score }}分
+                    </view>
+                    <view style="margin-top: 30rpx;">
+                        总分:{{ resultData.totalScore }}分&nbsp;&nbsp;&nbsp;&nbsp;答对:<span style="color: red;">{{
+                            resultData.answerNum
+                            }}</span>/{{ resultData.totalQuestion }}
+                    </view>
+                </view>
+                <view class="button-box">
+                    <u-button text="查看解析" @click="userExaminationRecord"></u-button>
+                    <template>
+                        <u-button type="primary" v-if="resultData.useNum === resultData.limitNum" @click="navigateBack"
+                            text="返回"></u-button>
+                        <u-button type="primary" @click="getData" v-else text="再考一次"></u-button>
+                    </template>
+
+                </view>
+            </view>
+        </u-popup>
+		<!-- 人脸校验 -->
+		<tq-face ref="faceRef" 
+			@success="faceSuccess"
+			@close="faceClose"
+		></tq-face>
+    </view>
+</template>
+<script>
+import { paperQuestionInfo, saveUserPaper, userExaminationRecord, recordByPaperId } from "@/api/learnMobile/index";
+export default {
+    paperQuestionInfo: '',
+    data() {
+        return {
+            oldData: {},
+            id: '',
+            recordId: '',
+            show: false,
+            warnShow: false,
+
+            cardsheet: [],
+            currentIndex: 0,//当前题下标
+            currentQuestion: {},
+            questionList: [],
+            options: [
+                // {
+                //     name: '苹果',
+                //     disabled: false
+                // },
+                // {
+                //     name: '香蕉',
+                //     disabled: false
+                // },
+                // {
+                //     name: '橙子',
+                //     disabled: false
+                // }, {
+                //     name: '榴莲',
+                //     disabled: false
+                // }
+            ],
+            startTime: '',
+            notReach: 0,
+            isPass: false,
+            resultShow: false,
+            resultData: {}, //考试结果
+            isExplain: false ,//是否是解析状态
+			isValidFace: false, //是否检验人脸
+        }
+    },
+    //计算属性得到当前的题目
+    computed: {
+        currentQuestionOption() {
+            let item = this.questionList[this.currentIndex]
+            if (item.type === 3) {//判断
+                return [{
+                    label: '对',
+                    value: '1',
+                }, {
+                    label: '错',
+                    value: '0',
+                }]
+            } else {
+                return item.option.map(e => {
+                    return {
+                        ...e,
+                        label: e.optionKey + ' ' + e.optionValue,// 
+                        value: e.optionKey
+                    }
+                })
+            }
+        },
+        isAllUseAnswer() {
+            //判断questionList数组的所有选项都有useAnswer参数值
+            return this.questionList.every(question => question.useAnswer);
+        }
+    },
+    watch: {
+        currentIndex: {
+            handler(newVal) {
+                let item = this.questionList[newVal]
+                this.$nextTick(() => {
+                    console.log('用户答案', item.useAnswer)
+                    this.currentQuestion = item
+                })
+            }
+        }
+    },
+    onLoad({ id, recordId, paperStatus }) {
+        this.id = id
+        if (!this.recordId) {
+            this.recordId = recordId
+        }
+        let status = paperStatus * 1
+        if (status === 0) { //未作答
+            this.getData()
+        } else if (status === 1) { //已作答
+            this.recordByPaperId()
+        } else {
+            this.userExaminationRecord()
+        }
+        // this.getData();
+        // this.userExaminationRecord();
+        // this.recordByPaperId()
+        this.startTime = this.dayjs().format('YYYY-MM-DD HH:mm:ss')
+    },
+    methods: {
+        /**获取结果 */
+        async recordByPaperId() {
+            let { data, code } = await recordByPaperId({
+                paperId: this.id
+            })
+            if (code == '0') {
+                this.resultShow = true
+                this.resultData = data
+            }
+        },
+        getanswer(e) {
+            if (e === '0') return '错'
+            if (e === '1') return '对'
+            return e
+        },
+        /**查看考试记录信息(试题解析) */
+        async userExaminationRecord() {
+            let { data, code } = await userExaminationRecord({
+                recordId: this.recordId
+            })
+            if (code == '0') {
+                this.questionList = data.questionList.map(e => {
+                    return {
+                        ...e,
+                        useAnswer: e.type === 2 ? e.useAnswer.split(',') : e.useAnswer
+                    }
+                })
+                this.currentQuestion = this.questionList[0]
+                this.currentIndex = 0
+                this.resultShow = false
+                this.isExplain = true
+            }
+        },
+        navigateBack() {
+            uni.navigateBack()
+        },
+        async saveUserPaper(is) { //提交试卷
+		
+            this.show = false
+            if (!is) { //非直接提交 判断
+                this.notReach = this.questionList.filter(e => !e.useAnswer).length
+                return this.warnShow = true //提示框
+            }
+            this.warnShow = false
+            let datas = this.questionList.map(e => {
+                return {
+                    ...e,
+                    useAnswer: e.useAnswer ? e.useAnswer.toString() : ''
+                }
+            })
+			
+			if(!this.isValidFace) {
+				this.openFace()
+			}
+			
+            let { data, code } = await saveUserPaper({
+                ...this.oldData,
+                questionList: datas,
+                startTime: this.startTime,
+
+            })
+            if (code == '0') {
+                this.resultShow = true
+                this.resultData = data
+                this.recordId = data.recordId
+            }
+        },
+        previousQuestion() {
+            this.currentIndex--
+        },
+        nextQuestion() {
+            this.currentIndex++
+        },
+        async getData() {
+            let { data, code } = await paperQuestionInfo({
+                id: this.id
+            })
+            if (code == '0') {
+                this.questionList = data.questionList
+                this.currentQuestion = this.questionList[0]
+                this.oldData = data
+                this.resultShow = false
+            }
+        },
+        toCurrentIndex(e) {
+            this.currentIndex = e
+        },
+        checkboxChange(e) {
+            console.log('选择', e)
+        },
+        open() {
+            // console.log('open');
+        },
+        close() {
+            this.show = false
+            // console.log('close');
+        },
+		
+		openFace() {	
+			this.$refs.faceRef.show()
+		},
+		faceClose() {
+			uni.navigateBack()
+		},
+		faceSuccess() {
+			this.isValidFace = true
+			this.saveUserPaper(true)
+		}
+    }
+}
+</script>
+<style lang="scss" scoped>
+.paperQuestionInfo-container {
+
+    .head {
+        border-left: solid 10rpx #2979FF;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        padding-left: 20rpx;
+
+        .tit {
+            font-size: 36rpx;
+            font-weight: bolder;
+            color: #333;
+        }
+    }
+
+    .box-body {
+
+
+
+        .item-content {
+            padding: 40rpx 0 20rpx 0;
+            color: #333;
+        }
+    }
+
+    .operation {
+        display: flex;
+        position: fixed;
+        bottom: 0;
+        width: 100%;
+        left: 0;
+    }
+
+    .answer-cardsheet {
+        display: flex;
+        flex-wrap: wrap;
+        padding-top: 20rpx;
+        max-height: 400px;
+        overflow-y: auto;
+
+        // justify-content: space-between;
+        .item-box {
+            display: flex;
+            justify-content: center;
+            width: 20%;
+
+            .item {
+                width: 80rpx;
+                height: 80rpx;
+                border: 1px solid #9e9b9b;
+                border-radius: 50%;
+                flex-shrink: 0;
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                margin: 20rpx 0;
+            }
+
+            .item1 {
+                //已答 正确
+                background: rgba(60, 156, 255, 0.4);
+                border: 1px solid rgba(60, 156, 255, 0.4);
+                color: #2979FF;
+            }
+
+            .item2 {
+                //当前
+                background: #fff;
+                color: #2979FF;
+                border: 1px solid rgba(60, 156, 255, 0.4);
+            }
+
+            .item3 {
+                //错误
+                background: rgba(217, 159, 159, 0.5);
+                border: 1px solid rgba(217, 159, 159, 0.5);
+                color: rgba(255, 25, 25, 0.337);
+            }
+        }
+
+        .button-box {
+            margin-top: 20rpx;
+            display: flex;
+            width: 100%;
+        }
+    }
+
+    .is-sub {
+        display: flex;
+        justify-content: center;
+        flex-direction: column;
+        padding: 40rpx 0;
+        width: 100%;
+        text-align: center;
+
+        .error-circle-fill {
+            margin: 0 auto;
+        }
+
+        .warn {
+            font-size: 32rpx;
+            margin-top: 12rpx;
+        }
+
+        .result-img {
+            margin: 0 auto;
+        }
+    }
+
+    .explain {
+        margin-top: 20px;
+    }
+}
+</style>

+ 370 - 0
learnMobile/record/record.vue

@@ -0,0 +1,370 @@
+<template>
+    <view class="registrationRecord-box">
+        <!-- 头部筛选 -->
+        <view class="page-head">
+
+            <!-- <u-tabs :list="tablist"
+                :itemStyle="{
+                    width: '330rpx',
+                    height: '80rpx'
+                }"
+                :activeStyle="{
+                    color: '#4573FC'
+                }"
+                lineWidth="40"
+                lineColor="#4573FC"
+                @click="({ index }) => {
+                    curNow = index
+                    anewQueryMyList()
+                }">
+            </u-tabs> -->
+
+
+            <u-row>
+                <u-col :span="12" style="padding: 16rpx 24rpx;">
+                    <view>
+                        <view class="input-wapper">
+                            <u--input
+                                v-model="form.queryParam"
+                                :disabled="false"
+                                :clearable="true"
+                                placeholder="请输入名称"
+                                prefixIcon="search"
+                                shape="circle"
+                                prefixIconStyle="font-size: 22px;color: #909399"
+                                inputAlign="center"
+                                fontSize="28rpx"
+                                disabledColor="#F2F2F2"
+                                border="none"
+                                :customStyle="{
+                                    height: '62rpx',
+                                    padding: '4rpx 28rpx',
+                                    background: '#F2F2F2'
+                                }"
+                                @confirm="anewQueryMyList">
+                            </u--input>
+                        </view>
+                    </view>
+                </u-col>
+
+            </u-row>
+        </view>
+        <view class="box-container">
+            <!-- @click="handleDetaisl(item)" -->
+            <view class="box-body" @click="handleDetaisl(item)" v-for="(item, index) in queryMyListdata" :key="index">
+                <view class="name">{{ item.materialName }}</view>
+                <view class="time">学习时间:{{ item.timeStr }}</view>
+                <view class="time">学习用时:{{ item.useTimeStr }}</view>
+            </view>
+        </view>
+        <view class="nodata-warp" v-if="!queryMyListdata.length">
+					  <image :src="$getImages('/assetsMobile/images/no-data.png')" class="nodata-image"></image>
+					</view>
+        <u-loadmore :status="status" />
+        <u-toast ref="uToast" />
+        <u-calendar :show="calendarShow" mode="range" :defaultDate="defaultDate" :maxDate="maxDate" minDate="2023-10-01"
+            monthNum="24" @confirm="calendarChange" @close="calendarShow = false"></u-calendar>
+        <!-- 评价 -->
+        <!-- <tq-rate-popup ref="ratePopupRef" @confirm="(e) => { postEvaluate(e) }"></tq-rate-popup> -->
+    </view>
+</template>
+<script>
+import { userMaterialRecord } from "@/api/learnMobile/index";
+
+export default {
+    data() {
+        return {
+            calendarShow: false, // 日历
+            calendarStart: this.dayjs().subtract(6, 'day').format('YYYY-MM-DD'),// 开始时间
+            calendarEnd: this.dayjs().subtract(0, 'day').format('YYYY-MM-DD'), // 结束时间
+            defaultDate: [this.dayjs().subtract(6, 'day').format('YYYY-MM-DD'), this.dayjs().subtract(0, 'day').format('YYYY-MM-DD')],
+            maxDate: this.dayjs().add(365, 'day').format('YYYY-MM-DD'),
+            tablist: [
+                { name: '未学习' },
+                { name: '已学习' },
+            ],
+            curNow: 0,
+            queryMyListdata: [],
+            form: {
+                queryParam: '',
+                // createTimeBegin: '',//开始时间
+                // createTimeEnd: '', //结束时间
+                // applyType: '',//用车类型(字典编码:matter_type)
+                // applyTypeName: '',
+                // flowStatus: '',//总经办部门领导审核状态:1.审核通过、2.审核不通过
+                // flowStatusName: '',
+            },
+            pageIndex: 1,
+            total: 0,
+            status: 'nomore' //loading nomore loadmore
+        }
+    },
+    onShow(option) {
+        this.queryMyListdata = []
+        this.pageIndex = 1
+        this.queryMyList();
+    },
+    methods: {
+        // http://tq.5000v.com:8025/workflow/mobile/dealTask?definitionId=system_cost:3:eb004fdf-4f0d-11ef-86fb-005056bb2bc2&category=undefined&taskId=&withDealTask=false&processInstanceId=61aef549-4f18-11ef-86fb-005056bb2bc2&fromMobile=true&authtoken=eyJhbGciOiJIUzUxMiJ9.eyJjbGllbnQiOiJUUS1IVy1NT0JJTEUiLCJsb2dpbl91c2VyX2tleSI6IjFkMTY1OGYzLThhOWMtNDM5Yy04Yzk3LTYwMjQyMDg2MzUyOCJ9.C0QYkgPxsjTPLOFTwrK6xjlzLLGMInzChQ7M1Rm6IupwLEI8HA1EK0HKm6y1IfWekmiybOyc3WSx838sRem-aQ
+        handleDetaisl({ id }) {
+            uni.navigateTo({
+                url: `/learnMobile/userMaterialRecord/details?id=` + id,
+            })
+        },
+        handleRestartWorkflow(instance) {
+            // http://localhost:9529/workflow/mobile/startFlow?id=system_cost%3A1%3A0cc5fb50-3dd3-11ef-80a8-000c29425992&processInstanceId=7455d6f9-4018-11ef-80a8-000c29425992&defaultTitle=%E8%B4%B9%E7%94%A8%E6%B5%81%E7%A8%8B_admin_20240712&name=%E9%87%8D%E6%96%B0%E5%8F%91%E8%B5%B7%E3%80%90%E8%B4%B9%E7%94%A8%E6%B5%81%E7%A8%8B%E3%80%91&restart=true
+            // instance = {
+            //     "procDefId": "system_cost:1:0cc5fb50-3dd3-11ef-80a8-000c29425992",
+            //     "companyName": "重新发起【费用流程】",
+            //     "id": "7455d6f9-4018-11ef-80a8-000c29425992"
+            // }
+            uni.navigateTo({
+                url: `/pages/flow/diboot?routes=/workflow/mobile/startFlow&id=${instance.procDefId}&processInstanceId=${instance.processInstanceId}&defaultTitle=${instance.companyName}&name=${instance.companyName}&restart=true`,
+            })
+        },
+        // handleTakeBack({ processInstanceId }) { //撤销
+        //     uni.showModal({
+        //         title: '',
+        //         content: `确认撤销?`,
+        //         showCancel: true,
+        //         confirmText: '确定',
+        //         success: async (res) => {
+        //             if (res.confirm) {
+        //                 let { code } = await recordtaskOperate({
+        //                     processInstanceId,
+        //                     taskOperate: 'cancel',
+        //                     validTask: false,
+        //                 })
+        //                 if (code == 0) {
+        //                     uni.showToast({
+        //                         title: '取消成功',
+        //                         icon: 'none',
+        //                     })
+        //                     this.anewQueryMyList()
+        //                 }
+        //             }
+        //         }
+        //     })
+        // },
+        calendarChange(e) {
+            console.info('选择的时间', e)
+
+            // 修改日期
+            this.form.createTimeBegin = e[0]; // 开始时间
+            this.form.createTimeEnd = e[e.length - 1]; // 结束时间
+            this.defaultDate = [e[0], e[e.length - 1]]
+            this.calendarShow = false;
+            this.anewQueryMyList();
+        },
+        anewQueryMyList() {
+            this.pageIndex = 1;
+            this.queryMyListdata = [];
+            this.queryMyList();
+        },
+        phoneCall(e) {
+            uni.makePhoneCall({
+                phoneNumber: e //仅为示例
+            });
+        },
+        pointAddrParse(e) {
+            let arr = JSON.parse(e)
+            let arrStg = ''
+            for (let item of arr) {
+                arrStg += item.address + '-'
+            }
+            return arrStg.slice(0, -1);
+        },
+        sectionChange(index) {
+            this.curNow = index;
+            this.anewQueryMyList()
+        },
+        toRealMap(item) {
+            let obj = {
+                routeId: item.routeId,
+                classId: item.classId,
+                startAndEnd: item.startAndEnd
+            }
+            let objst = encodeURIComponent(JSON.stringify(obj))
+            uni.navigateTo({
+                url: '/newdriversub/setClassPlan/details?item=' + objst
+            });
+        },
+        async queryMyList() {
+            this.loading = 'loading'
+            let { createTimeBegin, createTimeEnd } = this.form
+            let obj = {
+                ...this.form,
+                page: this.pageIndex,
+                size: 8,
+                // createTimeBegin: createTimeBegin ? createTimeBegin + ' 00:00:00' : '',
+                // createTimeEnd: createTimeEnd ? createTimeEnd + ' 23:59:59' : '',
+                learnStatus: this.curNow //查询类型:学习状态:0.未学习 1.已学习
+
+
+            }
+            for (let key in obj) {
+                if (obj[key] === '') delete obj[key]
+            }
+            let { data, code } = await userMaterialRecord(obj)
+            if (code == "0") {
+
+                let { list, total } = data
+                this.queryMyListdata = this.queryMyListdata.concat(list)
+                this.total = Number(total)
+                this.loading = 'loadmore'
+                if (this.queryMyListdata.length >= this.total) {
+                    this.status = 'nomore'
+                }
+                console.log('数据', this.queryMyListdata)
+            }
+        },
+        // 操作
+        // async postEvaluate(e) {
+        //     let { data, code, msg } = await evaluate(
+        //         {
+        //             evaluate: e.textarea,
+        //             evaluateScore: e.rateNum,
+        //             systemFlowVehId: e.item.id, //任务id
+        //         })
+        //     if (code == "0") {
+        //         uni.showToast({
+        //             title: msg || '评价成功',
+        //             duration: 2000
+        //         });
+        //         this.anewQueryMyList();
+        //     }
+        // }
+
+    },
+    // 触底
+    onReachBottom() {
+        console.log('当前条数', this.queryMyListdata.length, '总条数', this.total)
+        if (this.queryMyListdata.length >= this.total) return;
+        this.pageIndex++
+        this.queryMyList();
+    }
+}
+</script>
+<style lang="scss" scoped>
+.registrationRecord-box {
+
+    padding: 0 0 10rpx 0;
+    overflow: unset;
+
+    .page-head {
+        background: #fff;
+    }
+
+    .bg-purple-light {
+        word-wrap: break-word;
+    }
+
+    .list-box {
+        border-radius: 10rpx;
+        background: #fff;
+        // padding: 20rpx;
+        position: sticky;
+
+        // align-items: center;
+        // display: flex;
+        top: 0px;
+        width: 100%;
+        z-index: 999;
+
+        /deep/ {
+
+            .u-form-item__body {
+                padding: 0;
+            }
+        }
+
+    }
+
+    .item-list-box {
+        padding: 10rpx 0 0 0;
+        // background: #fff;
+        position: relative;
+
+
+        .info-box {
+            margin-bottom: 20rpx;
+            border: none;
+            width: 100%;
+            // border-radius: 20rpx;
+            box-shadow: 2rpx 2rpx 5rpx rgba(169, 169, 169, 0.34901960784313724);
+            // height: 150rpx;
+            background-color: rgba(255, 255, 255, 1);
+            display: flex;
+            align-items: center;
+            flex-wrap: wrap;
+            padding: 20rpx;
+            box-sizing: border-box;
+            position: relative;
+
+            font-size: 24rpx;
+
+
+
+
+        }
+
+
+    }
+
+
+}
+
+.box-container {
+    background: #fff;
+    margin: 20rpx 10rpx;
+    border-radius: 10rpx;
+    font-size: 24rpx;
+
+    .box-head {
+        border-bottom: 1px solid #ddd;
+        padding: 20rpx;
+        font-weight: 700;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+    }
+
+    .box-body {
+        padding:32rpx 20rpx;
+        // display: flex;
+        // align-items: center;
+        position: relative;
+
+        .name {
+            font-size: 36rpx;
+            font-weight: bolder;
+        }
+
+
+        .time {
+            color: #333;
+            margin-top: 8rpx;
+            font-size: 28;
+        }
+    }
+
+    .box-footer {
+        border-top: 1px solid #ddd;
+        // padding: 20rpx;
+    }
+}
+
+.primary {
+    color: $uni-color-primary;
+}
+.nodata-warp{
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 100rpx 0;
+  .nodata-image{
+    width: 218rpx;
+    height: 150rpx;
+  }
+}
+</style>

+ 405 - 0
learnMobile/userPaperRecordList/userPaperRecordList.vue

@@ -0,0 +1,405 @@
+<template>
+    <view class="registrationRecord-box">
+        <!-- 头部筛选 -->
+        <view class="page-head">
+
+            <!-- <u-tabs :list="tablist"
+                :itemStyle="{
+                    width: '330rpx',
+                    height: '80rpx'
+                }"
+                :activeStyle="{
+                    color: '#4573FC'
+                }"
+                lineWidth="40"
+                lineColor="#4573FC"
+                @click="({ index }) => {
+                    curNow = index
+                    anewQueryMyList()
+                }">
+            </u-tabs> -->
+
+
+            <u-row>
+                <u-col :span="12" style="padding: 16rpx 24rpx;">
+                    <view>
+                        <view class="input-wapper">
+                            <u--input
+                                v-model="form.queryParam"
+                                :disabled="false"
+                                :clearable="true"
+                                placeholder="请输入名称"
+                                prefixIcon="search"
+                                shape="circle"
+                                prefixIconStyle="font-size: 22px;color: #909399"
+                                inputAlign="center"
+                                fontSize="28rpx"
+                                disabledColor="#F2F2F2"
+                                border="none"
+                                :customStyle="{
+                                    height: '62rpx',
+                                    padding: '4rpx 28rpx',
+                                    background: '#F2F2F2'
+                                }"
+                                @confirm="anewQueryMyList">
+                            </u--input>
+                        </view>
+                    </view>
+                </u-col>
+
+            </u-row>
+        </view>
+        <view class="box-container">
+            <!-- @click="handleDetaisl(item)" -->
+            <view class="box-body" @click="handleDetaisl(item)" v-for="(item, index) in queryMyListdata" :key="index">
+                <u-row customStyle="margin-bottom: 0px">
+                    <u-col span="11">
+                        <view class="left">
+                            <view class="name">{{ item.name }}</view>
+                            <view class="status">
+                                <view class="time">考试成绩:{{item.score}}分,满分{{item.totalScore}}分</view>
+                                <view class="time">考试结果:{{item.viaStatusName}}</view>
+                                <view class="time">考试时间:{{item.startTime}}~{{item.endTime}}</view>
+                                <view class="time">考试用时:{{item.useTimeStr}}</view>
+                            </view>
+                        </view>
+                    </u-col>
+                    <u-icon class="arrow-right" name="arrow-right" color="#909399" size="22"></u-icon>
+                 
+                </u-row>
+            </view>
+        </view>
+         <view class="nodata-warp" v-if="!queryMyListdata.length">
+					  <image :src="$getImages('/assetsMobile/images/no-data.png')" class="nodata-image"></image>
+					</view>
+        <u-loadmore :status="status" />
+        <u-toast ref="uToast" />
+        <u-calendar :show="calendarShow" mode="range" :defaultDate="defaultDate" :maxDate="maxDate" minDate="2023-10-01"
+            monthNum="24" @confirm="calendarChange" @close="calendarShow = false"></u-calendar>
+        <!-- 评价 -->
+        <!-- <tq-rate-popup ref="ratePopupRef" @confirm="(e) => { postEvaluate(e) }"></tq-rate-popup> -->
+    </view>
+</template>
+<script>
+import { userPaperRecordList } from "@/api/learnMobile/index";
+
+export default {
+
+    data() {
+        return {
+            calendarShow: false, // 日历
+            calendarStart: this.dayjs().subtract(6, 'day').format('YYYY-MM-DD'),// 开始时间
+            calendarEnd: this.dayjs().subtract(0, 'day').format('YYYY-MM-DD'), // 结束时间
+            defaultDate: [this.dayjs().subtract(6, 'day').format('YYYY-MM-DD'), this.dayjs().subtract(0, 'day').format('YYYY-MM-DD')],
+            maxDate: this.dayjs().add(365, 'day').format('YYYY-MM-DD'),
+            tablist: [
+                { name: '未作答' },
+                { name: '已作答' },
+            ],
+            curNow: 0,
+            queryMyListdata: [],
+            form: {
+                queryParam: '',
+                // createTimeBegin: '',//开始时间
+                // createTimeEnd: '', //结束时间
+                // applyType: '',//用车类型(字典编码:matter_type)
+                // applyTypeName: '',
+                // flowStatus: '',//总经办部门领导审核状态:1.审核通过、2.审核不通过
+                // flowStatusName: '',
+            },
+            pageIndex: 1,
+            total: 0,
+            status: 'nomore' //loading nomore loadmore
+        }
+    },
+    onShow(option) {
+        this.queryMyListdata = []
+        this.pageIndex = 1
+        this.queryMyList();
+    },
+    methods: {
+        // http://tq.5000v.com:8025/workflow/mobile/dealTask?definitionId=system_cost:3:eb004fdf-4f0d-11ef-86fb-005056bb2bc2&category=undefined&taskId=&withDealTask=false&processInstanceId=61aef549-4f18-11ef-86fb-005056bb2bc2&fromMobile=true&authtoken=eyJhbGciOiJIUzUxMiJ9.eyJjbGllbnQiOiJUUS1IVy1NT0JJTEUiLCJsb2dpbl91c2VyX2tleSI6IjFkMTY1OGYzLThhOWMtNDM5Yy04Yzk3LTYwMjQyMDg2MzUyOCJ9.C0QYkgPxsjTPLOFTwrK6xjlzLLGMInzChQ7M1Rm6IupwLEI8HA1EK0HKm6y1IfWekmiybOyc3WSx838sRem-aQ
+        handleDetaisl({ recordId }) {
+            uni.navigateTo({
+                url: `/learnMobile/paperList/paperQuestionInfo?recordId=${recordId}`,
+            })
+        },
+        handleRestartWorkflow(instance) {
+            // http://localhost:9529/workflow/mobile/startFlow?id=system_cost%3A1%3A0cc5fb50-3dd3-11ef-80a8-000c29425992&processInstanceId=7455d6f9-4018-11ef-80a8-000c29425992&defaultTitle=%E8%B4%B9%E7%94%A8%E6%B5%81%E7%A8%8B_admin_20240712&name=%E9%87%8D%E6%96%B0%E5%8F%91%E8%B5%B7%E3%80%90%E8%B4%B9%E7%94%A8%E6%B5%81%E7%A8%8B%E3%80%91&restart=true
+            // instance = {
+            //     "procDefId": "system_cost:1:0cc5fb50-3dd3-11ef-80a8-000c29425992",
+            //     "companyName": "重新发起【费用流程】",
+            //     "id": "7455d6f9-4018-11ef-80a8-000c29425992"
+            // }
+            uni.navigateTo({
+                url: `/pages/flow/diboot?routes=/workflow/mobile/startFlow&id=${instance.procDefId}&processInstanceId=${instance.processInstanceId}&defaultTitle=${instance.companyName}&name=${instance.companyName}&restart=true`,
+            })
+        },
+        // handleTakeBack({ processInstanceId }) { //撤销
+        //     uni.showModal({
+        //         title: '',
+        //         content: `确认撤销?`,
+        //         showCancel: true,
+        //         confirmText: '确定',
+        //         success: async (res) => {
+        //             if (res.confirm) {
+        //                 let { code } = await recordtaskOperate({
+        //                     processInstanceId,
+        //                     taskOperate: 'cancel',
+        //                     validTask: false,
+        //                 })
+        //                 if (code == 0) {
+        //                     uni.showToast({
+        //                         title: '取消成功',
+        //                         icon: 'none',
+        //                     })
+        //                     this.anewQueryMyList()
+        //                 }
+        //             }
+        //         }
+        //     })
+        // },
+        calendarChange(e) {
+            console.info('选择的时间', e)
+
+            // 修改日期
+            this.form.createTimeBegin = e[0]; // 开始时间
+            this.form.createTimeEnd = e[e.length - 1]; // 结束时间
+            this.defaultDate = [e[0], e[e.length - 1]]
+            this.calendarShow = false;
+            this.anewQueryMyList();
+        },
+        anewQueryMyList() {
+            this.pageIndex = 1;
+            this.queryMyListdata = [];
+            this.queryMyList();
+        },
+        phoneCall(e) {
+            uni.makePhoneCall({
+                phoneNumber: e //仅为示例
+            });
+        },
+        pointAddrParse(e) {
+            let arr = JSON.parse(e)
+            let arrStg = ''
+            for (let item of arr) {
+                arrStg += item.address + '-'
+            }
+            return arrStg.slice(0, -1);
+        },
+        sectionChange(index) {
+            this.curNow = index;
+            this.anewQueryMyList()
+        },
+        toRealMap(item) {
+            let obj = {
+                routeId: item.routeId,
+                classId: item.classId,
+                startAndEnd: item.startAndEnd
+            }
+            let objst = encodeURIComponent(JSON.stringify(obj))
+            uni.navigateTo({
+                url: '/newdriversub/setClassPlan/details?item=' + objst
+            });
+        },
+        async queryMyList() {
+            
+            this.loading = 'loading'
+            let { createTimeBegin, createTimeEnd } = this.form
+            let obj = {
+                ...this.form,
+                page: this.pageIndex,
+                size: 8,
+                // createTimeBegin: createTimeBegin ? createTimeBegin + ' 00:00:00' : '',
+                // createTimeEnd: createTimeEnd ? createTimeEnd + ' 23:59:59' : '',
+                paperStatus: this.curNow //查询类型:作答状态:0.未作答 1.已作答
+            }
+            for (let key in obj) {
+                if (obj[key] === '') delete obj[key]
+            }
+            let { data, code } = await userPaperRecordList(obj)
+            if (code == "0") {
+
+                let { list, total } = data
+                this.queryMyListdata = this.queryMyListdata.concat(list)
+                this.total = Number(total)
+                this.loading = 'loadmore'
+                if (this.queryMyListdata.length >= this.total) {
+                    this.status = 'nomore'
+                }
+                console.log('数据', this.queryMyListdata)
+            }
+        },
+        // 操作
+        // async postEvaluate(e) {
+        //     let { data, code, msg } = await evaluate(
+        //         {
+        //             evaluate: e.textarea,
+        //             evaluateScore: e.rateNum,
+        //             systemFlowVehId: e.item.id, //任务id
+        //         })
+        //     if (code == "0") {
+        //         uni.showToast({
+        //             title: msg || '评价成功',
+        //             duration: 2000
+        //         });
+        //         this.anewQueryMyList();
+        //     }
+        // }
+
+    },
+    // 触底
+    onReachBottom() {
+        console.log('当前条数', this.queryMyListdata.length, '总条数', this.total)
+        if (this.queryMyListdata.length >= this.total) return;
+        this.pageIndex++
+        this.queryMyList();
+    }
+}
+</script>
+<style lang="scss" scoped>
+.registrationRecord-box {
+
+    padding: 0 0 10rpx 0;
+    overflow: unset;
+
+    .page-head {
+        background: #fff;
+    }
+
+    .bg-purple-light {
+        word-wrap: break-word;
+    }
+
+    .list-box {
+        border-radius: 10rpx;
+        background: #fff;
+        // padding: 20rpx;
+        position: sticky;
+
+        // align-items: center;
+        // display: flex;
+        top: 0px;
+        width: 100%;
+        z-index: 999;
+
+        /deep/ {
+
+            .u-form-item__body {
+                padding: 0;
+            }
+        }
+
+    }
+
+    .item-list-box {
+        padding: 10rpx 0 0 0;
+        // background: #fff;
+        position: relative;
+
+
+        .info-box {
+            margin-bottom: 20rpx;
+            border: none;
+            width: 100%;
+            // border-radius: 20rpx;
+            box-shadow: 2rpx 2rpx 5rpx rgba(169, 169, 169, 0.34901960784313724);
+            // height: 150rpx;
+            background-color: rgba(255, 255, 255, 1);
+            display: flex;
+            align-items: center;
+            flex-wrap: wrap;
+            padding: 20rpx;
+            box-sizing: border-box;
+            position: relative;
+
+            font-size: 24rpx;
+
+
+
+
+        }
+
+
+    }
+
+
+}
+
+.box-container {
+    // background: #fff;
+    margin: 20rpx 10rpx;
+    border-radius: 10rpx;
+    font-size: 24rpx;
+
+    .box-head {
+        border-bottom: 1px solid #ddd;
+        padding: 20rpx;
+        font-weight: 700;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+    }
+
+    .box-body {
+        background: #fff;
+        padding: 20rpx;
+        display: flex;
+        align-items: center;
+        position: relative;
+        margin-bottom: 20rpx;
+        // height: 180rpx;
+        .left {
+            display: flex;
+            flex-direction: column;
+            // height: 140rpx;
+            justify-content: space-between;
+
+            .name {
+                font-size: 32rpx;
+                font-weight: bold;
+            }
+
+            .status {
+                // display: flex;
+                // align-items: center;
+
+                .time {
+                    margin: 10rpx 0 0 4rpx;
+                    color: #333;
+                    font-size: 24rpx;
+                }
+            }
+        }
+        .arrow-right{
+            position: absolute;
+            right: 10rpx;
+            opacity: 0.6;
+          
+        }
+        .nodata-image {
+            width: 100%;
+            height: 140rpx;
+        }
+    }
+
+    .box-footer {
+        border-top: 1px solid #ddd;
+        // padding: 20rpx;
+    }
+}
+
+.primary {
+    color: $uni-color-primary;
+}
+.nodata-warp{
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 100rpx 0;
+  .nodata-image{
+    width: 218rpx;
+    height: 150rpx;
+  }
+}
+</style>

+ 42 - 0
main.js

@@ -0,0 +1,42 @@
+import App from './App'
+import uView from "uview-ui";
+import dayjs from "dayjs";
+import env from '@/env.js'
+
+// #ifdef H5
+import '@/assets/styles/commom.scss' // loading css
+import '@/utils/loadScript.js'
+import '@/utils/loadDDScript.js'
+
+// import VConsole from 'vconsole';
+// const vConsole = new VConsole(); 
+// #endif
+
+// #ifndef VUE3
+import Vue from 'vue'
+import './uni.promisify.adaptor'
+import plugins from './plugins' // plugins
+import store from './store' // store
+
+Vue.use(plugins)
+
+Vue.config.productionTip = false
+Vue.prototype.$store = store
+Vue.prototype.dayjs = dayjs
+App.mpType = 'app'
+Vue.use(uView);
+const app = new Vue({
+  ...App
+})
+app.$mount()
+// #endif
+
+// #ifdef VUE3
+import { createSSRApp } from 'vue'
+export function createApp() {
+  const app = createSSRApp(App)
+  return {
+    app
+  }
+}
+// #endif

+ 134 - 0
manifest.json

@@ -0,0 +1,134 @@
+{
+    "name" : "企事业车辆管理系统",
+    "appid" : "__UNI__C20958D",
+    "description" : "",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueStyleCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        /* 模块配置 */
+        "modules" : {},
+        /* 应用发布信息 */
+        "distribute" : {
+            /* android打包配置 */
+            "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打包配置 */
+            "ios" : {
+                "dSYMs" : false
+            },
+            /* SDK配置 */
+            "sdkConfigs" : {
+                "ad" : {}
+            },
+            "icons" : {
+                "android" : {
+                    "hdpi" : "unpackage/res/icons/72x72.png",
+                    "xhdpi" : "unpackage/res/icons/96x96.png",
+                    "xxhdpi" : "unpackage/res/icons/144x144.png",
+                    "xxxhdpi" : "unpackage/res/icons/192x192.png"
+                },
+                "ios" : {
+                    "appstore" : "unpackage/res/icons/1024x1024.png",
+                    "ipad" : {
+                        "app" : "unpackage/res/icons/76x76.png",
+                        "app@2x" : "unpackage/res/icons/152x152.png",
+                        "notification" : "unpackage/res/icons/20x20.png",
+                        "notification@2x" : "unpackage/res/icons/40x40.png",
+                        "proapp@2x" : "unpackage/res/icons/167x167.png",
+                        "settings" : "unpackage/res/icons/29x29.png",
+                        "settings@2x" : "unpackage/res/icons/58x58.png",
+                        "spotlight" : "unpackage/res/icons/40x40.png",
+                        "spotlight@2x" : "unpackage/res/icons/80x80.png"
+                    },
+                    "iphone" : {
+                        "app@2x" : "unpackage/res/icons/120x120.png",
+                        "app@3x" : "unpackage/res/icons/180x180.png",
+                        "notification@2x" : "unpackage/res/icons/40x40.png",
+                        "notification@3x" : "unpackage/res/icons/60x60.png",
+                        "settings@2x" : "unpackage/res/icons/58x58.png",
+                        "settings@3x" : "unpackage/res/icons/87x87.png",
+                        "spotlight@2x" : "unpackage/res/icons/80x80.png",
+                        "spotlight@3x" : "unpackage/res/icons/120x120.png"
+                    }
+                }
+            }
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "wx589a279f5507989c",
+        "setting" : {
+            "urlCheck" : false,
+            "minified" : true
+        },
+        "usingComponents" : true,
+        "optimization" : {
+            "subPackages" : true
+        },
+        "permission" : {
+            "scope.userLocation" : {
+                "desc" : "涉及地图定位功能"
+            }
+        }
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    },
+    "vueVersion" : "2",
+    "h5" : {
+        "router" : {
+            "mode" : "history",
+            "base" : "/mobile/"
+        },
+        "sdkConfigs" : {
+            "maps" : {
+                "amap" : {
+                    "key" : "155ee14f6c3558f8d8bffd5f44be8200",
+                    "securityJsCode" : "92ca05983dee6d33ada66bb0c75b6c5b",
+                    "serviceHost" : "https://80.5000v.com/"
+                }
+            }
+        },
+        "template" : "index.html",
+        "title" : "企事业车辆管理系统"
+    }
+}

+ 74 - 0
modifyManifest.js

@@ -0,0 +1,74 @@
+let env = require('./env.js')
+
+const {PLATFORM} = env
+
+const fs = require("fs");
+//此处如果是用HBuilderX创建的项目manifest.json文件在项目跟目录,如果是 cli 创建的则在 src 下,这里要注意
+//process.env.UNI_INPUT_DIR为项目所在的绝对路径,经测试,相对路径会找不到文件
+const revert = process.argv[process.argv.length - 1];
+// 根据还原参数区分使用原路径(最后执行命令的时候已经没有UNI全局变量了)
+const root = revert == "revert" ? "src" : process.env.UNI_INPUT_DIR;
+
+const manifestPath = root + "/manifest.json";
+let Manifest = fs.readFileSync(manifestPath, {
+  encoding: "utf-8",
+});
+
+// console.log(Manifest,'ManifestManifest')
+
+function replaceManifest(path, value) {
+  const arr = path.split(".");
+  const len = arr.length;
+  const lastItem = arr[len - 1];
+
+  let i = 0;
+  let ManifestArr = Manifest.split(/\n/);
+
+  for (let index = 0; index < ManifestArr.length; index++) {
+    const item = ManifestArr[index];
+    if (new RegExp(`"${arr[i]}"`).test(item)) ++i;
+    if (i === len) {
+      const hasComma = /,/.test(item);
+      ManifestArr[index] = item.replace(
+        new RegExp(`"${lastItem}"[\\s\\S]*:[\\s\\S]*`),
+        `"${lastItem}" : ${value}${hasComma ? "," : ""}`
+      );
+      break;
+    }
+  }
+
+  Manifest = ManifestArr.join("\n");
+}
+
+// 动态配置appid
+if (PLATFORM === "enterprises") {
+  // 企事业
+  let title = '"企事业车辆管理系统"'
+  replaceManifest("name", title);
+  replaceManifest("h5.router.base", '"/mobile/"');
+  replaceManifest("h5.title", title);
+  
+} else if (PLATFORM === "sanitation") {
+  // 环卫
+   let title = '"智慧环卫一体化管理平台"'
+  replaceManifest("name", title);
+  replaceManifest("h5.router.base", '"/mobile/"');
+  replaceManifest("h5.title", title);
+  
+} else if (PLATFORM === "mine") {
+	  // 矿山
+	let title = '"矿山车辆管理平台"'
+	replaceManifest("name", '"矿山车辆管理平台"');
+	replaceManifest("h5.router.base", '"/mobileMine/"');
+	replaceManifest("h5.title", title);
+}
+
+// console.log(Manifest,'Manifest')
+
+fs.writeFileSync(manifestPath, Manifest, {
+  flag: "w",
+});
+
+
+
+

+ 734 - 0
package-lock.json

@@ -0,0 +1,734 @@
+{
+  "name": "package",
+  "version": "1.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@amap/amap-jsapi-loader": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/@amap/amap-jsapi-loader/-/amap-jsapi-loader-1.0.1.tgz",
+      "integrity": "sha512-nPyLKt7Ow/ThHLkSvn2etQlUzqxmTVgK7bIgwdBRTg2HK5668oN7xVxkaiRe3YZEzGzfV2XgH5Jmu2T73ljejw=="
+    },
+    "@babel/runtime": {
+      "version": "7.24.8",
+      "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.24.8.tgz",
+      "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==",
+      "requires": {
+        "regenerator-runtime": "^0.14.0"
+      }
+    },
+    "@jridgewell/gen-mapping": {
+      "version": "0.3.5",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
+      "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
+      "dev": true,
+      "requires": {
+        "@jridgewell/set-array": "^1.2.1",
+        "@jridgewell/sourcemap-codec": "^1.4.10",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      }
+    },
+    "@jridgewell/resolve-uri": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+      "dev": true
+    },
+    "@jridgewell/set-array": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+      "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+      "dev": true
+    },
+    "@jridgewell/source-map": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.6.tgz",
+      "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
+      "dev": true,
+      "requires": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.25"
+      }
+    },
+    "@jridgewell/sourcemap-codec": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+      "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+      "dev": true
+    },
+    "@jridgewell/trace-mapping": {
+      "version": "0.3.25",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+      "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+      "dev": true,
+      "requires": {
+        "@jridgewell/resolve-uri": "^3.1.0",
+        "@jridgewell/sourcemap-codec": "^1.4.14"
+      }
+    },
+    "@types/eslint": {
+      "version": "8.56.10",
+      "resolved": "https://registry.npmmirror.com/@types/eslint/-/eslint-8.56.10.tgz",
+      "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==",
+      "dev": true,
+      "requires": {
+        "@types/estree": "*",
+        "@types/json-schema": "*"
+      }
+    },
+    "@types/eslint-scope": {
+      "version": "3.7.7",
+      "resolved": "https://registry.npmmirror.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
+      "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
+      "dev": true,
+      "requires": {
+        "@types/eslint": "*",
+        "@types/estree": "*"
+      }
+    },
+    "@types/estree": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.5.tgz",
+      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+      "dev": true
+    },
+    "@types/json-schema": {
+      "version": "7.0.15",
+      "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz",
+      "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+      "dev": true
+    },
+    "@types/node": {
+      "version": "20.14.10",
+      "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.14.10.tgz",
+      "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==",
+      "dev": true,
+      "requires": {
+        "undici-types": "~5.26.4"
+      }
+    },
+    "@webassemblyjs/ast": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/ast/-/ast-1.12.1.tgz",
+      "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/helper-numbers": "1.11.6",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.6"
+      }
+    },
+    "@webassemblyjs/floating-point-hex-parser": {
+      "version": "1.11.6",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
+      "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==",
+      "dev": true
+    },
+    "@webassemblyjs/helper-api-error": {
+      "version": "1.11.6",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
+      "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==",
+      "dev": true
+    },
+    "@webassemblyjs/helper-buffer": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz",
+      "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==",
+      "dev": true
+    },
+    "@webassemblyjs/helper-numbers": {
+      "version": "1.11.6",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
+      "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/floating-point-hex-parser": "1.11.6",
+        "@webassemblyjs/helper-api-error": "1.11.6",
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "@webassemblyjs/helper-wasm-bytecode": {
+      "version": "1.11.6",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
+      "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==",
+      "dev": true
+    },
+    "@webassemblyjs/helper-wasm-section": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz",
+      "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.12.1",
+        "@webassemblyjs/helper-buffer": "1.12.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+        "@webassemblyjs/wasm-gen": "1.12.1"
+      }
+    },
+    "@webassemblyjs/ieee754": {
+      "version": "1.11.6",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
+      "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
+      "dev": true,
+      "requires": {
+        "@xtuc/ieee754": "^1.2.0"
+      }
+    },
+    "@webassemblyjs/leb128": {
+      "version": "1.11.6",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
+      "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
+      "dev": true,
+      "requires": {
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "@webassemblyjs/utf8": {
+      "version": "1.11.6",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
+      "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==",
+      "dev": true
+    },
+    "@webassemblyjs/wasm-edit": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz",
+      "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.12.1",
+        "@webassemblyjs/helper-buffer": "1.12.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+        "@webassemblyjs/helper-wasm-section": "1.12.1",
+        "@webassemblyjs/wasm-gen": "1.12.1",
+        "@webassemblyjs/wasm-opt": "1.12.1",
+        "@webassemblyjs/wasm-parser": "1.12.1",
+        "@webassemblyjs/wast-printer": "1.12.1"
+      }
+    },
+    "@webassemblyjs/wasm-gen": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz",
+      "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.12.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+        "@webassemblyjs/ieee754": "1.11.6",
+        "@webassemblyjs/leb128": "1.11.6",
+        "@webassemblyjs/utf8": "1.11.6"
+      }
+    },
+    "@webassemblyjs/wasm-opt": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz",
+      "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.12.1",
+        "@webassemblyjs/helper-buffer": "1.12.1",
+        "@webassemblyjs/wasm-gen": "1.12.1",
+        "@webassemblyjs/wasm-parser": "1.12.1"
+      }
+    },
+    "@webassemblyjs/wasm-parser": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz",
+      "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.12.1",
+        "@webassemblyjs/helper-api-error": "1.11.6",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+        "@webassemblyjs/ieee754": "1.11.6",
+        "@webassemblyjs/leb128": "1.11.6",
+        "@webassemblyjs/utf8": "1.11.6"
+      }
+    },
+    "@webassemblyjs/wast-printer": {
+      "version": "1.12.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz",
+      "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==",
+      "dev": true,
+      "requires": {
+        "@webassemblyjs/ast": "1.12.1",
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "@xtuc/ieee754": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+      "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+      "dev": true
+    },
+    "@xtuc/long": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmmirror.com/@xtuc/long/-/long-4.2.2.tgz",
+      "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+      "dev": true
+    },
+    "acorn": {
+      "version": "8.12.1",
+      "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.12.1.tgz",
+      "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
+      "dev": true
+    },
+    "acorn-import-attributes": {
+      "version": "1.9.5",
+      "resolved": "https://registry.npmmirror.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
+      "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
+      "dev": true
+    },
+    "ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
+      "requires": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      }
+    },
+    "ajv-keywords": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+      "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+      "dev": true
+    },
+    "browserslist": {
+      "version": "4.23.2",
+      "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.23.2.tgz",
+      "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==",
+      "dev": true,
+      "requires": {
+        "caniuse-lite": "^1.0.30001640",
+        "electron-to-chromium": "^1.4.820",
+        "node-releases": "^2.0.14",
+        "update-browserslist-db": "^1.1.0"
+      }
+    },
+    "buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+      "dev": true
+    },
+    "caniuse-lite": {
+      "version": "1.0.30001642",
+      "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz",
+      "integrity": "sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==",
+      "dev": true
+    },
+    "chrome-trace-event": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz",
+      "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==",
+      "dev": true
+    },
+    "commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "dev": true
+    },
+    "copy-text-to-clipboard": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz",
+      "integrity": "sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q=="
+    },
+    "core-js": {
+      "version": "3.37.1",
+      "resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.37.1.tgz",
+      "integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw=="
+    },
+    "dayjs": {
+      "version": "1.11.11",
+      "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.11.tgz",
+      "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg=="
+    },
+    "dingtalk-jsapi": {
+      "version": "3.0.36",
+      "resolved": "https://registry.npmmirror.com/dingtalk-jsapi/-/dingtalk-jsapi-3.0.36.tgz",
+      "integrity": "sha512-oGDNwqpOzRtMifY/POqPvsLRUYpt/csw7mBfczFzRaPHYyezj7JPGkBybCNW/PA/4DvVIAe+RJEkM3cdtVHFtA==",
+      "requires": {
+        "promise-polyfill": "^7.1.0"
+      }
+    },
+    "electron-to-chromium": {
+      "version": "1.4.828",
+      "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.828.tgz",
+      "integrity": "sha512-QOIJiWpQJDHAVO4P58pwb133Cwee0nbvy/MV1CwzZVGpkH1RX33N3vsaWRCpR6bF63AAq366neZrRTu7Qlsbbw==",
+      "dev": true
+    },
+    "enhanced-resolve": {
+      "version": "5.17.0",
+      "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz",
+      "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.2.4",
+        "tapable": "^2.2.0"
+      }
+    },
+    "es-module-lexer": {
+      "version": "1.5.4",
+      "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz",
+      "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==",
+      "dev": true
+    },
+    "escalade": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.1.2.tgz",
+      "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
+      "dev": true
+    },
+    "eslint-scope": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz",
+      "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+      "dev": true,
+      "requires": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^4.1.1"
+      }
+    },
+    "esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz",
+      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+      "dev": true,
+      "requires": {
+        "estraverse": "^5.2.0"
+      },
+      "dependencies": {
+        "estraverse": {
+          "version": "5.3.0",
+          "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz",
+          "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+          "dev": true
+        }
+      }
+    },
+    "estraverse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-4.3.0.tgz",
+      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+      "dev": true
+    },
+    "events": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz",
+      "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+      "dev": true
+    },
+    "fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "dev": true
+    },
+    "fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true
+    },
+    "glob-to-regexp": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+      "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
+      "dev": true
+    },
+    "graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "dev": true
+    },
+    "has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true
+    },
+    "image-conversion": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/image-conversion/-/image-conversion-2.1.1.tgz",
+      "integrity": "sha512-hnMOmP7q2jxA+52FZ+wHNhg3fdFRlgfngsQH2JQHEQkafY7tj/8F15e6Rv/RxDegc872jvyaRHwMbkTZK1Cjbg=="
+    },
+    "jest-worker": {
+      "version": "27.5.1",
+      "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-27.5.1.tgz",
+      "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+      "dev": true,
+      "requires": {
+        "@types/node": "*",
+        "merge-stream": "^2.0.0",
+        "supports-color": "^8.0.0"
+      }
+    },
+    "jsencrypt": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmmirror.com/jsencrypt/-/jsencrypt-3.3.2.tgz",
+      "integrity": "sha512-arQR1R1ESGdAxY7ZheWr12wCaF2yF47v5qpB76TtV64H1pyGudk9Hvw8Y9tb/FiTIaaTRUyaSnm5T/Y53Ghm/A=="
+    },
+    "json-parse-even-better-errors": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+      "dev": true
+    },
+    "json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "loader-runner": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.0.tgz",
+      "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
+      "dev": true
+    },
+    "merge-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz",
+      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+      "dev": true
+    },
+    "mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "dev": true
+    },
+    "mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "dev": true,
+      "requires": {
+        "mime-db": "1.52.0"
+      }
+    },
+    "mutation-observer": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/mutation-observer/-/mutation-observer-1.0.3.tgz",
+      "integrity": "sha512-M/O/4rF2h776hV7qGMZUH3utZLO/jK7p8rnNgGkjKUw8zCGjRQPxB8z6+5l8+VjRUQ3dNYu4vjqXYLr+U8ZVNA=="
+    },
+    "neo-async": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz",
+      "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+      "dev": true
+    },
+    "node-releases": {
+      "version": "2.0.14",
+      "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.14.tgz",
+      "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
+      "dev": true
+    },
+    "picocolors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.1.tgz",
+      "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==",
+      "dev": true
+    },
+    "promise-polyfill": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmmirror.com/promise-polyfill/-/promise-polyfill-7.1.2.tgz",
+      "integrity": "sha512-FuEc12/eKqqoRYIGBrUptCBRhobL19PS2U31vMNTfyck1FxPyMfgsXyW4Mav85y/ZN1hop3hOwRlUDok23oYfQ=="
+    },
+    "punycode": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
+      "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+      "dev": true
+    },
+    "randombytes": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz",
+      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "regenerator-runtime": {
+      "version": "0.14.1",
+      "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+      "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
+    },
+    "safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "dev": true
+    },
+    "schema-utils": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.3.0.tgz",
+      "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
+      "dev": true,
+      "requires": {
+        "@types/json-schema": "^7.0.8",
+        "ajv": "^6.12.5",
+        "ajv-keywords": "^3.5.2"
+      }
+    },
+    "serialize-javascript": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmmirror.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+      "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
+      "dev": true,
+      "requires": {
+        "randombytes": "^2.1.0"
+      }
+    },
+    "source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true
+    },
+    "source-map-support": {
+      "version": "0.5.21",
+      "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz",
+      "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+      "dev": true,
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "supports-color": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz",
+      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+      "dev": true,
+      "requires": {
+        "has-flag": "^4.0.0"
+      }
+    },
+    "tapable": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmmirror.com/tapable/-/tapable-2.2.1.tgz",
+      "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+      "dev": true
+    },
+    "terser": {
+      "version": "5.31.2",
+      "resolved": "https://registry.npmmirror.com/terser/-/terser-5.31.2.tgz",
+      "integrity": "sha512-LGyRZVFm/QElZHy/CPr/O4eNZOZIzsrQ92y4v9UJe/pFJjypje2yI3C2FmPtvUEnhadlSbmG2nXtdcjHOjCfxw==",
+      "dev": true,
+      "requires": {
+        "@jridgewell/source-map": "^0.3.3",
+        "acorn": "^8.8.2",
+        "commander": "^2.20.0",
+        "source-map-support": "~0.5.20"
+      }
+    },
+    "terser-webpack-plugin": {
+      "version": "5.3.10",
+      "resolved": "https://registry.npmmirror.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz",
+      "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==",
+      "dev": true,
+      "requires": {
+        "@jridgewell/trace-mapping": "^0.3.20",
+        "jest-worker": "^27.4.5",
+        "schema-utils": "^3.1.1",
+        "serialize-javascript": "^6.0.1",
+        "terser": "^5.26.0"
+      }
+    },
+    "undici-types": {
+      "version": "5.26.5",
+      "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-5.26.5.tgz",
+      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+      "dev": true
+    },
+    "update-browserslist-db": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
+      "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==",
+      "dev": true,
+      "requires": {
+        "escalade": "^3.1.2",
+        "picocolors": "^1.0.1"
+      }
+    },
+    "uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "dev": true,
+      "requires": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "uview-ui": {
+      "version": "2.0.37",
+      "resolved": "https://registry.npmmirror.com/uview-ui/-/uview-ui-2.0.37.tgz",
+      "integrity": "sha512-iBcWNmQa01Wr+z004G6XIVPDctOrJIAx7LObQceUAPxZh6kJYjIOAMp5JE1K4VpoMV5bKYDpCd0gmX+M4nTEQQ=="
+    },
+    "vconsole": {
+      "version": "3.15.1",
+      "resolved": "https://registry.npmmirror.com/vconsole/-/vconsole-3.15.1.tgz",
+      "integrity": "sha512-KH8XLdrq9T5YHJO/ixrjivHfmF2PC2CdVoK6RWZB4yftMykYIaXY1mxZYAic70vADM54kpMQF+dYmvl5NRNy1g==",
+      "requires": {
+        "@babel/runtime": "^7.17.2",
+        "copy-text-to-clipboard": "^3.0.1",
+        "core-js": "^3.11.0",
+        "mutation-observer": "^1.0.3"
+      }
+    },
+    "watchpack": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmmirror.com/watchpack/-/watchpack-2.4.1.tgz",
+      "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==",
+      "dev": true,
+      "requires": {
+        "glob-to-regexp": "^0.4.1",
+        "graceful-fs": "^4.1.2"
+      }
+    },
+    "webpack": {
+      "version": "5.93.0",
+      "resolved": "https://registry.npmmirror.com/webpack/-/webpack-5.93.0.tgz",
+      "integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==",
+      "dev": true,
+      "requires": {
+        "@types/eslint-scope": "^3.7.3",
+        "@types/estree": "^1.0.5",
+        "@webassemblyjs/ast": "^1.12.1",
+        "@webassemblyjs/wasm-edit": "^1.12.1",
+        "@webassemblyjs/wasm-parser": "^1.12.1",
+        "acorn": "^8.7.1",
+        "acorn-import-attributes": "^1.9.5",
+        "browserslist": "^4.21.10",
+        "chrome-trace-event": "^1.0.2",
+        "enhanced-resolve": "^5.17.0",
+        "es-module-lexer": "^1.2.1",
+        "eslint-scope": "5.1.1",
+        "events": "^3.2.0",
+        "glob-to-regexp": "^0.4.1",
+        "graceful-fs": "^4.2.11",
+        "json-parse-even-better-errors": "^2.3.1",
+        "loader-runner": "^4.2.0",
+        "mime-types": "^2.1.27",
+        "neo-async": "^2.6.2",
+        "schema-utils": "^3.2.0",
+        "tapable": "^2.1.1",
+        "terser-webpack-plugin": "^5.3.10",
+        "watchpack": "^2.4.1",
+        "webpack-sources": "^3.2.3"
+      }
+    },
+    "webpack-sources": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmmirror.com/webpack-sources/-/webpack-sources-3.2.3.tgz",
+      "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
+      "dev": true
+    }
+  }
+}

+ 24 - 0
package.json

@@ -0,0 +1,24 @@
+{
+  "name": "package",
+  "version": "1.0.0",
+  "description": "",
+  "main": "main.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "",
+  "license": "ISC",
+  "dependencies": {
+    "@amap/amap-jsapi-loader": "^1.0.1",
+    "dayjs": "^1.11.10",
+    "dingtalk-design-libs": "^0.2.0",
+    "dingtalk-jsapi": "^3.0.36",
+    "image-conversion": "^2.1.1",
+    "jsencrypt": "^3.3.1",
+    "uview-ui": "^2.0.36",
+    "vconsole": "^3.15.1"
+  },
+  "devDependencies": {
+    "webpack": "^5.91.0"
+  }
+}

+ 489 - 0
pages.json

@@ -0,0 +1,489 @@
+{
+    "easycom": {
+        "^u-(.*)": "uview-ui/components/u-$1/u-$1.vue",
+		"^tq-(.*)": "@/components/tq-$1/index.vue"
+    },
+    "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+        {
+            "path": "pages/webViews/webViews",
+            "style": {
+                "navigationBarTitleText": "",
+                "enablePullDownRefresh": false
+                // "navigationStyle": "custom"
+				
+            }
+        }
+		/* {
+            "path": "pages/login/login",
+            "style": {
+                "navigationBarTitleText": "登录",
+                "enablePullDownRefresh": false,
+                "navigationStyle": "custom"
+				
+            }
+        },
+        {
+            "path": "pages/index/index",
+            "style": {
+                "navigationBarTitleText": "首页",
+				"enablePullDownRefresh": false		
+            }
+        },
+        {
+            "path": "pages/flow/flow",
+            "style": {
+                "navigationBarTitleText": "业务流程",
+				"enablePullDownRefresh": false		
+            }
+        },
+        {
+            "path": "pages/flow/diboot",
+            "style": {
+                "navigationBarTitleText": "业务流程",
+				"enablePullDownRefresh": false		
+            }
+        },
+        {
+            "path": "pages/user/user",
+            "style": {
+                "navigationBarTitleText": "我的",
+                "enablePullDownRefresh": false
+            }
+        },
+		{
+		    "path": "pages/realtimeWatch/realtimeWatch",
+		    "style": {
+		        "navigationBarTitleText": "实时定位",
+		        "enablePullDownRefresh": false
+		    }
+		},
+        {
+		    "path": "pages/realtimeWatch/module/lzRealtimeWatch",
+		    "style": {
+		        "navigationBarTitleText": "车辆实时定位",
+		        "enablePullDownRefresh": false
+		    }
+		},
+        {
+            "path": "pages/formTemplate/formTemplate",
+            "style": {
+                "navigationBarTitleText": "表单模板",
+                "enablePullDownRefresh": false
+            }
+        },
+		{
+		    "path": "pages/flowPc/flowPc",
+		    "style": {
+		        "navigationBarTitleText": "业务流程",
+				"enablePullDownRefresh": false		
+		    }
+		},
+		{
+			"path": "pages/driverTask/index",
+			"style": {
+				"navigationBarTitleText": "司机任务",
+				"enablePullDownRefresh": false,
+				"navigationBarBackgroundColor": "#548ce8",
+				"navigationBarTextStyle": "white"
+			}
+		} */
+    ],
+    /* "subPackages": [
+        {
+			"root": "pagesSanitation",
+			"pages": [
+				{
+					"path": "wasteTransfer/wasteTransfer",
+					"style": {
+						"navigationBarTitleText": "垃圾转运",
+						"enablePullDownRefresh": false
+					}
+				},
+                {
+					"path": "machineWorkRecord/machineWorkRecord",
+					"style": {
+						"navigationBarTitleText": "机械作业",
+						"enablePullDownRefresh": false
+					}
+				},
+                {
+					"path": "wasteTransport/wasteTransport",
+					"style": {
+						"navigationBarTitleText": "垃圾收运",
+						"enablePullDownRefresh": false
+					}
+				}
+				// #ifdef H5
+				,
+				{
+					"path": "workDetails/index",
+					"style": {
+						"navigationBarTitleText": "作业界面",
+						"enablePullDownRefresh": false
+					}
+				}
+				// #endif
+		    ]
+		},
+        
+		{
+			"root": "pagesMap",
+			"pages": [
+				{
+					"path": "pastRoute/pastRoute",
+					"style": {
+						"navigationBarTitleText": "历史轨迹",
+						"enablePullDownRefresh": false
+					}
+				},
+				{
+					"path": "viewRoute/viewRoute",
+					"style": {
+						"navigationBarTitleText": "查看路线",
+						"enablePullDownRefresh": false
+					}
+				}
+		    ]
+		},
+		{
+			"root": "pagesVideo",
+			"pages": [
+				// #ifdef H5
+				{
+					"path": "monitor/monitor",
+					"style": {
+						"navigationBarTitleText": "实时视频",
+						"enablePullDownRefresh": false
+					}
+				},
+                {
+					"path": "h5playback/index",
+					"style": {
+						"navigationBarTitleText": "视频回放",
+						"enablePullDownRefresh": false
+					}
+				}
+				// #endif
+				
+		    ]
+		},
+		{
+			"root": "pagesBase",
+			"pages": [
+				{
+					"path": "changePassword/changePassword",
+					"style": {
+						"navigationBarTitleText": "密码设置",
+						"enablePullDownRefresh": false
+					}
+				},
+				{
+					"path": "userData/userData",
+					"style": {
+						"navigationBarTitleText": "个人资料",
+						"enablePullDownRefresh": false
+					}
+				},
+				{
+					"path": "materialApplication/index",
+					"style": {
+						"navigationBarTitleText": "物品申领",
+						"enablePullDownRefresh": false
+					}
+				},
+				{
+					"path": "materialRecord/index",
+					"style": {
+						"navigationBarTitleText": "申领记录",
+						"enablePullDownRefresh": false
+					}
+				},
+				{
+					"path": "vehManage/index",
+					"style": {
+						"navigationBarTitleText": "车辆管理",
+						"enablePullDownRefresh": false
+					}
+				},
+				{
+					"path": "vehManage/add",
+					"style": {
+						"navigationBarTitleText": "新增车辆",
+						"enablePullDownRefresh": false
+					}
+				},
+				{
+					"path": "driverManage/index",
+					"style": {
+						"navigationBarTitleText": "司机管理",
+						"enablePullDownRefresh": false
+					}
+				},
+				{
+					"path": "driverManage/add",
+					"style": {
+						"navigationBarTitleText": "新增司机",
+						"enablePullDownRefresh": false
+					}
+				},
+				{
+					"path": "sysMessage/index",
+					"style": {
+						"navigationBarTitleText": "车务消息",
+						"enablePullDownRefresh": false
+					}
+				},
+				{
+					"path": "taskQrCode/index",
+					"style": {
+						"navigationBarTitleText": "扫码信息",
+						"enablePullDownRefresh": false
+					}
+				}
+		    ]
+		},
+		{
+			"root": "peixun",
+			"pages": [
+				{
+					"path": "ziliao/ziliao",
+					"style": {
+						"navigationBarTitleText": "培训资料",
+						"enablePullDownRefresh": false
+					}
+				}
+			]
+		},
+        {
+			"root": "pagesVehDispatch",
+			"pages": [
+				{
+					"path": "vehInspection/index",
+					"style": {
+						"navigationBarTitleText": "车辆点检",
+						"enablePullDownRefresh": false
+					}
+				},
+				{
+					"path": "oilApply/index",
+					"style": {
+						"navigationBarTitleText": "加油登记",
+						"enablePullDownRefresh": false
+					}
+				}
+            ]
+        },
+        {
+			"root": "workflow",
+			"pages": [
+				
+                {
+					"path": "vehicleRecord/index",
+					"style": {
+						"navigationBarTitleText": "用车记录",
+						"enablePullDownRefresh": false,
+						"navigationBarBackgroundColor": "#548ce8",
+						"navigationBarTextStyle": "white"
+					}
+				},
+                {
+					"path": "vehicleRepairRecord/index",
+					"style": {
+						"navigationBarTitleText": "车辆维修记录",
+						"enablePullDownRefresh": false,
+						"navigationBarBackgroundColor": "#548ce8",
+						"navigationBarTextStyle": "white"
+					}
+				},
+                {
+					"path": "vehicleMaintenanceRecord/index",
+					"style": {
+						"navigationBarTitleText": "车辆维保记录",
+						"enablePullDownRefresh": false,
+						"navigationBarBackgroundColor": "#548ce8",
+						"navigationBarTextStyle": "white"
+					}
+				},
+                {
+					"path": "recordCost/index",
+					"style": {
+						"navigationBarTitleText": "费用记录",
+						"enablePullDownRefresh": false,
+						"navigationBarBackgroundColor": "#548ce8",
+						"navigationBarTextStyle": "white"
+					}
+				},
+				{
+					"path": "eventRecord/index",
+					"style": {
+						"navigationBarTitleText": "事件记录",
+						"enablePullDownRefresh": false,
+						"navigationBarBackgroundColor": "#548ce8",
+						"navigationBarTextStyle": "white"
+					}
+				}
+            ]
+        },
+		{
+			"root": "alarm",
+			"pages": [
+				{
+					"path": "driverBehavior/index",
+					"style": {
+						"navigationStyle": "custom",
+						"navigationBarTitleText": "驾驶行为报警",
+						"enablePullDownRefresh": false
+					}
+				},
+				{
+					"path": "proactiveSecurity/index",
+					"style": {
+						"navigationBarTitleText": "主动安全报警",
+						"enablePullDownRefresh": false,
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "blindSpot/index",
+					"style": {
+						"navigationBarTitleText": "车辆盲点报警",
+						"enablePullDownRefresh": false,
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "proactiveSecurity/detail",
+					"style": {
+						"navigationBarTitleText": "主动安全报警详情证据",
+						"enablePullDownRefresh": false,
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "driverBehavior/detail",
+					"style": {
+						"navigationBarTitleText": "驾驶行为报警详情证据",
+						"enablePullDownRefresh": false,
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "blindSpot/detail",
+					"style": {
+						"navigationBarTitleText": "车辆盲点报警详情证据",
+						"enablePullDownRefresh": false,
+						"navigationStyle": "custom"
+					}
+				}
+			]
+		},
+		{
+			"root": "pagesMine",
+			"pages": [
+				{
+					"path": "driverTask/index",
+					"style": {
+						"navigationBarTitleText": "任务",
+						"enablePullDownRefresh": false
+					}
+				},
+				{
+					"path": "testPage/index",
+					"style": {
+						"navigationBarTitleText": "测试页面",
+						"enablePullDownRefresh": false
+					}
+				},
+				{
+					"path": "testPage2/index",
+					"style": {
+						"navigationBarTitleText": "测试页面",
+						"enablePullDownRefresh": false
+					}
+				}
+		    ]
+		},
+        {
+			"root": "learnMobile",
+			"pages": [
+               {
+					"path": "materialList/materialList",
+					"style": {
+						"navigationBarTitleText": "课程学习",
+						"enablePullDownRefresh": false
+					}
+				},
+                {
+					"path": "materialList/details",
+					"style": {
+						"navigationBarTitleText": "课程学习",
+						"enablePullDownRefresh": false
+					}
+				},
+                {
+					"path": "paperList/paperList",
+					"style": {
+						"navigationBarTitleText": "在线考试",
+						"enablePullDownRefresh": false
+					}
+				},
+                {
+					"path": "paperList/paperQuestionInfo",
+					"style": {
+						"navigationBarTitleText": "答题",
+						"enablePullDownRefresh": false
+					}
+				},
+                {
+					"path": "record/record",
+					"style": {
+						"navigationBarTitleText": "学习记录",
+						"enablePullDownRefresh": false
+					}
+				},
+                {
+					"path": "userPaperRecordList/userPaperRecordList",
+					"style": {
+						"navigationBarTitleText": "考试记录",
+						"enablePullDownRefresh": false
+					}
+				}
+			]
+		}
+    ], */
+    "globalStyle": {
+        "navigationBarBackgroundColor": "#FFFFFF",
+        "navigationBarTextStyle": "black",
+        "backgroundColor": "#FFFFFF",
+		"h5": {
+			"navigationStyle": "custom"//去除所有顶部
+		}
+    },
+    /* "tabBar": {
+        "color": "#7A7E83",
+        "selectedColor": "#007AFF",
+        "borderStyle": "white",
+        "backgroundColor": "#FFFFFF",
+        "list": [ 
+            {
+				"pagePath": "pages/index/index",
+				"text": "首页",
+				"iconPath": "static/tabbar/homepage-un.png",
+				"selectedIconPath": "static/tabbar/homepage.png"
+			},
+			{
+				"pagePath": "pages/driverTask/index",
+				"text": "司机",
+				"iconPath": "static/tabbar/driver-un.png",
+				"selectedIconPath": "static/tabbar/driver.png"
+			},
+			{
+				"pagePath": "pages/user/user",
+				"text": "我的",
+				"iconPath": "static/tabbar/management-un.png",
+				"selectedIconPath": "static/tabbar/management.png"
+			}
+        ]
+    }, */
+    "uniIdRouter": {}
+}

+ 658 - 0
pages/driverTask/index.vue

@@ -0,0 +1,658 @@
+<template>
+    <view class="registrationRecord-box">
+        <view class="list-box">
+            <u--form label-width="auto" ref="uForm" :model="form" labelAlign="right">
+                <!-- <u-subsection :list="list" mode="subsection" :current="curNow" @change="sectionChange"></u-subsection> -->
+                <u-tabs :list="tablist" 
+                  :itemStyle="{
+                    width: '330rpx',
+                    height: '80rpx'
+                  }"
+                  :activeStyle="{
+                    color:'#4573FC'
+                  }"
+                  lineColor="#4573FC"
+                  @click="({index}) => {
+                    curNow = index
+                    anewQueryMyList()
+                  }"
+                >
+                </u-tabs>
+                <u-cell-group customStyle="margin-top: 2px">
+                    <template>
+                        <u-cell title="任务开始时间" :border="true" :isLink="true"
+                            @click="calendarShow = true">
+                            <view slot="value">
+                                <u-form-item label="" prop="form.startTime">
+                                    <template v-if="form.createTimeEnd">
+                                        {{ form.createTimeBegin }} - {{ form.createTimeEnd
+                                        }}</template>
+                                    <template v-else></template>
+                                </u-form-item>
+                            </view>
+							
+							<u-icon v-if="form.createTimeEnd == ''" slot="right-icon" size="16" name="arrow-right"></u-icon>
+							<view v-else slot="right-icon" @click.stop="() => {
+								form.createTimeBegin = ''
+								form.createTimeEnd = ''
+								anewQueryMyList()
+							}" style="margin-left: 20rpx;">
+							  <u-icon size="16" name="close-circle"></u-icon>
+							</view>
+							
+                        </u-cell>
+
+                        <!-- <tq-date-time ref="startTimeRef"
+                            :disEndDate="form.createTimeBegin"
+                            @confirm="(data) => {
+                                form.createTimeBegin = data
+                            }">
+                        </tq-date-time> -->
+                    </template>
+
+                    <!-- <template>
+                        <u-cell title="结束时间" :border="true" :isLink="true"
+                            @click="$refs.endTimeRef.show()">
+                            <view slot="value">
+                                <u-form-item label="" prop="form.endTime">
+                                    {{ form.createTimeEnd }}
+                                </u-form-item>
+                            </view>
+                        </u-cell>
+                        <tq-date-time ref="endTimeRef"
+                            :disStartDate="form.createTimeEnd"
+                            @confirm="(data) => {
+                                form.createTimeEnd = data
+                            }">
+                        </tq-date-time>
+                    </template> -->
+
+                    <template>
+                        <u-cell title="选择任务状态" :border="true" :isLink="true"
+                            @click="$refs.selectRef.show()">
+                            <view slot="value">
+                                <u-form-item label="" prop="form.endTime">
+                                    {{ form.exStsName }}
+                                </u-form-item>
+                            </view>
+							
+							<u-icon v-if="form.exStsName == ''" slot="right-icon" size="16" name="arrow-right"></u-icon>
+							<view v-else slot="right-icon" @click.stop="() => {
+								form.exSts = ''
+								form.exStsName = ''
+								anewQueryMyList()
+							}" style="margin-left: 20rpx;">
+							  <u-icon size="16" name="close-circle"></u-icon>
+							</view>
+							
+                        </u-cell>
+                        <tq-select ref="selectRef"
+                            :columns="taskStatus"
+                            @confirm="(data) => {
+                form.exSts = data.value
+                form.exStsName = data.label
+                anewQueryMyList()
+            }">
+                        </tq-select>
+                    </template>
+
+                    <template v-if="curNow === 1">
+                        <u-cell title="车队类型" :border="true" :isLink="true"
+                            @click="$refs.selectOwnTypeRef.show()">
+                            <view slot="value">
+                                <u-form-item label="" prop="form.ownTypeName">
+                                    {{ form.ownTypeName }}
+                                </u-form-item>
+                            </view>
+							
+							<u-icon v-if="form.ownTypeName == ''" slot="right-icon" size="16" name="arrow-right"></u-icon>
+							<view v-else slot="right-icon" @click.stop="() => {
+								form.ownType = ''
+								form.ownTypeName = ''
+								anewQueryMyList()
+							}" style="margin-left: 20rpx;">
+							  <u-icon size="16" name="close-circle"></u-icon>
+							</view>
+							
+                        </u-cell>
+                        <tq-select ref="selectOwnTypeRef"
+                            :columns="ownTypeList"
+                            @confirm="(data) => {
+                form.ownType = data.value
+                form.ownTypeName = data.label
+                anewQueryMyList()
+            }">
+                        </tq-select>
+                    </template>
+
+
+                    <template v-if="curNow === 1">
+                        <u-cell title="司机" :border="true" :isLink="true"
+                            @click="$refs.caruserChooseRef.show()">
+                            <view slot="value">
+                                <u-form-item label="" prop="form.vehicleId">
+                                    {{ form.driverName }}
+                                </u-form-item>
+                            </view>
+							
+							<u-icon v-if="form.driverName == ''" slot="right-icon" size="16" name="arrow-right"></u-icon>
+							<view v-else slot="right-icon" @click.stop="() => {
+								form.driverId = ''
+								form.driverName = ''
+								anewQueryMyList()
+							}" style="margin-left: 20rpx;">
+							  <u-icon size="16" name="close-circle"></u-icon>
+							</view>
+							
+                        </u-cell>
+                        <tq-car-user ref="caruserChooseRef"
+                            :isGetIcon="true"
+                            :queryType="3"
+                            @confirm="(data) => {
+                form.driverId = data.value
+                form.driverName = data.label
+                anewQueryMyList()
+            }"
+                            @onDataLoad="">
+                        </tq-car-user>
+                    </template>
+
+                    <template>
+                        <u-cell title="车辆" :border="true" :isLink="true"
+                            @click="$refs.carChooseRef.show()">
+                            <view slot="value">
+                                <u-form-item label="" prop="form.vehicleId">
+                                    {{ form.plate }}
+                                </u-form-item>
+                            </view>
+							
+							<u-icon v-if="form.plate == ''" slot="right-icon" size="16" name="arrow-right"></u-icon>
+							<view v-else slot="right-icon" @click.stop="() => {
+								form.vehicleId = ''
+								form.plate = ''
+								anewQueryMyList()
+							}" style="margin-left: 20rpx;">
+							  <u-icon size="16" name="close-circle"></u-icon>
+							</view>
+							
+                        </u-cell>
+                        <tq-car-user ref="carChooseRef"
+                            :isGetIcon="true"
+                            @confirm="(data) => {
+                form.vehicleId = data.value
+                form.plate = data.label
+                anewQueryMyList()
+            }"
+                            @onDataLoad="">
+                        </tq-car-user>
+                    </template>
+
+                    <template>
+                        <!-- 重置按钮 -->
+                         <u-button  customStyle="font-size: 24rpx;height: 64rpx;"  :plain="false" size="mini" @click="resetData()">重置</u-button>
+
+                    </template>
+
+
+                </u-cell-group>
+            </u--form>
+        </view>
+        <view class="item-list-box">
+            <view class="info-box" v-for="(item, index) in queryMyListdata" :key="index">
+                <u-row customStyle="margin-bottom: 10px">
+                    <u-col span="6">
+                        <view class="demo-layout bg-purple-light">申请人:{{ item.applyName }}</view>
+                    </u-col>
+                    <u-col span="6">
+                        <view class="demo-layout bg-purple">部门:{{ item.deptName }}</view>
+                    </u-col>
+                </u-row>
+                <u-row customStyle="margin-bottom: 10px">
+                    <u-col span="12">
+                        <view class="demo-layout bg-purple-light">申请人联系电话:{{ item.applyTel }}</view>
+                    </u-col>
+					
+                </u-row>
+                <u-row customStyle="margin-bottom: 10px">
+                    <u-col span="6">
+                        <view class="demo-layout bg-purple">用车类型:{{ item.matterType }}</view>
+                    </u-col>
+					<!-- <u-col span="6">
+					    <view class="demo-layout bg-purple">任务状态:{{ item.exStsName }}</view>
+					</u-col> -->
+                </u-row>
+                <u-row customStyle="margin-bottom: 10px">
+                    <u-col span="12">
+                        <view class="demo-layout bg-purple-light">预计执行时间:{{ item.startPlanTime }} — {{ item.endPlanTime
+                            }}
+                        </view>
+                    </u-col>
+                </u-row>
+                <u-row customStyle="margin-bottom: 10px" v-if="item.pointAddr">
+                    <u-col span="12">
+                        <view class="demo-layout bg-purple-light">起止点:{{ pointAddrParse(item.pointAddr) }}</view>
+                    </u-col>
+                </u-row>
+                <u-row customStyle="margin-bottom: 10px">
+                    <u-col span="6">
+                        <view class="demo-layout bg-purple-light">用车人数:{{ item.totalNum }}</view>
+                    </u-col>
+                    <u-col span="6">
+                        <view class="demo-layout bg-purple">任务状态:{{ item.exStsName }}</view>
+                    </u-col>
+                </u-row>
+                <view class="hide-warp" v-if="item.openOther">
+                  <u-row customStyle="margin-bottom: 10px">
+                      <u-col span="6">
+                          <view class="demo-layout bg-purple-light">车辆:{{ item.plate }}({{ item.ownType }})</view>
+                      </u-col>
+                      <u-col span="6">
+                          <view class="demo-layout bg-purple">车辆所属:{{ item.vehicleDeptName }}</view>
+                      </u-col>
+                  </u-row>
+                  <u-row customStyle="margin-bottom: 10px">
+                      <u-col span="6">
+                          <view class="demo-layout bg-purple-light">司机:{{ item.driverName }}({{ item.ownType }})</view>
+                      </u-col>
+                      <u-col span="6">
+                          <view class="demo-layout bg-purple">司机所属:{{ item.driverDeptName }}</view>
+                      </u-col>
+                  </u-row>
+                  <u-row customStyle="margin-bottom: 10px">
+                      <u-col span="12">
+                          <view class="demo-layout bg-purple-light">备注信息:{{ item.remark }}</view>
+                      </u-col>
+                  </u-row>
+                </view>
+                <view @click="item.openOther = !item.openOther" class="open-warp">
+                  {{ item.openOther?'收起':'展开' }}
+                </view>
+                <u-row customStyle="margin-bottom: 10px">
+                    <u-col span="12">
+                        <view style="display: flex; justify-content: center;">
+                            <u-button type="primary" icon="phone" @tap="phoneCall(item.driverTel)" :plain="true"
+                              color="#4573FC"
+							  v-if="$hasPermi('mobile:index:base:driverTask:driverPhone')"
+                                customStyle="width: 200rpx;height:64rpx" shape="circle"
+                                text="司机"></u-button>
+                            <u-button type="warning" icon="phone" @tap="phoneCall(item.applyTel)" :plain="true"
+                              color="#F7A254"
+							  v-if="$hasPermi('mobile:index:base:driverTask:applyPhone')"
+                                customStyle="width:200rpx;height:64rpx" shape="circle"
+                                text="申请人"></u-button>
+                            <u-button type="primary" icon="phone" @tap="phoneCall(item.userTel)" :plain="true"
+                              color="#4573FC"
+							  v-if="$hasPermi('mobile:index:base:driverTask:usePhone')"
+                                customStyle="width: 200rpx;height:64rpx" shape="circle"
+                                text="用车人"></u-button>
+                        </view>
+                    </u-col>
+                </u-row>
+				
+				<u-row customStyle="margin-bottom: 10px">
+				    <u-col span="9" style="color: #4573FC;font-size: 28rpx;">
+						<view v-for="(fitem,findex) in item.listCost" :key="findex">
+							{{fitem.costName}}:{{fitem.price}} ;{{fitem.processStatusName}}
+						</view>
+					</u-col>
+					 
+					 <u-col span="3">
+						 <view 
+							style="color: #4573FC;display: flex;font-size: 28rpx;text-decoration: underline;justify-content: flex-end;"
+							@click="goFlow(item)"
+						 >
+							 <u-icon name="plus" color="#4573FC" size="14"></u-icon>
+							 费用报销
+						 </view>
+					</u-col>
+				</u-row>
+                <!-- <u-button type="warning" customStyle="margin-left: 2rpx;" text="退回"
+                    @click="rebutConfirmation('rebut', item)"></u-button> -->
+                <u-row customStyle="margin-bottom: 10px;" v-if="curNow === 0">
+                    <u-col span="12">
+                        <view style="display: flex;">
+                            <!-- exSts 车辆调度状态:1.未派车、2.已派车、3.已结束、4.已中止 -->
+                            <!-- 任务操作类型:approve.出发和结束、rebut.驳回,可用值:addSignature,approve,cancel,cc,claim,delSignature,delegate,hold,move,msg,postSignature,preSignature,read,rebut,resubmit,start,stop,submit,takeBack,turnInto,unclaim -->
+                            <u-button type="primary" text="出发" @click="postTaskOperate('approve', item)"
+                                v-if="item.exSts === '1'"></u-button>
+                            <u-button type="error" text="结束" @click="postTaskOperate('approve', item)"
+                                v-if="item.exSts === '2'"></u-button>
+                            <u-button type="warning" customStyle="margin-left: 2rpx;" text="退回"
+                                @click="rebutConfirmation('rebut', item)"
+                                v-if="item.exSts === '1'"></u-button>
+                            <u-button :plain="true" :disabled="true" text="已结束" v-if="item.exSts === '3'"></u-button>
+                            <u-button type="error" :plain="true" :disabled="true" text="已中止"
+                                v-if="item.exSts === '4'"></u-button>
+                        </view>
+                    </u-col>
+                </u-row>
+            </view>
+        </view>
+        <u-loadmore :status="status" />
+        <u-toast ref="uToast" />
+        <u-calendar :show="calendarShow" mode="range" :defaultDate="defaultDate" :maxDate="maxDate" minDate="2023-10-01"
+            monthNum="24"
+            @confirm="calendarChange" @close="calendarShow = false"></u-calendar>
+
+        <u-modal :show="modalshow" @confirm="modalconfirm" showCancelButton @cancel="modalcancel" :title="title">
+            <view class="slot-content">
+                <u--textarea
+                    placeholder="请输入退回原因"
+                    border="surround"
+                    v-model="operateMessage"></u--textarea>
+            </view>
+        </u-modal>
+    </view>
+</template>
+<script>
+import { drivertaskList, taskOperate } from "@/api/wfApi/index.js";
+export default {
+    data() {
+        return {
+            calendarShow: false, // 日历
+            calendarStart: this.dayjs().subtract(6, 'day').format('YYYY-MM-DD'),// 开始时间
+            calendarEnd: this.dayjs().subtract(0, 'day').format('YYYY-MM-DD'), // 结束时间
+            defaultDate: [this.dayjs().subtract(6, 'day').format('YYYY-MM-DD'), this.dayjs().subtract(0, 'day').format('YYYY-MM-DD')],
+            maxDate: this.dayjs().add(365, 'day').format('YYYY-MM-DD'),
+            list: ['我的', '全部'],
+            tablist: [
+              { name: '我的'},
+              { name: '全部'},
+            ],
+            curNow: 0,
+            queryMyListdata: [],
+            form: {
+                createTimeBegin: '',//开始时间
+                createTimeEnd: '', //结束时间
+                exSts: '', //车辆调度状态:
+                exStsName: '',
+                vehicleId: '',
+                plate: '',
+                driverId: '',
+                driverName: '',
+                ownType: '',//车队类型
+                ownTypeName: '',
+            },
+            // 车辆调度状态:1.未派车、2.已派车、3.已结束、4.已中止
+            taskStatus: [
+                { label: '未开始', value: 1 },
+                { label: '执行中', value: 2 },
+                { label: '已结束', value: 3 },
+                { label: '已终止', value: 4 },
+            ],
+            // 车队类型:1.内部车队、2.外部车队
+            ownTypeList: [
+                { label: '内部车队', value: 1 },
+                { label: '外部车队', value: 2 },
+            ],
+            operateMessage: '',//退回原因
+            modalshow: false,
+            modalItem: {},
+            title: '请输入退回原因',
+            content: '请输入退回原因',
+            pageIndex: 1,
+            total: 0,
+            status: 'nomore', //loading nomore loadmore
+            openOther:false
+        }
+    },
+    onShow(option) {
+        this.queryMyListdata = []
+        this.pageIndex = 1
+        this.queryMyList();
+    },
+    methods: {
+        resetData(){
+            this.form = {
+                createTimeBegin: '',//开始时间
+                createTimeEnd: '', //结束时间
+                exSts: '', //车辆调度状态:
+                exStsName: '',
+                vehicleId: '',
+                plate: '',
+                driverId: '',
+                driverName: '',
+                ownType: '',//车队类型
+                ownTypeName:''
+            }
+            this.anewQueryMyList()
+        },
+        calendarChange(e) {
+            console.info('选择的时间', e)
+
+            // 修改日期
+            this.form.createTimeBegin = e[0]; // 开始时间
+            this.form.createTimeEnd = e[e.length - 1]; // 结束时间
+            this.defaultDate = [e[0], e[e.length - 1]]
+            this.calendarShow = false;
+            this.anewQueryMyList();
+        },
+        anewQueryMyList() {
+            this.pageIndex = 1;
+            this.queryMyListdata = [];
+            this.queryMyList();
+        },
+        phoneCall(e) {
+            uni.makePhoneCall({
+                phoneNumber: e //仅为示例
+            });
+        },
+        pointAddrParse(e) {
+            let arr = JSON.parse(e)
+            let arrStg = ''
+            for (let item of arr) {
+                arrStg += item.address + '-'
+            }
+            return arrStg.slice(0, -1);
+        },
+        sectionChange(index) {
+            this.curNow = index;
+            this.anewQueryMyList()
+        },
+        toRealMap(item) {
+            let obj = {
+                routeId: item.routeId,
+                classId: item.classId,
+                startAndEnd: item.startAndEnd
+            }
+            let objst = encodeURIComponent(JSON.stringify(obj))
+            uni.navigateTo({
+                url: '/newdriversub/setClassPlan/details?item=' + objst
+            });
+        },
+        async queryMyList() {
+            this.loading = 'loading'
+            let obj = {
+                ...this.form,
+                pageIndex: this.pageIndex,
+                pageSize: 3,
+                queryType: this.curNow + 1 //查询类型:1.本人、2.全部
+            }
+            for (let key in obj) {
+                if (obj[key] === '') delete obj[key]
+            }
+            let { data, code, page } = await drivertaskList(obj)
+            if (code == "0") {
+
+                let list = data.map(item=>
+                  {
+                    return {
+                      ...item,
+                      openOther:false
+                    }
+                  }
+                )
+                this.queryMyListdata = this.queryMyListdata.concat(list)
+                this.total = Number(page.totalCount)
+                this.loading = 'loadmore'
+                if (this.queryMyListdata.length >= this.total) {
+                    this.status = 'nomore'
+                }
+                console.log('数据', this.queryMyListdata)
+            }
+        },
+        rebutConfirmation(e, item) {
+            //请输入原因
+            this.modalshow = true
+            this.modalItem = item
+        },
+        modalcancel(e){
+            this.modalshow = false
+            this.operateMessage=''
+        },
+        modalconfirm(e) {
+            if (this.operateMessage === '') {
+                return uni.showToast({
+                    title: '请输入退回原因',
+                    icon: 'none'
+                });
+            } else {
+                this.modalshow = false
+                this.postTaskOperate('rebut', this.modalItem)
+            }
+
+        },
+        // 操作
+        async postTaskOperate(e, item) {
+            let { data, code, msg } = await taskOperate(
+                {
+                    taskOperate: e,
+                    taskId: item.taskId, //任务id
+                    processInstanceId: item.processInstanceId,//流程实例id
+                    operateMessage: this.operateMessage, //操作原因
+                })
+            if (code == "0") {
+                this.operateMessage = ''
+                uni.showToast({
+                    title: msg || '操作成功',
+                    duration: 2000
+                });
+                this.anewQueryMyList();
+            }
+        },
+		async goFlow(item) {
+			const {id} = item
+			
+			uni.navigateTo({
+			    url: `/pages/flow/diboot?routes=/workflow/mobile/startFlow&workflowKey=system_cost&fromKey=system_flow_veh&fromId=${id}`,
+			})
+			
+			/* let res
+			
+			let isDetails = false
+			let defaultTitle
+			let processInstanceId
+			if(type == 'technologySafe') {
+				res =  await getStartTechnologySafeFormInfo()
+				if(item.tsProcessInstanceId) isDetails=true
+				processInstanceId = item.tsProcessInstanceId
+			} else if (type == 'behaviorSafe') {
+				res =  await getStartBehaviorSafeFormInfo()
+				if(item.bsProcessInstanceId) isDetails=true
+				processInstanceId = item.bsProcessInstanceId
+			}
+			
+			let {code,data} = res
+			
+			if(code == 0 && data) {
+				
+				if(isDetails) {
+					
+					uni.navigateTo({
+					    url: `/pages/flow/diboot?routes=/workflow/mobile/dealTask&taskId=${-1}&processInstanceId=${processInstanceId}&category=${'cc'}&withDealTask=true&fromMobile=true`,
+					})
+				} else {
+					uni.navigateTo({
+						url: `/pages/flow/diboot?routes=/workflow/mobile/startFlow&id=${data}&taskId=${id}&taskType=${type}`
+					})
+				}
+				
+			} */
+			
+		
+		} 
+
+    },
+    // 触底
+    onReachBottom() {
+        console.log('当前条数', this.queryMyListdata.length, '总条数', this.total)
+        if (this.queryMyListdata.length >= this.total) return;
+        this.pageIndex++
+        this.queryMyList();
+    }
+}
+</script>
+<style lang="scss" scoped>
+.registrationRecord-box {
+
+    padding: 0 0 10rpx 0;
+    overflow: unset;
+
+    .list-box {
+        border-radius: 10rpx;
+        background: #fff;
+        // padding: 20rpx;
+        position: sticky;
+
+        // align-items: center;
+        // display: flex;
+        top: 0px;
+        width: 100%;
+        z-index: 999;
+
+        /deep/ {
+
+            .u-form-item__body {
+                padding: 0;
+            }
+        }
+
+    }
+
+    .item-list-box {
+        padding: 10rpx 0 0 0;
+        // background: #fff;
+        position: relative;
+
+
+        .info-box {
+            margin-bottom: 20rpx;
+            border: none;
+            width: 100%;
+            // border-radius: 20rpx;
+            box-shadow: 2rpx 2rpx 5rpx rgba(169, 169, 169, 0.34901960784313724);
+            // height: 150rpx;
+            background-color: rgba(255, 255, 255, 1);
+            display: flex;
+            align-items: center;
+            flex-wrap: wrap;
+            padding: 20rpx;
+            box-sizing: border-box;
+            position: relative;
+
+            font-size: 24rpx;
+
+
+
+
+        }
+
+
+    }
+
+    .slot-content {
+        width: 100%;
+    }
+    .hide-warp{
+      width: 100%;
+    }
+    .open-warp{
+      width: 100%;
+      padding: 20rpx;
+      display: flex;
+      justify-content: flex-end;
+      color: #4573FC;
+    }
+
+}
+</style>

+ 62 - 0
pages/flow/diboot.vue

@@ -0,0 +1,62 @@
+<template>
+    <web-view
+        v-if="authtoken"
+        :src="dibootUrl">
+    </web-view>
+</template>
+
+<script>
+import webview from './webview'
+export default {
+    mixins: [webview],
+    data() {
+        return {
+            dibootUrl: '',
+            query: {},
+        };
+    },
+    onLoad(query) {
+        this.query = query
+
+        this.initData(query)
+    },
+    watch: {
+        authtoken: {
+            immediate: false,
+            deep: true,
+            handler(newVal) {
+                this.initData(this.query)
+            }
+        }
+    },
+    methods: {
+        initData(routeQuery) {
+            let protocol = 'https:'
+            if (document.location.protocol) {
+                protocol = document.location.protocol
+            }
+            let urlParams = ''
+            for (let key in routeQuery) {
+                if (key != 'routes') {
+                    urlParams += `&${key}=${routeQuery[key]}`
+                }
+            }
+            urlParams += `&token=${this.authtoken}`
+            if (process.env.NODE_ENV === 'development') {
+                this.dibootUrl = `${protocol}//tq.5000v.com:8035${routeQuery.routes}?${urlParams}`
+            } else {
+                this.dibootUrl = `${protocol}//${window.location.host}${routeQuery.routes}?${urlParams}`
+            }
+
+        },
+        getLocation() {
+
+        },
+        scanCode() {
+
+        }
+    }
+}
+</script>
+
+<style lang="scss"></style>

+ 64 - 0
pages/flow/flow.vue

@@ -0,0 +1,64 @@
+<template>
+    <web-view
+		v-if="authtoken"
+        :src="dibootUrl">
+    </web-view>
+    <!-- 测试地址 http://localhost:8080/mobile/pages/flow/flow?redirect=/pages/workflowTask/webviewPages/dealTask&id=cb864164-d638-11ee-b785-00ff0b7e144b&definitionId=system_flow_veh%3A46%3A40240578-d634-11ee-8076-00ff0b7e144b&instId=a7551e2f-d638-11ee-b785-00ff0b7e144b&category=todo&allowDeal=true-->
+</template>
+
+<script>
+import webview from './webview'
+export default {
+    mixins: [webview],
+    data() {
+        return {
+            dibootUrl: '',
+			query: {},
+        };
+    },
+    onLoad(query) {
+		this.query = query
+        this.initData(query)
+    },
+	watch: {
+		authtoken: {
+			immediate: false,
+			deep: true,
+			handler(newVal) {
+				this.initData(this.query)
+			}
+		}
+	},
+    methods: {
+		initData(query) {
+			console.log('query', query)
+			let urlParams = ''
+			for (let key in query) {
+			    if (key != 'authtoken') {
+			        urlParams += `&${key}=${query[key]}`
+			    }
+			}
+			urlParams = urlParams.replace('&', '?')
+			this.dibootUrl = `${this.webviewServer}${urlParams}&authtoken=${this.authtoken}`
+			console.log('工作流地址', this.dibootUrl)
+			
+			window.addEventListener("message", receiveMessage, false);
+			
+			function receiveMessage(event) {
+			    console.log('收到消息',event)
+			    uni.switchTab({
+					url: event.data
+				})
+			}
+		},
+        getLocation() {
+
+        },
+        scanCode() {
+
+        }
+    }
+}
+</script>
+
+<style lang="scss"></style>

+ 21 - 0
pages/flow/webview.js

@@ -0,0 +1,21 @@
+import { getToken } from '@/utils/auth'
+let server_url = window.location.host
+if(process.env.NODE_ENV === 'development'){
+    server_url = 'tq.5000v.com:8035'
+}
+export default {
+	data() {
+		return {
+			webviewServer: window.location.protocol+'//'+server_url+'/workflow-h5/#/',
+			// webviewServer: 'http://tq.5000v.com:8035/workflow-h5/#/',
+			// webviewServer: 'http://192.168.31.108:8035/workflow-h5/#/',
+			// webviewServer: 'http://tq.5000v.com:8032/workflow-h5/#/',
+			// authtoken: getToken()
+        }
+	},
+	computed: {
+		authtoken() {
+			return this.$store.state.user.token 
+		}
+	},
+}

+ 73 - 0
pages/flowPc/flowPc.vue

@@ -0,0 +1,73 @@
+<template>
+    <web-view
+		v-if="authtoken"
+        :src="dibootUrl">
+    </web-view>
+    <!-- 测试地址 http://localhost:8080/mobile/pages/flow/flow?redirect=/pages/workflowTask/webviewPages/dealTask&id=cb864164-d638-11ee-b785-00ff0b7e144b&definitionId=system_flow_veh%3A46%3A40240578-d634-11ee-8076-00ff0b7e144b&instId=a7551e2f-d638-11ee-b785-00ff0b7e144b&category=todo&allowDeal=true-->
+</template>
+
+<script>
+import webview from './webview'
+export default {
+    mixins: [webview],
+    data() {
+        return {
+            dibootUrl: '',
+			query: {},
+			fetched: false,
+        };
+    },
+    onLoad(query) {
+		this.query = query
+		// console.log(query,'queryqueryquery')
+		if(this.authtoken) this.initData(query)
+        
+    },
+	watch: {
+		authtoken: {
+			immediate: true,
+			deep: true,
+			handler(newVal) {
+				// console.log(newVal,'newValnewValnewVal')
+				this.initData(this.query)
+			}
+		}
+	},
+    methods: {
+		initData(query) {
+			console.log('query', query)
+			let urlParams = ''
+			let redirect = ''
+			for (let key in query) {
+			    if (key != 'authtoken') {
+			        urlParams += `&${key}=${query[key]}`
+			    }
+				if (key == 'redirect') {
+			        redirect = query[key]
+			    }
+			}
+			if(!redirect) return
+			urlParams = urlParams.replace('&', '?')
+			this.dibootUrl = `${this.webviewServer}${redirect}${urlParams}&authtoken=${this.authtoken}`
+			console.log('工作流地址', this.dibootUrl)
+			
+			window.addEventListener("message", receiveMessage, false);
+			
+			function receiveMessage(event) {
+			    console.log('收到消息',event)
+			    uni.switchTab({
+					url: event.data
+				})
+			}
+		},
+        getLocation() {
+
+        },
+        scanCode() {
+
+        }
+    }
+}
+</script>
+
+<style lang="scss"></style>

+ 21 - 0
pages/flowPc/webview.js

@@ -0,0 +1,21 @@
+import { getToken } from '@/utils/auth'
+let server_url = window.location.host
+if(process.env.NODE_ENV === 'development'){
+    server_url = 'tq.5000v.com:8035'
+}
+export default {
+	data() {
+		return {
+			webviewServer: window.location.protocol+'//'+server_url,
+			// webviewServer: 'http://tq.5000v.com:8035/workflow-h5/#/',
+			// webviewServer: 'http://192.168.31.108:8035/workflow-h5/#/',
+			// webviewServer: 'http://tq.5000v.com:8032/workflow-h5/#/',
+			// authtoken: getToken()
+        }
+	},
+	computed: {
+		authtoken() {
+			return this.$store.state.user.token 
+		}
+	},
+}

+ 117 - 0
pages/formTemplate/formTemplate.vue

@@ -0,0 +1,117 @@
+<template>
+    <view class="form-box">
+        <!-- 注意,如果需要兼容微信小程序,最好通过setRules方法设置rules规则 -->
+        <u--form labelPosition="left" :model="model1" :rules="rules" ref="uForm" labelWidth="60">
+            <u-form-item label="姓名" prop="userInfo.name" borderBottom :required="true">
+                <u--input v-model="model1.userInfo.name" placeholder="请输入姓名" border="none"></u--input>
+            </u-form-item>
+            <u-form-item label="手机号" prop="userInfo.phone" borderBottom :required="true">
+                <u--input v-model="model1.userInfo.phone" placeholder="请输入手机号"  border="none"></u--input>
+            </u-form-item>
+            <u-form-item label="性别" prop="userInfo.sex" borderBottom @click="showSex = true;" :required="true">
+                <u--input v-model="model1.userInfo.sex" disabled disabledColor="#ffffff" placeholder="请选择性别"
+                    border="none"></u--input>
+                <u-icon slot="right" name="arrow-right"></u-icon>
+            </u-form-item>
+        </u--form>
+        <u-action-sheet :show="showSex" :actions="actions" title="请选择性别" description="如果选择保密会报错"
+            @close="showSex = false" @select="sexSelect">
+        </u-action-sheet>
+
+        <u-button type="primary" class="submit" @click="submit" text="提交"></u-button>
+    </view>
+</template>
+
+<script>
+import { submitForm } from "@/api/formTemplate.js"
+export default {
+    data() {
+        return {
+            showSex: false,
+            model1: {
+                userInfo: {
+                    name: 'uView UI',
+                    phone: '',
+                    sex: '男',
+                },
+            },
+            actions: [{
+                name: '男',
+            },
+            {
+                name: '女',
+            },
+            {
+                name: '保密',
+            },
+            ],
+            rules: {
+                'userInfo.name': {
+                    min: 1,
+                    max: 12,
+                    type: 'string',
+                    required: true,
+                    message: '请填写姓名',
+                    trigger: ['blur', 'change']
+                },
+                'userInfo.phone': [{
+                    type: 'number',
+                    required: true,
+                    message: '请填写手机号',
+                    trigger: ['blur', 'change']
+                }, {
+                    type: 'string',
+                    required: true,
+                    pattern: this.$rule.phone, //正则
+                    message: '请填写正确的手机号',
+                    trigger: ['blur', 'change']
+                }],
+                'userInfo.sex': {
+                    type: 'string',
+                    max: 1,
+                    required: true,
+                    message: '请选择男或女',
+                    trigger: ['blur', 'change']
+                },
+            },
+            radio: '',
+            switchVal: false
+        };
+    },
+	onLoad(option) {
+		console.log('页面参',option)
+	},
+    methods: {
+        sexSelect(e) {
+            this.model1.userInfo.sex = e.name
+            this.$refs.uForm.validateField('userInfo.sex')
+        },
+        async submitForm() {
+            console.log('提交')
+            let { data } = await submitForm({ ...this.model1.userInfo })
+        },
+        submit() {
+            this.$refs.uForm.validate().then(async res => {
+                // this.submitForm()
+                uni.$u.toast('校验通过')
+            }).catch(errors => {
+                uni.$u.toast('校验失败')
+            })
+        }
+    },
+    onReady() {
+        //如果需要兼容微信小程序,并且校验规则中含有方法等,只能通过setRules方法设置规则。
+        this.$refs.uForm.setRules(this.rules)
+    },
+};
+</script>
+<style lang="scss" scoped>
+.form-box {
+    padding: 0 20upx;
+    background: #fff;
+
+    .submit {
+        margin-top: 80upx;
+    }
+}
+</style>

+ 1005 - 0
pages/index/index - 副本.vue

@@ -0,0 +1,1005 @@
+<template>
+  <view class="home-container">
+    <template v-if="hasPlat(['sanitation'])">
+      <view class="index-title">
+        智慧环卫 <view class="dot"></view> 让科技建设城市
+      </view>
+      <image :src="$getImages('/assetsMobile/images/index/page.png')" class="background-image"></image>
+      <view class="page-warp">
+        <template v-for="(item,index) in sanitationArr">
+          <view v-if="$hasPermiOr(allPermission(item.sidebarList))" :key="index" class="home-munes" :class="index==0?'pd8':''">
+              <view class="sidebar">
+                  <Sidebar :direction="item.direction" :sidebarList="item.sidebarList" :label="item.label" :titleIcon="item.titleIcon"></Sidebar>
+              </view>
+          </view>
+        </template>
+      </view>
+    </template>
+    <template v-else-if="hasPlat(['enterprises'])">
+
+		<view class="tipfixed" v-if="hasHostPlat(['dingH5']) && $store.state.user.showSts==1">
+			<template v-if="!$store.state.user.isModel">
+				<div>点击演示按钮后进入演示状态,显示模拟数据</div>
+				<u-button type="success" size="mini" 
+					:custom-style="{width: '160rpx'}" @click="$store.dispatch('setIsModel',true)">开始演示</u-button>
+			</template>
+			<template v-else-if="$store.state.user.isModel">
+				<div>点击退出演示后退出演示状态,显示真实数据</div>
+				<u-button type="error" size="mini" 
+					:custom-style="{width: '160rpx'}" @click="$store.dispatch('setIsModel',false)">退出演示</u-button>
+			</template>		
+		</view>
+	
+      <view class="index-title">
+        精准管理 <view class="dot"></view> 让管车更简单
+      </view>
+      <image :src="$getImages('/assetsMobile/images/index/page.png')" class="background-image"></image>
+      <view class="page-warp">
+        <template v-for="(item,index) in enterprisesArr">
+          <view v-if="$hasPermiOr(allPermission(item.sidebarList))" :key="index" class="home-munes" :class="index==0?'pd8':''">
+              <view class="sidebar">
+                  <Sidebar :direction="item.direction" :sidebarList="item.sidebarList" :label="item.label" :titleIcon="item.titleIcon"></Sidebar>
+              </view>
+          </view>
+        </template>
+      </view>
+    </template>
+    <template v-else>
+      <view class="page1-warp">
+        <template v-for="(item,index) in otherArr">
+          <view v-if="$hasPermiOr(allPermission(item.sidebarList))" :key="index" class="home-munes">
+              <view class="sidebar">
+                <Sidebar :sidebarList="item.sidebarList" :label="item.label"></Sidebar>
+              </view>
+          </view>
+        </template>
+      </view>
+    </template>
+    
+  </view>
+</template>
+
+<script>
+import { taskCount } from "@/api/wfApi/index";
+import Sidebar from '@/components/sidebar'
+export default {
+    components: {
+        Sidebar,
+    },
+    data() {
+        return {
+           sanitationArr: [
+            {
+              // label:'',
+              // titleIcon:'',
+              direction:'vertical',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:sanTask:machineWorkRecord', // 权限
+                    label: '机械作业', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/machine-bg.png'), // 图片
+                    url: '/pagesSanitation/machineWorkRecord/machineWorkRecord', // 路径
+                },
+                {
+                    permission: 'mobile:index:sanTask:wasteTransfer', // 权限
+                    label: '垃圾转运', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/transport-bg.png'), // 图片
+                    url: '/pagesSanitation/wasteTransfer/wasteTransfer', // 路径
+                },
+                {
+                    permission: 'mobile:index:sanTask:wasteTransport', // 权限
+                    label: '垃圾收运', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/collect-bg.png'), // 图片
+                    url: '/pagesSanitation/wasteTransport/wasteTransport', // 路径
+                },
+                
+                // {
+                //     permission: '', // 权限
+                //     label: '垃圾分类', // 标题
+                //     icon: this.$getImages('/assetsMobile/images/index/classify-bg.png'), // 图片
+                //     url: '', // 路径
+                // },
+                // {
+                //     permission: '', // 权限
+                //     label: '保洁作业', // 标题
+                //     icon: this.$getImages('/assetsMobile/images/index/clean-bg.png'), // 图片
+                //     url: '', // 路径
+                // },
+              ]
+            },
+            {
+              label:'监控中心',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/monitor-icon.png`),
+              // direction:'',
+              sidebarList:[ // 监控中心
+                {
+                    permission: 'mobie:index:base:realtimeWatch', // 权限
+                    label: '实时定位', // 标题
+                    iconbg:'0',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-real-time-position.png'),//`http://tq.5000v.com:8035/assetsMobile/images/index/menu/icon-real-time-position.png`, // 图片
+                    url: '/pages/realtimeWatch/realtimeWatch', // 路径
+                },
+                {
+                    permission: 'mobie:index:base:pastRoute', // 权限
+                    label: '历史轨迹', // 标题
+                    iconbg:'3',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-history-track.png'), // 图片
+                    url: '/pagesMap/pastRoute/pastRoute', // 路径
+                },
+                // {
+                //     permission: '*', // 权限
+                //     label: '视频回放', // 标题
+                //     icon: this.$getImages('/assetsMobile/images/index/historicaltrack.png'), // 图片
+                //     url: '/pagesVideo/h5playback/index?deviceId=13306349381', // 路径
+                // }
+              ]
+            },
+            {
+              label:'报警报表',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/task-icon.png`),
+              // direction:'',
+              sidebarList:[ // 报警报表
+                {
+                    permission: 'mobile:index:base:proactiveSecurity', // 权限
+                    label: '主动安全报警', // 标题
+                    iconbg:'0',// 图标背景
+                    icon:  this.$getImages('/assetsMobile/images/index/menu/icon-security.png'), // 图片
+                    url: '/alarm/proactiveSecurity/index', // 路径
+                },
+                {
+                    permission: 'mobile:index:base:driverBehavior', // 权限
+                    label: '驾驶行为报警', // 标题
+                    iconbg:'1',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-behavior.png'), // 图片
+                    url: '/alarm/driverBehavior/index', // 路径
+                },
+                {
+                    permission: 'mobile:index:base:blindSpot', // 权限
+                    label: '车辆盲点报警', // 标题
+                    iconbg:'2',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-blind.png'), // 图片
+                    url: '/alarm/blindSpot/index', // 路径
+                },
+              ]
+            },
+            {
+              label:'派车流程',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/flow-icon.png`),
+              // direction:'',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:dispatching:initiate', // 权限
+                    label: '发起流程', // 标题
+                    iconbg:'3',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-initiate-flow.png'), // 图片
+                    url: '/pages/flow/diboot?routes=/workflow/mobile/startFlow&workflowKey=system_flow_veh', // 路径
+                },
+                {
+                    permission: 'mobile:index:dispatching:list', // 权限
+                    label: '用车记录', // 标题
+                    iconbg:'0',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-vehrecord.png'), // 图片
+                    url: '/workflow/vehicleRecord/index', // 路径
+                },
+             ]
+            },
+            {
+              label:'维保流程',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/flow-icon.png`),
+              // direction:'',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:maintenance:initiate', // 权限
+                    label: '发起流程', // 标题
+                    iconbg:'3',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-initiate-flow.png'), // 图片
+                    url: '/pages/flow/diboot?routes=/workflow/mobile/startFlow&workflowKey=system_flow_maintenance', // 路径
+                },
+                {
+                    permission: 'mobile:index:maintenance:list', // 权限
+                    label: '车辆维保记录', // 标题
+                    iconbg:'1',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-mainrecord.png'), // 图片
+                    url: '/workflow/vehicleMaintenanceRecord/index', // 路径
+                },
+             ]
+            },
+            {
+              label:'费用流程',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/flow-icon.png`),
+              // direction:'',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:maintenance:initiate', // 权限
+                    label: '发起流程', // 标题
+                    iconbg:'3',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-initiate-flow.png'), // 图片
+                    url: '/pages/flow/diboot?routes=/workflow/mobile/startFlow&workflowKey=system_cost', // 路径
+                },
+                {
+                    permission: 'mobile:index:maintenance:list', // 权限
+                    label: '费用记录', // 标题
+                    iconbg:'1',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-mainrecord.png'), // 图片
+                    url: '/workflow/recordCost/index', // 路径
+                },
+             ]
+            },
+            {
+              label:'待办',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/flow-icon.png`),
+              // direction:'',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:base:flowstart', // 权限
+                    label: '发起流程', // 标题
+                    iconbg:'3',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-initiate-flow.png'), // 图片
+                    url: '/pages/flow/flow?redirect=/pages/workflowTask/index&type=start', // 路径
+                },
+                {
+                    permission: 'mobile:index:base:flowmyLaunch', // 权限
+                    label: '我的申请', // 标题
+                    iconbg:'2',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-initiate-it.png'), // 图片
+                    url: '/pages/flow/flow?redirect=/pages/workflowTask/index&type=myLaunch', // 路径
+                },
+                {
+                    permission: 'mobile:index:base:flowtodo', // 权限
+                    label: '我的待办', // 标题
+                    iconbg:'1',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-to-do-list.png'), // 图片
+                    url: '/pages/flow/flow?redirect=/pages/workflowTask/index&type=todo', // 路径
+                    hint:''
+                },
+                {
+                    permission: 'mobile:index:base:flowdone', // 权限
+                    label: '我的已办', // 标题
+                    iconbg:'0',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-already-done.png'), // 图片
+                    url: '/pages/flow/flow?redirect=/pages/workflowTask/index?type=done', // 路径
+                },
+
+              ]
+            },
+            // {
+            //   label:'业务记录',
+            //   titleIcon:this.$getImages(`/assetsMobile/images/index/menu/business-records-icon.png`),
+            //   // direction:'',
+            //   sidebarList:[
+            //     // {
+            //     //     permission: 'mobile:index:base:vehicleRecord', // 权限
+            //     //     label: '用车记录', // 标题
+            //     //     iconbg:'0',// 图标背景
+            //     //     icon: this.$getImages('/assetsMobile/images/index/menu/icon-vehrecord.png'), // 图片
+            //     //     url: '/workflow/vehicleRecord/index', // 路径
+            //     // },
+            //     // {
+            //     //     permission: 'mobile:index:base:vehicleRepairRecord', // 权限
+            //     //     label: '车辆维修记录', // 标题
+            //     //     icon: this.$getImages('/assetsMobile/images/flow/vehicleRepairRecord.png'), // 图片
+            //     //     url: '/workflow/vehicleRepairRecord/index', // 路径
+            //     // },
+            //     {
+            //         permission: 'mobile:index:base:vehicleMaintenanceRecord', // 权限
+            //         label: '车辆维保记录', // 标题
+            //         iconbg:'1',// 图标背景
+            //         icon: this.$getImages('/assetsMobile/images/index/menu/icon-mainrecord.png'), // 图片
+            //         url: '/workflow/vehicleMaintenanceRecord/index', // 路径
+            //     },
+            //   ]
+            // },
+            // {
+            //   label:'司机任务',
+            //   titleIcon:this.$getImages(`/assetsMobile/images/index/menu/driver-task-icon.png`),
+            //   // direction:'',
+            //   sidebarList:[
+            //     {
+            //         permission: 'mobile:index:base:driverTask', // 权限
+            //         label: '任务列表', // 标题
+            //         iconbg:'3',// 图标背景
+            //         icon: this.$getImages('/assetsMobile/images/index/menu/icon-task-list.png'), // 图片
+            //         url: '/workflow/driverTask/index', // 路径
+            //     },
+            //   ]
+            // },
+            {
+              label:'物品管理',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/item-manage-icon.png`),
+              // direction:'',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:material:materialApplication', // 权限
+                    label: '物品申领', // 标题
+                    iconbg:'3',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-item-application.png'), // 图片
+                    url: '/pagesBase/materialApplication/index', // 路径
+                }, 
+				        {
+                    permission: 'mobile:index:material:materialRecord', // 权限
+                    label: '申领记录', // 标题
+                    iconbg:'2',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-application-record.png'), // 图片
+                    url: '/pagesBase/materialRecord/index', // 路径
+                },
+              ]
+            },
+			{
+			  label:'车队管理',
+			  titleIcon:this.$getImages(`/assetsMobile/images/index/menu/chedui.png`),
+			  // direction:'',
+			  sidebarList:[
+			    {
+			        permission: 'mobile:index:baseMange:driverManage', // 权限
+			        label: '司机管理', // 标题
+			        iconbg:'3',// 图标背景
+			        icon: this.$getImages('/assetsMobile/images/index/menu/driverManage.png'), // 图片
+			        url: '/pagesBase/driverManage/index', // 路径
+			    }, 
+				{
+			        permission: 'mobile:index:baseMange:vehManage', // 权限
+			        label: '车辆管理', // 标题
+			        iconbg:'2',// 图标背景
+			        icon: this.$getImages('/assetsMobile/images/index/menu/vehMange.png'), // 图片
+			        url: '/pagesBase/vehManage/index', // 路径
+			    },
+			  ]
+			},
+            // {
+            //   label:'矿山任务',
+            //   titleIcon:'',
+            //   // direction:'',
+            //   sidebarList:[
+            //     {
+            //         permission: 'mobile:index:mine:task', // 权限
+            //         label: '任务', // 标题
+            //         iconbg:'0',// 图标背景
+            //         icon: this.$getImages('/assetsMobile/images/flow/driverTask.png'), // 图片
+            //         url: '/pagesMine/driverTask/index', // 路径
+            //     },
+            //   ]
+            // },
+           ],
+           enterprisesArr:[
+            {
+              // label:'',
+              // titleIcon:'',
+              direction:'vertical',
+              sidebarList:[
+                {
+                    permission: 'mobie:index:base:realtimeWatch', // 权限
+                    label: '实时监控', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/monitor-bg.png'), // 图片
+                    url: '/pages/realtimeWatch/realtimeWatch', // 路径
+                },
+                {
+                    permission: 'mobile:index:dispatching:initiate', // 权限
+                    label: '用车申请', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/vehApplication-bg.png'), // 图片
+                    url: '/pages/flow/diboot?routes=/workflow/mobile/startFlow&workflowKey=system_flow_veh', // 路径
+                },
+                {
+                    permission: 'mobile:index:cost:initiate', // 权限
+                    label: '费用申请', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/expApplication-bg.png'), // 图片
+                    url: '/pages/flow/diboot?routes=/workflow/mobile/startFlow&workflowKey=system_cost', // 路径
+                },
+              ]
+            },
+            {
+              label:'监控中心',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/monitor-icon.png`),
+              // direction:'',
+              sidebarList:[ // 监控中心
+                // {
+                  // permission: 'mobie:index:base:realtimeWatch', // 权限
+                  // label: '实时定位', // 标题
+                  // iconbg:'0',// 图标背景
+                  // icon: this.$getImages('/assetsMobile/images/index/menu/icon-real-time-position.png'),//`http://tq.5000v.com:8035/assetsMobile/images/index/menu/icon-real-time-position.png`, // 图片
+                  // url: '/pages/realtimeWatch/realtimeWatch', // 路径
+                // },
+                {
+                    permission: 'mobie:index:base:pastRoute', // 权限
+                    label: '历史轨迹', // 标题
+                    iconbg:'3',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-history-track.png'), // 图片
+                    url: '/pagesMap/pastRoute/pastRoute', // 路径
+                },
+                // {
+                //     permission: '*', // 权限
+                //     label: '视频回放', // 标题
+                //     icon: this.$getImages('/assetsMobile/images/index/historicaltrack.png'), // 图片
+                //     url: '/pagesVideo/h5playback/index?deviceId=13306349381', // 路径
+                // }
+              ]
+            },
+            {
+              label:'报警报表',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/task-icon.png`),
+              // direction:'',
+              sidebarList:[ // 报警报表
+                {
+                    permission: 'mobile:index:base:proactiveSecurity', // 权限
+                    label: '主动安全报警', // 标题
+                    iconbg:'0',// 图标背景
+                    icon:  this.$getImages('/assetsMobile/images/index/menu/icon-security.png'), // 图片
+                    url: '/alarm/proactiveSecurity/index', // 路径
+                },
+                {
+                    permission: 'mobile:index:base:driverBehavior', // 权限
+                    label: '驾驶行为报警', // 标题
+                    iconbg:'1',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-behavior.png'), // 图片
+                    url: '/alarm/driverBehavior/index', // 路径
+                },
+                {
+                    permission: 'mobile:index:base:blindSpot', // 权限
+                    label: '车辆盲点报警', // 标题
+                    iconbg:'2',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-blind.png'), // 图片
+                    url: '/alarm/blindSpot/index', // 路径
+                },
+              ]
+            },
+            {
+              label:'派车管理',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/vehdispatch-icon.png`),
+              // direction:'',
+              sidebarList:[ // 
+                {
+                    permission: 'mobile:index:dispatch:vehInspection', // 权限
+                    label: '车辆点检', // 标题
+                    iconbg:'2',// 图标背景
+                    icon: this.$getImages(`/assetsMobile/images/index/menu/icon-vehinspection.png`), // 图片
+                    url: '/pagesVehDispatch/vehInspection/index', // 路径
+                },
+              
+              ]
+            },
+            {
+              label:'派车流程',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/flow-icon.png`),
+              // direction:'',
+              sidebarList:[
+                // {
+                //    permission: 'mobile:index:dispatching:initiate', // 权限
+                //    label: '发起流程', // 标题
+                //    iconbg:'3',// 图标背景
+                //    icon: this.$getImages('/assetsMobile/images/index/menu/icon-initiate-flow.png'), // 图片
+                //    url: '/pages/flow/diboot?routes=/workflow/mobile/startFlow&workflowKey=system_flow_veh', // 路径
+                //},
+                {
+                    permission: 'mobile:index:dispatching:list', // 权限
+                    label: '用车记录', // 标题
+                    iconbg:'0',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-vehrecord.png'), // 图片
+                    url: '/workflow/vehicleRecord/index', // 路径
+                },
+             ]
+            },
+            {
+              label:'维保流程',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/flow-icon.png`),
+              // direction:'',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:maintenance:initiate', // 权限
+                    label: '发起流程', // 标题
+                    iconbg:'3',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-initiate-flow.png'), // 图片
+                    url: '/pages/flow/diboot?routes=/workflow/mobile/startFlow&workflowKey=system_flow_maintenance', // 路径
+                },
+                {
+                    permission: 'mobile:index:maintenance:list', // 权限
+                    label: '车辆维保记录', // 标题
+                    iconbg:'1',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-mainrecord.png'), // 图片
+                    url: '/workflow/vehicleMaintenanceRecord/index', // 路径
+                },
+             ]
+            },
+            {
+              label:'费用流程',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/flow-icon.png`),
+              // direction:'',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:maintenance:initiate', // 权限
+                    label: '发起流程', // 标题
+                    iconbg:'3',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-initiate-flow.png'), // 图片
+                    url: '/pages/flow/diboot?routes=/workflow/mobile/startFlow&workflowKey=system_cost', // 路径
+                },
+                {
+                    permission: 'mobile:index:maintenance:list', // 权限
+                    label: '费用记录', // 标题
+                    iconbg:'1',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-mainrecord.png'), // 图片
+                    url: '/workflow/recordCost/index', // 路径
+                },
+             ]
+            },
+            {
+              label:'待办',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/flow-icon.png`),
+              // direction:'',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:base:flowstart', // 权限
+                    label: '发起流程', // 标题
+                    iconbg:'3',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-initiate-flow.png'), // 图片
+                    url: '/pages/flow/flow?redirect=/pages/workflowTask/index&type=start', // 路径
+                },
+                {
+                    permission: 'mobile:index:base:flowmyLaunch', // 权限
+                    label: '我的申请', // 标题
+                    iconbg:'2',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-initiate-it.png'), // 图片
+                    url: '/pages/flow/flow?redirect=/pages/workflowTask/index&type=myLaunch', // 路径
+                },
+                {
+                    permission: 'mobile:index:base:flowtodo', // 权限
+                    label: '我的待办', // 标题
+                    iconbg:'1',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-to-do-list.png'), // 图片
+                    url: '/pages/flow/flow?redirect=/pages/workflowTask/index&type=todo', // 路径
+                    hint:''
+                },
+                {
+                    permission: 'mobile:index:base:flowdone', // 权限
+                    label: '我的已办', // 标题
+                    iconbg:'0',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-already-done.png'), // 图片
+                    url: '/pages/flow/flow?redirect=/pages/workflowTask/index?type=done', // 路径
+                },
+
+              ]
+            },
+            // {
+            //   label:'业务记录',
+            //   titleIcon:this.$getImages(`/assetsMobile/images/index/menu/business-records-icon.png`),
+            //   // direction:'',
+            //   sidebarList:[
+            //     {
+            //         permission: 'mobile:index:base:vehicleRecord', // 权限
+            //         label: '用车记录', // 标题
+            //         iconbg:'0',// 图标背景
+            //         icon: this.$getImages('/assetsMobile/images/index/menu/icon-vehrecord.png'), // 图片
+            //         url: '/workflow/vehicleRecord/index', // 路径
+            //     },
+            //     // {
+            //     //     permission: 'mobile:index:base:vehicleRepairRecord', // 权限
+            //     //     label: '车辆维修记录', // 标题
+            //     //     icon: this.$getImages('/assetsMobile/images/flow/vehicleRepairRecord.png'), // 图片
+            //     //     url: '/workflow/vehicleRepairRecord/index', // 路径
+            //     // },
+            //     {
+            //         permission: 'mobile:index:base:vehicleMaintenanceRecord', // 权限
+            //         label: '车辆维保记录', // 标题
+            //         iconbg:'1',// 图标背景
+            //         icon: this.$getImages('/assetsMobile/images/index/menu/icon-mainrecord.png'), // 图片
+            //         url: '/workflow/vehicleMaintenanceRecord/index', // 路径
+            //     },
+            //   ]
+            // },
+            {
+              label:'司机任务',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/driver-task-icon.png`),
+              // direction:'',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:base:driverTask', // 权限
+                    label: '任务列表', // 标题
+                    iconbg:'3',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-task-list.png'), // 图片
+                    url: '/workflow/driverTask/index', // 路径
+                },
+              ]
+            },
+            {
+              label:'物品管理',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/item-manage-icon.png`),
+              // direction:'',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:material:materialApplication', // 权限
+                    label: '物品申领', // 标题
+                    iconbg:'3',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-item-application.png'), // 图片
+                    url: '/pagesBase/materialApplication/index', // 路径
+                }, 
+				        {
+                    permission: 'mobile:index:material:materialRecord', // 权限
+                    label: '申领记录', // 标题
+                    iconbg:'2',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-application-record.png'), // 图片
+                    url: '/pagesBase/materialRecord/index', // 路径
+                },
+              ]
+            },
+			{
+			  label:'车队管理',
+			  titleIcon:this.$getImages(`/assetsMobile/images/index/menu/chedui.png`),
+			  // direction:'',
+			  sidebarList:[
+			    {
+			        permission: 'mobile:index:baseMange:driverManage', // 权限
+			        label: '司机管理', // 标题
+			        iconbg:'3',// 图标背景
+			        icon: this.$getImages('/assetsMobile/images/index/menu/driverManage.png'), // 图片
+			        url: '/pagesBase/driverManage/index', // 路径
+			    }, 
+				{
+			        permission: 'mobile:index:baseMange:vehManage', // 权限
+			        label: '车辆管理', // 标题
+			        iconbg:'2',// 图标背景
+			        icon: this.$getImages('/assetsMobile/images/index/menu/vehMange.png'), // 图片
+			        url: '/pagesBase/vehManage/index', // 路径
+			    },
+			  ]
+			},
+			
+            // {
+            //   label:'矿山任务',
+            //   titleIcon:'',
+            //   // direction:'',
+            //   sidebarList:[
+            //     {
+            //         permission: 'mobile:index:mine:task', // 权限
+            //         label: '任务', // 标题
+            //         iconbg:'0',// 图标背景
+            //         icon: this.$getImages('/assetsMobile/images/flow/driverTask.png'), // 图片
+            //         url: '/pagesMine/driverTask/index', // 路径
+            //     },
+            //   ]
+            // },
+           ],
+           otherArr:[
+            {
+              label:'监控中心',
+              sidebarList:[ // 监控中心
+                {
+                    permission: 'mobie:index:base:realtimeWatch', // 权限
+                    label: '实时定位', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/realTimePosition.png'),//`https://www.yihaocg.com/mobile/image/indexNew/realTimePosition.png`, // 图片
+                    url: '/pages/realtimeWatch/realtimeWatch', // 路径
+                },
+                {
+                    permission: 'mobie:index:base:pastRoute', // 权限
+                    label: '历史轨迹', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/historicaltrack.png'),//`https://www.yihaocg.com/mobile/image/indexNew/historicaltrack.png`, // 图片
+                    url: '/pagesMap/pastRoute/pastRoute', // 路径
+                },
+                // {
+                //     permission: '*', // 权限
+                //     label: '视频回放', // 标题
+                //     icon: this.$getImages('/assetsMobile/images/index/historicaltrack.png'),//`https://www.yihaocg.com/mobile/image/indexNew/historicaltrack.png`, // 图片
+                //     url: '/pagesVideo/h5playback/index?deviceId=13306349381', // 路径
+                // }
+            ]
+            },
+            {
+              label:'报警报表',
+              sidebarList:[ // 报警报表
+                {
+                    permission: 'mobile:index:base:proactiveSecurity', // 权限
+                    label: '主动安全报警', // 标题
+                    icon:  this.$getImages('/assetsMobile/images/index/zhudong.png'),//`https://www.yihaocg.com/mobile/image/alarm/zhudong.png`, // 图片
+                    url: '/alarm/proactiveSecurity/index', // 路径
+                },
+                {
+                    permission: 'mobile:index:base:driverBehavior', // 权限
+                    label: '驾驶行为报警', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/jiashi.png'),//`https://www.yihaocg.com/mobile/image/indexNew/jiashi.png`, // 图片
+                    url: '/alarm/driverBehavior/index', // 路径
+                },
+                {
+                    permission: 'mobile:index:base:blindSpot', // 权限
+                    label: '车辆盲点报警', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/blindSpot.png'),//`https://www.yihaocg.com/mobile/image/indexNew/blindSpot.png`, // 图片
+                    url: '/alarm/blindSpot/index', // 路径
+                },
+            ]
+            },
+		
+			
+           /* {
+              label:'任务情况',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:sanTask:machineWorkRecord', // 权限
+                    label: '机械作业', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/jiashi.png'),//`http://tq.5000v.com:8032/assetsMobile/images/flow/start2.png`, // 图片
+                    url: '/pagesSanitation/machineWorkRecord/machineWorkRecord', // 路径
+                },
+                {
+                    permission: 'mobile:index:sanTask:wasteTransport', // 权限
+                    label: '垃圾收运', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/jiashi.png'),//`http://tq.5000v.com:8032/assetsMobile/images/flow/start2.png`, // 图片
+                    url: '/pagesSanitation/wasteTransport/wasteTransport', // 路径
+                },
+                {
+                    permission: 'mobile:index:sanTask:wasteTransfer', // 权限
+                    label: '垃圾转运', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/jiashi.png'),//`http://tq.5000v.com:8032/assetsMobile/images/flow/start2.png`, // 图片
+                    url: '/pagesSanitation/wasteTransfer/wasteTransfer', // 路径
+                },
+              ]
+            }, */
+            {
+              label:'派车流程',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/flow-icon.png`),
+              // direction:'',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:dispatching:initiate', // 权限
+                    label: '发起流程', // 标题
+                    iconbg:'3',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-initiate-flow.png'), // 图片
+                    url: '/pages/flow/diboot?routes=/workflow/mobile/startFlow&workflowKey=system_flow_veh', // 路径
+                },
+                {
+                    permission: 'mobile:index:dispatching:list', // 权限
+                    label: '用车记录', // 标题
+                    iconbg:'0',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-vehrecord.png'), // 图片
+                    url: '/workflow/vehicleRecord/index', // 路径
+                },
+             ]
+            },
+            {
+              label:'维保流程',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/flow-icon.png`),
+              // direction:'',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:maintenance:initiate', // 权限
+                    label: '发起流程', // 标题
+                    iconbg:'3',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-initiate-flow.png'), // 图片
+                    url: '/pages/flow/diboot?routes=/workflow/mobile/startFlow&workflowKey=system_flow_maintenance', // 路径
+                },
+                {
+                    permission: 'mobile:index:maintenance:list', // 权限
+                    label: '车辆维保记录', // 标题
+                    iconbg:'1',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-mainrecord.png'), // 图片
+                    url: '/workflow/vehicleMaintenanceRecord/index', // 路径
+                },
+             ]
+            },
+            {
+              label:'费用流程',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/flow-icon.png`),
+              // direction:'',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:maintenance:initiate', // 权限
+                    label: '发起流程', // 标题
+                    iconbg:'3',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-initiate-flow.png'), // 图片
+                    url: '/pages/flow/diboot?routes=/workflow/mobile/startFlow&workflowKey=system_cost', // 路径
+                },
+                {
+                    permission: 'mobile:index:maintenance:list', // 权限
+                    label: '费用记录', // 标题
+                    iconbg:'1',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-mainrecord.png'), // 图片
+                    url: '/workflow/recordCost/index', // 路径
+                },
+             ]
+            },
+            {
+              label:'待办',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:base:flowstart', // 权限
+                    label: '发起流程', // 标题
+                    icon: this.$getImages('/assetsMobile/images/flow/start2.png'),//`http://tq.5000v.com:8032/assetsMobile/images/flow/start2.png`, // 图片
+                    url: '/pages/flow/flow?redirect=/pages/workflowTask/index&type=start', // 路径
+                },
+                {
+                    permission: 'mobile:index:base:flowmyLaunch', // 权限
+                    label: '我发起的', // 标题
+                    icon: this.$getImages('/assetsMobile/images/flow/myLanuch2.png'),//`http://tq.5000v.com:8032/assetsMobile/images/flow/myLanuch2.png`, // 图片
+                    url: '/pages/flow/flow?redirect=/pages/workflowTask/index&type=myLaunch', // 路径
+                },
+                {
+                    permission: 'mobile:index:base:flowtodo', // 权限
+                    label: '我的待办', // 标题
+                    icon: this.$getImages('/assetsMobile/images/flow/todo2.png'), // 图片
+                    url: '/pages/flow/flow?redirect=/pages/workflowTask/index&type=todo', // 路径
+                    hint:''
+                },
+                {
+                    permission: 'mobile:index:base:flowdone', // 权限
+                    label: '我的已办', // 标题
+                    icon: this.$getImages('/assetsMobile/images/flow/done2.png'),//`http://tq.5000v.com:8032/assetsMobile/images/flow/done2.png`, // 图片
+                    url: '/pages/flow/flow?redirect=/pages/workflowTask/index?type=done', // 路径
+                },
+              ]
+            },
+            // {
+            //   label:'业务记录',
+            //   sidebarList:[
+            //     {
+            //         permission: 'mobile:index:base:vehicleRecord', // 权限
+            //         label: '用车记录', // 标题
+            //         icon: this.$getImages('/assetsMobile/images/flow/vehicleRecord.png'), // 图片
+            //         url: '/workflow/vehicleRecord/index', // 路径
+            //     },
+            //     // {
+            //     //     permission: 'mobile:index:base:vehicleRepairRecord', // 权限
+            //     //     label: '车辆维修记录', // 标题
+            //     //     icon: this.$getImages('/assetsMobile/images/flow/vehicleRepairRecord.png'), // 图片
+            //     //     url: '/workflow/vehicleRepairRecord/index', // 路径
+            //     // },
+            //     {
+            //         permission: 'mobile:index:base:vehicleMaintenanceRecord', // 权限
+            //         label: '车辆维保记录', // 标题
+            //         icon: this.$getImages('/assetsMobile/images/flow/vehicleMaintenanceRecord.png'), // 图片
+            //         url: '/workflow/vehicleMaintenanceRecord/index', // 路径
+            //     },
+            //   ]
+            // },
+            {
+              label:'司机任务',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:base:driverTask', // 权限
+                    label: '任务列表', // 标题
+                    icon: this.$getImages('/assetsMobile/images/flow/driverTask.png'), // 图片
+                    url: '/workflow/driverTask/index', // 路径
+                },
+              ]
+            },
+            {
+              label:'物品管理',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:material:materialApplication', // 权限
+                    label: '物品申领', // 标题
+                    icon: this.$getImages('/assetsMobile/images/flow/vehicleRecord.png'), // 图片
+                    url: '/pagesBase/materialApplication/index', // 路径
+                }, 
+				        {
+                    permission: 'mobile:index:material:materialRecord', // 权限
+                    label: '申领记录', // 标题
+                    icon: this.$getImages('/assetsMobile/images/flow/done2.png'), // 图片
+                    url: '/pagesBase/materialRecord/index', // 路径
+                },
+              ]
+            },
+            {
+              label:'矿山任务',
+              sidebarList:[
+                {
+                  permission: 'mobile:index:mine:task', // 权限
+                  label: '任务', // 标题
+                  icon: this.$getImages('/assetsMobile/images/flow/driverTask.png'), // 图片
+                  url: '/pagesMine/driverTask/index', // 路径
+                },
+              ]
+            },
+           ],
+            testList: [
+                {
+                    permission: '*', // 权限
+                    label: '测试页面', // 标题
+                    icon: this.$getImages('/assetsMobile/images/flow/driverTask.png'), // 图片
+                    url: '/pagesMine/testPage/index', // 路径
+                },
+            ],
+            workflowNum:{
+                todo:''
+            },
+			
+	
+        }
+    },
+    onLoad() {
+        console.log('顶顶顶顶')
+        taskCount({}).then(res=>{
+          this.setHint('待办',2,res.data.todo)
+            // this.flowManageList[2].hint = res.data.todo;
+        })
+    },
+    methods: {
+        toformTemplate() {
+            uni.navigateTo({
+                url: '/pages/formTemplate/formTemplate?id=1&name=uniapp'
+            });
+        },
+        allPermission(list){
+          return list.map((e) => e.permission)
+        },
+        setHint(listLabel,index,data){
+          let findArr=[];
+          if(this.hasPlat(['sanitation'])){
+            findArr = this.sanitationArr.find(item=>item.label==listLabel)
+          }else if(this.hasPlat(['enterprises'])){
+            findArr = this.enterprisesArr.find(item=>item.label==listLabel)
+          }else{
+            findArr = this.otherArr.find(item=>item.label==listLabel)
+          }
+          findArr.sidebarList[index].hint=data
+        }
+    }
+    ,
+}
+</script>
+
+<style lang="scss" scoped>
+.home-container {
+  
+  background: #F7F8FA;
+  position: relative;
+  
+  .tipfixed {
+  	position: fixed;
+  	top: 0;
+  	left: 0;
+  	padding: 20rpx;
+  	width: 100%;
+  	background: rgba(0, 0, 0, 0.3);
+  	z-index: 100;
+  	color: #FFFFFF;
+  	font-size: 24rpx;
+  	
+  	display: flex;
+  	justify-content: flex-start;
+  	align-items: center;
+  }
+  
+  .background-image{
+    width: 100%;
+    height: 362rpx;
+  }
+  .index-title{
+    width: 100%;
+    font-family: PangMenZhengDao;
+    font-size: 26px;
+    color: #FFFFFF;
+    position: absolute;
+    top: 110rpx;
+    z-index: 1;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    .dot{
+      width: 10rpx;
+      height: 10rpx;
+      border-radius: 10rpx;
+      background-color: #fff;
+      margin: 0 22rpx;
+    }
+  }
+  .page-warp{
+    position: absolute;
+    top:240rpx;
+    margin: 0 28rpx;
+    width: calc(100% - 56rpx);
+    overflow-y: auto;
+    padding-bottom: 120rpx;
+    .home-munes {
+      padding: 20rpx;
+      border-radius: 16rpx;
+      background: #fff;
+      margin-bottom: 16rpx;
+    }
+    .pd8{
+      padding: 16rpx;
+    }
+  }
+  .page1-warp{
+    top:26rpx;
+    padding-bottom: 120rpx;
+    .home-munes {
+      padding: 20rpx;
+      background: #fff;
+    }
+  }
+  
+}
+</style>

+ 660 - 0
pages/index/index.vue

@@ -0,0 +1,660 @@
+<template>
+  <view class="home-container">
+	  <!-- 环卫 start-->
+    <template v-if="hasPlat(['sanitation'])">
+      <view class="index-title">
+        智慧环卫 <view class="dot"></view> 让科技建设城市
+      </view>
+      <image :src="$getImages('/assetsMobile/images/index/page.png')" class="background-image"></image>
+      <view class="page-warp">
+        <template v-for="(item,index) in sanitationArr">
+          <view v-if="$hasPermiOr(allPermission(item.sidebarList))" :key="index" class="home-munes" :class="index==0?'pd8':''">
+              <view class="sidebar">
+                  <Sidebar :direction="item.direction" :sidebarList="item.sidebarList" :label="item.label" :titleIcon="item.titleIcon"></Sidebar>
+              </view>
+          </view>
+        </template>
+		
+		<!-- 动态菜单 start-->
+		<template v-for="(item,index) in $store.state.user.dynList">
+		  <view :key="`dyn${index}`" class="home-munes">
+		      <view class="sidebar">
+		          <sidebardyn :children="item.children" :label="item.meta.title" :titleIcon="item.mobileIcon" :himObj="himObj"></sidebardyn>
+		      </view>
+		  </view>
+		</template>
+		<!-- 动态菜单 end-->
+      </view>
+    </template>
+     <!-- 环卫 end-->
+	 <!-- 企事业 start-->
+	<template v-else-if="hasPlat(['enterprises'])">
+
+		<view class="tipfixed" v-if="hasHostPlat(['dingH5']) && $store.state.user.showSts==1">
+
+			<template v-if="!$store.state.user.isModel">
+				<div>点击演示按钮后进入演示状态,显示模拟数据</div>
+				<u-button type="success" size="mini" 
+					:custom-style="{width: '160rpx'}" @click="$store.dispatch('setIsModel',true)">开始演示</u-button>
+			</template>
+			<template v-else-if="$store.state.user.isModel">
+				<div>点击退出演示后退出演示状态,显示真实数据</div>
+				<u-button type="error" size="mini" 
+					:custom-style="{width: '160rpx'}" @click="$store.dispatch('setIsModel',false)">退出演示</u-button>
+			</template>		
+		</view>
+		
+		<view class="icon-fixed-right" v-if="hasHostPlat(['dingH5'])">
+			<image v-if="$hasPermi('mobile:index:base:scanCode')" :src="$getImages('/assetsMobile/images/qrcode.png')" class="munes-icon" @click="handleQrCode"></image>
+		</view>
+		
+		<view class="swiper-wrap" v-if="$hasPermi('mobie:index:base:swiper')">
+			<u-swiper :list="swiperList" :height="166" :radius="8" indicator indicatorMode="dot"></u-swiper>
+		</view>
+		
+		<view class="data-wrap">
+			<view class="data-wrap-item" v-if="$hasPermi('mobie:index:base:realMile')">
+				<image :src="$getImages('/assetsMobile/images/enterprises/data/licheng.png')" class="data-wrap-item-bg"></image>
+				<view class="data-wrap-item-content">
+					<view class="data-wrap-item-title">里程</view>
+					<view class="data-wrap-item-label">今日里程</view>
+					<view class="data-wrap-item-label">{{todayVehicleMileStats.realMile}}</view>
+				</view>
+				
+			</view>
+			<view class="data-wrap-item" v-if="$hasPermi('mobie:index:base:task')">
+				<image :src="$getImages('/assetsMobile/images/enterprises/data/renwu.png')" class="data-wrap-item-bg"></image>
+				<view class="data-wrap-item-content">
+					<view class="data-wrap-item-title">任务</view>
+					<view class="data-wrap-item-label">未完成  <text style="margin-left: 20rpx;">{{vehicleTaskStats.beginCount+vehicleTaskStats.unBeginCount}}</text></view>
+					<view class="data-wrap-item-label">已完成  <text style="margin-left: 20rpx;">{{vehicleTaskStats.endCount}}</text></view>
+				</view>
+			</view>
+			<view class="data-wrap-item" v-if="$hasPermi('mobie:index:base:vehicle')">
+				<image :src="$getImages('/assetsMobile/images/enterprises/data/cheliang.png')" class="data-wrap-item-bg"></image>
+				<view class="data-wrap-item-content">
+					<view class="data-wrap-item-title">车辆</view>
+					<view class="data-wrap-item-label">出车  <text style="margin-left: 20rpx;">{{vehicleDispatchStats.runCount}}</text></view>
+					<view class="data-wrap-item-label">空闲  <text style="margin-left: 20rpx;">{{vehicleDispatchStats.freeCount}}</text></view>
+				</view>
+			</view>
+			<view class="data-wrap-item" v-if="$hasPermi('mobie:index:base:driver')">
+				<image :src="$getImages('/assetsMobile/images/enterprises/data/siji.png')" class="data-wrap-item-bg"></image>
+				<view class="data-wrap-item-content">
+					<view class="data-wrap-item-title">司机</view>
+					<view class="data-wrap-item-label">出车  <text style="margin-left: 20rpx;">{{driverDispatchStats.runCount}}</text></view>
+					<view class="data-wrap-item-label">空闲  <text style="margin-left: 20rpx;">{{driverDispatchStats.freeCount}}</text></view>
+				</view>
+			</view>
+		</view>
+			
+      <!-- <view class="index-title">
+        精准管理 <view class="dot"></view> 让管车更简单
+      </view>
+      <image :src="$getImages('/assetsMobile/images/index/page.png')" class="background-image"></image> -->
+      <view class="page-warp-enterprises">
+       <!-- <template v-for="(item,index) in enterprisesArr">
+          <view v-if="$hasPermiOr(allPermission(item.sidebarList))" :key="index" class="home-munes" :class="index==0?'pd8':''">
+              <view class="sidebar">
+                  <Sidebar :direction="item.direction" :sidebarList="item.sidebarList" :label="item.label" :titleIcon="item.titleIcon"></Sidebar>
+              </view>
+          </view>
+        </template> -->
+		
+		<!-- 动态菜单 start-->
+		<template v-for="(item,index) in $store.state.user.dynList">
+		  <view :key="`dyn${index}`" class="home-munes">
+		      <view class="sidebar">
+		          <sidebardyn :children="item.children" :label="item.meta.title" :titleIcon="item.mobileIcon" :himObj="himObj"></sidebardyn>
+		      </view>
+		  </view>
+		</template>
+		<!-- 动态菜单 end-->
+      </view>
+    </template>
+    <!-- 企事业 end-->
+	<template v-else>
+      <view class="page1-warp">
+        <template v-for="(item,index) in otherArr">
+          <view v-if="$hasPermiOr(allPermission(item.sidebarList))" :key="index" class="home-munes">
+              <view class="sidebar">
+                <Sidebar :sidebarList="item.sidebarList" :label="item.label"></Sidebar>
+              </view>
+          </view>
+        </template>
+      </view>
+    </template>
+    
+  </view>
+</template>
+
+<script>
+import { getMobileRouters,boardDetail,getMessageNum } from "@/api/common/system";
+import Sidebar from '@/components/sidebar'
+import sidebardyn from '@/components/sidebardyn'
+import * as dd from 'dingtalk-jsapi'; // 此方式为整体加载,也可按需进行加载
+import { isExternal } from "@/utils/validate";
+
+export default {
+    components: {
+        Sidebar,
+        sidebardyn,
+    },
+    data() {
+        return {
+           sanitationArr: [
+            {
+              // label:'',
+              // titleIcon:'',
+              direction:'vertical',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:sanTask:machineWorkRecord', // 权限
+                    label: '机械作业', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/machine-bg.png'), // 图片
+                    url: '/pagesSanitation/machineWorkRecord/machineWorkRecord', // 路径
+                },
+                
+                {
+                    permission: 'mobile:index:sanTask:wasteTransport', // 权限
+                    label: '垃圾收运', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/collect-bg.png'), // 图片
+                    url: '/pagesSanitation/wasteTransport/wasteTransport', // 路径
+                },
+				{
+				    permission: 'mobile:index:sanTask:wasteTransfer', // 权限
+				    label: '垃圾转运', // 标题
+				    icon: this.$getImages('/assetsMobile/images/index/transport-bg.png'), // 图片
+				    url: '/pagesSanitation/wasteTransfer/wasteTransfer', // 路径
+				},
+                
+             
+              ]
+            },
+          
+           ],
+           /* enterprisesArr:[
+            {
+              // label:'',
+              // titleIcon:'',
+              direction:'vertical',
+              sidebarList:[
+                {
+                    permission: 'mobie:index:base:realtimeWatch', // 权限
+                    label: '实时监控', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/monitor-bg.png'), // 图片
+                    url: '/pages/realtimeWatch/realtimeWatch', // 路径
+                },
+                {
+                    permission: 'mobile:index:dispatching:initiate', // 权限
+                    label: '用车申请', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/vehApplication-bg.png'), // 图片
+                    url: '/pages/flow/diboot?routes=/workflow/mobile/startFlow&workflowKey=system_flow_veh', // 路径
+                },
+                {
+                    permission: 'mobile:index:cost:initiate', // 权限
+                    label: '费用申请', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/expApplication-bg.png'), // 图片
+                    url: '/pages/flow/diboot?routes=/workflow/mobile/startFlow&workflowKey=system_cost', // 路径
+                },
+              ]
+            },
+           
+           ], */
+           otherArr:[
+            {
+              label:'监控中心',
+              sidebarList:[ // 监控中心
+                {
+                    permission: 'mobie:index:base:realtimeWatch', // 权限
+                    label: '实时定位', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/realTimePosition.png'),//`https://www.yihaocg.com/mobile/image/indexNew/realTimePosition.png`, // 图片
+                    url: '/pages/realtimeWatch/realtimeWatch', // 路径
+                },
+                {
+                    permission: 'mobie:index:base:pastRoute', // 权限
+                    label: '历史轨迹', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/historicaltrack.png'),//`https://www.yihaocg.com/mobile/image/indexNew/historicaltrack.png`, // 图片
+                    url: '/pagesMap/pastRoute/pastRoute', // 路径
+                },
+               
+            ]
+            },
+            {
+              label:'报警报表',
+              sidebarList:[ // 报警报表
+                {
+                    permission: 'mobile:index:base:proactiveSecurity', // 权限
+                    label: '主动安全报警', // 标题
+                    icon:  this.$getImages('/assetsMobile/images/index/zhudong.png'),//`https://www.yihaocg.com/mobile/image/alarm/zhudong.png`, // 图片
+                    url: '/alarm/proactiveSecurity/index', // 路径
+                },
+                {
+                    permission: 'mobile:index:base:driverBehavior', // 权限
+                    label: '驾驶行为报警', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/jiashi.png'),//`https://www.yihaocg.com/mobile/image/indexNew/jiashi.png`, // 图片
+                    url: '/alarm/driverBehavior/index', // 路径
+                },
+                {
+                    permission: 'mobile:index:base:blindSpot', // 权限
+                    label: '车辆盲点报警', // 标题
+                    icon: this.$getImages('/assetsMobile/images/index/blindSpot.png'),//`https://www.yihaocg.com/mobile/image/indexNew/blindSpot.png`, // 图片
+                    url: '/alarm/blindSpot/index', // 路径
+                },
+            ]
+            },
+		
+			
+         
+            {
+              label:'派车流程',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/flow-icon.png`),
+              // direction:'',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:dispatching:initiate', // 权限
+                    label: '发起流程', // 标题
+                    iconbg:'3',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-initiate-flow.png'), // 图片
+                    url: '/pages/flow/diboot?routes=/workflow/mobile/startFlow&workflowKey=system_flow_veh', // 路径
+                },
+                {
+                    permission: 'mobile:index:dispatching:list', // 权限
+                    label: '用车记录', // 标题
+                    iconbg:'0',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-vehrecord.png'), // 图片
+                    url: '/workflow/vehicleRecord/index', // 路径
+                },
+             ]
+            },
+            {
+              label:'维保流程',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/flow-icon.png`),
+              // direction:'',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:maintenance:initiate', // 权限
+                    label: '发起流程', // 标题
+                    iconbg:'3',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-initiate-flow.png'), // 图片
+                    url: '/pages/flow/diboot?routes=/workflow/mobile/startFlow&workflowKey=system_flow_maintenance', // 路径
+                },
+                {
+                    permission: 'mobile:index:maintenance:list', // 权限
+                    label: '车辆维保记录', // 标题
+                    iconbg:'1',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-mainrecord.png'), // 图片
+                    url: '/workflow/vehicleMaintenanceRecord/index', // 路径
+                },
+             ]
+            },
+            {
+              label:'费用流程',
+              titleIcon:this.$getImages(`/assetsMobile/images/index/menu/flow-icon.png`),
+              // direction:'',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:maintenance:initiate', // 权限
+                    label: '发起流程', // 标题
+                    iconbg:'3',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-initiate-flow.png'), // 图片
+                    url: '/pages/flow/diboot?routes=/workflow/mobile/startFlow&workflowKey=system_cost', // 路径
+                },
+                {
+                    permission: 'mobile:index:maintenance:list', // 权限
+                    label: '费用记录', // 标题
+                    iconbg:'1',// 图标背景
+                    icon: this.$getImages('/assetsMobile/images/index/menu/icon-mainrecord.png'), // 图片
+                    url: '/workflow/recordCost/index', // 路径
+                },
+             ]
+            },
+            {
+              label:'待办',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:base:flowstart', // 权限
+                    label: '发起流程', // 标题
+                    icon: this.$getImages('/assetsMobile/images/flow/start2.png'),//`http://tq.5000v.com:8032/assetsMobile/images/flow/start2.png`, // 图片
+                    url: '/pages/flow/flow?redirect=/pages/workflowTask/index&type=start', // 路径
+                },
+                {
+                    permission: 'mobile:index:base:flowmyLaunch', // 权限
+                    label: '我发起的', // 标题
+                    icon: this.$getImages('/assetsMobile/images/flow/myLanuch2.png'),//`http://tq.5000v.com:8032/assetsMobile/images/flow/myLanuch2.png`, // 图片
+                    url: '/pages/flow/flow?redirect=/pages/workflowTask/index&type=myLaunch', // 路径
+                },
+                {
+                    permission: 'mobile:index:base:flowtodo', // 权限
+                    label: '我的待办', // 标题
+                    icon: this.$getImages('/assetsMobile/images/flow/todo2.png'), // 图片
+                    url: '/pages/flow/flow?redirect=/pages/workflowTask/index&type=todo', // 路径
+                    hint:''
+                },
+                {
+                    permission: 'mobile:index:base:flowdone', // 权限
+                    label: '我的已办', // 标题
+                    icon: this.$getImages('/assetsMobile/images/flow/done2.png'),//`http://tq.5000v.com:8032/assetsMobile/images/flow/done2.png`, // 图片
+                    url: '/pages/flow/flow?redirect=/pages/workflowTask/index?type=done', // 路径
+                },
+              ]
+            },
+        
+            {
+              label:'司机任务',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:base:driverTask', // 权限
+                    label: '任务列表', // 标题
+                    icon: this.$getImages('/assetsMobile/images/flow/driverTask.png'), // 图片
+                    url: '/workflow/driverTask/index', // 路径
+                },
+              ]
+            },
+            {
+              label:'物品管理',
+              sidebarList:[
+                {
+                    permission: 'mobile:index:material:materialApplication', // 权限
+                    label: '物品申领', // 标题
+                    icon: this.$getImages('/assetsMobile/images/flow/vehicleRecord.png'), // 图片
+                    url: '/pagesBase/materialApplication/index', // 路径
+                }, 
+				        {
+                    permission: 'mobile:index:material:materialRecord', // 权限
+                    label: '申领记录', // 标题
+                    icon: this.$getImages('/assetsMobile/images/flow/done2.png'), // 图片
+                    url: '/pagesBase/materialRecord/index', // 路径
+                },
+              ]
+            },
+            {
+              label:'矿山任务',
+              sidebarList:[
+                {
+                  permission: 'mobile:index:mine:task', // 权限
+                  label: '任务', // 标题
+                  icon: this.$getImages('/assetsMobile/images/flow/driverTask.png'), // 图片
+                  url: '/pagesMine/driverTask/index', // 路径
+                },
+              ]
+            },
+           ],
+           // 动态菜单
+			dynList: [],
+			himObj: {
+				'mobile:index:base:flowtodo': 0, //待办
+				'mobile:index:base:sysMessage': 0, //车务消息
+			}, 
+			
+			swiperList: [
+				this.$getImages('/assetsMobile/images/enterprises/swipe1.png'),
+				this.$getImages('/assetsMobile/images/enterprises/swipe2.png'),
+				this.$getImages('/assetsMobile/images/enterprises/swipe3.png'),
+				this.$getImages('/assetsMobile/images/enterprises/swipe4.png'),
+				this.$getImages('/assetsMobile/images/enterprises/swipe5.png'),
+			],
+			
+			todayVehicleMileStats: {
+				realMile: 0,
+			},
+			vehicleTaskStats: {
+				beginCount: 0,
+				endCount: 0,
+				unBeginCount: 0,
+			},
+			vehicleDispatchStats: {
+				runCount: 0,
+				freeCount: 0,
+			},
+			driverDispatchStats: {
+				runCount: 0,
+				freeCount: 0,
+			},
+		
+        }
+    },
+    watch: {
+        '$store.state.user.wsMsgNum': {
+
+            handler(newVal) {
+                if(this.$store.state.user.wsMsg==='flush_todo_num'){
+                    this.updatataskCount()
+                }
+            }
+        }
+    },
+	async created() {
+		this.$store.dispatch('GetMobileRouters')
+	},
+    onLoad() {
+        this.updatataskCount()
+    },
+	onShow() {
+		/* 企事业数据看板 */
+		if(this.hasPlat(['enterprises'])) {
+			
+			boardDetail({}).then((res) => {
+				let {todayVehicleMileStats,vehicleTaskStats,vehicleDispatchStats,driverDispatchStats} = res.data
+				
+				this.copyProps(this.todayVehicleMileStats,todayVehicleMileStats)
+				this.copyProps(this.vehicleTaskStats,vehicleTaskStats)
+				this.copyProps(this.vehicleDispatchStats,vehicleDispatchStats)
+				this.copyProps(this.driverDispatchStats,driverDispatchStats)
+			})
+		}
+	},
+    methods: {
+		updatataskCount(){
+            getMessageNum({}).then(res=>{
+             this.himObj['mobile:index:base:flowtodo'] = res.data.todoTotalNum
+             this.himObj['mobile:index:base:sysMessage'] = res.data.total
+            })
+        },
+        toformTemplate() {
+            uni.navigateTo({
+                url: '/pages/formTemplate/formTemplate?id=1&name=uniapp'
+            });
+        },
+        allPermission(list){
+          return list.map((e) => e.permission)
+        },
+		handleQrCode() {
+			
+			if(dd.env.platform == "notInDingTalk") {
+	
+				uni.showToast({
+					icon: 'none',
+					title: '请在钉钉应用内使用该功能'
+				})
+				return
+			} else {
+				dd.scan({
+				  type: 'qr',
+				  success: (res) => {
+				    const { text } = res;
+					if (isExternal(text)) {
+						window.location.href= text
+					}
+				  },
+				  fail: () => {},
+				  complete: () => {},
+				});
+			}
+		},
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+.home-container {
+  
+  background: #F7F8FA;
+  position: relative;
+  
+  .tipfixed {
+  	position: fixed;
+  	top: 0;
+  	left: 0;
+  	padding: 20rpx;
+  	width: 100%;
+  	background: rgba(0, 0, 0, 0.3);
+  	z-index: 100;
+  	color: #FFFFFF;
+  	font-size: 24rpx;
+  	
+  	display: flex;
+  	justify-content: flex-start;
+  	align-items: center;
+  }
+  
+  .icon-fixed-right {
+	  position: fixed;
+	  top: 710rpx;
+	  right: 0;
+	  z-index: 10000;
+	  .munes-icon{
+	    width: 80rpx;
+	    height: 80rpx;
+		&+.munes-icon{
+			margin-top: 40rpx;
+		}
+	  }
+  }
+  
+  .background-image{
+    width: 100%;
+    height: 362rpx;
+  }
+  .index-title{
+    width: 100%;
+    font-family: PangMenZhengDao;
+    font-size: 26px;
+    color: #FFFFFF;
+    position: absolute;
+    top: 110rpx;
+    z-index: 1;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    .dot{
+      width: 10rpx;
+      height: 10rpx;
+      border-radius: 10rpx;
+      background-color: #fff;
+      margin: 0 22rpx;
+    }
+  }
+  
+  .swiper-wrap {
+	 margin: 16rpx 28rpx 16rpx;
+	 width: calc(100% - 56rpx);
+	 border-radius: 16rpx;
+  }
+  
+  .data-wrap {
+	  display: flex;
+	  flex-wrap: wrap;
+	  // gap: 16rpx 18rpx;
+	  justify-content: space-between;
+	  margin: 0 28rpx 16rpx;
+	  
+	  .data-wrap-item {
+		  // width: calc(50% - 16rpx);
+		  width: 338rpx;
+		  height: 164rpx;
+		  position: relative;
+		  
+		 margin-top: 16rpx; 
+		 
+		 display: flex;
+		 flex-direction: column;
+		 justify-content: space-between;
+		 
+		  
+		  .data-wrap-item-bg {
+			  position: absolute;
+			  bottom: 0;
+			  right: 0;
+			  width: 100%;
+			  height: 100%;
+		  }
+		  .data-wrap-item-content {
+			   padding: 10rpx 28rpx;
+			    z-index: 100;
+				height: 100%;
+				display: flex;
+				flex-direction: column;
+				justify-content: space-between;
+				
+			  .data-wrap-item-title {
+				  font-family: PingFangSC, PingFang SC;
+				  font-weight: 500;
+				  font-size: 32rpx;
+				  color: #FFFFFF;
+				  line-height: 44rpx;
+				  text-align: left;
+				  font-style: normal;
+			  			  
+			  }
+			  .data-wrap-item-label {
+				  font-family: PingFangSC, PingFang SC;
+				  font-weight: 400;
+				  font-size: 28rpx;
+				  color: #FFFFFF;
+				  line-height: 44rpx;
+				  text-align: left;
+				  font-style: normal;
+			  }
+		  }
+		  
+	  }
+  }
+  
+  .page-warp-enterprises{
+  /*  position: absolute;
+    top:240rpx; */
+    margin: 0 28rpx;
+    width: calc(100% - 56rpx);
+    overflow-y: auto;
+    padding-bottom: 120rpx;
+    .home-munes {
+      padding: 20rpx;
+      border-radius: 16rpx;
+      background: #fff;
+      margin-bottom: 16rpx;
+    }
+    .pd8{
+      padding: 16rpx;
+    }
+  }
+  
+  
+  .page-warp{
+    position: absolute;
+    top:240rpx;
+    margin: 0 28rpx;
+    width: calc(100% - 56rpx);
+    overflow-y: auto;
+    padding-bottom: 120rpx;
+    .home-munes {
+      padding: 20rpx;
+      border-radius: 16rpx;
+      background: #fff;
+      margin-bottom: 16rpx;
+    }
+    .pd8{
+      padding: 16rpx;
+    }
+  }
+  .page1-warp{
+    top:26rpx;
+    padding-bottom: 120rpx;
+    .home-munes {
+      padding: 20rpx;
+      background: #fff;
+    }
+  }
+  
+}
+</style>

+ 814 - 0
pages/login/login.vue

@@ -0,0 +1,814 @@
+<template>
+<view>
+	<template v-if="hasPlat(['sanitation'])">
+		<view class="page-container-sanitation">
+			<image :src="$getImages(configList.h5_login_bg.configValue||'/assetsMobile/images/loginBg2.png')" class="background-image-new"></image>
+			<!-- <image :src="$getImages('/assetsMobile/images/loginBg2.png')" class="background-image"></image> -->
+			<!-- <image :src="$getImages('/assetsMobile/images/logo.png')" class="logo-image"></image> -->
+
+			<view class="login-content" style="background-color: transparent; z-index: 99; overflow: hidden;">
+
+				<view class="welcome-copywriting">
+          <!-- <image :src="$getImages('/assetsMobile/images/hi.png')" class="hi-img"></image> -->
+					<text class="h5_login_title"
+						v-if="configList.h5_login_title">{{ configList.h5_login_title.configValue || '欢迎登录!' }}</text>
+					<text class="h5_login_subtitle" v-if="configList.h5_login_subtitle&&configList.h5_login_subtitle.isShow===1">{{configList.h5_login_subtitle.configValue || '智慧环卫管理系统'}}</text>
+				</view>
+
+				<view class="user-info">
+
+					<view class="verification">
+						<u--form :model="model" :rules="rules" ref="uForm" label-width="0">
+
+							<u-form-item label=" " prop="loginForm.username">
+								<view class="user-input">
+                  <image :src="$getImages('/assetsMobile/images/user.png')" class="img"></image>
+									<u-input
+										v-model="model.loginForm.username"
+										placeholder-style="color:#CCCCCC;font-size: 30rpx"
+										border="none"
+										type="text"
+										color="#333"
+										:clearable="true"
+										placeholder="请输入账号">
+                  </u-input>
+								</view>
+							</u-form-item>
+							<u-form-item label=" " prop="loginForm.password">
+								<view class="user-input">
+									<image :src="$getImages('/assetsMobile/images/psw.png')" class="img"></image>
+									<u-input
+										v-model="model.loginForm.password"
+										placeholder-style="color:#CCCCCC;font-size: 30rpx"
+										border="none"
+										:type="showPassword?'text':'password'"
+										color="#333"
+										:clearable="true"
+										placeholder="请输入密码" />
+									<image @click="showPassword=!showPassword" :src="showPassword?$getImages('/assetsMobile/images/enterprises/login/visiabled.png'):$getImages('/assetsMobile/images/enterprises/login/unVisiabled.png')" class="img-append"></image>
+								</view>
+							</u-form-item>
+
+							<u-form-item label=" " prop="loginForm.code" v-if="captchaEnabled">
+								<view class="user-input" style="width: 40%;">
+									<u-input
+										v-model="model.loginForm.code"
+										placeholder-style="color:#CCCCCC;font-size: 30rpx"
+										border="none"
+										type="text"
+										:clearable="true"
+										placeholder="验证码" />
+								</view>
+								<view class="login-code">
+									<image :src="codeUrl" @click="getCode" class="login-code-img"></image>
+								</view>
+							</u-form-item>
+
+							<!-- 记住密码 -->
+							<view class="login-remember">
+								<u-checkbox-group>
+									<u-checkbox size="16" activeColor="#2693FB" :checked="checked" inactiveColor="#39B2FC" labelColor="#ffffff"
+										@change="checkboxChange" v-model="checked"
+										label="记住密码"></u-checkbox>
+								</u-checkbox-group>
+							</view>
+
+
+							<u-button type="primary" shape="circle" :custom-style="{
+                width: '100%',
+                border:'none'
+              }" class="login-btn" @click="handleLogin">登录
+							</u-button>
+
+						</u--form>
+					</view>
+				</view>
+			</view>
+		</view>
+	</template>
+	
+	<template v-else-if="hasPlat(['enterprises'])">
+		<view class="page-container-enterprises">
+			
+	
+			<view class="login-content" style="background-color: transparent; z-index: 99; overflow: hidden;">
+				
+				<template v-if="configList.h5_login_title">
+					<view class="welcome-copywriting" >
+			  
+						<text class="h5_login_title"
+							v-if="configList.h5_login_title">{{ configList.h5_login_title.configValue || '欢迎登录' }}</text>
+						<text class="h5_login_subtitle" v-if="configList.h5_login_subtitle&&configList.h5_login_subtitle.isShow===1">{{configList.h5_login_subtitle.configValue || '企事业车辆管理系统'}}</text>
+					</view>
+					
+					<view class="bg-img">
+						<image :src="$getImages(configList.h5_login_bg.configValue || '/assetsMobile/images/enterprises/login/loginBg.png')" class="background-image"></image>
+					</view>
+				</template>
+				
+	
+				<view class="user-info">
+	
+					<view class="verification">
+						<u--form :model="model" :rules="rules" ref="uForm" label-width="0">
+	
+							<u-form-item label=" " prop="loginForm.username">
+								<view class="user-input">
+									<image :src="$getImages('/assetsMobile/images/enterprises/login/acount.png')" class="img"></image>
+									<u-input
+										v-model="model.loginForm.username"
+										placeholder-style="color:#CCCCCC;font-size: 32rpx"
+										border="none"
+										type="text"
+										color="#333"
+										:clearable="true"
+										placeholder="请输入账号">
+									</u-input>
+								</view>
+							</u-form-item>
+							<u-form-item label=" " prop="loginForm.password">
+								<view class="user-input">
+									<image :src="$getImages('/assetsMobile/images/enterprises/login/password.png')" class="img"></image>
+									<u-input
+										v-model="model.loginForm.password"
+										placeholder-style="color:#DDDDDD;font-size: 32rpx"
+										border="none"
+										:type="showPassword?'text':'password'"
+										color="#333"
+										:clearable="true"
+										placeholder="请输入密码" />
+									<image @click="showPassword=!showPassword" :src="showPassword?$getImages('/assetsMobile/images/enterprises/login/visiabled.png'):$getImages('/assetsMobile/images/enterprises/login/unVisiabled.png')" class="img-append"></image>
+								</view>
+							</u-form-item>
+	
+							<u-form-item label=" " prop="loginForm.code" v-if="captchaEnabled">
+								<view class="user-input" style="width: 40%;">
+									<u-input
+										v-model="model.loginForm.code"
+										placeholder-style="color:#DDDDDD;font-size: 32rpx"
+										border="none"
+										type="text"
+										:clearable="true"
+										placeholder="验证码" />
+								</view>
+								<view class="login-code">
+									<image :src="codeUrl" @click="getCode" class="login-code-img"></image>
+								</view>
+							</u-form-item>
+	
+							<!-- 记住密码 -->
+							<view class="login-remember">
+								<u-checkbox-group>
+									<u-checkbox size="16" activeColor="#4573FC" :checked="checked" inactiveColor="##4573FC" labelColor="#4573FC"
+										@change="checkboxChange" v-model="checked"  shape="circle"
+										label="记住密码"></u-checkbox>
+								</u-checkbox-group>
+							</view>
+						
+	
+							<u-button type="primary" shape="circle" :custom-style="{
+								width: '100%',
+								border:'none'
+							  }" class="login-btn" @click="handleLogin">登录
+							</u-button>
+	
+						</u--form>
+					</view>
+				</view>
+			</view>
+		</view>
+	</template>
+	
+	<template v-else>
+		<view class="page-container">
+			<image :src="$getImages(configList.h5_login_bg.configValue||'/assetsMobile/images/loginBg.png')" class="background-image"></image>
+		
+			<view class="login-content" style="background-color: transparent; z-index: 99; overflow: hidden;">
+				
+				<view class="welcome-copywriting">
+					<text class="h5_login_title" v-if="configList.h5_login_title">{{configList.h5_login_title.configValue||'欢迎登录!'}}</text>
+					<text class="h5_login_subtitle" v-if="configList.h5_login_subtitle&&configList.h5_login_subtitle.isShow===1">{{configList.h5_login_subtitle.configValue || ''}}</text>
+				</view>
+		
+				<view class="user-info">    
+					<view class="verification">
+						<u--form :model="model" :rules="rules" ref="uForm" label-width="0">
+		
+							<u-form-item label=" " prop="loginForm.username">
+								<view class="user-input">
+									<u-input 
+										v-model="model.loginForm.username" 
+										placeholder-style="color:#80A0C6;font-size: 30rpx"
+										border="none"
+										type="text" 
+										:clearable="true" 
+										placeholder="请输入账号"
+									/>
+								</view>
+							</u-form-item>
+							<u-form-item label=" " prop="loginForm.password">
+								<view class="user-input">
+									<u-input 
+										v-model="model.loginForm.password" 
+										placeholder-style="color:#80A0C6;font-size: 30rpx"
+										border="none"
+										type="password" 
+										:clearable="true"
+										 placeholder="请输入密码"
+									/>
+								</view>
+							</u-form-item>
+							
+							<u-form-item label=" " prop="loginForm.code" v-if="captchaEnabled">
+								<view class="user-input" style="width: 40%;">
+									<u-input 
+										v-model="model.loginForm.code" 
+										placeholder-style="color:#80A0C6;font-size: 30rpx"
+										border="none"
+										type="text" 
+										:clearable="true" 
+										placeholder="验证码"
+									/>
+								</view>
+								<view class="login-code"> 
+								  <image :src="codeUrl" @click="getCode" class="login-code-img"></image>
+								</view>
+							</u-form-item>
+		
+							<u-button type="primary" shape="circle" :custom-style="{
+									width: '100%',
+								  }" class="login-btn" @click="handleLogin">登录
+							</u-button>
+						
+						</u--form>
+					</view>
+				</view>
+			</view>
+		</view>
+	</template>
+</view>
+</template>
+
+<script>
+import { mapState } from 'vuex'
+import { getCodeImg } from '@/api/login'
+import { getToken } from '@/utils/auth'
+
+export default {
+    data() {
+        return {
+            codeUrl: "",
+            captchaEnabled: false,
+            checked: false,//记住密码
+            model: {
+                loginForm: {
+                    username: "",
+                    password: "",
+                    code: "",
+                    uuid: '',
+                    client: "TQ-HW-MOBILE",
+                }
+            },
+            rules: {
+                'loginForm.username': {
+                    type: 'string',
+                    required: true,
+                    message: '请输入账号',
+                    trigger: ['blur', 'change']
+                },
+                'loginForm.password': {
+                    type: 'string',
+                    required: true,
+                    message: '请输入密码',
+                    trigger: ['blur', 'change']
+                },
+                'loginForm.code': {
+                    type: 'string',
+                    required: true,
+                    message: '请输入验证码',
+                    trigger: ['blur', 'change']
+                },
+            },
+			showPassword: false,
+
+        }
+    },
+    created() {
+        this.getCode()
+    },
+	onLoad: function (option) {
+		   // #ifdef H5
+			if(process.env.NODE_ENV == "production")  {
+				
+				if(this.hasPlat(['enterprises']) && this.hasHostPlat(['wxH5'])) {
+	
+					let {code} =  option
+					if (this.isNull(code) && !getToken()) {
+						let appid = "wx0f491ff49441493e"; // 公众号ID
+						// 回调页面
+						let redirect_uri = encodeURI(`${window.location.protocol}//${window.location.host}/mobile/`);	
+						window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${redirect_uri}&response_type=code&scope=snsapi_base&#wechat_redirect`;
+					} else {
+						this.model.loginForm.authCode = code
+					}
+				}
+				
+			}
+		  // #endif
+		  if(process.env.NODE_ENV != "production")  {
+			  this.model.loginForm.username = 'admin'
+			  this.model.loginForm.password = 'Gdtq1981%%'
+		  }
+		  // console.log(this.$PLATFORM,'thisthis')
+	},
+    onShow() {
+		// console.log(this.hasPlat,'thisthisthis')
+        //从本地存储中获取密码
+        let loginFormInfo = uni.getStorageSync('loginFormInfo')
+        if (loginFormInfo) {
+            console.log('缓存的信息',loginFormInfo)
+            let { username, password ,checked} = JSON.parse(loginFormInfo)
+            this.model.loginForm.username = username
+            this.model.loginForm.password = password
+            this.checked = checked
+        }
+    },
+    computed: {
+        ...mapState('appConfig', ['configList'])
+    },
+    methods: {
+        // 记住密码
+        checkboxChange(e) {
+            this.checked = e
+        },
+        // 获取图形验证码
+        getCode() {
+            getCodeImg().then(res => {
+                console.log(res, '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() {
+            this.$refs.uForm.validate().then(res => {
+                this.pwdLogin()
+            }).catch(errors => {
+                console.log('校验失败', errors)
+            })
+        },
+        // 密码登录
+        async pwdLogin() {
+            //保存密码到本地存储
+
+            if (this.checked) {
+                uni.setStorageSync('loginFormInfo', JSON.stringify({...this.model.loginForm,checked:this.checked}))
+            } else { uni.removeStorageSync('loginFormInfo') }
+
+            this.$store.dispatch('Login', this.model.loginForm).then(() => {
+                this.$modal.closeLoading()
+                this.loginSuccess()
+            }).catch(() => {
+                if (this.captchaEnabled) {
+                    this.getCode()
+                }
+            })
+        },
+        // 登录成功
+        loginSuccess(result) {
+            // 设置用户信息
+            this.$store.dispatch('GetInfo').then(res => {
+                uni.reLaunch({
+                    url: '/pages/index/index'
+                })
+				getApp().checkTabPer()
+            })
+        }
+    },
+
+    onReady() {
+        this.$refs.uForm.setRules(this.rules)
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+@font-face {
+    font-family: 'MaoKenTangYuan';
+    /* 自定义的字体名称 */
+    src: url('@/assets/fonts/MaoKenTangYuan.ttf') format('truetype');
+    /* 字体文件路径及格式 */
+}
+
+.page-container-sanitation {
+    font-family: PingFangSC, PingFang SC;
+    height: 100%;
+    width: 100%;
+    height:100vh;
+    background: linear-gradient(135deg, #49CDFC 0%, #2693FB 100%);
+
+    .background-image-new{
+        position: absolute;
+        bottom: 0;
+        right: 0;
+        width: 100%;
+        height: 100%;
+    }
+    .background-image {
+        position: absolute;
+        bottom: 0;
+        right: 0;
+        width: 100%;
+        height: 362rpx;
+    }
+    .logo-image{
+      width: 174rpx;
+      height: 58rpx;
+      position: absolute;
+      top: 58rpx;
+      right: 58rpx;
+    }
+
+    .login-content {
+        background-color: transparent;
+        z-index: 99;
+        overflow: hidden;
+
+        // 登录提示
+        .welcome-copywriting {
+            position: absolute;
+            width: 100%;
+            // top: 35%;
+            top: 190rpx;
+            height: auto;
+            .hi-img{
+              width: 92rpx;
+              height: 92rpx;
+              position: absolute;
+              left: 24rpx;
+              top: -72rpx;
+            }
+
+            text {
+                width: 100%;
+                float: left;
+                padding: 0 60rpx 0 92rpx;
+                box-sizing: border-box;
+                font-family: YSBTH;
+                font-weight: normal;
+                font-size: 68rpx;
+                color: #FFFFFF;
+                line-height: 72rpx;
+                text-align: left;
+                font-style: normal;
+            }
+            .h5_login_subtitle{
+              padding-left: 144rpx;
+            }
+        }
+
+        //登录提示框
+        .user-info {
+            position: absolute;
+            top: 50%;
+            transform: translate(0,-50%);
+            width: 100%;
+
+            .verification {
+                padding: 0 60rpx;
+                .img{
+                  width: 36rpx;
+                  height: 36rpx;
+                  margin-right: 10rpx;
+                }
+				.img-append {
+					width: 42rpx;
+					height: 40rpx;
+					margin-left: 16rpx;
+				}
+                .user-input {
+                    padding: 20rpx 48rpx;
+                    width: calc(100% - 96rpx);
+                    border-radius: 30px;
+                    // background-color: #EDF0F7;
+                    border-radius: 46rpx;
+                    border: 2rpx solid #B2E1FD;
+                    display: flex;
+                    justify-content: center;
+                    align-items: center;
+                    color: #FFFFFF;
+                    background-color: #fff;
+                }
+
+                .login-code {
+                    height: 38px;
+                    float: right;
+
+                    .login-code-img {
+                        height: 38px;
+                        position: absolute;
+                        margin-left: 10px;
+                        width: 200rpx;
+                    }
+                }
+
+
+                .login-btn {
+                    margin-top: 68rpx;
+                    background: linear-gradient(to right, #26E3B6, #2ED7FF);
+                    height: 96rpx;
+                    font-size: 36rpx;
+                    font-family: PingFangSC, PingFang SC;
+                    font-weight: 500;
+                    font-size: 32rpx;
+                    color: #fff;
+                    line-height: 96rpx;
+                    text-align: center;
+					display: flex;
+					justify-content: center;
+                    font-style: normal;
+                }
+
+                .login-remember {
+                    display: flex;
+                    align-items: center;
+                    margin-top: 28rpx;
+                    padding-left: 16rpx;
+
+                    /deep/ .u-checkbox__icon-wrap {
+                        background-color: rgba(57, 178, 252,0) !important;
+                        border: 2rpx solid #FFFFFF!important;
+                    }
+                }
+
+            }
+        }
+    }
+}
+
+.page-container-enterprises {
+    font-family: PingFangSC, PingFang SC;
+    height: 100%;
+    width: 100%;
+    height:100vh;
+    background: #FFFFFF;
+
+   
+
+
+    .login-content {
+        background-color: transparent;
+        z-index: 99;
+        overflow: hidden;
+		
+		
+        // 登录提示
+        .welcome-copywriting {
+            position: absolute;
+            width: 100%;
+            top: 6%;
+            // top: 110rpx;
+            height: auto;
+          
+
+           text {
+                width: 100%;
+                float: left;
+                padding: 0 54rpx 0 54rpx;
+                box-sizing: border-box;
+                font-family: YSBTH;
+                font-weight: normal;
+                font-size: 68rpx;
+                color: #FFFFFF;
+                line-height: 76rpx;
+                text-align: left;
+                font-style: normal;
+            }
+			.h5_login_title {
+				font-family: PingFangSC, PingFang SC;
+				font-weight: 500;
+				font-size: 60rpx;
+				color: #4573FC;
+				line-height: 76rpx;
+				text-align: left;
+				font-style: normal;
+			}
+            .h5_login_subtitle{
+			  font-family: PingFangSC, PingFang SC;
+			  font-weight: 500;
+			  font-size: 40rpx;
+			  color: #4573FC;
+			  line-height: 76rpx;
+			  text-align: left;
+			  font-style: normal;
+            }
+        }
+		
+		.bg-img {
+			position: absolute;
+			 top: 21%;
+			  width: 100%;
+			 display: flex;
+			 justify-content: center;
+			 align-items: center;
+			.background-image {
+				
+			    width: 578rpx;
+			    height: 458rpx;
+			}
+		}
+		
+
+        //登录提示框
+        .user-info {
+            position: absolute;
+            top: 71%;
+            transform: translate(0,-50%);
+            width: 100%;
+
+            .verification {
+                padding: 0 74rpx;
+                .img{
+                  width: 42rpx;
+                  height: 40rpx;
+                  margin-right: 16rpx;
+                }
+				.img-append {
+					width: 42rpx;
+					height: 40rpx;
+					margin-left: 16rpx;
+				}
+                .user-input {
+                    padding: 20rpx 0rpx;
+                    width: calc(100%);
+                    border-radius: 0px;
+                    border-bottom: 2rpx solid #DDDDDD;
+                    display: flex;
+                    justify-content: center;
+                    align-items: center;
+          
+					
+					font-family: PingFangSC, PingFang SC;
+					font-weight: 400;
+					font-size: 32rpx;
+					color: #DDDDDD;
+					line-height: 44rpx;
+					text-align: left;
+					font-style: normal;
+                }
+
+                .login-code {
+                    height: 38px;
+                    float: right;
+
+                    .login-code-img {
+                        height: 38px;
+                        position: absolute;
+                        margin-left: 10px;
+                        width: 200rpx;
+                    }
+                }
+
+
+                .login-btn {
+                    margin-top: 40rpx;
+                   background: #4573FC;
+                   border-radius: 40rpx;
+				   
+                    height: 80rpx;
+                    font-family: PingFangSC, PingFang SC;
+                    font-weight: 400;
+                    font-size: 36rpx;
+                    color: #FFFFFF;
+                    line-height: 80rpx;
+                    text-align: center;
+                    font-style: normal;
+                }
+
+                .login-remember {
+                    display: flex;
+                    align-items: center;
+                    margin-top: 28rpx;
+                    padding-left: 0rpx;
+					
+					
+
+                    /deep/ .u-checkbox-label--left {
+                     
+						font-family: PingFangSC, PingFang SC;
+						font-weight: 400;
+						font-size: 28rpx;
+						color: #4573FC;
+						line-height: 40rpx;
+						text-align: left;
+						font-style: normal;
+                    }
+                }
+
+            }
+        }
+    }
+}
+
+
+ .page-container {
+      font-family: PingFangSC, PingFang SC;
+      height: 100%;
+        width: 100%;
+        height:100vh;
+	   .background-image {
+		   position: absolute;
+		   top: 0;
+		   right: 0;
+		   width: 100%;
+		   height: 100%;
+	   }
+	   
+	   .login-content {
+		   background-color: transparent; 
+		   z-index: 99; 
+		   overflow: hidden;
+		   
+		   // 登录提示
+		   .welcome-copywriting {
+			   position: absolute;
+			   width: 100%;
+			   top: 35%;
+			   height: auto;
+			   
+			   text {
+					width: 100%;
+					float: left;
+					padding: 0 90rpx 0 90rpx;
+					color: #1990FF;
+					box-sizing: border-box;
+                   
+					// &:first-child {
+					// 	font-size: 48rpx;
+					// 	font-weight: 600;
+					// 	margin-bottom: 20rpx;
+					// }
+					
+					// &:last-child {
+					// 	font-size: 40rpx;
+					// 	font-weight: 600;
+					// }
+
+			   }
+                           .h5_login_title {
+                               font-size: 48rpx;
+                               font-weight: 600;
+                               margin-bottom: 20rpx;
+                           }
+            
+                           .h5_login_subtitle {
+                               font-size: 40rpx;
+                               font-weight: 600;
+                           }
+		   }
+		   //登录提示框
+		   .user-info {
+			   position: absolute;
+			   top: 48%;
+			   width: 100%;
+			   
+			   .verification {
+					padding: 0 40rpx;
+		
+					.user-input {
+						padding: 20rpx 48rpx;
+						width: calc(100% - 96rpx);
+						border-radius: 30px;
+						background-color: #EDF0F7;
+						display: flex;
+						justify-content: center;
+						align-items: center;
+					}
+					
+					.login-code {
+						height: 38px;
+						float: right;
+					  
+						.login-code-img {
+						  height: 38px;
+						  position: absolute;
+						  margin-left: 10px;
+						  width: 200rpx;
+						}
+					}
+					
+					.login-btn {
+						margin-top: 20rpx;
+						background: #1990FF;
+						height: 96rpx; 
+						font-size: 36rpx;
+					}
+					
+			   }
+		   }
+	   }
+   }
+</style>

+ 208 - 0
pages/realtimeWatch/module/appMap.vue

@@ -0,0 +1,208 @@
+<template>
+	<view class="realtimeWatch">
+		<!-- <map
+		  id="map"
+		  :latitude="lat"
+		  :longitude="lng"
+		>
+		</map>
+		
+		<view class="options">
+			<view class="options-item" @click="showCar">
+				<image
+				  style="width: 100%; height: 100%"
+				  :src="'https://www.yihaocg.com/mobile/image/location/boxselection.png'"
+				/>
+			</view>
+		</view>
+		<tq-car-user 
+			ref="carChooseRef"
+			@choosed="getChoosed"
+		>
+		</tq-car-user> -->
+	</view>
+</template>
+
+<script>
+const img = 'http://tq.5000v.com:8035/img/upload/2023/11/27/1_1_20231127115853A031.png';
+export default {
+	components: {
+	},
+	data() {
+		return {
+			_mapContext: null,
+			lat: 23.099994,
+			lng: 113.324520,
+			markers: [], // 标注点
+		}
+	},
+	onReady () {
+		// this.queryLocation()
+		// 创建map对象
+		this._mapContext = uni.createMapContext("map", this);
+		this.cluster();
+			  
+	},
+	methods: {
+		 // 查询自身位置
+		queryLocation() {
+		 
+		  uni.getLocation({
+		    type: "gcj02",
+		    success: (res) => {
+				const {latitude,longitude} = res
+				  this.lat = latitude;
+				  this.lng = longitude;
+		    },
+		    fail: (res) => {
+		      console.log('定位失败',JSON.stringify(res));
+		    }
+		  });
+		},
+		//
+		showCar() {
+			this.$refs.carChooseRef.showDialog()
+		},
+		//选中数据
+		getChoosed(data) {
+			console.log(data,'data')
+		},
+		
+		// 点聚合
+		cluster() {
+			// 仅调用初始化,才会触发 on.("markerClusterCreate", (e) => {})
+			this._mapContext.initMarkerCluster({
+				enableDefaultStyle: false, // 是否使用默认样式
+				zoomOnClick: true, // 点击聚合的点,是否改变地图的缩放级别
+				gridSize: 60, // 聚合计算时网格的像素大小,默认60
+				complete(res) {
+					console.log('initMarkerCluster', res)
+				}
+			});
+
+			this._mapContext.on("markerClusterCreate", (res) => {
+				console.log("markerClusterCreate", res);
+				const clusters = res.clusters
+				const markers = clusters.map(cluster => {
+					const {
+						center,
+						clusterId,
+						markerIds
+					} = cluster
+					return {
+						...center,
+						width: 0,
+						height: 0,
+						clusterId, // 必须
+						label: {
+							content: markerIds.length + '',
+							fontSize: 16,
+							width: 50,
+							height: 50,
+							bgColor: '#00A3FA',
+							borderRadius: 25,
+							textAlign: 'center',
+							anchorX: 0,
+							anchorY: -20,
+						}
+					}
+				})
+				this._mapContext.addMarkers({
+					markers,
+					clear: false,
+					complete(res) {
+						console.log('clusterCreate addMarkers', res)
+					}
+				})
+			});
+			this._mapContext.on('markerClusterClick', (res) => {
+				console.log('markerClusterClick', res)
+			})
+			this.addMarkers();
+		},
+
+		// 添加标记点
+		addMarkers() {
+			const positions = [
+				{
+					latitude: 23.099994,
+					longitude: 113.324520,
+				}, {
+					latitude: 23.099994,
+					longitude: 113.322520,
+				}, {
+					latitude: 23.099994,
+					longitude: 113.326520,
+				}, {
+					latitude: 23.096994,
+					longitude: 113.329520,
+				}
+			]
+
+			const markers = []
+			positions.forEach((p, i) => {
+				markers.push(
+					Object.assign({}, {
+						id: i + 1,
+						iconPath: img,
+						width: 28,
+						height: 29,
+						joinCluster: true, // 指定了该参数才会参与聚合
+						callout:{
+							bgColor: "#5AC2EB",
+							color: "#fff",
+							content: "客户名称",
+							display: "ALWAYS",
+							fontSize: "14",
+							fontWeight: "bold",
+							padding: 8,
+							textAlign: "center"
+						}
+					}, p)
+				)
+			})
+			console.log('markers', markers)
+			this._mapContext.addMarkers({
+				markers,
+				clear: false,
+				complete(res) {
+					console.log('addMarkers', res)
+				}
+			})
+		},
+
+			  
+		// 点击图标
+		handleClickMarker(markerId) {
+		  this.detailsId = this.vehMarkerId[markerId.detail.markerId].id;
+		  this.vehicleDetails = this.vehMarkerId[markerId.detail.markerId].value;
+		  this.details = true;
+		},
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.realtimeWatch {
+	width: 100%;
+	height: 100vh;
+	position: relative;
+	#map {
+		width: 750rpx; 
+		height: 100%;
+	}
+	.options {
+		position: absolute;
+		width: 72rpx;
+		right: 30rpx;
+		bottom: 156rpx;
+		box-shadow: 0px 3px 30px 0px rgba(0, 0, 0, 0.15);
+		z-index: 9999;
+		
+		.options-item {
+			height: 72rpx;
+			
+		}
+	}
+}	
+</style>

+ 563 - 0
pages/realtimeWatch/module/eleChoose.vue

@@ -0,0 +1,563 @@
+<template>
+	<view class="container">	
+		<view class="search">
+			<u-row>
+				<u-col :span="2">
+					<view class="all-choosed" @click="handleChooseAll">
+						<image
+							:src="allChecked?'/static/icon/choosed.png':'/static/icon/nochoose.png'" 
+							class="choosed-image">
+						</image>
+						全选
+					</view>
+				</u-col>
+				<u-col :span="10">
+					<u-search
+						v-model="search.text"
+						placeholder="关键字搜索" 
+						@search="handleSearch"
+						@custom="handleSearch"
+						:actionStyle="{
+							color: '#2979ff'
+						}"
+					>
+					</u-search>
+				</u-col>
+			</u-row>
+
+		</view>
+		<view class="last">
+			<u-button type="primary" :plain="true" :disabled="page==1" @click="handleLast"
+				:custom-style="{
+					borderColor: '#FFF',
+					background: '#F2f2f2',
+				}"
+			>
+				<!-- <u-icon name="arrow-up" color="#2979ff" size="28"></u-icon> -->
+				<image
+					:src="'/static/icon/up.png'" 
+					class="option-image">
+				</image>
+			</u-button>
+		</view>
+		<view class="list">
+			<template v-for="(item,index) in showList">
+				<view class="item" :key="index" @click="handleChoose(item)">
+					<image 
+						:src="item.checked?'/static/icon/choosed.png':'/static/icon/nochoose.png'" 
+						class="choosed-image">
+					</image>
+					<image 
+						:src="item.icon" 
+						class="icon-image">
+					</image>
+					<view>
+						<view class="item-value">
+							{{item.label}}<text v-if="item.workStatusName">({{item.workStatusName}})</text>
+						</view>
+						<view class="item-label">
+							{{item.deptName}}
+						</view>
+					</view>
+					
+				</view>
+			</template>
+			
+			<view class="empty" v-if="showList.length==0">{{tips}}</view>
+		</view>
+		<view class="next">
+			<u-button type="primary" :plain="true" :disabled="page==totalPage" @click="handleNext"
+				:custom-style="{
+					borderColor: '#FFF',
+					background: '#F2f2f2',
+				}"
+			>
+				<!-- <u-icon name="arrow-down" color="#2979ff" size="28"></u-icon> -->
+				<image
+					:src="'/static/icon/down.png'" 
+					class="option-image">
+				</image>
+			</u-button>
+		</view>
+	</view>
+</template>
+
+<script>
+import {isArray,clone,isObject } from 'lodash'
+import {realTimeList, realTimeMap} from '@/api/realtimeWatch.js'
+import {getImages} from '@/plugins/images'
+import {copyProps,clearProps} from '@/utils/gdtq.js'
+//资源引入
+// 车辆图片
+let car_list_online = getImages('/assetsMobile/images/map/icon/vehicle/car_list_online.png') //行驶在线
+let car_list_engin = getImages('/assetsMobile/images/map/icon/vehicle/car_list_engin.png') //停驶在线 
+let car_list_unline = getImages('/assetsMobile/images/map/icon/vehicle/car_list_unline.png') //离线
+let car_list_warning = getImages('/assetsMobile/map/icon/vehicle/car_list_warning.png') //报警
+
+let car_online = getImages('/assetsMobile/images/map/icon/vehicle/car_online.png') //行驶在线--地图
+let car_engin = getImages('/assetsMobile/images/map/icon/vehicle/car_engin.png') //停驶在线  -- 地图
+let car_unline = getImages('/assetsMobile/images/map/icon/vehicle/car_unline.png') //离线 --地图
+let car_warning = getImages('/assetsMobile/images/map/icon/vehicle/car_warning.png') //报警 --地图
+//人员图片
+let people_list_online = getImages('/assetsMobile/images/map/icon/people/people_list_online.png') //在线
+let people_list_engin = getImages('/assetsMobile/images/map/icon/people/people_list_engin.png') //停留在线
+let people_list_unline = getImages('/assetsMobile/images/map/icon/people/people_list_unline.png') //离线
+let people_list_warning = getImages('/assetsMobile/images/map/icon/people/people_list_warning.png') //报警
+
+let people_online = getImages('/assetsMobile/images/map/icon/people/people_online.png') //在线--地图
+let people_engin = getImages('/assetsMobile/images/map/icon/people/people_engin.png') //停留在线--地图
+let people_unline = getImages('/assetsMobile/images/map/icon/people/people_unline.png') //离线--地图
+let people_warning = getImages('/assetsMobile/images/map/icon/people/people_warning.png') //报警--地图
+
+export default {
+	props: {
+		size: {
+			type: Number,
+			default: 30
+		},
+		// 类型 1.人员 2.车辆
+		queryType: {
+			type: Number,
+			default: 2
+		}
+	},
+    data() {
+        return {
+			allChecked: false,
+		   
+		   page: 1,
+		   total: 0,
+		   totalPage: 0,
+		   search: {
+			   text: ''
+		   },
+		   
+		   allData: {},
+		   orginList: [],
+		   resultList: [], //搜索结果集合
+		   showList: [], 
+		   
+			checkedKey: [], //选中id
+		   lastTime: null,
+		   tips: '加载中'
+        }
+    },
+	mounted() {
+		
+		if(this.queryType == 2) {
+			this.checkedKey = this.$store.state.realtimeWatch.vheicleCheckArr.map(e => e.id)
+			this.$store.dispatch('getVehIcon').then((res) => {
+				
+			}).finally(() => {
+				this.getData()
+				
+			})
+		}
+		
+		if(this.queryType == 1) {
+			this.checkedKey = this.$store.state.realtimeWatch.peopleCheckArr.map(e => e.id)
+			this.$store.dispatch('getUserIcon').then((res) => {
+				
+			}).finally(() => {
+				this.getData()
+			})
+		}
+		
+	},
+	
+    methods: {
+		
+		handleChooseAll() {
+			
+			this.allChecked = !this.allChecked
+
+			this.resultList.map(e => {
+				e.checked = this.allChecked
+			}) 
+		},
+		handleChoose(item) {
+			item.checked=!item.checked
+			this.computeCheckedData()
+		},
+		
+		computeCheckedData() {
+			let allLength = this.resultList.length
+			let checedLength = this.resultList.filter((e) => e.checked).length
+			if(allLength == checedLength) {
+				this.allChecked = true
+			} else {
+				this.allChecked = false
+			}
+		},
+		
+		getChoosed() {
+			let data = this.orginList.filter((e) => e.checked && !e.disabled)
+			// console.log(data,'datadtatatdata')
+			this.$emit('choosed',data)
+			if(this.queryType == 2) {
+				this.$store.dispatch('setVheicleCheckArr',data)
+			} else if (this.queryType == 1) {
+				this.$store.dispatch('setPeopleCheckArr',data)
+			}
+			
+		},
+		
+		handleSearch() {
+			this.page = 1
+			if(this.search.text) {
+				this.resultList = this.orginList.filter((e) => e.label.includes(this.search.text) || e.deptName.includes(this.search.text))
+			} else {
+				this.resultList = clone(this.orginList)
+			}
+			this.computeCheckedData()
+			this.totalPage = Math.ceil(this.resultList.length / this.size)
+			this.handlePageData()	
+		},
+		//上一页
+		handleLast() {
+			if(this.page != 1) this.page -= 1;
+			this.handlePageData()
+		},
+		//下一页
+		handleNext() {
+			if(this.page != this.totalPage) this.page += 1;	
+			this.handlePageData()
+		},
+		//处理分页数据
+		handlePageData() {
+			let index = (this.page - 1) * this.size
+			this.showList = clone(this.resultList).splice(index,this.size)
+		},
+        getData() {
+			let that = this;
+
+			this.$store.dispatch('setLoading',true)
+			
+			this.$store.dispatch('setTreeInterVal', {
+				cb: () => {
+					 that.refreshData();
+				}
+			})
+			
+		    realTimeList({
+				type: this.queryType,
+			}).then((res) => {
+				
+			    if(isArray(res.data)) {
+					this.lastTime = res.time
+					
+					res.data.forEach((item) => {
+						let disabled = false
+						if(this.isNull(item.lat)||this.isNull(item.lng)){
+							disabled=true
+						}
+						let iconData = this.getIcons(item)
+			
+						let obj = {
+							...item,
+							...iconData,
+							checked: this.checkedKey.includes(item.id),
+							disabled
+						}
+						
+						this.orginList.push(obj);
+						this.allData[obj.id] = obj
+					})				
+					
+					this.handleSearch()
+					
+					this.$emit('getDataed')
+					
+				} else {
+					this.orginList = [];
+					this.showList = [];
+					this.allData = {};
+					this.page = 1
+					this.totalPage = 0
+				}
+				
+				 if(Object.prototype.hasOwnProperty.call(res,'extra')) {
+						this.handleCount(res.extra)
+					} else {
+						this.handleCount({})
+					}
+		   }).finally(() => {
+			   this.$store.dispatch('setLoading',false)
+			   this.tips = '暂无数据'
+		   })
+	   },
+	   
+	   refreshData() {		   
+		   
+		   realTimeMap({
+			   type: this.queryType,
+			   time: this.lastTime,
+		   }).then((res) => {
+				if(res.code == "0") {
+					this.lastTime = res.time
+					 
+					 for(let i in res.data) {
+						 let item = res.data[i]
+						 
+						 if(this.allData[i]) {
+							 let iconData = this.getIcons(item)
+							 let disabled = false
+			 
+							 if(this.isNull(item.lat)||this.isNull(item.lng)){
+							 	disabled=true
+							 }
+							 item.disabled = disabled
+							 
+							 if(iconData) {
+								 copyProps(this.allData[i],iconData)
+							 }
+							 // console.log(this.allData[i].speed,'this.allData[i]')
+							 copyProps(this.allData[i],item)
+							
+						 } else  {
+							 let disabled = false
+							 if(this.isNull(item.lat)||this.isNull(item.lng)){
+							 	disabled=true
+							 }
+							 let iconData = this.getIcons(item)
+							 let obj = {
+								 ...item,
+								 ...iconData,
+								 checked: this.checkedKey.includes(item.id),
+								 disabled
+							 }
+							 this.originList.push(obj);
+							 this.allData[obj.id] = obj
+						 }
+					 }
+					
+				} 
+		   }).finally(() => {
+
+		   })
+		   
+	   },
+		
+		
+		getIcons(item) {
+		   if(this.queryType == 2) {
+			   return this.getVehIcon(item)
+		   } else if (this.queryType == 1) {
+			   return this.getUserIcon(item)
+		   }
+		},
+		
+		getVehIcon(item) {
+		    //列表图标
+		    let listIcon = this.$store.state.icon.vehIcon[2]
+		    //地图图标
+		    let mapIcon = this.$store.state.icon.vehIcon[1]
+		
+		    let icon = ""
+		    let img = ""
+		    let tips = ""
+		    let filterStatus = ""  //用筛选
+		    if(item.isAlarm == '1') {
+		        icon = listIcon[4][item.vehType]? getImages(listIcon[4][item.vehType]):car_list_warning
+		        img = mapIcon[4][item.vehType]? getImages(mapIcon[4][item.vehType]):car_warning
+		
+		        tips = '报警'
+		        filterStatus = '0'
+		    } else if (item.online == "1" && item.engineStatus == "1" && item.speed > 0) {
+		        icon = listIcon[1][item.vehType]?getImages(listIcon[1][item.vehType]):car_list_online
+		        img = mapIcon[1][item.vehType]? getImages(mapIcon[1][item.vehType]):car_online
+		        tips = '行驶在线'
+		        filterStatus = '1'
+		    } else if (item.online == "1" && (item.engineStatus == "0" || item.speed == 0)) {
+		        icon = listIcon[2][item.vehType]?getImages(listIcon[2][item.vehType]):car_list_engin
+		        img = mapIcon[2][item.vehType]? getImages(mapIcon[2][item.vehType]):car_engin
+		        tips = '停驶在线'
+		        filterStatus = '2'
+		    } else if (item.online == "0") {
+		        icon = listIcon[3][item.vehType]?getImages(listIcon[3][item.vehType]):car_list_unline
+		        img = mapIcon[3][item.vehType]? getImages(mapIcon[3][item.vehType]):car_unline
+		        tips = '离线'
+		        filterStatus = '3'
+		    } else if (item.online == "2") {
+		        icon = listIcon[3][item.vehType]?getImages(listIcon[3][item.vehType]):car_list_unline
+		        img = mapIcon[3][item.vehType]? getImages(mapIcon[3][item.vehType]):car_unline
+		        tips = '从未上线'
+		        filterStatus = '3'
+		    } else {
+		        icon = listIcon[3][item.vehType]?getImages(listIcon[3][item.vehType]):car_list_unline
+		        img = mapIcon[3][item.vehType]? getImages(mapIcon[3][item.vehType]):car_unline
+		        tips = '离线'
+		        filterStatus = '3'
+		    }
+		    return {
+		        icon,
+		        img,
+		        tips,
+		        filterStatus,
+				code: 'vehicle', 
+		    }
+		},
+	   
+	  
+	   //人员图标
+	   getUserIcon(item) {
+	       let listIcon = this.$store.state.icon.userIcon[2]
+	       //地图图标
+	       let mapIcon = this.$store.state.icon.userIcon[1]
+	   
+	       let icon = ""
+		   let img = ""
+		   let tips = ""
+		   let filterStatus = ""  //用筛选
+		   if(item.isAlarm == '1') {
+		       icon = listIcon[4][item.workType]? getImages(listIcon[4][item.workType]):people_list_warning
+		       img = mapIcon[4][item.workType]? getImages(mapIcon[4][item.workType]):people_warning
+		       tips = '报警'
+		       filterStatus = '0'
+		   } else if (item.online == "1" && item.speed > 0) {
+	          icon = listIcon[1][item.workType]?getImages(listIcon[1][item.workType]):people_list_online
+	          img = mapIcon[1][item.workType]? getImages(mapIcon[1][item.workType]):people_online
+			  tips = '行走在线'
+			   filterStatus = '1'
+	       } else if (item.online == "1" && item.speed == 0) {
+	          icon = listIcon[2][item.workType]?getImages(listIcon[2][item.workType]):people_list_engin
+	          img = mapIcon[2][item.workType]? getImages(mapIcon[2][item.workType]):people_engin
+			  tips = '停留在线'
+			   filterStatus = '2'
+	       } else {
+	           icon = listIcon[3][item.workType]?getImages(listIcon[3][item.workType]):people_list_unline
+	           img = mapIcon[3][item.workType]? getImages(mapIcon[3][item.workType]):people_unline
+			    tips = '离线'
+				filterStatus = '3'
+	       }   
+	   
+	       return {
+	           icon,
+			   img,
+			   tips,
+			   filterStatus,
+			    code: 'people', 
+	       }
+	   },
+	   
+	   //计算在线数量
+	handleCount(obj) {
+	     
+	       let cout = {
+	           alarmNum: 0,
+	           totalNum: 0,
+	           offLine: 0,
+	           onlineNum: 0,
+	       }
+	           
+	       if (isObject(obj)) {
+	           for(let i in cout) {
+				   if(obj[i]) {
+					    cout[i] = obj[i]
+				   }
+	              
+	           }   
+	       }
+	       let countObj = [
+	           { label: "全部", value: cout.totalNum },
+	           {
+	               label: "在线",
+	               value: cout.onlineNum,
+	           },
+	           {
+	               label: "离线",
+	               value: cout.offLine,
+	           },
+	           {
+	               label: "报警",
+	               value: cout.alarmNum,
+	               style: 'color:#D40000;'
+	           },
+	       ];
+	       
+	       this.$store.dispatch('setCountInfo',countObj)
+	      
+	   },
+	   
+    },
+	
+	
+	beforeDestroy() {
+		this.$store.dispatch('clearTreeInterVal')
+	}
+
+}
+</script>
+
+<style lang="scss" scoped>
+	
+	/* 内容satrt */
+	.container {
+		background: #fff;
+		.search {
+			padding: 20rpx 40rpx 10rpx 40rpx;
+			
+			.all-choosed {
+				display: flex;
+				justify-content: flex-start;
+				align-items: center;
+				font-size: 24rpx;
+			}
+		}
+		.list {
+			height: calc(85vh - 400rpx);
+			padding: 0 20rpx;
+			overflow-y: auto;
+			
+			.item {
+				padding: 10rpx 20rpx;
+				display: flex;
+				align-items: center;
+	 
+				.item-value {
+					font-size: 22rpx;
+				}
+				.item-label {
+					font-size: 22rpx;
+				}
+				// border-bottom: 2rpx solid #ddd;
+			}
+			.empty {
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				height: calc(100vh - 500rpx);
+				color: #909193;
+				min-height: 800rpx;
+			}
+		}
+		
+		.last,.next {
+			margin: 10rpx 40rpx;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+		}
+	
+	}
+	
+	/* 内容end */
+	
+	.choosed-image {
+		width: 32rpx;
+		height: 32rpx;
+		margin-right: 10rpx;
+	}
+	.icon-image {
+		width: 32rpx;
+		height: 32rpx;
+		margin-right: 10rpx;
+	}
+	.option-image {
+		width: 40rpx;
+		height: 40rpx;
+		margin-right: 10rpx;
+	}
+	
+
+</style>

+ 207 - 0
pages/realtimeWatch/module/h5Map.vue

@@ -0,0 +1,207 @@
+<template>
+	<view class="realtimeWatch">
+		<map-container 
+			ref="mapContainerRef"
+			:pointsData="pointsData"
+			@handleMarkerClick="handleMarkerClick"
+		>
+		</map-container>
+		
+		<view class="counts">
+			<u-row>
+				<u-col :span="3" class="counts-item" v-for="(item,index) in countInfo" :key="index" :class="item.class">
+					<view class="counts-item-label" :style="item.style">{{ item.label }}</view>
+					<view class="counts-item-value" :style="item.style">{{ item.value }}</view>
+				</u-col>				
+			</u-row>
+		</view>
+		
+		<view class="options">
+			<view class="options-item" @click="showSatelliteLayer">
+				<image
+				  style="width: 100%; height: 100%"
+				  :src="$getImages('/assetsMobile/images/map/icon/weixing.png')"
+				/>
+			</view>
+			
+			<view class="options-item" @click="showPanel">
+				<image
+				  style="width: 100%; height: 100%"
+				  :src="$getImages('/assetsMobile/images/map/icon/boxselection.png')"
+				/>
+			</view>
+			
+			<view class="options-item" @click="queryLocation">
+				<image
+				  style="width: 100%; height: 100%"
+				  :src="$getImages('/assetsMobile/images/map/icon/position.png')"
+				/>
+			</view>
+			
+		</view>
+	
+		<panel ref="panelRef"></panel>
+
+		<veh-info ref="vehInfoRef"></veh-info>
+	</view>
+</template>
+
+<script>
+import MapContainer from '@/components/MapContainer'
+import panel from './panel.vue'
+import vehInfo from './vehInfo.vue'
+export default {
+	components: {
+		MapContainer,
+		panel,
+		vehInfo,
+	},
+	data() {
+		return {
+			//卫星图层
+			isShowStelliteLayer: false,
+		}
+	},
+	computed: {
+		pointsData() {
+			// console.log(this.$store.state.realtimeWatch.vheicleCheckArr,'realtimeWatch')
+			let vehmap = new Map()
+			    let vehKey = 'id'
+			    let list = []
+				
+			 let vehicleList = this.$store.state.realtimeWatch.vheicleCheckArr.filter(
+			        (item) => item.code=='vehicle' && !vehmap.has(item[vehKey].toString()) && vehmap.set(item[vehKey].toString())
+			).map((item) => {
+				return {
+					lnglat: [item.lng,item.lat],
+					item: item,
+				}
+			})
+			
+			 let usermap = new Map()
+			let userKey = 'id'
+			
+			let peopleList = this.$store.state.realtimeWatch.peopleCheckArr.filter(
+				(item) => item.code=='people' && !usermap.has(item[userKey].toString()) && usermap.set(item[userKey].toString())
+			).map((item) => {
+				console.log('数据是',item)
+		   
+				return {
+					lnglat: [item.lng,item.lat],
+					item
+				}
+			})
+				
+			
+			list = vehicleList.concat(peopleList)
+			
+			
+		  return list;
+		},
+		countInfo() {
+			return this.$store.state.realtimeWatch.countInfo
+		},
+	},
+	onReady () {		  
+	},
+	methods: {
+		 // 查询自身位置
+		queryLocation() {
+		  uni.getLocation({
+		    type: "gcj02",
+		    success: (res) => {
+				const {latitude,longitude} = res
+				
+				  this.$refs.mapContainerRef.setZoomAndCenter(12,[longitude,latitude],true)
+		    },
+		    fail: (res) => {
+		      console.log('定位失败',JSON.stringify(res));
+		    }
+		  });
+		},
+		//选车辆
+		showPanel() {
+			this.$refs.panelRef.show()
+		},
+		//展示卫星图层
+		showSatelliteLayer() {
+			this.isShowStelliteLayer = !this.isShowStelliteLayer
+			if(this.$refs.mapContainerRef) this.$refs.mapContainerRef.setStelliteLayer(this.isShowStelliteLayer)
+		},
+		//点击标注点
+		handleMarkerClick(e) {
+			// console.log(e,'handleMarkerClick')
+			const {cluster} = e
+			let item = cluster.p.item
+			this.$refs.vehInfoRef.show(item)
+	
+		}
+	},
+	beforeDestroy() {
+		this.$store.dispatch('setVheicleCheckArr',[])
+		this.$store.dispatch('setPeopleCheckArr',[])
+	}
+}
+</script>
+
+<style lang="scss" scoped>
+.realtimeWatch {
+	width: 100%;
+	height: 100vh;
+	position: relative;
+	#map {
+		width: 750rpx; 
+		height: 100%;
+	}
+	
+	.counts {
+		position: absolute;
+		left: 2%;
+		top: 20rpx;
+		width: 96%;
+		background: #fff;
+		border-radius: 10rpx;
+		
+		box-shadow: 0 2px 6px #3f57c252;
+		// margin: 10rpx;
+		.counts-item {
+			padding: 14rpx 10rpx;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			flex-direction: column;
+			.counts-item-label {
+				 font-size: 24rpx;
+				font-family: PingFangSC-Medium, PingFang SC;
+				font-weight: 400;
+				color: #333333;
+				text-align: center;
+			}
+			.counts-item-value {
+				margin-top: 10rpx;
+				 font-size: 26rpx;
+				font-family: PingFangSC-Regular, PingFang SC;
+				font-weight: 600;
+				color: #333333;
+				text-align: center;
+			}
+		}
+	}
+	.options {
+		position: absolute;
+		width: 72rpx;
+		right: 30rpx;
+		bottom: 156rpx;
+		// box-shadow: 0px 3px 30px 0px rgba(0, 0, 0, 0.15);
+		z-index: 9999;
+		
+		.options-item {
+			height: 72rpx;
+			
+		}
+		.options-item+.options-item{
+			margin-top: 20rpx;
+		}
+	}
+}	
+</style>

部分文件因为文件数量过多而无法显示