zhuangyunsheng 3 місяців тому
батько
коміт
6f0befd43c
77 змінених файлів з 2391 додано та 3015 видалено
  1. 1 0
      package.json
  2. 39 21
      src/api/model/carwash.js
  3. 18 6
      src/api/model/common.js
  4. 63 3
      src/api/model/system.js
  5. 7 0
      src/components/scTable/helper.js
  6. 27 4
      src/components/scTable/index.vue
  7. 2 2
      src/components/scUpload/file.vue
  8. 3 3
      src/components/scUpload/index.vue
  9. 4 4
      src/components/scUpload/multiple.vue
  10. 1 1
      src/config/index.js
  11. 141 16
      src/config/route.js
  12. 14 15
      src/config/table.js
  13. 7 7
      src/layout/components/password.vue
  14. 1 1
      src/layout/components/setting.vue
  15. 5 1
      src/main.js
  16. 0 374
      src/mock/mock.js
  17. 14 11
      src/router/index.js
  18. 8 11
      src/style/app.scss
  19. 1 1
      src/style/dark.scss
  20. 0 136
      src/utils/basicDic.js
  21. 89 0
      src/views/basic/acceptItems/detail.vue
  22. 119 0
      src/views/basic/acceptItems/index.vue
  23. 4 0
      src/views/basic/acceptItems/main.js
  24. 580 0
      src/views/basic/project/detail.vue
  25. 132 0
      src/views/basic/project/index.vue
  26. 194 0
      src/views/basic/project/items.vue
  27. 16 0
      src/views/basic/project/main.js
  28. 1 2
      src/views/basic/supplier/detail.vue
  29. 2 2
      src/views/basic/supplier/index.vue
  30. 2 3
      src/views/basic/tag/detail.vue
  31. 1 1
      src/views/basic/tag/index.vue
  32. 188 0
      src/views/dataMock/carwash/components/form/index.vue
  33. 8 3
      src/views/dataMock/carwash/components/index.js
  34. 104 0
      src/views/dataMock/carwash/components/info/detail.vue
  35. 121 0
      src/views/dataMock/carwash/components/info/index.vue
  36. 107 102
      src/views/dataMock/carwash/components/monos.vue
  37. 0 24
      src/views/dataMock/carwash/components/record.vue
  38. 104 0
      src/views/dataMock/carwash/components/record/detail.vue
  39. 117 0
      src/views/dataMock/carwash/components/record/index.vue
  40. 0 171
      src/views/dataMock/carwash/components/table.vue
  41. 0 24
      src/views/dataMock/carwash/components/template.vue
  42. 0 119
      src/views/dataMock/carwash/detail.vue
  43. 3 17
      src/views/dataMock/carwash/index.vue
  44. 23 0
      src/views/dataMock/carwash/main.js
  45. 0 47
      src/views/dataMock/carwash/mono/index.vue
  46. 0 33
      src/views/dataMock/carwash/mono/tableExpand.vue
  47. 1 1
      src/views/dataMock/env/detail.vue
  48. 1 1
      src/views/dataMock/standard/detail.vue
  49. 1 1
      src/views/dataMock/ugliAi/detail.vue
  50. 0 92
      src/views/env/monitor/index.vue
  51. 0 0
      src/views/equipment/env.vue
  52. 5 47
      src/views/facerec/device/index.vue
  53. 5 61
      src/views/passqrcode/device/index.vue
  54. 0 0
      src/views/equipment/tower.vue
  55. 0 96
      src/views/facerec/device/fluxs.vue
  56. 0 98
      src/views/facerec/device/groups/index.vue
  57. 0 31
      src/views/facerec/device/groups/tableExpand.vue
  58. 0 57
      src/views/facerec/device/logDetail.vue
  59. 0 116
      src/views/facerec/platform/push/index.vue
  60. 0 120
      src/views/facerec/platform/task/index.vue
  61. 0 19
      src/views/facerec/platform/task/json.vue
  62. 83 138
      src/views/home/index.vue
  63. 20 0
      src/views/home/main.js
  64. 1 5
      src/views/login/index.vue
  65. 0 93
      src/views/passqrcode/device/fluxs.vue
  66. 0 99
      src/views/passqrcode/device/groups/index.vue
  67. 0 34
      src/views/passqrcode/device/groups/tableExpand.vue
  68. 0 57
      src/views/passqrcode/device/logDetail.vue
  69. 0 117
      src/views/passqrcode/device/stateDev.vue
  70. 0 112
      src/views/passqrcode/platform/push/index.vue
  71. 0 100
      src/views/passqrcode/platform/task/index.vue
  72. 0 51
      src/views/passqrcode/platform/task/mono/index.vue
  73. 0 70
      src/views/passqrcode/platform/task/mono/tableExpand.vue
  74. 0 38
      src/views/passqrcode/platform/task/sync.vue
  75. 0 95
      src/views/tower/monitor/index.vue
  76. 0 98
      src/views/tower/warning/index.vue
  77. 3 3
      src/views/userCenter/index.vue

+ 1 - 0
package.json

@@ -8,6 +8,7 @@
         "lint": "vue-cli-service lint"
     },
     "dependencies": {
+        "@amap/amap-jsapi-loader": "^1.0.1",
         "@element-plus/icons-vue": "2.0.10",
         "@vxe-ui/plugin-export-xlsx": "^4.0.13",
         "@vxe-ui/plugin-render-element": "^4.0.10",

+ 39 - 21
src/api/model/carwash.js

@@ -2,37 +2,55 @@ import config from "@/config"
 import http from "@/utils/request"
 
 export default {
-    check: {
-        url: `${config.API_URL}/zeroapi/v2/cwashm/device/check`,
-        name: "查询在线状态",
+    gate: {
+        name: "安装点查询",
+        url: `${config.API_URL}/api/carRinseTemp/getMountedPage`,
         get: async function (data = {}) {
             return await http.post(this.url, data);
         }
     },
 
-    gate: {
-        url: `${config.API_URL}/api/carRinseTemp/getMountedPage`,
-        name: "安装点查询",
+    carInfo: {
+        name: "车辆信息",
+        url: `${config.API_URL}/api/carRinseInfo`,
         get: async function (data = {}) {
-            return await http.post(this.url, data);
+            return await http.post(`${this.url}/getPage`, data);
+        },
+
+        add: async function (data = {}) {
+            return await http.post(`${this.url}/save`, data);
+        },
+
+        edit: async function (data = {}) {
+            return await http.post(`${this.url}/update`, data);
+        },
+
+        del: async function (data = {}) {
+            return await http.post(`${this.url}/remove`, data);
         }
     },
 
-    records: {
-        temp: {
-            url: `${config.API_URL}/api/carRinseTemp/getPage`,
-            name: "监测记录-图片",
-            get: async function (data = {}) {
-                return await http.post(this.url, data);
-            }
+    record: {
+        name: "冲洗记录",
+        url: `${config.API_URL}/api/carRinseTemp`,
+        get: async function (data = {}) {
+            return await http.post(`${this.url}/getPage`, data);
+        },
+
+        edit: async function (data = {}) {
+            return await http.post(`${this.url}/updateCarRinse`, data);
         },
-    
-        camera: {
-            url: `${config.API_URL}/api/carRinseCamera/getPage`,
-            name: "监测记录-视频",
-            get: async function (data = {}) {
-                return await http.post(this.url, data);
-            }
+
+        del: async function (data = {}) {
+            return await http.post(`${this.url}/deleteCarRinse`, data);
+        }
+    },
+
+    makeData: {
+        url: `${config.API_URL}/ops/carRinse/makeData`,
+        name: "数据模拟",
+        add: async function (data = {}) {
+            return await http.post(this.url, data);
         }
     }
 }

+ 18 - 6
src/api/model/common.js

@@ -3,18 +3,30 @@ import http from "@/utils/request"
 
 export default {
 	folder: {
-		url: `${config.API_URL}/zcxt`,
+		url: `${config.API_URL}/api/folder`,
 		name: "文件上传",
 		up: async function (data, config = {}) {
-			return await http.post(`${this.url}/file/upload`, data, config);
+			return await http.post(`${this.url}/up`, data, config);
 		},
-
+        
 		rm: async function (entityID) {
-			return await http.post(`${this.url}/folder/rm`, { querys: [], expands: { entityID } });
+			return await http.post(`${this.url}/rm`, { querys: [], expands: { entityID } });
 		},
 
 		get: async function (entityID) {
-			return await http.get(`${this.url}/folder/${entityID}`, {}, { responseType: "blob" });
+			return await http.get(`${this.url}/${entityID}`, {}, { responseType: "blob" });
 		}
-	}
+	},
+
+    opsTask: {
+        name: "任务列表",
+        url: `${config.API_URL}/ops/opsTask`,
+        get: async function (data = {}) {
+            return await http.post(`${this.url}/getPage`, data);
+        },
+
+        stop: async function (data = {}) {
+            return await http.post(`${this.url}/stopTask`, data);
+        }
+    },
 }

+ 63 - 3
src/api/model/system.js

@@ -3,10 +3,70 @@ import http from "@/utils/request"
 
 export default {
 	project: {
-		url: `${config.API_URL}/api/factory/getFactoryProject`,
+		url: `${config.API_URL}/ops/projectInfo`,
 		name: "获取项目",
-		get: async function (data = {}) {
-			return await http.post(this.url, data);
+        dept: async function (data = {}) {
+			return await http.post(`${this.url}/getName`, data);
+		},
+
+        get: async function (data = {}) {
+			return await http.post(`${this.url}/getPage`, data);
+		},
+
+		all: async function (data = {}) {
+			return await http.post(`${this.url}/getList`, data);
+		},
+
+        detail: async function (data = {}) {
+			return await http.post(`${this.url}/getById`, data);
+		},
+
+        add: async function (data = {}) {
+			return await http.post(`${this.url}/save`, data);
+		},
+
+        edit: async function (data = {}) {
+			return await http.post(`${this.url}/update`, data);
+		},
+
+        del: async function (data = {}) {
+			return await http.post(`${this.url}/remove`, data);
+		},
+
+        bindItem: {
+            url: `${config.API_URL}/ops/acceptanceProject`,
+            name: "项目绑定验收清单项",
+            get: async function (data = {}) {
+                return await http.post(`${this.url}/getList`, data);
+            },
+
+            add: async function (data = {}) {
+                return await http.post(`${this.url}/save`, data);
+            }
+        }
+	},
+
+    acceptItems: {
+		url: `${config.API_URL}/ops/acceptanceItems`,
+		name: "获取验收清单项",
+        get: async function (data = {}) {
+			return await http.post(`${this.url}/getPage`, data);
+		},
+
+        all: async function (data = {}) {
+			return await http.post(`${this.url}/getList`, data);
+		},
+
+        add: async function (data = {}) {
+			return await http.post(`${this.url}/save`, data);
+		},
+
+        edit: async function (data = {}) {
+			return await http.post(`${this.url}/update`, data);
+		},
+
+        del: async function (data = {}) {
+			return await http.post(`${this.url}/remove`, data);
 		}
 	},
 

+ 7 - 0
src/components/scTable/helper.js

@@ -78,6 +78,13 @@ export const mapFormItemDatePicker = (field, title, config = {}) => ({
             [XEUtils.get(config, "props.type")?.includes("range") ? "defaultTime" : ""]: [new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)],
             ...XEUtils.get(config, "props")
         },
+        events: {
+            focus: (_, $event) => {
+                // 解决点击确认按钮无效的问题
+                const btn = document.querySelector(".el-picker-panel__footer .is-plain.el-picker-panel__link-btn")
+                btn.addEventListener("click", () => $event.target.blur());
+            }
+        },
         ...XEUtils.omit(config, "props")
     },
     ...config

+ 27 - 4
src/components/scTable/index.vue

@@ -88,7 +88,15 @@ const gridOptions = ref({
                 className: ({ $grid, item, data }) => {
                     const showItems = XEUtils.filter(XEUtils.orderBy(XEUtils.get(props, "formConfig.items", []), "orderBy"), formItem => (XEUtils.isUndefined(formItem.visible) || formItem.visible) && (XEUtils.isUndefined(formItem.visibleMethod) || formItem.visibleMethod({ data })));
                     const spanItems = (!gridOptions.value.formConfig.collapseStatus && showItems) || XEUtils.filter(showItems, f_item => !f_item.folding);
-                    const remainder = 24 - (XEUtils.sum(spanItems, s_item => s_item.span || 6) % 24);
+
+                    let spanItemsSum = 0;
+                    XEUtils.arrayEach(spanItems, s_item => {
+                        const spanCount = (s_item.span || 6);
+                        if (spanCount > 24 - (spanItemsSum % 24)) spanItemsSum += 24 - (spanItemsSum % 24);
+                        spanItemsSum += spanCount;
+                    })
+                    const remainder = 24 - (spanItemsSum % 24);
+
                     item.visible = showItems.length > 0;
                     item.collapseNode = showItems.length > 3;
                     item.span = remainder < 6 && 24 || remainder;
@@ -162,6 +170,7 @@ const gridOptions = ref({
         ...props.editConfig
     },
     pagerConfig: {
+        queryType: "limit", // 页码筛选类型
         pageSizes: config.pageSizes,
         layouts: config.layouts,
         currentPage: 1,
@@ -224,14 +233,27 @@ const searchData = (mode = "add") => {
 const resetData = () => {
     gridOptions.value.pagerConfig.currentPage = 1;
     gridOptions.value.pagerConfig.pageSize = config.pageSize;
-    XEUtils.arrayEach(gridOptions.value.formConfig.items, item => XEUtils.set(gridOptions.value.formConfig.data, item.field, XEUtils.get(item, "resetValue")));
-    XEUtils.merge(gridOptions.value.formConfig.data, XEUtils.omit(gridOptions.value.formConfig.data, item => XEUtils.isEmpty(item) && !XEUtils.isNumber(item)));
-
+    xGrid.value.resetForm();
+    
+    // 解决重置 datepicker触发焦点问题
+    XEUtils.arrayEach(xGrid.value.getRefMaps().refForm.value.getRefMaps().refElem.value, ele => {
+        if (XEUtils.includes(ele.parentNode.className, "el-date-editor--datetimerange")) {
+            const popper = document.querySelector(".el-picker__popper.datetime-picker-popper");
+            nextTick(() => {
+                if (popper) popper.style.display = "none";
+                ele.focus();
+                ele.blur();
+            });
+        }
+    });
+    
     getData();
 }
 
 const formCollapseEvent = ({ collapse }) => gridOptions.value.formConfig.collapseStatus = collapse;
 
+const toggleTableLoading = value => gridOptions.value.loading = value;
+
 const toggleFormEnabled = () => gridOptions.value.formConfig.enabled = !gridOptions.value.formConfig.enabled;
 
 const toggleToolbarProps = obj => XEUtils.objectEach(obj, (value, key) => XEUtils.set(gridOptions.value.toolbarConfig, key, value));
@@ -261,6 +283,7 @@ const exportEvent = async ($grid, options) => {
 
 defineExpose({
     selectedRows,
+    toggleTableLoading,
     toggleFormEnabled,
     toggleToolbarProps,
     reloadColumn,

+ 2 - 2
src/components/scUpload/file.vue

@@ -132,10 +132,10 @@
 			async handlePreview(uploadFile) {
 				if (config.imageIncludes(uploadFile.mineType)) {
 					this.showPictureViewer = true;
-					this.previewImageList = ["/zcxt/folder/" + uploadFile.path];
+					this.previewImageList = ["/api/folder/" + uploadFile.path];
 				} else if (config.videoIncludes(uploadFile.mineType)) {
 					this.showVideoViewer = true;
-					this.previewVideoUrl = "/zcxt/folder/" + uploadFile.path;
+					this.previewVideoUrl = "/api/folder/" + uploadFile.path;
 				} else {
 					const res = await this.$API.common.folder.get(uploadFile.path);
 					const a = document.createElement("a");

+ 3 - 3
src/components/scUpload/index.vue

@@ -7,12 +7,12 @@
 			<el-image class="image" :src="file.tempFile" fit="cover"></el-image>
 		</div>
 		<div v-if="file && file.status=='success'" class="sc-upload__img">
-			<el-image v-if="isImage(file.mineType)" class="image" :src="'/zcxt/folder/' + file.path" :preview-src-list="['/zcxt/folder/' + file.path]" fit="cover" preview-teleported :z-index="9999">
+			<el-image v-if="isImage(file.mineType)" class="image" :src="'/api/folder/' + file.path" :preview-src-list="['/api/folder/' + file.path]" fit="cover" preview-teleported :z-index="9999">
 				<template #placeholder>
 					<div class="sc-upload__img-slot">Loading...</div>
 				</template>
 			</el-image>
-			<sc-video v-if="isVideo(file.mineType)" :src="'/zcxt/folder/' + file.path" showMask @play="videoPlay"></sc-video>
+			<sc-video v-if="isVideo(file.mineType)" :src="'/api/folder/' + file.path" showMask @play="videoPlay"></sc-video>
 			
 			<div class="sc-upload__img-actions" v-if="!disabled">
 				<span class="del" @click="handleRemove()"><el-icon><el-icon-delete /></el-icon></span>
@@ -235,7 +235,7 @@
 
 			videoPlay() {
 				this.showVideoViewer = true;
-				this.previewVideoUrl = "/zcxt/folder/" + this.file.path;
+				this.previewVideoUrl = "/api/folder/" + this.file.path;
 			}
 		}
 	}

+ 4 - 4
src/components/scUpload/multiple.vue

@@ -21,12 +21,12 @@
 			</template>
 			<template #file="{ file }">
 				<div class="sc-upload-list-item">
-					<el-image v-if="isImage(file.mineType)" class="el-upload-list__item-thumbnail" :src="'/zcxt/folder/' + file.path" :preview-src-list="preview" fit="cover" preview-teleported :z-index="9999">
+					<el-image v-if="isImage(file.mineType)" class="el-upload-list__item-thumbnail" :src="'/api/folder/' + file.path" :preview-src-list="preview" fit="cover" preview-teleported :z-index="9999">
 						<template #placeholder>
 							<div class="sc-upload-multiple-image-slot">Loading...</div>
 						</template>
 					</el-image>
-					<sc-video v-if="isVideo(file.mineType)" :src="'/zcxt/folder/' + file.path" showMask @play="videoPlay(file)"></sc-video>
+					<sc-video v-if="isVideo(file.mineType)" :src="'/api/folder/' + file.path" showMask @play="videoPlay(file)"></sc-video>
 
 					<div v-if="!disabled && file.status == 'success'" class="sc-upload__item-actions">
 						<span class="del" @click="handleRemove(file)"><el-icon><el-icon-delete /></el-icon></span>
@@ -86,7 +86,7 @@
 
 		computed: {
 			preview() {
-				return this.defaultFileList.map(v => "/zcxt/folder/" + v.path);
+				return this.defaultFileList.map(v => "/api/folder/" + v.path);
 			}
 		},
 
@@ -177,7 +177,7 @@
 
 			videoPlay(file) {
 				this.showVideoViewer = true;
-				this.previewVideoUrl = "/zcxt/folder/" + file.path;
+				this.previewVideoUrl = "/api/folder/" + file.path;
 			}
 		}
 	}

+ 1 - 1
src/config/index.js

@@ -15,7 +15,7 @@ const DEFAULT_CONFIG = {
 	TOKEN_NAME: "Authorization",
 
 	//Token前缀,注意最后有个空格,如不需要需设置空字符串
-	TOKEN_PREFIX: "Bearer ",
+	TOKEN_PREFIX: "",
 
 	//追加其他头
 	HEADERS: {},

+ 141 - 16
src/config/route.js

@@ -6,28 +6,153 @@
 
 const routes = [
     {
-        name: "device",
-        path: "/device",
-        meta: { title: "在线设备", icon: "ant-design:code-sandbox-outlined", affix: true },
-        component: "device"
+        name: "home",
+        path: "/home",
+        meta: { title: "首页", affix: true },
+        component: "home"
     },
     {
-        name: "group",
-        path: "/group",
-        meta: { title: "组控识别", icon: "clarity:organization-line" },
-        component: "group"
+        name: "userCenter",
+        path: "/usercenter",
+        meta: { title: "个人信息", hidden: true },
+        component: "userCenter"
     },
     {
-        name: "record",
-        path: "/record",
-        meta: { title: "识别记录", icon: "ant-design:reconciliation-outlined" },
-        component: "record"
+        name: "basic",
+        path: "/basic",
+        meta: { title: "基础数据管理", icon: "ri:apps-line" },
+        redirect: "/basic/project",
+        children: [
+            {
+                name: "project",
+                path: "/basic/project",
+                meta: { title: "项目管理", icon: "ix:projects" },
+                component: "basic/project"
+            },
+            {
+                name: "acceptItems",
+                path: "/basic/acceptItems",
+                meta: { title: "验收清单项", icon: "pajamas:list-bulleted" },
+                component: "basic/acceptItems"
+            },
+            {
+                name: "supplier",
+                path: "/basic/supplier",
+                meta: { title: "供应商管理", icon: "ant-design:phone-outlined" },
+                component: "basic/supplier"
+            },
+            {
+                name: "customer",
+                path: "/basic/customer",
+                meta: { title: "客户管理", icon: "garden:customer-lists-fill-26" },
+                component: "basic/customer"
+            },
+            {
+                name: "tag",
+                path: "/basic/tag",
+                meta: { title: "标签管理", icon: "mingcute:tag-line" },
+                component: "basic/tag"
+            }
+        ]
+    },
+
+    {
+        name: "equipment",
+        path: "/equipment",
+        meta: { title: "设备管理", icon: "ant-design:code-sandbox-outlined" },
+        redirect: "/equipment/facerec",
+        children: [
+            {
+                name: "equipmentFacerec",
+                path: "/equipment/facerec",
+                meta: { title: "实名制人脸识别设备", icon: "tabler:face-id" },
+                component: "equipment/facerec"
+            },
+            {
+                name: "equipmentPassqrcode",
+                path: "/equipment/passqrcode",
+                meta: { title: "临时访客设备", icon: "ant-design:qrcode-outlined" },
+                component: "equipment/passqrcode"
+            },
+            {
+                name: "equipmentTower",
+                path: "/equipment/tower",
+                meta: { title: "塔机监测设备", icon: "mingcute:tower-crane-line" },
+                component: "equipment/tower"
+            },
+            {
+                name: "equipmentEnv",
+                path: "/equipment/env",
+                meta: { title: "环境监测设备", icon: "fluent:earth-leaf-16-regular" },
+                component: "equipment/env"
+            }
+        ]
+    },
+    {
+        name: "dataMock",
+        path: "/dataMock",
+        meta: { title: "数据管理与模拟", icon: "majesticons:data-plus-line" },
+        redirect: "/dataMock/env",
+        children: [
+            {
+                name: "envMock",
+                path: "/dataMock/env",
+                meta: { title: "数据管理与模拟-环境监测", icon: "fluent:earth-leaf-16-regular" },
+                component: "dataMock/env"
+            },
+            {
+                name: "standardMock",
+                path: "/dataMock/standard",
+                meta: { title: "数据管理与模拟-标养室监测", icon: "dashicons:dashboard" },
+                component: "dataMock/standard"
+            },
+            {
+                name: "carwashMock",
+                path: "/dataMock/carwash",
+                meta: { title: "数据管理与模拟-渣土运输管理", icon: "map:car-wash" },
+                component: "dataMock/carwash"
+            },
+            {
+                name: "ugliAiMock",
+                path: "/dataMock/ugliAi",
+                meta: { title: "数据管理与模拟-AI视频危险源识别", icon: "hugeicons:ai-brain-02" },
+                component: "dataMock/ugliAi"
+            }
+        ]
+    },
+    {
+        name: "EasyRun",
+        path: "/easyRun",
+        meta: { title: "EasyRun", icon: "fluent-emoji-high-contrast:hammer-and-wrench" },
+        redirect: "/easyRun/saleOrder",
+        children: [
+            {
+                name: "saleOrder",
+                path: "/easyRun/saleOrder",
+                meta: { title: "销售订单", icon: "material-symbols:inactive-order-outline-sharp" },
+                component: "easyRun/saleOrder"
+            },
+            {
+                name: "purchaseOrder",
+                path: "/easyRun/purchaseOrder",
+                meta: { title: "采购订单", icon: "hugeicons:file-01" },
+                component: "easyRun/purchaseOrder"
+            }
+        ]
+    },
+    {
+        name: "warranty",
+        path: "/warranty",
+        meta: { title: "质保管理", icon: "streamline-flex:warranty-badge-highlight-remix" },
+        redirect: "/warranty/warranty",
+        children: []
     },
     {
-        name: "system",
-        path: "/system",
-        meta: { title: "系统配置", icon: "uil:setting" },
-        component: "system"
+        name: "afterSales",
+        path: "/afterSales",
+        meta: { title: "售后管理", icon: "icon-park-outline:market" },
+        redirect: "/afterSales/afterSales",
+        children: []
     }
 ]
 

+ 14 - 15
src/config/table.js

@@ -9,30 +9,28 @@ export default {
     
     framework: {
         common: {
-            queryData: function ({ formConfig: { data }, pagerConfig: { currentPage, pageSize } }, paramsColumns) {
-                const query = {
-                    current: currentPage,
-                    size: pageSize
-                }
+            queryData: function ({ formConfig: { data }, pagerConfig: { queryType, currentPage, pageSize } }, paramsColumns) {
+                const query = queryType == "limit" ? { current: currentPage, size: pageSize } : {}
+
                 XEUtils.arrayEach(XEUtils.filter(paramsColumns, item => !valueIsNull(data, item.field || item.column)), item => XEUtils.set(query, item.column, XEUtils.get(data, item.field || item.column)))
 
                 return XEUtils.omit(query, val => XEUtils.isEmpty(val) && !XEUtils.isNumber(val))
             },
             parseData: function (res) {
                 return {
-                    data: res.records,			    // 分析数据字段结构
+                    data: res.records || res,			    // 分析数据字段结构
                     total: res.total	            // 分析总数字段结构
                 }
             }
         },
 
         zeroLiteOld: {
-            queryData: function ({ formConfig: { data }, pagerConfig: { currentPage, pageSize } }, paramsColumns) {
-                const expands = {}
+            queryData: function ({ formConfig: { data }, pagerConfig: { queryType, currentPage, pageSize } }, paramsColumns) {
+                const expands = queryType == "expands" ? { length: pageSize, start: (currentPage - 1) * pageSize } : {}
                 XEUtils.arrayEach(XEUtils.filter(paramsColumns, item => item.type == "expands" && !valueIsNull(data, item.field || item.column)), item => XEUtils.set(expands, item.field || item.column, data[item.field || item.column]))
 
                 const query = {
-                    limit: { length: pageSize, start: (currentPage - 1) * pageSize },
+                    limit: queryType == "limit" ? { length: pageSize, start: (currentPage - 1) * pageSize } : {},
                     columns: XEUtils.get(XEUtils.find(paramsColumns, item => item.type == "columns"), "field", [])
                 }
                 XEUtils.arrayEach(XEUtils.filter(paramsColumns, item => item.type == "limit"), item => XEUtils.set(query, `limit.${item.column}`, item.field))
@@ -58,7 +56,7 @@ export default {
                 if (relation.length == 1) XEUtils.set(query, "condition", XEUtils.first(relation))
                 if (relation.length > 1) XEUtils.set(query, "condition", { symbol: "AND", relation })
 
-                return { querys: XEUtils.isEmpty(XEUtils.omit(query, item => XEUtils.isEmpty(item))) ? [] : [XEUtils.omit(query, item => XEUtils.isEmpty(item))], expands }
+                return { querys: XEUtils.isEmpty(XEUtils.omit(query, item => XEUtils.isEmpty(item))) ? []: [XEUtils.omit(query, item => XEUtils.isEmpty(item))], expands }
             },
             parseData: function (res) {
                 return {
@@ -69,12 +67,12 @@ export default {
         },
 
         zeroLite: {
-            queryData: function ({ formConfig: { data }, pagerConfig: { currentPage, pageSize } }, paramsColumns) {
-                const expands = {}
+            queryData: function ({ formConfig: { data }, pagerConfig: { queryType, currentPage, pageSize } }, paramsColumns) {
+                const expands = queryType == "expands" ? { length: pageSize, start: (currentPage - 1) * pageSize } : {}
                 XEUtils.arrayEach(XEUtils.filter(paramsColumns, item => item.type == "expands" && !valueIsNull(data, item.field || item.column)), item => XEUtils.set(expands, item.field || item.column, data[item.field || item.column]))
 
                 const query = {
-                    limit: { length: pageSize, start: (currentPage - 1) * pageSize },
+                    limit: queryType == "limit" ? { length: pageSize, start: (currentPage - 1) * pageSize } : {},
                     orderby: XEUtils.map(XEUtils.filter(paramsColumns, item => item.type == "orderby"), item => ({ column: item.column, seq: item.symbol.toUpperCase() })),
                     columns: XEUtils.get(XEUtils.find(paramsColumns, item => item.type == "columns"), "field", [])
                 }
@@ -99,9 +97,10 @@ export default {
                 return { querys: XEUtils.isEmpty(XEUtils.omit(query, item => XEUtils.isEmpty(item))) ? [] : [XEUtils.omit(query, item => XEUtils.isEmpty(item))], expands }
             },
             parseData: function (res) {
+                const data = XEUtils.get(res, "datas", XEUtils.get(res, "expands.connects", []))
                 return {
-                    data: res.datas,			    // 分析数据字段结构
-                    total: XEUtils.has(res, "expands.total") ? res.expands.total : res.datas.length > 0 ? Infinity : undefined	    //分析总数字段结构
+                    data,			    // 分析数据字段结构
+                    total: XEUtils.has(res, "expands.total") ? res.expands.total : data.length	    //分析总数字段结构
                 }
             }
         }

+ 7 - 7
src/layout/components/password.vue

@@ -1,22 +1,22 @@
 <template>
-    <el-dialog v-model="visible" title="修改密码" width="500px">
-        <el-form ref="dialogForm" :model="form" :rules="rules" label-width="120px">
-            <el-form-item label="当前密码" prop="userPassword">
+    <el-dialog v-model="visible" title="修改密码" width="480">
+        <el-form ref="dialogForm" :model="form" :rules="rules" label-width="120">
+            <el-form-item label="当前密码" prop="userPassword">
                 <el-input v-model="form.userPassword" type="password" show-password placeholder="请输入当前密码"></el-input>
 				<div class="el-form-item-msg">必须提供当前登录用户密码才能进行更改</div>
             </el-form-item>
-            <el-form-item label="新密码" prop="newPassword">
+            <el-form-item label="新密码" prop="newPassword">
                 <el-input v-model="form.newPassword" type="password" show-password placeholder="请输入新密码"></el-input>
 				<sc-password-strength v-model="form.newPassword"></sc-password-strength>
             </el-form-item>
-            <el-form-item label="确认新密码:" prop="confirmNewPassword">
+            <el-form-item label="确认新密码" prop="confirmNewPassword">
 				<el-input v-model="form.confirmNewPassword" type="password" show-password placeholder="请再次输入新密码"></el-input>
 			</el-form-item>
         </el-form>
 
         <template #footer>
-            <el-button @click="cancel">取 消</el-button>
-            <el-button type="primary" :loading="isSaveing" @click="save">确 定</el-button>
+            <el-button auto-insert-space @click="cancel">取消</el-button>
+            <el-button :loading="isSaveing" type="primary" auto-insert-space @click="save">确定</el-button>
         </template>
     </el-dialog>
 </template>

+ 1 - 1
src/layout/components/setting.vue

@@ -1,5 +1,5 @@
 <template>
-	<el-form ref="form" label-width="120px" label-position="left" style="padding:0 20px;">
+	<el-form ref="form" label-width="120" label-position="left" style="padding:0 20px;">
 		<el-form-item :label="$t('user.nightmode')">
 			<el-switch v-model="dark"></el-switch>
 		</el-form-item>

+ 5 - 1
src/main.js

@@ -20,4 +20,8 @@ app.use(scui);
 app.use(vxeTable);
 
 //挂载app
-app.mount("#app");
+app.mount("#app");
+
+window._AMapSecurityConfig = {
+    securityJsCode: "3f2b53d5769f440ab7a6118390ddf257"
+}

+ 0 - 374
src/mock/mock.js

@@ -1,374 +0,0 @@
-export const mockData = {
-	menus: [
-		{
-			name: "home",
-			path: "/home",
-			meta: { title: "首页", affix: true },
-			component: "home"
-		},
-		{
-			name: "userCenter",
-			path: "/usercenter",
-			meta: { title: "个人信息", hidden: true },
-			component: "userCenter"
-		},
-        {
-			name: "basic",
-			path: "/basic",
-			meta: { title: "基础数据管理", icon: "ri:apps-line" },
-            redirect: "/basic/supplier",
-			children: [
-				{
-                    name: "supplier",
-					path: "/basic/supplier",
-					meta: { title: "供应商管理", icon: "ant-design:phone-outlined" },
-					component: "basic/supplier"
-				},
-                {
-                    name: "customer",
-					path: "/basic/customer",
-					meta: { title: "客户管理", icon: "garden:customer-lists-fill-26" },
-					component: "basic/customer"
-				},
-                {
-                    name: "tag",
-					path: "/basic/tag",
-					meta: { title: "标签管理", icon: "mingcute:tag-line" },
-					component: "basic/tag"
-				}
-            ]
-		},
-        {
-			name: "facerec",
-			path: "/facerec",
-			meta: { title: "人脸识别", icon: "tabler:face-id" },
-            redirect: "/facerec/device",
-			children: [
-				{
-                    name: "facerecDevice",
-					path: "/facerec/device",
-					meta: { title: "人脸识别-全部设备", icon: "ant-design:code-sandbox-outlined" },
-					component: "facerec/device"
-				},
-                {
-                    name: "facerecPlatformTask",
-					path: "/facerec/platformTask",
-					meta: { title: "人脸识别-第三方平台下发任务", icon: "solar:list-arrow-down-bold" },
-					component: "facerec/platform/task"
-				},
-                {
-                    name: "facerecPlatformTaskPush",
-					path: "/facerec/platformTaskPush",
-					meta: { title: "人脸识别-第三方平台推送任务", icon: "cil:list-high-priority" },
-					component: "facerec/platform/push"
-				}
-            ]
-		},
-        {
-			name: "passqrcode",
-			path: "/passqrcode",
-			meta: { title: "临时访客", icon: "ant-design:qrcode-outlined" },
-            redirect: "/passqrcode/device",
-			children: [
-				{
-                    name: "passqrcodeDevice",
-					path: "/passqrcode/device",
-					meta: { title: "临时访客-全部设备", icon: "ant-design:code-sandbox-outlined" },
-					component: "passqrcode/device"
-				},
-                {
-                    name: "passqrcodePlatformTask",
-					path: "/passqrcode/platformTask",
-					meta: { title: "临时访客-青岛地铁卡号同步", icon: "mdi:calendar-sync-outline" },
-					component: "passqrcode/platform/task"
-				},
-                {
-                    name: "passqrcodePlatformTaskPush",
-					path: "/passqrcode/platformTaskPush",
-					meta: { title: "临时访客-第三方平台推送任务", icon: "cil:list-high-priority" },
-					component: "passqrcode/platform/push"
-				}
-            ]
-		},
-        {
-			name: "tower",
-			path: "/tower",
-			meta: { title: "塔基监测", icon: "mingcute:tower-crane-line" },
-            redirect: "/tower/device",
-			children: [
-				{
-                    name: "towerDevice",
-					path: "/tower/device",
-					meta: { title: "塔基监测-全部设备", icon: "ant-design:code-sandbox-outlined" },
-					component: "tower/device"
-				},
-                {
-                    name: "towerRecord",
-					path: "/tower/monitor",
-					meta: { title: "塔基监测-监测记录", icon: "ant-design:reconciliation-outlined" },
-					component: "tower/monitor"
-				},
-                {
-                    name: "towerWarning",
-					path: "/tower/warning",
-					meta: { title: "塔基监测-告警记录", icon: "fluent:text-bullet-list-square-warning-16-regular" },
-					component: "tower/warning"
-				}
-            ]
-		},
-        {
-			name: "env",
-			path: "/env",
-			meta: { title: "环境监测", icon: "fluent:earth-leaf-16-regular" },
-            redirect: "/env/device",
-			children: [
-				{
-                    name: "envDevice",
-					path: "/env/device",
-					meta: { title: "环境监测-全部设备", icon: "ant-design:code-sandbox-outlined" },
-					component: "env/device"
-				},
-                {
-                    name: "envRecord",
-					path: "/env/monitor",
-					meta: { title: "环境监测-监测记录", icon: "ant-design:reconciliation-outlined" },
-					component: "env/monitor"
-				}
-            ]
-		},
-        /* ***************************************************************** */ 
-        {
-			name: "dataMock",
-			path: "/dataMock",
-			meta: { title: "数据管理与模拟", icon: "majesticons:data-plus-line" },
-            redirect: "/dataMock/env",
-			children: [
-                {
-                    name: "envMock",
-                    path: "/dataMock/env",
-                    meta: { title: "数据管理与模拟-环境监测", icon: "fluent:earth-leaf-16-regular" },
-                    component: "dataMock/env"
-                },
-                {
-                    name: "standardMock",
-                    path: "/dataMock/standard",
-                    meta: { title: "数据管理与模拟-标养室监测", icon: "dashicons:dashboard" },
-                    component: "dataMock/standard"
-                },
-                {
-                    name: "carwashMock",
-                    path: "/dataMock/carwash",
-                    meta: { title: "数据管理与模拟-车辆冲洗", icon: "map:car-wash" },
-                    component: "dataMock/carwash"
-                },
-                {
-                    name: "ugliAiMock",
-                    path: "/dataMock/ugliAi",
-                    meta: { title: "数据管理与模拟-AI识别", icon: "hugeicons:ai-brain-02" },
-                    component: "dataMock/ugliAi"
-                }
-            ]
-		},
-        {
-			name: "EasyRun",
-			path: "/easyRun",
-			meta: { title: "EasyRun", icon: "fluent-emoji-high-contrast:hammer-and-wrench" },
-            redirect: "/easyRun/saleOrder",
-			children: [
-                {
-                    name: "saleOrder",
-                    path: "/easyRun/saleOrder",
-                    meta: { title: "销售订单", icon: "material-symbols:inactive-order-outline-sharp" },
-                    component: "easyRun/saleOrder"
-                },
-                {
-                    name: "purchaseOrder",
-                    path: "/easyRun/purchaseOrder",
-                    meta: { title: "采购订单", icon: "hugeicons:file-01" },
-                    component: "easyRun/purchaseOrder"
-                }
-            ]
-		},
-        {
-			name: "warranty",
-			path: "/warranty",
-			meta: { title: "质保管理", icon: "streamline-flex:warranty-badge-highlight-remix" },
-            redirect: "/warranty/warranty",
-			children: []
-		},
-        {
-			name: "afterSales",
-			path: "/afterSales",
-			meta: { title: "售后管理", icon: "icon-park-outline:market" },
-            redirect: "/afterSales/afterSales",
-			children: []
-		}
-	],
-
-    // 预警
-    warning: [
-        // 设备离线,
-        // 数据异常
-        {
-			name: "facerec",
-            meta: { title: "人脸识别", icon: "tabler:face-id" },
-			children: [
-				{
-					path: "/facerec/device",
-					meta: { title: "实名制设备" }
-				},
-                {
-					path: "/facerec/tower",
-					meta: { title: "塔机设备" }
-				},
-                {
-					path: "/facerec/elevator",
-					meta: { title: "升降机设备" }
-				}
-            ]
-        },
-        {
-			name: "towerDevice",
-            path: "/tower/device",
-            meta: { title: "塔机监测", icon: "mingcute:tower-crane-line" }
-        },
-        {
-			name: "elevatorDevice",
-            path: "/elevator/device",
-            meta: { title: "升降机监测", icon: "icon-park-outline:elevator" }
-        },
-        {
-			name: "envDevice",
-            path: "/env/device",
-            meta: { title: "环境监测", icon: "fluent:earth-leaf-16-regular" }
-        },
-        {
-			name: "standardDevice",
-            path: "/standard/device",
-            meta: { title: "标养室监测", icon: "dashicons:dashboard" }
-        },
-        {
-			name: "ugliAiDevice",
-            path: "/ugliAi/device",
-            meta: { title: "AI识别", icon: "hugeicons:ai-brain-02" }
-        },
-        {
-			name: "carwashDevice",
-            path: "/carwash/device",
-            meta: { title: "车辆冲洗", icon: "map:car-wash" }
-        },
-        {
-			name: "smokeDevice",
-            path: "/smoke/device",
-            meta: { title: "智能烟感", icon: "cbi:smoke-detector" }
-        },
-        {
-			name: "broadcastDevice",
-            path: "/broadcast/device",
-            meta: { title: "智能广播", icon: "ri:broadcast-fill" }
-        },
-    ],
-
-    // 待办
-    toDo: [
-        {
-			name: "saleOrder",
-            path: "/easyRun/saleOrder",
-            meta: { title: "待发布", icon: "Release" }
-        },
-        {
-			name: "broadcast",
-            path: "/easyRun/device",
-            meta: { title: "待盘货", icon: "Stock" }
-        },
-        {
-			name: "broadcast",
-            path: "/easyRun/device",
-            meta: { title: "待建计划", icon: "pajamas:todo-add" }
-        },
-        {
-			name: "broadcast",
-            path: "/easyRun/device",
-            meta: { title: "待实施", icon: "fluent:window-wrench-32-regular" }
-        },
-        {
-			name: "broadcast",
-            path: "/easyRun/device",
-            meta: { title: "待验收", icon: "bi:clipboard2-check" }
-        },
-        {
-			name: "broadcast",
-            path: "/easyRun/device",
-            meta: { title: "待付款", icon: "mingcute:refund-cny-line" }
-        },
-        {
-			name: "broadcast",
-            path: "/easyRun/device",
-            meta: { title: "待收款", icon: "fluent:payment-32-regular" },
-        },
-    ],
-
-    // 数据模拟
-    dataMock: [
-        {
-            name: "envMock",
-            path: "/dataMock/env",
-            meta: { title: "环境监测", icon: "fluent:earth-leaf-16-regular" },
-            tags: {
-                formData: {
-                    name: "",
-                    type: ""
-                },
-                list: [
-                    { name: "数量过多", type: "danger" },
-                    { name: "分布不均", type: "danger" }
-                ]
-            }
-        },
-        {
-            name: "standardMock",
-            path: "/dataMock/standard",
-            meta: { title: "标养室监测", icon: "dashicons:dashboard" },
-            tags: {
-                formData: {
-                    name: "",
-                    type: ""
-                },
-                list: [
-                    { name: "数量不足", type: "danger" },
-                    { name: "时间段缺失", type: "danger" }
-                ]
-            }
-        },
-        {
-            name: "carwashMock",
-            path: "/dataMock/carwash",
-            meta: { title: "车辆冲洗", icon: "map:car-wash" },
-            tags: {
-                formData: {
-                    name: "",
-                    type: ""
-                },
-                list: [
-                    { name: "数量过多", type: "danger" },
-                    { name: "分布不均", type: "danger" }
-                ]
-            }
-        },
-        {
-            name: "ugliAiMock",
-            path: "/dataMock/ugliAi",
-            meta: { title: "AI识别", icon: "hugeicons:ai-brain-02" },
-            tags: {
-                formData: {
-                    name: "",
-                    type: ""
-                },
-                list: [
-                    { name: "数量不足", type: "danger" },
-                    { name: "时间段缺失", type: "danger" }
-                ]
-            }
-        }
-    ]
-};

+ 14 - 11
src/router/index.js

@@ -5,7 +5,7 @@ import "nprogress/nprogress.css"
 import config from "@/config";
 import tool from "@/utils/tool";
 import api from "@/api";
-import { mockData } from "@/mock/mock";
+import userRoutes from "@/config/route";
 import systemRouter from "./systemRouter";
 import { beforeEach, afterEach } from "./scrollBehavior";
 
@@ -69,17 +69,14 @@ router.beforeEach(async (to, from, next) => {
     // 所有闸口/安装点
     const index = ["facerec", "passqrcode", "tower", "env", "carwash", "ugliAi"].findIndex(key => to.fullPath.includes(key));
     if (to.name && index !== -1) router.getGates(["facerec", "passqrcode", "tower", "env", "carwash", "ugliAi"][index])
-    // 所有项目
-    if (!tool.data.get("PROJECT")) {
-        let projectRes = await api.system.project.get({ size: 99999 });
-        tool.data.set("PROJECT", projectRes.records);
-    }
-
+    
     // 加载动态/静态路由
 	if (!isGetRouter) {
-        tool.data.set("MENU", mockData.menus);
-        let menu = tool.data.get("MENU") || [];
-        XEUtils.arrayEach(XEUtils.toTreeArray(filterAsyncRouter(menu)), item => router.addRoute("layout", item))
+        // 所有项目
+        if (!tool.data.get("PROJECT")) router.getProject()
+
+        tool.data.set("MENU", [...userRoutes]);
+        XEUtils.arrayEach(XEUtils.toTreeArray(filterAsyncRouter(userRoutes)), item => router.addRoute("layout", item))
 		routes_404_r = router.addRoute(routes_404)
         !to.matched.length && router.push(to.fullPath);
 
@@ -95,7 +92,7 @@ router.afterEach((to, from) => {
 	NProgress.done();
 });
 
-router.onError((error) => {
+router.onError(error => {
     NProgress.done();
     clearIntervals();
 	ElNotification.error({
@@ -133,6 +130,12 @@ function loadComponent(component) {
 	}
 }
 
+router.getProject = async fpiId => {
+    let projectRes = await api.system.project.all();
+    tool.data.set("PROJECT", projectRes);
+    (!tool.data.get("PROJECT_ID") || fpiId && fpiId == tool.data.get("PROJECT_ID")) && tool.data.set("PROJECT_ID", XEUtils.get(XEUtils.first(projectRes), "fpiId"));
+}
+
 router.getGates = async (storagePath, start = 0, gates = []) => {
     const path = storagePath == "carwash" ? "ugliAi" : storagePath;
 

+ 8 - 11
src/style/app.scss

@@ -95,9 +95,9 @@ a,button,input,textarea{-webkit-tap-highlight-color:rgba(0,0,0,0);box-sizing: bo
     .el-dropdown .el-button [class*=el-icon] + span {margin-left: 0;margin-right: 4px;}
     
     .vxe-table.size--mini {
-        .vxe-table--scroll-y-virtual {width: 0!important;}
-        .vxe-table--scroll-x-virtual {height: 0!important;}
-        .vxe-table--scroll-x-wrapper {width: 100%!important;}
+        // .vxe-table--scroll-y-virtual {width: 1px!important;}
+        // .vxe-table--scroll-x-virtual {height: 2px!important;}
+        // .vxe-table--scroll-x-wrapper {width: 100%!important;}
 
         .vxe-table--empty-content {padding: 40px 0;}
         .vxe-cell {padding: var(--vxe-ui-table-cell-padding-mini) var(--vxe-ui-table-cell-padding-default);}
@@ -109,16 +109,13 @@ a,button,input,textarea{-webkit-tap-highlight-color:rgba(0,0,0,0);box-sizing: bo
         .query-action__item .vxe-form--item-inner {white-space: nowrap;}
         .query-action__item .vxe-form--item-trigger-node {min-width: unset;margin-left: 12px;}
     }
+
+    .vxe-table-top__button {padding-bottom: 1em;}
 }
 
-.vxe-table-slot--popper .el-select-dropdown__item, .vxe-table-slot--popper .el-dropdown-menu__item {font-size: inherit;}
+.vxe-table-slot--popper .el-select-dropdown__item, .vxe-table-slot--popper .el-dropdown-menu__item, .vxe-table-slot--popper .el-tree {font-size: inherit;}
+.vxe-table-slot--popper .el-select-dropdown__item, .vxe-table-slot--popper .el-tree-node__content {height: 32px;line-height: 32px;}
 .vxe-table-slot--top__form .el-form-item {margin-bottom: 0;padding-bottom: .6em;}
 .vxe-table-slot--top__form .el-form-item__label, .vxe-table-slot--top__form .el-form-item__content {font-size: var(--el-font-size-medium);color: #000;}
 
-.el-dialog .vxe-grid.size--mini .vxe-table-query {background-color: unset;}
-
-.download-button-popper {
-    width: unset !important;min-width: unset !important;max-width: 200px;text-align: center;
-    .el-button {justify-content: unset;max-width: 100%;height: var(--vxe-ui-button-height-mini);font-size: inherit;font-weight: 400;}
-    .el-button > span {max-width: 100%;}
-}
+.el-dialog .vxe-grid.size--mini .vxe-table-query {background-color: unset;}

+ 1 - 1
src/style/dark.scss

@@ -36,7 +36,7 @@ html.dark {
     .el-card .el-card__header {background: var(--el-bg-color-overlay);}
     .el-badge__content {border: none;}
 
-    .home-container .el-card.home-menu .el-card__body {
+    .home-container .home-menu .el-card__body {
         .menu-item {background-color: var(--el-bg-color-overlay);}
         .menu-item:hover {box-shadow: 1px 0 var(--el-text-color-disabled), 0 1px var(--el-text-color-disabled), 1px 1px var(--el-text-color-disabled), 1px 0 var(--el-text-color-disabled) inset, 0 1px var(--el-text-color-disabled) inset;}
     }

+ 0 - 136
src/utils/basicDic.js

@@ -12,117 +12,6 @@ export const deviceStateDic = {
     offline: "离线"
 }
 
-export const deviceRecordStateDic = {
-    "device.online": "上线",
-    "device.offline": "离线"
-}
-
-export const monoStatusDic = {
-    "mono.status.ready": "准备就绪",
-    "mono.status.pending": "等待执行",
-    "mono.status.executing": "执行中",
-    "mono.status.complete": "已完成",
-    "mono.status.failed": "执行失败",
-    "mono.status.retrying": "重试中",
-    "mono.status.revoke": "已撤销",
-    "mono.status.timeout": "任务超时"
-}
-
-export const monoOptionDic = {
-    facerec: {
-        "mono.option.add": "新增人员",
-        "mono.option.rm": "删除人员",
-        "mono.option.clean": "清空人员",
-        "mono.option.fetch": "查询人员ID"
-    },
-
-    passqrcode: {
-        "mono.option.remoteopen": "远程开门",
-        "mono.option.readtime": "读取控制板时间",
-        "mono.option.writetime": "写入控制板时间",
-        "mono.option.pushunseq": "写入非排序区",
-        "mono.option.pushseq": "写入排序区",
-        "mono.option.rm": "删除卡号",
-        "mono.option.cleanall": "清空全部区域",
-        "mono.option.cleanunseq": "清空非排序区",
-        "mono.option.cleanseq": "清空排序区",
-        "mono.option.fetch": "查询单个卡号",
-        "mono.option.fetchall": "查询全部卡号",
-        "mono.option.fetchunseq": "查询非排序区",
-        "mono.option.fetchseq": "查询排序区"
-    }
-}
-
-export const monogroupStatusDic = {
-    "monogroup.status.ready": "准备就绪",
-    "monogroup.status.pending": "等待执行",
-    "monogroup.status.executing": "执行中",
-    "monogroup.status.complete": "已完成",
-    "monogroup.status.failed": "执行失败"
-}
-
-export const monogroupOptionDic = {
-    facerec: {
-        "monogroup.option.clean.add": "清空并新增",
-        "monogroup.option.clean.incomp": "清理人员(清理身份证异常人员)",
-        "monogroup.option.compare.addall": "比对全量下发(删除多余人员)",
-        "monogroup.option.compare.add": "比对下发(新增没有的人员,删除多余人员)"
-    },
-
-    passqrcode: {
-        "monogroup.option.add": "组合新增卡号",
-        "monogroup.option.reset": "全部重置",
-        "monogroup.option.resetseq": "重置排序区",
-        "monogroup.option.resetunseq": "重置非排序区",
-        "monogroup.option.fetch": "查询全部区域卡号",
-        "monogroup.option.fetchseq": "查询排序区卡号",
-        "monogroup.option.fetchunseq": "查询非排序区卡号"
-    }
-}
-
-export const platformStatusDic = {
-    executing: "执行中",
-    success: "任务成功",
-    failed: "任务失败",
-    timeout: "任务超时"
-}
-
-export const platformTypeDic = {
-    "platfrom.sdplat": "省平台",
-    "platfrom.qdmetro": "青岛地铁"
-}
-
-export const cardinfoZoneDic = {
-    "zone.seq": "排序区",
-    "zone.unseq": "非排序区"
-}
-
-export const cardinfoStatusDic = {
-    0: "正常",
-    1: "挂失",
-    2: "黑名单"
-}
-
-export const synModeDic = {
-    "mode.syn.none": "无设备关联模式",
-    "mode.syn.project": "关联项目设备模式",
-    "mode.syn.ground": "关联工区设备模式"
-}
-
-export const synStatusDic = {
-    "workersyn.status.ready": "准备就绪",
-    "workersyn.status.pending": "等待执行",
-    "workersyn.status.executing": "执行中",
-    "workersyn.status.complete": "已完成",
-    "workersyn.status.failed": "执行失败",
-    "workersyn.status.revoke": "已撤销"
-}
-
-export const synExecStatusDic = {
-    enable: "执行",
-    disable: "不执行"
-}
-
 export const towerWarningDic = {
     WARNING_TILT: "倾斜告警",
     WARNING_WEIGHT: "重量告警",
@@ -131,26 +20,11 @@ export const towerWarningDic = {
     WARNING_XIANWEI: "限位告警"
 }
 
-export const envWarningDic = {
-    maxPM2_5: 75,
-    maxPM10: 150,
-    maxPM100: 300,
-    maxNoise: 70,
-    maxHumidity: 80,
-    maxTemperature: 35
-}
-
 export const sccRecordTypeDic = {
     SCC_RECORD_VTYPE_C: { label: "温度", unit: "℃" },
     SCC_RECORD_VTYPE_RH: { label: "湿度", unit: "%rh" }
 }
 
-export const carWashDic = {
-    carType: [null, "小型车", "工程车"],
-    carColor: [null, "蓝色", "黄色", "黑色", "白色", "绿色"],
-    alarmType: [null, "未冲洗", "冲洗不足", "冲洗完成", "绕道未冲洗", "其他"]
-}
-
 export const aiTypeDic = {
     AIHAZARD_REC_NO_HELMET: '未带安全帽',
     AIHAZARD_REC_NO_CLOTHES: '未穿工服',
@@ -170,16 +44,6 @@ export const aiTypeDic = {
     AIHAZARD_REC_VEHICLE_NOT_CLEANED: '车辆未清洗'
 }
 
-export function cardinfoOpenTime(str) {
-    let result = "";
-    while (str.length > 2) {
-        result = result + str.slice(0, 2) + ",";
-        str = str.slice(2);
-    }
-    if (str) result = result + str;
-    return result;
-}
-
 export function objectToArray(obj) {
     return XEUtils.map(XEUtils.keys(obj), value => ({ label: XEUtils.get(obj, value), value }))
 }

+ 89 - 0
src/views/basic/acceptItems/detail.vue

@@ -0,0 +1,89 @@
+<template>
+    <el-dialog v-model="visible" :title="titleMap[mode]" width="480" :close-on-click-modal="false" @closed="$emit('closed')">
+        <el-form ref="formRef" :model="form" :rules="rules" label-width="120">
+            <el-form-item label="验收项类别" prop="itemCategory">
+                <el-select v-model="form.itemCategory" placeholder="请选择验收项类别">
+                    <el-option v-for="(item, index) in categoryDic" :key="index" :label="item" :value="XEUtils.toValueString(index)"></el-option>
+                </el-select>
+            </el-form-item>
+            <el-form-item label="验收项名称" prop="acceptItem">
+                <el-input v-model="form.acceptItem" placeholder="请输入验收项名称"></el-input>
+            </el-form-item>
+            <el-form-item label="验收项级别" prop="acceptItemType">
+                <el-select v-model="form.acceptItemType" placeholder="请选择验收项级别">
+                    <el-option v-for="item in levelDic" :key="item" :value="item"></el-option>
+                </el-select>
+            </el-form-item>
+        </el-form>
+
+        <template #footer>
+            <el-button :loading="isSaving" type="primary" auto-insert-space @click="submit">保存</el-button>
+            <el-button auto-insert-space @click="visible = false">取消</el-button>
+        </template>
+    </el-dialog>
+</template>
+
+<script setup>
+import API from "@/api";
+import XEUtils from "xe-utils";
+import { categoryDic, levelDic } from "./main";
+
+const $emit = defineEmits(["success", "closed"]);
+const visible = ref(false);
+const isSaving = ref(false);
+
+const mode = ref("add");
+const titleMap = reactive({
+    add: "新增",
+    edit: "修改"
+});
+
+const form = ref({
+    id: null,
+    projectType: "房屋建筑工程",
+    itemCategory: null,
+    acceptItem: null,
+    acceptItemType: null
+});
+const rules = reactive({
+    projectType: [{ required: true }],
+    itemCategory: [{ required: true, message: "请选择验收项类别" }],
+    acceptItem: [{ required: true, message: "请输入验收项名称" }],
+    acceptItemType: [{ required: true, message: "请选择验收项级别" }]
+})
+
+const open = () => visible.value = true;
+const setData = data => {
+    open();
+    mode.value = "edit";
+    XEUtils.objectEach(form.value, (_, key) => XEUtils.set(form.value, key, XEUtils.get(data, key)));
+}
+
+const formRef = ref();
+const submit = () => {
+    formRef.value.validate(valid => {
+        if (valid) {
+            isSaving.value = true;
+            API.system.acceptItems[mode.value](form.value).then(res => {
+                isSaving.value = false;
+                ElMessage.success("操作成功");
+                visible.value = false;
+                $emit("success", mode.value);
+            }).catch(() => isSaving.value = false);
+        } else {
+            return false;
+        }
+    });
+}
+
+defineExpose({
+    open,
+    setData
+})
+</script>
+
+<style scoped>
+.el-form {
+    padding-right: calc(var(--el-dialog-padding-primary) + var(--el-message-close-size, 16px));
+}
+</style>

+ 119 - 0
src/views/basic/acceptItems/index.vue

@@ -0,0 +1,119 @@
+<template>
+	<el-container class="is-vertical">
+        <sc-page-header @filter="toggleFormEnabled" @add="table_add"></sc-page-header>
+
+        <scTable ref="xGridTable" :apiObj="$API.system.acceptItems" apiKey="all" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns" :options="options">
+            <template #action="{ row }">
+                <el-button type="primary" link @click="table_edit(row)">
+                    <template #icon><sc-iconify icon="ant-design:edit-outlined"></sc-iconify></template>修改
+                </el-button>
+                <el-button type="primary" link @click="table_del(row)">
+                    <template #icon><sc-iconify icon="ant-design:delete-outlined"></sc-iconify></template>删除
+                </el-button>
+            </template>
+        </scTable>
+	</el-container>
+
+    <accept-item-detail v-if="dialog" ref="acceptItemRef" @success="refreshTable" @closed="dialog = false"></accept-item-detail>
+</template>
+
+<script setup>
+import XEUtils from "xe-utils";
+import API from "@/api";
+import { mapFormItemInput, mapFormItemSelect } from "@/components/scTable/helper";
+import { categoryDic, levelDic } from "./main";
+import acceptItemDetail from "./detail";
+
+const selectConfig = reactive({
+    options: categoryDic.map((label, value) => ({ label, value })),
+    events: {
+        change: data => XEUtils.merge(formConfig.data, data)
+    }
+})
+
+const toolbarConfig = reactive({
+    enabled: true,
+    print: false
+});
+
+const formConfig = reactive({
+    data: {
+        orderBy: "itemCategory_asc,createTime_asc"
+    },
+    items: [
+        mapFormItemSelect("itemCategory", "验收项类别", selectConfig),
+        mapFormItemInput("acceptItemLike", "验收项名称"),
+        mapFormItemSelect("acceptItemType", "验收项级别", { ...selectConfig, options: levelDic.map(label => ({ label, value: label })) })
+    ]
+});
+
+const paramsColums = reactive([
+    { column: "orderBy" },
+    { column: "itemCategory" },
+    { column: "acceptItemLike" },
+    { column: "acceptItemType" }
+])
+
+const columns = reactive([
+    { type: "seq", width: 60 },
+    { type: "html", field: "itemCategoryName", title: "验收项类别", minWidth: 120, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(categoryDic, row.itemCategory) },
+    { type: "html", field: "acceptItem", title: "验收项名称", minWidth: 120 },
+    { type: "html", field: "acceptItemType", title: "验收项级别", minWidth: 120 },
+    { title: "操作", width: 140, slots: { default: "action" } }
+])
+
+const options = reactive({
+    align: "center",
+    pagerConfig: { enabled: false, queryType: "none" },
+    spanMethod ({ row, rowIndex, column: { field }, visibleData }) {
+        if (field == "itemCategoryName") {
+            const prevRow = visibleData[rowIndex - 1];
+            let nextRow = visibleData[rowIndex + 1];
+            
+            if (prevRow && XEUtils.get(prevRow, "itemCategory") === XEUtils.get(row, "itemCategory")) return { rowspan: 0, colspan: 0 }
+            else {
+                let rowspan = 1;
+                while (nextRow && XEUtils.get(nextRow, "itemCategory") === XEUtils.get(row, "itemCategory")) {
+                    nextRow = visibleData[++rowspan + rowIndex];
+                }
+                if (rowspan > 1) return { rowspan, colspan: 1 }
+            }
+        }
+    }
+})
+
+// 显示隐藏 筛选表单
+const xGridTable = ref();
+const toggleFormEnabled = () => xGridTable.value.toggleFormEnabled();
+
+const refreshTable = (mode = "add") => {
+    xGridTable.value.reloadColumn(columns);
+    xGridTable.value.searchData(mode);
+}
+
+const acceptItemRef = ref();
+const dialog = ref(false);
+
+const table_add = () => {
+    dialog.value = true;
+    nextTick(() => acceptItemRef.value?.open());
+}
+
+const table_edit = row => {
+    dialog.value = true;
+    nextTick(() => acceptItemRef.value?.setData(row));
+}
+
+const table_del = ({ id }) => {
+    ElMessageBox.confirm("是否确认删除该验收项?", "删除警告", {
+        type: "warning",
+        confirmButtonText: "确定",
+        cancelButtonText: "取消"
+    }).then(() => {
+        API.system.acceptItems.del({ id }).then(() => {
+            ElMessage.success("操作成功");
+            refreshTable();
+        });
+    });
+}
+</script>

+ 4 - 0
src/views/basic/acceptItems/main.js

@@ -0,0 +1,4 @@
+export const categoryDic = ["管理平台", "施工安全管理类", "施工质量管理类", "绿色文明施工类", "施工综合管理类", "人员管理类", "BIM技术应用", "建筑工业化类"]
+export const levelDic = ["基础项", "推广项", "提升项", "推广项(AA必选项)", "提升项(AAA必选项)"]
+
+export const statisticTemp = "基础项{{ b_length }}项,已选{{ b_select }}项;推广项{{ p_length }}项,已选{{ p_select }}项(包含必选{{ p_required }}项);提升项{{ i_length }}项,已选{{ i_select }}项(包含必选项{{ i_required }}项)"

+ 580 - 0
src/views/basic/project/detail.vue

@@ -0,0 +1,580 @@
+<template>
+    <el-dialog v-model="visible" :title="titleMap[mode]" width="860" :close-on-click-modal="false" @closed="$emit('closed')">
+        <el-form ref="formRef" :model="form" :rules="rules" label-width="120">
+            <el-card header="基础信息" shadow="never">
+                <el-row>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="项目名称" prop="projectName">
+                            <el-input v-model="form.projectName" placeholder="请输入项目名称"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="项目别名">
+                            <el-input v-model="form.projectAliasName" placeholder="请输入项目别名"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="所属企业" prop="deptId">
+                            <el-tree-select v-model="form.deptId" v-bind="props.treeSelectProps" :props="{ label: 'name', value: 'deptId' }" ></el-tree-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="项目类型" prop="projectType">
+                            <el-select v-model="form.projectType" placeholder="请选择项目类型">
+                                <el-option v-for="item in typeDic" :key="item" :value="item"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="项目状态" prop="projectStatus">
+                            <el-select v-model="form.projectStatus" placeholder="请选择项目状态">
+                                <el-option v-for="item in statusDic" :key="item" :value="item"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="建设规模" prop="projectScale">
+                            <el-radio-group v-model="form.projectScale">
+                                <el-radio value="3">小型</el-radio>
+                                <el-radio value="6">中型</el-radio>
+                                <el-radio value="9">大型</el-radio>
+                            </el-radio-group>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="投资总额">
+                            <el-input-number v-model="form.projectMoney" :min="0" :precision="2" :controls="false" placeholder="请输入投资总额">
+                                <template #suffix>万元</template>
+                            </el-input-number>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="安监备案号">
+                            <el-input v-model="form.projectBackupsNumber" clearable placeholder="请输入安监备案号"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="建筑面积">
+                            <el-input-number v-model="form.projectBuildingArea" :min="0" :precision="2" :controls="false" placeholder="请输入建筑面积">
+                                <template #suffix>㎡</template>
+                            </el-input-number>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="结构形式">
+                            <el-input v-model="form.projectStructure" clearable placeholder="请输入结构形式"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="合同工期">
+                            <el-date-picker v-model="form.contractDate" type="daterange" value-format="YYYY-MM-DD" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="设计使用年限">
+                            <el-input-number v-model="form.designLife" :min="0" :controls="false" placeholder="请输入设计使用年限"></el-input-number>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="发证机关">
+                            <el-input v-model="form.licenceAuthority" clearable placeholder="请输入发证机关"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="发证日期">
+                            <el-date-picker v-model="form.licenceDate" value-format="YYYY-MM-DD" placeholder="请选择发证日期"></el-date-picker>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="监督机构">
+                            <el-input v-model="form.superviseAuthority" clearable placeholder="请输入工程质量监督机构"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="施工许可证编号">
+                            <el-input v-model="form.licenceSgNo" clearable placeholder="请输入施工许可证编号"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24">
+                        <el-form-item class="m-b-0" label="施工许可证">
+                            <sc-upload-file v-model="form.folders['project/licence_sg'].entities" :limit="1">
+                                <el-button type="primary" size="small">
+                                    <template #icon><sc-iconify icon="ant-design:plus-outlined"></sc-iconify></template>
+                                </el-button>
+                            </sc-upload-file>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+            </el-card>
+
+            <el-card header="接口信息" shadow="never">
+                <el-card header="青岛市市平台接口信息" shadow="never">
+                    <el-row>
+                        <el-col :md="12" :xs="24">
+                            <el-form-item label="项目名称">
+                                <el-input v-model="form.qdProjectName" clearable placeholder="请输入项目名称"></el-input>
+                            </el-form-item>
+                        </el-col>
+                        <el-col :md="12" :xs="24">
+                            <el-form-item label="项目ID" prop="qdProjectId">
+                                <el-input v-model="form.qdProjectId" clearable placeholder="请输入项目ID"></el-input>
+                            </el-form-item>
+                        </el-col>
+                        <el-col :md="12" :xs="24">
+                            <el-form-item label="项目TOKEN">
+                                <el-input v-model="form.qdProjectToken" clearable placeholder="请输入项目TOKEN"></el-input>
+                            </el-form-item>
+                        </el-col>
+                        <el-col :md="12" :xs="24">
+                            <el-form-item label="项目PROVIDER">
+                                <el-input v-model="form.qdProjectProvider" clearable placeholder="请输入项目PROVIDER"></el-input>
+                            </el-form-item>
+                        </el-col>
+                        <el-col :md="12" :xs="24">
+                            <el-form-item label="劳务ID">
+                                <el-input v-model="form.qdProjectNo" clearable placeholder="请输入劳务ID"></el-input>
+                            </el-form-item>
+                        </el-col>
+                    </el-row>
+                </el-card>
+
+                <el-card header="崂山区平台接口信息" shadow="never">
+                    <el-row>
+                        <el-col :md="12" :xs="24">
+                            <el-form-item label="项目名称">
+                                <el-input v-model="form.lsProjectName" clearable placeholder="请输入项目名称"></el-input>
+                            </el-form-item>
+                        </el-col>
+                        <el-col :md="12" :xs="24">
+                            <el-form-item label="项目ID" prop="qdProjectId">
+                                <el-input v-model="form.lsProjectId" clearable placeholder="请输入项目ID"></el-input>
+                            </el-form-item>
+                        </el-col>
+                        <el-col :md="12" :xs="24">
+                            <el-form-item label="项目TOKEN">
+                                <el-input v-model="form.lsProjectToken" clearable placeholder="请输入项目TOKEN"></el-input>
+                            </el-form-item>
+                        </el-col>
+                        <el-col :md="12" :xs="24">
+                            <el-form-item label="项目PROVIDER">
+                                <el-input v-model="form.lsProjectProvider" clearable placeholder="请输入项目PROVIDER"></el-input>
+                            </el-form-item>
+                        </el-col>
+                        <el-col :md="12" :xs="24">
+                            <el-form-item label="劳务ID">
+                                <el-input v-model="form.lsProjectNo" clearable placeholder="请输入劳务ID"></el-input>
+                            </el-form-item>
+                        </el-col>
+                    </el-row>
+                </el-card>
+
+                <el-card header="青岛市市政平台接口信息" shadow="never">
+                    <el-row>
+                        <el-col :md="12" :xs="24">
+                            <el-form-item label="项目名称">
+                                <el-input v-model="form.szProjectName" clearable placeholder="请输入项目名称"></el-input>
+                            </el-form-item>
+                        </el-col>
+                        <el-col :md="12" :xs="24">
+                            <el-form-item label="项目ID" prop="qdProjectId">
+                                <el-input v-model="form.szProjectId" clearable placeholder="请输入项目ID"></el-input>
+                            </el-form-item>
+                        </el-col>
+                        <el-col :md="12" :xs="24">
+                            <el-form-item label="项目TOKEN">
+                                <el-input v-model="form.szProjectToken" clearable placeholder="请输入项目TOKEN"></el-input>
+                            </el-form-item>
+                        </el-col>
+                        <el-col :md="12" :xs="24">
+                            <el-form-item label="项目PROVIDER">
+                                <el-input v-model="form.szProjectProvider" clearable placeholder="请输入项目PROVIDER"></el-input>
+                            </el-form-item>
+                        </el-col>
+                        <el-col :md="12" :xs="24">
+                            <el-form-item label="劳务ID">
+                                <el-input v-model="form.szProjectNo" clearable placeholder="请输入劳务ID"></el-input>
+                            </el-form-item>
+                        </el-col>
+                    </el-row>
+                </el-card>
+            </el-card>
+
+            <el-card header="位置信息" shadow="never">
+                <el-row>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="省/直辖市">
+                            <el-select v-model="form.province" clearable :disabled="!districtComplete" placeholder="选择省/直辖市" @change="changeDistrict($event, 'province')">
+                                <el-option v-for="(item, index) in districtList.province" :key="index" :label="item.name" :value="item.name"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="地级市">
+                            <el-select v-model="form.city" clearable :disabled="!districtComplete" placeholder="选择地级市" @change="changeDistrict($event, 'city')">
+                                <el-option v-for="(item, index) in districtList.city" :key="index" :label="item.name" :value="item.name"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="区县">
+                            <el-select v-model="form.region" clearable :disabled="!districtComplete" placeholder="选择区县" @change="changeDistrict($event, 'region')">
+                                <el-option v-for="(item, index) in districtList.region" :key="index" :label="item.name" :value="item.name"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="街道">
+                            <el-select v-model="form.street" clearable :disabled="!districtComplete" placeholder="选择街道" @change="changeDistrict($event, 'street')">
+                                <el-option v-for="(item, index) in districtList.street" :key="index" :label="item.name" :value="item.name"></el-option>
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="项目地址">
+                            <el-input v-model="form.address" clearable :disabled="!districtComplete" placeholder="请输入项目地址" @change="addressChange"></el-input>
+				            <div class="el-form-item-msg">输入地址进行查询,并点击坐标点以确认坐标。若无坐标点请输入更详情地址</div>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :md="12" :xs="24">
+                        <el-form-item label="项目坐标">
+                            <el-input v-model="form.projectCoordinates" readonly placeholder="选择行政区级别或输入地址查询"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :xs="24">
+                        <div id="container"></div>
+                    </el-col>
+                </el-row>
+            </el-card>
+        </el-form>
+
+        <template #footer>
+            <el-button :loading="isSaving" type="primary" auto-insert-space @click="submit">保存</el-button>
+            <el-button auto-insert-space @click="visible = false">取消</el-button>
+        </template>
+    </el-dialog>
+</template>
+
+<script setup>
+import API from "@/api";
+import TOOL from "@/utils/tool";
+import XEUtils from "xe-utils";
+import AMapLoader from '@amap/amap-jsapi-loader';
+import { typeDic, statusDic, interfaceDic, polygonOptions } from "./main";
+import scUploadFile from "@/components/scUpload/file";
+import { ElMessage } from "element-plus";
+
+const $emit = defineEmits(["success", "closed"]);
+const props = defineProps({
+    treeSelectProps: { type: Object, default: () => {} }
+});
+const visible = ref(false);
+const isSaving = ref(false);
+
+const mode = ref("add");
+const titleMap = reactive({
+    add: "新增",
+    edit: "修改"
+});
+
+const form = ref({
+    fpiId: null,
+    projectName: null,
+    projectAliasName: null,
+    deptId: null,
+    projectFirmName: null,
+    projectType: null,
+    projectStatus: null,
+    projectScale: null,
+    projectMoney: null,
+    projectBackupsNumber: null,
+    projectBuildingArea: null,
+    projectStructure: null,
+    contractDate: [],
+    contractBeginDate: null,
+    contractEndDate: null,
+    designLife: null,
+    licenceAuthority: null,
+    licenceDate: null,
+    superviseAuthority: null,
+    licenceSgNo: null,
+    folders: {
+        "project/licence_sg": {
+            entities: []
+        }
+    },
+
+    qdProjectName: null, 
+    qdProjectId: null,
+    qdProjectToken: null,
+    qdProjectProvider: null,
+    qdProjectNo: null,
+
+    lsProjectName: null, 
+    lsProjectId: null,
+    lsProjectToken: null,
+    lsProjectProvider: null,
+    lsProjectNo: null,
+
+    szProjectName: null,
+    szProjectId: null,
+    szProjectToken: null,
+    szProjectProvider: null,
+    szProjectNo: null,
+
+    province: null,
+	city: null,
+	region: null,
+	street: null,
+	address: null,
+	longitude: null,
+	latitude: null,
+    projectCoordinates: ""
+});
+const rules = reactive({
+    projectName: [{ required: true, message: "请输入项目名称" }],
+    deptId: [{ required: true, message: "请选择所属企业" }],
+    projectType: [{ required: true, message: "请选择项目类型" }],
+    projectStatus: [{ required: true, message: "请选择项目状态" }],
+    projectScale: [{ required: true, message: "请选择建设规模" }],
+    
+    qdProjectId: [{ len: 16, message: "请输入16位项目ID" }],
+    lsProjectId: [{ len: 16, message: "请输入16位项目ID" }],
+    szProjectId: [{ len: 16, message: "请输入16位项目ID" }]
+})
+
+let AMap = null, map = null, marker = null, polygons = null, districtSearch = null, geocoder = null
+const districtComplete = ref(false);
+const districtList = reactive({
+    province: [],
+    city: computed(() => XEUtils.map(XEUtils.get(XEUtils.find(districtList.province, item => item.name == form.value.province), "districtList"), item => ({ ...item, preLevel: form.value.province }))),
+    region: computed(() => XEUtils.map(XEUtils.get(XEUtils.find(districtList.city, item => item.name == form.value.city), "districtList"), item => ({ ...item, preLevel: form.value.city }))),
+    street: computed(() => XEUtils.map(XEUtils.get(XEUtils.find(districtList.region, item => item.name == form.value.region), "districtList"), item => ({ ...item, preLevel: form.value.region })))
+});
+const boundAreas = reactive({});
+
+const open = () => {
+    visible.value = true;
+    initMap();
+}
+const setData = async fpiId => {
+    open();
+    mode.value = "edit";
+    API.system.project.detail({ fpiId }).then(res => {
+        XEUtils.objectEach(form.value, (_, key) => {
+            if (key == "projectFirmName") XEUtils.set(form.value, "deptId", XEUtils.get(XEUtils.findTree(props.treeSelectProps.data, item => item.name == XEUtils.get(res.project, key)), "item.deptId", null))
+            else if (key == "folders") {
+                XEUtils.objectEach(form.value.folders, (_, folder_key) => {
+                    XEUtils.set(form.value, `${key}.${folder_key}.entities`, XEUtils.map(XEUtils.get(res.project, `${key}.${folder_key}.entities`), ({ id, mineType, name, path }) => ({ id, mineType, name, path })))
+                });
+            } else XEUtils.set(form.value, key, XEUtils.get(res.project, key) || XEUtils.get(res.position, key, null))
+        });
+        XEUtils.arrayEach(res.apiList, item => {
+            const prefix = XEUtils.get(interfaceDic, item.platformType);
+            XEUtils.objectEach(XEUtils.pick(item, ["projectName", "projectId", "projectToken", "projectProvider", "projectNo"]), (val, key) => {
+                XEUtils.set(form.value, prefix + TOOL.capitalizeWords(key), val);
+            });
+        });
+
+		drawMap();
+    });
+}
+
+const initMap = async () => {
+    AMap = await AMapLoader.load({ key: "2beebf5f21929258fd62a8ea4223e837", version: "2.0", plugins: ["AMap.DistrictSearch", "AMap.Geocoder"] });
+    map = new AMap.Map("container", { zoom: 18 });
+    marker = new AMap.Marker();
+    districtSearch = new AMap.DistrictSearch({ level: "country", subdistrict: 4, extensions: "all" });
+    districtSearch.search("中国", (status, result) => {
+        districtList.province = status == "complete" ? XEUtils.get(result, "districtList[0].districtList", []) : [];
+        districtComplete.value = status == "complete";
+    });
+
+    geocoder = new AMap.Geocoder({ batch: false });
+    map.on("click", ({ lnglat }) => {
+        if (districtComplete.value) {
+            map.clearMap();
+            const position = [lnglat.lng, lnglat.lat];
+            marker.setPosition(position);
+            marker.add(map);
+            map.setFitView();
+
+            geocoder.getAddress(position, (status, result) => {
+                if (status === "complete" && result.regeocode) setPosition(position, result.regeocode);
+            });
+        }
+    });
+}
+
+const drawMap = () => {
+    geocoder.getAddress(XEUtils.get(form.value, "projectCoordinates", "").split(","), (status, result) => {
+        if (status === "complete" && result.regeocode) {
+            form.value.longitude = XEUtils.first(form.value.projectCoordinates.split(","));
+            form.value.latitude = XEUtils.last(form.value.projectCoordinates.split(","));
+            map.clearMap();
+            marker.setPosition(form.value.projectCoordinates.split(","));
+            map.add(marker);
+            map.setFitView();
+        } else {
+            form.value.longitude = null;
+            form.value.latitude = null;
+            form.value.projectCoordinates = "";
+        }
+    });
+}
+
+const getBound = (keyword, key) => {
+    const { preLevel, citycode } = XEUtils.find(districtList[key], item => item.name == keyword);
+    districtSearch.setLevel(key == "region" ? "district" : key);
+    districtSearch.setSubdistrict(1);
+    districtSearch.search(keyword, (status, result) => {
+        if (status == "complete" && result.districtList.length) {
+            const { boundaries } = key == "province" ? XEUtils.first(result.districtList) : XEUtils.find(result.districtList, item => item.citycode == citycode);
+            if (boundaries) {
+                const boundAreas_key = key == "province" ? keyword : preLevel + keyword;
+                XEUtils.set(boundAreas, boundAreas_key, boundaries);
+                drawPolygons(keyword, key);
+            }
+        }
+    });
+}
+
+const drawPolygons = (keyword, key) => {
+    const distData = XEUtils.find(districtList[key], item => item.name == keyword);
+    const boundAreas_key = key == "province" ? keyword : distData.preLevel + keyword;
+    if (XEUtils.has(boundAreas, boundAreas_key)) {
+        form.value.longitude = XEUtils.get(distData, "center.lng", null);
+        form.value.latitude = XEUtils.get(distData, "center.lat", null);
+        form.value.projectCoordinates = XEUtils.get(distData, "center.lng", "") + "," + XEUtils.get(distData, "center.lat", "") || "";
+        polygons = XEUtils.map(boundAreas[boundAreas_key], path => new AMap.Polygon({ path, ...polygonOptions }));
+        map.add(polygons);
+        map.setFitView(polygons);
+    } else getBound(keyword, key);
+}
+
+const changeDistrict = (keyword, key) => {
+    if (key == "province") {
+        form.value.city = null;
+        form.value.region = null;
+        form.value.street = null;
+        form.value.longitude = null;
+        form.value.latitude = null;
+        form.value.projectCoordinates = "";
+    }
+    if (key == "city") {
+        form.value.region = null;
+        form.value.street = null;
+    }
+    if (key == "region") form.value.street = null;
+    form.value.address = null;
+    map.clearMap();
+    if (keyword) drawPolygons(keyword, key);
+    else {
+        if (key == "city") drawPolygons(form.value.province, "province");
+        if (key == "region") drawPolygons(form.value.city, "city");
+        if (key == "street") drawPolygons(form.value.region, "region");
+    }
+}
+
+const addressChange = keyword => {
+    polygons && map.remove(polygons);
+    form.value.city && geocoder.setCity(form.value.city);
+    if (keyword) {
+        geocoder.getLocation(keyword, (status, result) => {
+            if (status === "complete" && result.geocodes.length) {
+                map.clearMap();
+                const poiMarkers = result.geocodes.slice(0, 5).map((item, index) => {
+                    const poiMarker = new AMap.Marker({ icon: `//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-${index + 1}.png`, position: [item.location.lng, item.location.lat], offset: new AMap.Pixel(-13, -30), extData: item });
+                    poiMarker.on("click", ({ target }) => {
+                        const { lng, lat } = target.getPosition();
+                        setPosition([lng, lat], target.getExtData());
+                        map.setFitView(target);
+                    });
+                    return poiMarker;
+                });
+                map.add(poiMarkers);
+                map.setFitView();
+            } else ElMessage.error("根据地址查询位置失败");
+        });
+    }
+}
+
+const setPosition = (position, { addressComponent, formattedAddress }) => {
+    const { province, city, district, township } = addressComponent;
+    XEUtils.merge(form.value, {
+        province, city, region: district, street: township,
+        address: formattedAddress.split(province + city + district + township)[1],
+        longitude: position[0], latitude: position[1], projectCoordinates: position.join()
+    });
+}
+
+const formRef = ref();
+const submit = () => {
+    formRef.value.validate(valid => {
+        if (valid) {
+            const query = XEUtils.omit(form.value, ["contractDate", "folders"]);
+            
+            XEUtils.merge(query, {
+                projectFirmName: XEUtils.get(XEUtils.findTree(props.treeSelectProps.data, item => item.deptId == form.value.deptId), "item.name"),
+                contractBeginDate: XEUtils.first(XEUtils.get(form.value, "contractDate")) || null,
+                contractEndDate: XEUtils.last(XEUtils.get(form.value, "contractDate")) || null,
+                longitude: XEUtils.multiply(form.value.longitude, 1000000),
+                latitude: XEUtils.multiply(form.value.latitude, 1000000)
+            })
+
+            const newFiles = form.value.folders["project/licence_sg"].entities.filter(item => !item.id).map(item => item.path);
+            if (newFiles.length) {
+                XEUtils.set(query, "folders", {
+                    "project/licence_sg": {
+                        entities: newFiles.map(ticket => ({ features: { ticket } }))
+                    }
+                })
+            }
+
+            isSaving.value = true;
+            API.system.project[mode.value](query).then(res => {
+                isSaving.value = false;
+                ElMessage.success("操作成功");
+                visible.value = false;
+                $emit("success", mode.value);
+            }).catch(() => isSaving.value = false);
+        } else {
+            return false;
+        }
+    });
+}
+
+onUnmounted(() => {
+    map && map.destroy();
+});
+
+defineExpose({
+    open,
+    setData
+})
+</script>
+
+<style lang="scss" scoped>
+.m-b-0 {margin-bottom: 0;}
+.el-card :deep(.el-card__header) {background: #f9fdff;}
+.el-card :deep(.el-card__body) {padding-right: calc(var(--el-dialog-padding-primary) + var(--el-card-padding));}
+.el-card + .el-card {margin-top: 15px;}
+
+.el-form {
+    padding: 0 var(--el-dialog-padding-primary);
+
+    .el-input-number, :deep(.el-date-editor.el-input) {width: 100%;}
+    .el-input-number :deep(.el-input__inner) {text-align: unset;}
+    .el-input-number :deep(.el-input__suffix) {font-size: 12px;}
+    
+    .el-form-item-msg {line-height: 1.5em;margin-top: .6em;}
+    
+    #container {
+        height: 300px;
+
+        .amap-icon img {
+            width: 25px;
+            height: 34px;
+        }
+    }
+}
+</style>

+ 132 - 0
src/views/basic/project/index.vue

@@ -0,0 +1,132 @@
+<template>
+	<el-container class="is-vertical">
+        <sc-page-header @filter="toggleFormEnabled" @add="table_add"></sc-page-header>
+
+        <scTable ref="xGridTable" :apiObj="$API.system.project" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns">
+            <template #tree_select>
+                <el-tree-select v-model="formConfig.data.projectFirmName" v-bind="treeSelectProps"></el-tree-select>
+            </template>
+            
+            <template #action="{ row }">
+                <el-button type="primary" link @click="table_items(row)">
+                    <template #icon><sc-iconify icon="pajamas:list-bulleted"></sc-iconify></template>验收清单
+                </el-button>
+                <el-button type="primary" link @click="table_edit(row)">
+                    <template #icon><sc-iconify icon="ant-design:edit-outlined"></sc-iconify></template>修改
+                </el-button>
+                <el-button type="primary" link @click="table_del(row)">
+                    <template #icon><sc-iconify icon="ant-design:delete-outlined"></sc-iconify></template>删除
+                </el-button>
+            </template>
+        </scTable>
+	</el-container>
+
+    <project-detail v-if="dialog.detail" ref="projectRef" :treeSelectProps="treeSelectProps" @success="refreshTable" @closed="dialog.detail = false"></project-detail>
+    <accept-item-detail v-if="dialog.items" ref="acceptItemRef" @closed="dialog.items = false"></accept-item-detail>
+</template>
+
+<script setup>
+import XEUtils from "xe-utils";
+import API from "@/api";
+import TOOL from "@/utils/tool";
+import { mapFormItemInput, mapFormItemSelect } from "@/components/scTable/helper";
+import { typeDic, statusDic } from "./main";
+import projectDetail from "./detail";
+import acceptItemDetail from "./items";
+
+const router = useRouter();
+
+const treeSelectProps = reactive({
+    popperClass: "vxe-table-slot--popper",
+    data: [],
+    filterable: true,
+    clearable: true,
+    checkStrictly: true,
+    clearable: true,
+    placeholder: "请选择所属企业",
+    props: { label: "name", value: "name" }
+})
+
+const toolbarConfig = reactive({
+    enabled: true,
+    print: false
+});
+
+const formConfig = reactive({
+    data: {
+        projectStatusNot: "学校"
+    },
+    items: [
+        mapFormItemInput("projectNameLike", "项目名称"),
+        mapFormItemInput("projectFirmName", "所属企业", { slots: { default: "tree_select" } })
+    ]
+});
+
+const paramsColums = reactive([
+    { column: "projectStatusNot" },
+    { column: "projectNameLike" },
+    { column: "projectFirmName" }
+])
+
+const columns = reactive([
+    { type: "html", field: "fpiId", title: "ID", width: 60, sortable: true },
+    { type: "html", field: "projectName", title: "项目名称", minWidth: 160, sortable: true },
+    { type: "html", field: "projectFirmName", title: "所属企业", minWidth: 160, sortable: true },
+    { type: "html", field: "projectType", title: "项目类型", width: 120, sortable: true },
+    { type: "html", field: "projectStatus", title: "项目状态", width: 100, sortable: true },
+    { title: "操作", fixed: "right", width: 220, align: "center", slots: { default: "action" } }
+])
+
+// 获取组织树
+const getRemoteData = async () => {
+    const res = await API.system.project.dept();
+    treeSelectProps.data = XEUtils.toArrayTree(XEUtils.filter(res, item => item.firmNature !== "学校"), { parentKey: "pid", key: "deptId" });
+};
+
+// 显示隐藏 筛选表单
+const xGridTable = ref();
+const toggleFormEnabled = () => xGridTable.value.toggleFormEnabled();
+
+const refreshTable = (mode = "add", fpiId) => {
+    xGridTable.value.reloadColumn(columns);
+    xGridTable.value.searchData(mode);
+    router.getProject(fpiId);
+}
+
+const projectRef = ref();
+const acceptItemRef = ref();
+const dialog = reactive({
+    detail: false,
+    items: false
+});
+
+const table_add = () => {
+    dialog.detail = true;
+    nextTick(() => projectRef.value?.open());
+}
+
+const table_edit = row => {
+    dialog.detail = true;
+    nextTick(() => projectRef.value?.setData(row.fpiId));
+}
+
+const table_items = row => {
+    dialog.items = true;
+    nextTick(() => acceptItemRef.value?.setData(row));
+}
+
+const table_del = ({ fpiId }) => {
+    ElMessageBox.confirm("是否确认删除该项目?", "删除警告", {
+        type: "warning",
+        confirmButtonText: "确定",
+        cancelButtonText: "取消"
+    }).then(() => {
+        API.system.project.del({ fpiId }).then(() => {
+            ElMessage.success("操作成功");
+            refreshTable("add", fpiId);
+        });
+    });
+}
+
+getRemoteData();
+</script>

+ 194 - 0
src/views/basic/project/items.vue

@@ -0,0 +1,194 @@
+<template>
+    <el-dialog v-model="visible" :title="`${form.projectName}-验收清单`" width="870" :close-on-click-modal="false" @closed="$emit('closed')">
+        <el-form ref="formRef" :model="form" label-width="110px">
+            <el-form-item required>
+                <template #label>
+                    <el-tooltip placement="top">
+                        <i class="vxe-icon-question-circle-fill"></i>
+                        <template #content>
+                            &nbsp;&nbsp;&nbsp;&nbsp;A级智慧化工地:18项基础项<br />
+                            &nbsp;&nbsp;AA级智慧化工地:18项基础项、不少于10项推广项(包含必选项),2项非必选推广项可替代1项必选推广项<br />
+                            AAA级智慧化工地:18项基础项、不少于20项推广项(包含必选项)、2项提升项(包含必选项),2项非必选提升项可替代1项必选提升项
+                        </template>
+                    </el-tooltip>申报等级
+                </template>
+                <el-radio-group v-model="form.starLevel" @change="changeLevel">
+                    <el-radio label="A"></el-radio>
+                    <el-radio label="AA"></el-radio>
+                    <el-radio label="AAA"></el-radio>
+                </el-radio-group>
+            </el-form-item>
+
+            <div class="items-statistic">{{ XEUtils.template(statisticTemp, statisticData) }}</div>
+            <el-form-item label-width="0">
+                <el-radio-group v-model="itemsLevel" @change="refreshTable">
+                    <el-radio-button label="基础项" />
+                    <el-radio-button v-if="form.starLevel == 'AA' || form.starLevel == 'AAA'" label="推广项" />
+                    <el-radio-button v-if="form.starLevel == 'AAA'" label="提升项" />
+                </el-radio-group>
+            </el-form-item>
+
+            <scTable ref="xGridTable" :maxHeight="1048" :columns="columns" :options="options">
+                <template #checked_content="{ row }">
+                    <el-checkbox v-model="row.checked"></el-checkbox>
+                </template>
+
+                <template #checked_replace="{ row }">
+                    <el-checkbox v-if="row.acceptItemType.includes('必选项')" v-model="row.replaceIds" true-value="1" :false-value="0"></el-checkbox>&nbsp;&nbsp;
+                    <!-- <div v-if="row.acceptItemType.includes('必选项')" class="replace-content">
+                        <el-select v-model="row.replaceIds" multiple collapse-tags collapse-tags-tooltip placeholder="选择替换推广项">
+                            <el-option v-for="item in XEUtils.filter(acceptItems, acc => acc.acceptItemType == '推广项')" :key="item.id" :label="item.acceptItem" :value="item.id"></el-option>
+                        </el-select>
+                    </div> -->
+                </template>
+
+                <template #date_picker_begin="{ row }">
+                    <el-date-picker v-model="row.beginTime" :disabled-date="date => disabledDate(date, row, 'beginTime')" value-format="YYYY-MM-DD 00:00:00" placeholder="选择开始时间"></el-date-picker>
+                </template>
+
+                <template #date_picker_end="{ row }">
+                    <el-date-picker v-model="row.endTime" :disabled-date="date => disabledDate(date, row, 'endTime')" value-format="YYYY-MM-DD 23:59:59" placeholder="选择结束时间"></el-date-picker>
+                </template>
+            </scTable>
+        </el-form>
+
+        <template #footer>
+            <el-button :loading="isSaving" type="primary" auto-insert-space @click="submit">保存</el-button>
+            <el-button auto-insert-space @click="visible = false">取消</el-button>
+        </template>
+    </el-dialog>
+</template>
+
+<script setup>
+import moment from "moment";
+import XEUtils from "xe-utils";
+import API from "@/api";
+import { categoryDic, statisticTemp } from "@/views/basic/acceptItems/main";
+
+const $emit = defineEmits(["success", "closed"]);
+const visible = ref(false);
+const isSaving = ref(false);
+
+const form = ref({
+    projectId: null,
+    projectName: null,
+    starLevel: "A"
+});
+
+const itemsLevel = ref("基础项");
+const acceptItems = ref([]);
+const filterItem = value => XEUtils.filter(acceptItems.value, item => item.acceptItemType.includes(value))
+const statisticData = reactive({
+    b_length: computed(() => filterItem("基础项").length),
+    p_length: computed(() => filterItem("推广项").length),
+    i_length: computed(() => filterItem("提升项").length),
+    b_select: computed(() => XEUtils.filter(filterItem("基础项"), item => item.checked).length),
+    p_select: computed(() => XEUtils.filter(filterItem("推广项"), item => item.checked).length),
+    i_select: computed(() => XEUtils.filter(filterItem("提升项"), item => item.checked).length),
+    p_required: computed(() => XEUtils.filter(filterItem("推广项"), item => item.acceptItemType.includes("必选项") && item.checked).length),
+    i_required: computed(() => XEUtils.filter(filterItem("提升项"), item => item.acceptItemType.includes("必选项") && item.checked).length)
+})
+
+const options = reactive({
+    align: "center",
+    formConfig: { enabled: false },
+    pagerConfig: { enabled: false }
+})
+const columns = reactive([
+    { type: "seq", width: 60 },
+    { field: "itemCategoryName", title: "验收项类别", minWidth: 120, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(categoryDic, row.itemCategory) },
+    { field: "acceptItem", title: "验收项名称", minWidth: 160 },
+    { field: "acceptItemType", title: "验收项级别", minWidth: 160 },
+    { field: "checked", title: "选择", width: 80, slots: { default: "checked_content" } },
+    { visible: computed(() => itemsLevel.value == "推广项" || itemsLevel.value == "提升项"), field: "replaceIds", title: "验收项替换", minWidth: 100, slots: { default: "checked_replace" } },
+    { field: "beginTime", title: "数据开始时间", width: 160, slots: { default: "date_picker_begin" } },
+    { field: "endTime", title: "数据结束时间", width: 160, slots: { default: "date_picker_end" } },
+])
+
+const disabledDate = (date, row, field) => {
+    if (field == "beginTime" && XEUtils.get(row, "endTime")) return moment(date).diff(XEUtils.get(row, "endTime")) > 0
+    if (field == "endTime" && XEUtils.get(row, "beginTime")) return moment(date).diff(XEUtils.get(row, "beginTime")) < 0
+    return false
+}
+
+const open = () => visible.value = true;
+const setData = ({ fpiId, projectName }) => {
+    open();
+    form.value.projectId = fpiId;
+    form.value.projectName = projectName;
+    getAcceptItems(fpiId);
+}
+
+const xGridTable = ref();
+const refreshTable = () => {
+    xGridTable.value.reloadColumn(columns);
+    xGridTable.value.reloadData(filterItem(itemsLevel.value));
+    xGridTable.value.toggleTableLoading(false);
+}
+
+const getAcceptItems = (projectId) => {
+    nextTick(() => xGridTable.value?.toggleTableLoading(true));
+    Promise.all([
+        API.system.acceptItems.all({ orderBy: "itemCategory_asc,createTime_asc" }),
+        API.system.project.bindItem.get({ projectId })
+    ]).then(([itemsRes, infoRes]) => {
+        let count = 0;
+        XEUtils.arrayEach(itemsRes, item => {
+            if (!XEUtils.get(infoRes, "data", []).length) {
+                if (item.acceptItemType == "基础项" && count < 18) {
+                    item.checked = true;
+                    count++;
+                }
+            } else {
+                form.value.starLevel = infoRes.starLevel;
+                const selectInfo = XEUtils.find(XEUtils.get(infoRes, "data", []), info => info.itemId == item.id);
+                selectInfo && XEUtils.merge(item, { checked: true, ...XEUtils.omit(XEUtils.pick(selectInfo, "beginTime", "endTime", "replaceIds"), val => XEUtils.isEmpty(val)) })
+            }
+        });
+        
+        acceptItems.value = itemsRes;
+        refreshTable();
+    }).catch(() => {
+        nextTick(() => xGridTable.value?.toggleTableLoading(false));
+        acceptItems.value = [];
+    });
+}
+const changeLevel = e => {
+    if (e == "A" || e == "AA" && itemsLevel.value == "提升项") {
+        itemsLevel.value = "基础项";
+        refreshTable();
+    }
+};
+
+const formRef = ref();
+const submit = () => {
+    if (statisticData.b_select < 18) return ElMessage.warning("基础项不足18项");
+    if (form.value.starLevel == "AA" && statisticData.p_select < 10) return ElMessage.warning("推广项不足10项");
+    if (form.value.starLevel == "AAA" && statisticData.p_select < 20) return ElMessage.warning("推广项不足20项");
+    if (form.value.starLevel == "AAA" && statisticData.i_select < 2) return ElMessage.warning("提升项不足20项");
+
+    const query = XEUtils.omit(form.value, "projectName");
+    XEUtils.set(query, "data", XEUtils.map(acceptItems.value, item => ({ itemId: item.id, ...XEUtils.pick(item, "beginTime", "endTime", "replaceIds") })));
+    
+    isSaving.value = true;
+    API.system.project.bindItem.add(query).then(res => {
+        isSaving.value = false;
+        ElMessage.success("操作成功");
+        visible.value = false;
+    }).catch(() => isSaving.value = false);
+}
+
+defineExpose({
+    open,
+    setData
+})
+</script>
+
+<style lang="scss" scoped>
+.el-form {padding-right: var(--el-message-close-size, 16px);}
+.el-form .el-main {padding: 0;}
+.el-form-item :deep(.el-form-item__label) {align-items: center;}
+.vxe-icon-question-circle-fill {cursor: help;margin-right: .25em;font-size: var(--vxe-ui-font-size-mini, 12px);}
+
+.items-statistic {margin-bottom: 18px;padding-left: 8px;font-size: 15px;color: var(--el-color-primary);}
+</style>

+ 16 - 0
src/views/basic/project/main.js

@@ -0,0 +1,16 @@
+export const typeDic = ["房屋建筑工程", "市政公用工程", "机电安装工程", "铁路工程", "公路工程", "港口与航道工程", "水利水电工程", "电力工程", "矿山工程", "冶炼工程", "化工石油工程", "通信工程", "其他"]
+export const statusDic = ["在建", "完工", "立项", "停工", "筹备"]
+
+export const interfaceDic = {
+    qingdao_msp: "qd",
+    laoshan_msp: "ls",
+    shizheng_msp: "sz"
+}
+
+export const polygonOptions = {
+    bubble: true, // 事件冒泡到地图上
+    strokeColor: "#1890ff", // 轮廓线颜色
+    strokeWeight: 1, // 轮廓线宽度
+    fillColor: "#80d8ff", // 多边形填充颜色
+    fillOpacity: 0.2, // 多边形填充透明度
+}

+ 1 - 2
src/views/basic/supplier/detail.vue

@@ -26,7 +26,7 @@
 
         <template #footer>
             <el-button :loading="isSaving" type="primary" auto-insert-space @click="submit">保存</el-button>
-            <el-button auto-insert-space @click="dialog = false">取消</el-button>
+            <el-button auto-insert-space @click="visible = false">取消</el-button>
         </template>
     </el-dialog>
 </template>
@@ -102,5 +102,4 @@ defineExpose({
 .el-form {
     padding-right: calc(var(--el-dialog-padding-primary) + var(--el-message-close-size, 16px));
 }
-
 </style>

+ 2 - 2
src/views/basic/supplier/index.vue

@@ -14,7 +14,7 @@
         </scTable>
 	</el-container>
 
-    <supplierDetail v-if="dialog" ref="supplierRef" @success="refreshTable" @closed="dialog = false"></supplierDetail>
+    <supplier-detail v-if="dialog" ref="supplierRef" @success="refreshTable" @closed="dialog = false"></supplier-detail>
 </template>
 
 <script setup>
@@ -52,7 +52,7 @@ const columns = reactive([
     { type: "html", field: "creditNo", title: "统一社会信用代码", minWidth: 150, sortable: true },
     { type: "html", field: "contactsName", title: "联系人", minWidth: 120, sortable: true },
     { type: "html", field: "contactsPhone", title: "联系方式", minWidth: 120, sortable: true },
-    { title: "操作", minWidth: 130, slots: { default: "action" } }
+    { title: "操作", width: 140, slots: { default: "action" } }
 ])
 
 // 显示隐藏 筛选表单

+ 2 - 3
src/views/basic/tag/detail.vue

@@ -1,6 +1,6 @@
 <template>
     <el-dialog v-model="visible" :title="titleMap[mode]" width="480" :close-on-click-modal="false" @closed="$emit('closed')">
-        <el-form ref="formRef" :model="form" :rules="rules" label-width="140px">
+        <el-form ref="formRef" :model="form" :rules="rules" label-width="110">
             <el-form-item label="标签名称" prop="name">
                 <el-input v-model="form.name" placeholder="请输入标签名称"></el-input>
             </el-form-item>
@@ -14,7 +14,7 @@
 
         <template #footer>
             <el-button :loading="isSaving" type="primary" auto-insert-space @click="submit">保存</el-button>
-            <el-button auto-insert-space @click="dialog = false">取消</el-button>
+            <el-button auto-insert-space @click="visible = false">取消</el-button>
         </template>
     </el-dialog>
 </template>
@@ -78,5 +78,4 @@ defineExpose({
 .el-form {
     padding-right: calc(var(--el-dialog-padding-primary) + var(--el-message-close-size, 16px));
 }
-
 </style>

+ 1 - 1
src/views/basic/tag/index.vue

@@ -54,7 +54,7 @@ const options = reactive({
         { type: "seq", width: 60 },
         { type: "html", field: "name", title: "标签名称", minWidth: 150, sortable: true },
         { type: "html", field: "type", title: "标签类型", minWidth: 100, sortable: true },
-        { title: "操作", minWidth: 130, slots: { default: "action" } }
+        { title: "操作", width: 140, slots: { default: "action" } }
     ]
 })
 

+ 188 - 0
src/views/dataMock/carwash/components/form/index.vue

@@ -0,0 +1,188 @@
+<template>
+    <el-main>
+        <el-form ref="formRef" :model="form" :rules="rules" label-width="110px">
+            <el-row>
+                <el-col :md="8" :xs="24">
+                    <el-form-item label="所属项目" prop="fpiId">{{ projectName }}</el-form-item>
+                </el-col>
+                <el-col :md="8" :xs="24">
+                    <el-form-item label="时间范围" prop="dateRange">
+                        <el-date-picker v-model="form.dateRange" type="daterange" :clearable="false" :shortcuts="shortcuts" value-format="YYYY-MM-DD" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
+                    </el-form-item>
+                </el-col>
+                <el-col :md="8" :xs="24">
+                    <el-form-item label="循环次数" prop="cpTimes">
+                        <el-input-number v-model="form.cpTimes" :min="1" :controls="false" placeholder="请输入循环次数"></el-input-number>
+                    </el-form-item>
+                </el-col>
+                <el-col :md="8" :xs="24">
+                    <el-form-item label="车牌号" prop="licensePlate">
+                        <el-select :loading="loading" v-model="form.licensePlate" filterable remote allow-create default-first-option placeholder="请输入车牌号" :remote-method="remoteMethod" @change="licensePlateChange">
+                            <el-option v-for="item in carInfoList" :key="item.id" :value="item.plateNumber"></el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+                <el-col :md="8" :xs="24">
+                    <el-form-item label="车牌颜色" prop="licensePlateColor">
+                        <el-select v-model="form.licensePlateColor" placeholder="请选择车牌颜色">
+                            <el-option v-for="(item, index) in carWashDic.plateColor.slice(1)" :key="index" :label="item" :value="index + 1"></el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+                <el-col :md="8" :xs="24">
+                    <el-form-item label="车辆类型" prop="carType">
+                        <el-select v-model="form.carType" placeholder="请选择车辆类型">
+                            <el-option v-for="(item, index) in carWashDic.carType" :key="index" :label="item" :value="index"></el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+                <el-col :md="8" :xs="24">
+                    <el-form-item label="车辆颜色" prop="vehicleColor">
+                        <el-select v-model="form.vehicleColor" placeholder="请选择车辆颜色">
+                            <el-option v-for="(item, index) in carWashDic.carColor" :key="index" :label="item" :value="index"></el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+                <el-col :md="8" :xs="24">
+                    <el-form-item label="识别结果" prop="alarmType">
+                        <el-select v-model="form.alarmType" placeholder="请选择识别结果">
+                            <el-option v-for="(item, index) in carWashDic.alarmType.slice(1)" :key="index" :label="item" :value="index + 1"></el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+            <el-row>
+                <el-col :md="8" :xs="24">
+                    <el-form-item label="车身清洗图片" prop="folders[carrinse/attach].entities[0]">
+                        <sc-upload v-model="form.folders['carrinse/attach'].entities[0]" :width="140" :height="180" accept="image/jpeg, image/png"></sc-upload>
+                    </el-form-item>
+                </el-col>
+                <el-col :md="8" :xs="24">
+                    <el-form-item label="后盖密闭图片" prop="folders[carrinse/side].entities[0]">
+                        <sc-upload v-model="form.folders['carrinse/side'].entities[0]" :width="140" :height="180" accept="image/jpeg, image/png"></sc-upload>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+        </el-form>
+    </el-main>
+
+    <el-footer>
+        <el-button :loading="isSaving" type="primary" auto-insert-space @click="submit">保存</el-button>
+        <el-button auto-insert-space @click="cancel">取消</el-button>
+    </el-footer>
+</template>
+
+<script setup>
+import moment from "moment";
+import XEUtils from "xe-utils";
+import API from "@/api";
+import TOOL from "@/utils/tool";
+import scUpload from "@/components/scUpload/index";
+import { carWashDic } from "@/views/dataMock/carwash/main";
+
+const $emit = defineEmits(["success", "closed"]);
+const loading = ref(false);
+const isSaving = ref(false);
+const carInfoList = ref([]);
+
+const shortcuts = [
+    { text: "上周", value: () => [moment().subtract(1, "week"), moment()] },
+    { text: "上月", value: () => [moment().subtract(1, "month"), moment()] },
+    { text: "去年", value: () => [moment().subtract(1, "year"), moment()] }
+]
+
+const projectName = ref(XEUtils.get(XEUtils.find(TOOL.data.get("PROJECT"), item => item.fpiId === TOOL.data.get("PROJECT_ID")), "projectName"))
+const form = ref({
+    fpiId: TOOL.data.get("PROJECT_ID"),
+    dateRange: [],
+    cpTimes: 1,
+    licensePlate: null,
+    licensePlateColor: null,
+    carType: null,
+    vehicleColor: null,
+    alarmType: 3,
+    folders: {
+        "carrinse/attach": {
+            entities: [{}]
+        },
+
+        "carrinse/side": {
+            entities: [{}]
+        }
+    }
+});
+
+const rules = reactive({
+    dateRange: [{ required: true, message: "请选择时间范围" }],
+    cpTimes: [{ required: true, message: "请输入循环次数" }],
+    licensePlate: [{ required: true, message: "请输入车牌号" }],
+    licensePlateColor: [{ required: true, message: "请选择车牌颜色" }],
+    carType: [{ required: true, message: "请选择车辆类型" }],
+    vehicleColor: [{ required: true, message: "请选择车辆颜色" }],
+    alarmType: [{ required: true, message: "请选择识别结果" }],
+    "folders[carrinse/attach].entities[0]": [{ required: true, validator: (rule, value, callback) => {
+        if (XEUtils.isEmpty(value)) return callback(new Error("请上传车身清洗图片"));
+        callback();
+    }}],
+    "folders[carrinse/side].entities[0]": [{ required: true, validator: (rule, value, callback) => {
+        if (XEUtils.isEmpty(value)) return callback(new Error("请上传后盖密闭图片"));
+        callback();
+    }}]
+})
+
+const remoteMethod = plateNumberLike => {
+    if (plateNumberLike) {
+        loading.value = true;
+        API.carwash.carInfo.get({ current: 1, size: 99999, projectId: TOOL.data.get("PROJECT_ID"), plateNumberLike }).then(res => {
+            loading.value = false;
+            carInfoList.value = res.records || [];
+        }).catch(() => loading.value = false);
+    } else carInfoList.value = [];
+}
+
+const licensePlateChange = e => {
+    const carinfo = XEUtils.find(carInfoList.value, item => item.plateNumber == e);
+    if (carinfo) {
+        form.value.carType = carinfo.vehicleType
+        form.value.vehicleColor = carinfo.vehicleColor
+    }
+}
+
+const formRef = ref();
+const cancel = () => formRef.value.resetFields();
+const submit = () => {
+    formRef.value.validate(valid => {
+        if (valid) {
+            const data = XEUtils.omit(form.value, ["dateRange", "folders"]);
+            XEUtils.set(data, "beginCreateDate", XEUtils.first(form.value.dateRange));
+            XEUtils.set(data, "endCreateDate", XEUtils.last(form.value.dateRange));
+            XEUtils.set(data, "folders", {
+                "carrinse/attach": {
+                    entities: form.value.folders["carrinse/attach"].entities.map(item => ({ features: { ticket: item.path } }))
+                },
+                "carrinse/side": {
+                    entities: form.value.folders["carrinse/side"].entities.map(item => ({ features: { ticket: item.path } }))
+                }
+            });
+
+            isSaving.value = true;
+            API.carwash.makeData.add(data).then(res => {
+                isSaving.value = false;
+                ElMessage.success("操作成功");
+                cancel();
+            }).catch(() => isSaving.value = false);
+        } else {
+            return false;
+        }
+    });
+}
+</script>
+
+<style lang="scss" scoped>
+.el-main {background: #fff;}
+
+.el-form-item .el-input-number {width: 100%;}
+.el-form-item .el-input-number :deep(.el-input__inner) {text-align: unset;}
+
+.el-footer {display: flex;justify-content: flex-end;align-items: center;}
+</style>

+ 8 - 3
src/views/dataMock/carwash/components/index.js

@@ -1,12 +1,17 @@
+import XEUtils from "xe-utils"
+
 const resultComps = {}
 let requireComponent = require.context(
     "./", // 在当前目录下查找
-    false, // 不遍历子文件夹
+    true, // 遍历子文件夹
     /\.vue$/ // 正则匹配 以 .vue结尾的文件
 )
 requireComponent.keys().forEach(fileName => {
-    let comp = requireComponent(fileName)
-    resultComps[fileName.replace(/^\.\/(.*)\.\w+$/, "$1")] = comp.default
+    const compName = fileName.replace(/^\.\/(.*)\.\w+$/, "$1")
+    const comp = requireComponent(fileName)
+    if (compName.includes("/")) {
+        if (XEUtils.last(compName.split("/")) == "index") resultComps[XEUtils.first(compName.split("/"))] = comp.default
+    } else resultComps[compName] = comp.default
 })
 
 export default resultComps

+ 104 - 0
src/views/dataMock/carwash/components/info/detail.vue

@@ -0,0 +1,104 @@
+<template>
+    <el-dialog v-model="visible" :title="titleMap[mode]" width="480" :close-on-click-modal="false" @closed="$emit('closed')">
+        <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
+            <el-form-item label="车牌号" prop="plateNumber">
+                <el-input v-model="form.plateNumber" placeholder="请输入车牌号"></el-input>
+            </el-form-item>
+            <el-form-item label="车辆类型:" prop="vehicleType">
+                <el-select v-model="form.vehicleType" placeholder="请选择车辆类型">
+                    <el-option v-for="(label, key) in carWashDic.carType" :key="key" :label="label" :value="key + ''"></el-option>
+                </el-select>
+            </el-form-item>
+            <el-form-item label="车辆颜色:" prop="vehicleColor">
+                <el-select v-model="form.vehicleColor" placeholder="请选择车辆颜色">
+                    <el-option v-for="(label, key) in carWashDic.carColor" :key="key" :label="label" :value="key + ''"></el-option>
+                </el-select>
+            </el-form-item>
+            <el-form-item label="使用状态:" prop="isSupervise">
+                <el-select v-model="form.isSupervise" placeholder="请选择使用状态">
+                    <el-option label="使用中" :value="true"></el-option>
+                    <el-option label="已拆除" :value="false"></el-option>
+                </el-select>
+            </el-form-item>
+            <el-form-item label="数据来源:">
+                <el-select v-model="form.dataSources" clearable placeholder="请选择数据来源">
+                    <el-option label="三方系统推送" :value="0"></el-option>
+                    <el-option label="其他" :value="1"></el-option>
+                </el-select>
+            </el-form-item>
+        </el-form>
+
+        <template #footer>
+            <el-button :loading="isSaving" type="primary" auto-insert-space @click="submit">保存</el-button>
+            <el-button auto-insert-space @click="visible = false">取消</el-button>
+        </template>
+    </el-dialog>
+</template>
+
+<script setup>
+import XEUtils from "xe-utils";
+import API from "@/api";
+import TOOL from "@/utils/tool";
+import { carWashDic } from "@/views/dataMock/carwash/main";
+
+const $emit = defineEmits(["success", "closed"]);
+const visible = ref(false);
+const isSaving = ref(false);
+
+const mode = ref("add");
+const titleMap = reactive({
+    add: "新增",
+    edit: "修改"
+});
+
+const form = ref({
+    id: null,
+    projectId: TOOL.data.get("PROJECT_ID"),
+    plateNumber: null,
+    vehicleType: null,
+    vehicleColor: null,
+    isSupervise: null,
+    dataSources: null
+});
+const rules = reactive({
+    plateNumber: [{ required: true, message: "请输入车牌号" }],
+    vehicleType: [{ required: true, message: "请选择车辆类型" }],
+    vehicleColor: [{ required: true, message: "请选择车辆颜色" }],
+    isSupervise: [{ required: true, message: "请选择使用状态" }]
+})
+
+const open = () => visible.value = true;
+const setData = data => {
+    open();
+    mode.value = "edit";
+    XEUtils.objectEach(form.value, (_, key) => XEUtils.set(form.value, key, XEUtils.get(data, key)));
+}
+
+const formRef = ref();
+const submit = () => {
+    formRef.value.validate(valid => {
+        if (valid) {
+            isSaving.value = true;
+            API.carwash.carInfo[mode.value](form.value).then(res => {
+                isSaving.value = false;
+                ElMessage.success("操作成功");
+                visible.value = false;
+                $emit("success", mode.value);
+            }).catch(() => isSaving.value = false);
+        } else {
+            return false;
+        }
+    });
+}
+
+defineExpose({
+    open,
+    setData
+})
+</script>
+
+<style scoped>
+.el-form {
+    padding-right: calc(var(--el-dialog-padding-primary) + var(--el-message-close-size, 16px));
+}
+</style>

+ 121 - 0
src/views/dataMock/carwash/components/info/index.vue

@@ -0,0 +1,121 @@
+<template>
+    <scTable ref="xGridTable" :apiObj="$API.carwash.carInfo" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns">
+        <template #top>
+            <div class="vxe-table-top__button">
+                <el-button type="primary" @click="table_add">
+                    <template #icon><sc-iconify icon="ant-design:plus-outlined"></sc-iconify></template>新增
+                </el-button>
+            </div>
+        </template>
+
+        <template #action="{ row }">
+            <el-button type="primary" link @click="table_edit(row)">
+                <template #icon><sc-iconify icon="ant-design:edit-outlined"></sc-iconify></template>修改
+            </el-button>
+            <el-button type="primary" link @click="table_del(row)">
+                <template #icon><sc-iconify icon="ant-design:delete-outlined"></sc-iconify></template>删除
+            </el-button>
+        </template>
+    </scTable>
+
+    <info-detail v-if="dialog" ref="infoRef" @success="refreshTable" @closed="dialog = false"></info-detail>
+</template>
+
+<script setup>
+import XEUtils from "xe-utils";
+import API from "@/api";
+import TOOL from "@/utils/tool";
+import { mapFormItemInput, mapFormItemSelect } from "@/components/scTable/helper";
+import { carWashDic } from "@/views/dataMock/carwash/main";
+import infoDetail from "./detail";
+
+const proConfig = reactive({
+    storageKey: "PROJECT",
+    resetValue: TOOL.data.get("PROJECT_ID"),
+    optionProps: { label: "projectName", value: "fpiId" },
+    events: {
+        change: data => XEUtils.assign(formConfig.data, data)
+    }
+})
+
+const selectConfig = reactive({
+    options: carWashDic.carType.map((label, value) => ({ label, value })),
+    events: {
+        change: data => XEUtils.merge(formConfig.data, data)
+    }
+})
+
+const toolbarConfig = reactive({
+    enabled: true,
+    print: false
+})
+
+const formConfig = reactive({
+    data: {
+        orderBy: "createTime_desc",
+        projectId: TOOL.data.get("PROJECT_ID")
+    },
+    items: [
+        mapFormItemSelect("projectId", "所属项目", proConfig),
+        mapFormItemInput("plateNumberLike", "车牌号"),
+        mapFormItemSelect("vehicleType", "车辆类型", selectConfig),
+        mapFormItemSelect("vehicleColor", "车辆颜色", { ...selectConfig, options: carWashDic.carColor.map((label, value) => ({ label, value })) }),
+        mapFormItemSelect("isSupervise", "使用状态", { ...selectConfig, options: carWashDic.isSupervise })
+    ]
+})
+
+const paramsColums = reactive([
+    { column: "orderBy" },
+    { column: "projectId" },
+    { column: "plateNumberLike" },
+    { column: "vehicleType" },
+    { column: "vehicleColor" },
+    { column: "isSupervise" }
+])
+
+const columns = reactive([
+    { type: "seq", width: 60 },
+    { type: "html", field: "plateNumber", title: "车牌号", minWidth: 120, sortable: true },
+    { type: "html", field: "vehicleType", title: "车辆类型", minWidth: 120, sortable: true, formatter: ({ cellValue }) => XEUtils.get(carWashDic.carType, cellValue, "/") },
+    { type: "html", field: "vehicleColor", title: "车辆颜色", minWidth: 100, sortable: true, formatter: ({ cellValue }) => XEUtils.get(carWashDic.carColor, cellValue, "/") },
+    { type: "html", field: "status", title: "使用状态", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(XEUtils.find(carWashDic.isSupervise, item => item.value == row.isSupervise), "label", "/") },
+    { type: "html", field: "dataSources", title: "数据来源", minWidth: 120, sortable: true, formatter: ({ cellValue }) => XEUtils.get(carWashDic.dataSources, cellValue, "/") },
+    { type: "html", field: "createTime", title: "创建时间", minWidth: 160, sortable: true },
+    { title: "操作", width: 140, slots: { default: "action" } }
+])
+
+// 显示隐藏 筛选表单
+const xGridTable = ref();
+const toggleFormEnabled = () => xGridTable.value.toggleFormEnabled();
+
+const refreshTable = (mode = "add") => {
+    xGridTable.value.reloadColumn(columns);
+    xGridTable.value.searchData(mode);
+}
+
+const infoRef = ref();
+const dialog = ref(false);
+
+const table_add = () => {
+    dialog.value = true;
+    nextTick(() => infoRef.value?.open());
+}
+
+const table_edit = row => {
+    dialog.value = true;
+    nextTick(() => infoRef.value?.setData(row));
+}
+
+const table_del = ({ id }) => {
+    ElMessageBox.confirm("是否确认删除该车辆信息?", "删除警告", {
+        type: "warning",
+        confirmButtonText: "确定",
+        cancelButtonText: "取消"
+    }).then(() => {
+        API.carwash.carInfo.del({ id }).then(() => {
+            ElMessage.success("操作成功");
+            refreshTable();
+        });
+    });
+}
+</script>

+ 107 - 102
src/views/dataMock/carwash/components/monos.vue

@@ -1,116 +1,121 @@
 <template>
-    <el-main>
-        <el-card>
-            <template #header>Group任务池配置<el-button type="primary" @click="mono_detail">任务列表</el-button></template>
+    <scTable ref="xGridTable" :apiObj="$API.common.opsTask" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns">
+        <template #action="{ row }">
+            <el-button v-if="row.taskStatus == 'active'" type="primary" link @click="table_stop(row)">
+                <template #icon><sc-iconify icon="mdi:stop-pause-outline" size="13"></sc-iconify></template>终止
+            </el-button>
+        </template>
+    </scTable>
+</template>
 
-            <el-form>
-                <el-row :gutter="15">
-                    <el-col :span="8">
-                        <el-form-item label="最大组任务数:">{{ XEUtils.get(defaultGroups, "configs.maxGroupQueues") }}</el-form-item>
-                    </el-col>
-                    <el-col :span="8">
-                        <el-form-item label="队列最大任务数:">{{ XEUtils.get(defaultGroups, "configs.maxQueueLimit") }}</el-form-item>
-                    </el-col>
-                    <el-col :span="8">
-                        <el-form-item label="最大并发队列数:">{{ XEUtils.get(defaultGroups, "configs.maxQueues") }}</el-form-item>
-                    </el-col>
-                    <el-col :span="8">
-                        <el-form-item label="队列任务间隔(秒):">{{ XEUtils.get(defaultGroups, "configs.taskIntervalSeconds") }}</el-form-item>
-                    </el-col>
-                    <el-col :span="8">
-                        <el-form-item label="重试任务间隔(秒):">{{ XEUtils.get(defaultGroups, "configs.taskRetryInterval") }}</el-form-item>
-                    </el-col>
-                    <el-col :span="8">
-                        <el-form-item label="任务最大重试次数(秒):">{{ XEUtils.get(defaultGroups, "configs.taskRetryTimes") }}</el-form-item>
-                    </el-col>
-                    <el-col :span="8">
-                        <el-form-item label="任务最大等待时间(秒):">{{ XEUtils.get(defaultGroups, "configs.taskWaitSeconds") }}</el-form-item>
-                    </el-col>
-                </el-row>
-            </el-form>
-        </el-card>
+<script setup>
+import moment from "moment";
+import XEUtils from "xe-utils";
+import API from "@/api";
+import TOOL from "@/utils/tool";
+import { mapFormItemSelect, mapFormItemDatePicker } from "@/components/scTable/helper";
+import { objectToArray } from "@/utils/basicDic";
+import { taskStateDic } from "../main";
 
-        <el-card>
-            <el-form>
-                <el-form-item label="workers忙碌数量:">0</el-form-item>
-                <el-form-item label="workers空闲数量:">50</el-form-item>
-                <el-form-item label="Groups忙碌数量:">0</el-form-item>
-                <el-form-item label="Groups队列数量:">0</el-form-item>
-                <el-form-item label="mono总数:">0</el-form-item>
-            </el-form>
-        </el-card>
-        <div class="echart-bar">
-            <sc-echarts :option="option" @chartClick="chart_click"></sc-echarts>
-        </div>
-    </el-main>
+const proConfig = reactive({
+    storageKey: "PROJECT",
+    resetValue: TOOL.data.get("PROJECT_ID"),
+    optionProps: { label: "projectName", value: "fpiId" },
+    events: {
+        change: data => XEUtils.assign(formConfig.data, data)
+    }
+})
 
-    <mono-detail v-if="dialog" ref="monoRef" @closed="dialog = false"></mono-detail>
-</template>
+const selectConfig = reactive({
+    options: objectToArray(taskStateDic),
+    events: {
+        change: data => XEUtils.merge(formConfig.data, data)
+    }
+})
 
-<script setup>
-import XEUtils from "xe-utils"
-import { defaultGroups } from "../../main";
-import monoDetail from "../mono/index"
+const daterangeConfig = reactive({
+    props: {
+        type: "daterange",
+        startPlaceholder: "开始时间",
+        endPlaceholder: "结束时间",
+        valueFormat: "YYYY-MM-DD"
+    }
+})
+
+const datetimerangeConfig = reactive({
+    span: 7,
+    resetValue: [moment().startOf("month").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")],
+    props: {
+        popperClass: "datetime-picker-popper",
+        type: "datetimerange",
+        startPlaceholder: "开始时间",
+        endPlaceholder: "结束时间",
+        format: "YYYY-MM-DD HH:mm"
+    }
+})
 
-const $emit = defineEmits(["closed"]);
+const toolbarConfig = reactive({
+    enabled: true,
+    print: false
+})
 
-const option = ref({
-    grid: { left: "5%", right: "10%", bottom: 60 },
-    dataZoom: [
-        { type: "inside", endValue: 15, zoomLock: true }, { 
-        type: "slider",
-        showDetail: false,
-        moveHandleIcon: "none", // 移动手柄
-        moveHandleStyle: { opacity: 0 }
-    }],
-    xAxis: {
-        name: "workers",
-        nameTextStyle: { fontSize: 14, color: "#606266" },
-        axisLabel: { color: '#5d5d5d', rotate: -30 },
-        data: XEUtils.keys(XEUtils.get(defaultGroups, "workers"))
+const formConfig = reactive({
+    data: {
+        orderBy: "createTime_desc",
+        projectId: TOOL.data.get("PROJECT_ID"),
+        taskType: "car_rinse",
+        dateRange: [],
+        createTime: [moment().startOf("month").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")]
     },
-    yAxis: {
-        name: "mono",
-        type: "value",
-        min: 0,
-        max: XEUtils.max([8, 5]),
-        nameTextStyle: { fontSize: 14, color: "#606266" },
-        minInterval: 1,
-        axisLabel: { color: '#5d5d5d' },
-        axisLine: { show: true, lineStyle: { color: "#d3d2d3" } },
-        splitLine: { show: false }
-    },
-    series: [{
-        type: "bar",
-        data: [8, 5],
-        animationDuration: 2500,
-        animationDurationUpdate: 2500
-    }]
+    items: [
+        mapFormItemSelect("projectId", "所属项目", proConfig),
+        mapFormItemSelect("taskStatus", "任务状态", selectConfig),
+        mapFormItemDatePicker("dateRange", "时间范围", daterangeConfig),
+        mapFormItemDatePicker("createTime", "创建时间", datetimerangeConfig)
+    ]
 })
 
-const dialog = ref(false);
-const monoRef = ref();
-const chart_click = ({ name }) => {
-    dialog.value = true;
-    nextTick(() => monoRef.value.open(XEUtils.get(defaultGroups, `groups.${name}.monos`)));
-}
-
-const mono_detail = () => {
-    dialog.value = true;
-    nextTick(() => monoRef.value.open());
-}
-</script>
+const paramsColums = reactive([
+    { column: "orderBy" },
+    { column: "projectId" },
+    { column: "taskType" },
+    { column: "taskStatus" },
+    { column: "planBeginTimeBegin", field: "dateRange[0]" },
+    { column: "planEndTimeEnd", field: "dateRange[1]" },
+    { column: "createTimeBegin", field: "createTime[0]" },
+    { column: "createTimeEnd", field: "createTime[1]" }
+])
 
-<style lang="scss" scoped>
-.el-main {padding: 0 12px 12px;background: #fff;}
-.el-card :deep(.el-card__header) {display: flex;justify-content: space-between;align-items: center;padding-left: 20px;background: #f9fdff;font-weight: 700;}
-.el-card :deep(.el-card__body) {padding: 10px var(--el-card-padding);}
-.el-card + .el-card {margin-top: 10px;}
+const columns = reactive([
+    { type: "seq", width: 60 },
+    { type: "html", field: "planNumber", title: "任务总数", minWidth: 100, sortable: true },
+    { type: "html", field: "finishNumber", title: "已完成数量", minWidth: 120, sortable: true },
+    { type: "html", field: "taskStatus", title: "任务状态", minWidth: 100, sortable: true, formatter: ({ cellValue }) => XEUtils.get(taskStateDic, cellValue, cellValue) },
+    { type: "html", field: "dateRange", title: "时间范围", minWidth: 190, sortable: true, formatter: ({ cellValue, row }) => cellValue || TOOL.dateFormat(row.planBeginTime, "YYYY-MM-DD") + " 至 " + TOOL.dateFormat(row.planEndTime, "YYYY-MM-DD") },
+    { type: "html", field: "createTime", title: "创建时间", minWidth: 160, sortable: true },
+    { type: "html", field: "finishTime", title: "任务终止/完成时间", minWidth: 160, sortable: true },
+    { title: "操作", fixed: "right", minWidth: 100, align: "center", slots: { default: "action" } }
+])
 
-.el-form .el-form-item {margin-bottom: 0;}
-.el-form :deep(.el-form-item__content) {font: 22px calculator-all;color: #1890ff;letter-spacing: 2px;}
+// 显示隐藏 筛选表单
+const xGridTable = ref();
+const toggleFormEnabled = () => xGridTable.value.toggleFormEnabled();
 
-.el-card + .el-card .el-form {display: flex;justify-content: space-evenly;}
+const refreshTable = () => {
+    xGridTable.value.reloadColumn(columns);
+    xGridTable.value.searchData();
+}
 
-.echart-bar {width: 100%;height: 400px;}
-</style>
+const table_stop = row => {
+    ElMessageBox.confirm("是否确认终止该任务?", "提示", {
+        type: "warning",
+        confirmButtonText: "确定",
+        cancelButtonText: "取消"
+    }).then(() => {
+        API.common.opsTask.stop({ id: row.id }).then(() => {
+            ElMessage.success("操作成功");
+            refreshTable();
+        });
+    });
+}
+</script>

+ 0 - 24
src/views/dataMock/carwash/components/record.vue

@@ -1,24 +0,0 @@
-<template>
-    <el-tabs v-model="tabsConfig.modelValue" @tab-change="refreshTable">
-        <el-tab-pane v-for="(label, key) in tabsConfig.options" :key="key" :label="label" :name="key"></el-tab-pane>
-    </el-tabs>
-    
-    <data-table ref="tableRef" :apiKey="tabsConfig.modelValue"></data-table>
-</template>
-
-<script setup>
-import dataTable from "./table.vue";
-
-const tabsConfig = reactive({
-    modelValue: "temp",
-    options: { temp: "设备监控", camera: "视频监控" }
-});
-
-// 显示隐藏 筛选表单
-const tableRef = ref();
-const refreshTable = e => tableRef.value.refreshTable(e);
-</script>
-
-<style lang="scss" scoped>
-.el-tabs {padding: 0 12px;background: #fff;}
-</style>

+ 104 - 0
src/views/dataMock/carwash/components/record/detail.vue

@@ -0,0 +1,104 @@
+<template>
+    <el-dialog v-model="visible" :title="titleMap[mode]" width="480" :close-on-click-modal="false" @closed="$emit('closed')">
+        <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
+            <el-form-item label="车牌号" prop="plateNumber">
+                <el-input v-model="form.plateNumber" placeholder="请输入车牌号"></el-input>
+            </el-form-item>
+            <el-form-item label="车辆类型:" prop="vehicleType">
+                <el-select v-model="form.vehicleType" placeholder="请选择车辆类型">
+                    <el-option v-for="(label, key) in carWashDic.carType" :key="key" :label="label" :value="key + ''"></el-option>
+                </el-select>
+            </el-form-item>
+            <el-form-item label="车辆颜色:" prop="vehicleColor">
+                <el-select v-model="form.vehicleColor" placeholder="请选择车辆颜色">
+                    <el-option v-for="(label, key) in carWashDic.carColor" :key="key" :label="label" :value="key + ''"></el-option>
+                </el-select>
+            </el-form-item>
+            <el-form-item label="使用状态:" prop="isSupervise">
+                <el-select v-model="form.isSupervise" placeholder="请选择使用状态">
+                    <el-option label="使用中" :value="true"></el-option>
+                    <el-option label="已拆除" :value="false"></el-option>
+                </el-select>
+            </el-form-item>
+            <el-form-item label="数据来源:">
+                <el-select v-model="form.dataSources" clearable placeholder="请选择数据来源">
+                    <el-option label="三方系统推送" :value="0"></el-option>
+                    <el-option label="其他" :value="1"></el-option>
+                </el-select>
+            </el-form-item>
+        </el-form>
+
+        <template #footer>
+            <el-button :loading="isSaving" type="primary" auto-insert-space @click="submit">保存</el-button>
+            <el-button auto-insert-space @click="visible = false">取消</el-button>
+        </template>
+    </el-dialog>
+</template>
+
+<script setup>
+import XEUtils from "xe-utils";
+import API from "@/api";
+import TOOL from "@/utils/tool";
+import { carWashDic } from "@/views/dataMock/carwash/main";
+
+const $emit = defineEmits(["success", "closed"]);
+const visible = ref(false);
+const isSaving = ref(false);
+
+const mode = ref("add");
+const titleMap = reactive({
+    add: "新增",
+    edit: "修改"
+});
+
+const form = ref({
+    id: null,
+    projectId: TOOL.data.get("PROJECT_ID"),
+    plateNumber: null,
+    vehicleType: null,
+    vehicleColor: null,
+    isSupervise: null,
+    dataSources: null
+});
+const rules = reactive({
+    plateNumber: [{ required: true, message: "请输入车牌号" }],
+    vehicleType: [{ required: true, message: "请选择车辆类型" }],
+    vehicleColor: [{ required: true, message: "请选择车辆颜色" }],
+    isSupervise: [{ required: true, message: "请选择使用状态" }]
+})
+
+const open = () => visible.value = true;
+const setData = data => {
+    open();
+    mode.value = "edit";
+    XEUtils.objectEach(form.value, (_, key) => XEUtils.set(form.value, key, XEUtils.get(data, key)));
+}
+
+const formRef = ref();
+const submit = () => {
+    formRef.value.validate(valid => {
+        if (valid) {
+            isSaving.value = true;
+            API.carwash.carInfo[mode.value](form.value).then(res => {
+                isSaving.value = false;
+                ElMessage.success("操作成功");
+                visible.value = false;
+                $emit("success", mode.value);
+            }).catch(() => isSaving.value = false);
+        } else {
+            return false;
+        }
+    });
+}
+
+defineExpose({
+    open,
+    setData
+})
+</script>
+
+<style scoped>
+.el-form {
+    padding-right: calc(var(--el-dialog-padding-primary) + var(--el-message-close-size, 16px));
+}
+</style>

+ 117 - 0
src/views/dataMock/carwash/components/record/index.vue

@@ -0,0 +1,117 @@
+<template>
+    <scTable ref="xGridTable" :apiObj="$API.carwash.record" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns">
+        <template #default_imgUrl="{ row, column }">
+            <vxe-image v-if="XEUtils.get(row, `folders.${column.field}.entities[0].path`)" :src="'/api/folder/' + XEUtils.get(row, `folders.${column.field}.entities[0].path`)" width="40" height="40"></vxe-image>
+        </template>
+        
+        <template #action="{ row }">
+            <el-button type="primary" link @click="table_edit(row)">
+                <template #icon><sc-iconify icon="ant-design:edit-outlined"></sc-iconify></template>修改
+            </el-button>
+            <el-button type="primary" link @click="table_del(row)">
+                <template #icon><sc-iconify icon="ant-design:delete-outlined"></sc-iconify></template>删除
+            </el-button>
+        </template>
+    </scTable>
+</template>
+
+<script setup>
+import moment from "moment";
+import XEUtils from "xe-utils";
+import API from "@/api";
+import TOOL from "@/utils/tool";
+import { mapFormItemInput, mapFormItemSelect, mapFormItemDatePicker } from "@/components/scTable/helper";
+import { carWashDic } from "@/views/dataMock/carwash/main";
+
+const proConfig = reactive({
+    storageKey: "PROJECT",
+    resetValue: TOOL.data.get("PROJECT_ID"),
+    optionProps: { label: "projectName", value: "fpiId" },
+    events: {
+        change: data => XEUtils.assign(formConfig.data, data)
+    }
+})
+
+const selectConfig = reactive({
+    options: carWashDic.carType.map((label, value) => ({ label, value })),
+    events: {
+        change: data => XEUtils.merge(formConfig.data, data)
+    }
+})
+
+const datetimerangeConfig = reactive({
+    span: 7,
+    resetValue: [moment().startOf("month").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")],
+    props: {
+        popperClass: "datetime-picker-popper",
+        type: "datetimerange",
+        startPlaceholder: "开始时间",
+        endPlaceholder: "结束时间",
+        format: "YYYY-MM-DD HH:mm"
+    }
+})
+
+const toolbarConfig = reactive({
+    enabled: true,
+    print: false
+})
+
+const formConfig = reactive({
+    data: {
+        projectId: TOOL.data.get("PROJECT_ID"),
+        createTime: [moment().startOf("month").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")]
+    },
+    items: [
+        mapFormItemSelect("projectId", "所属项目", proConfig),
+        mapFormItemInput("licensePlate", "车牌号"),
+        mapFormItemSelect("carType", "车辆类型", selectConfig),
+        mapFormItemSelect("alarmType", "识别结果", { ...selectConfig, options: carWashDic.alarmType.map((label, value) => ({ label, value })) }),
+        mapFormItemDatePicker("createTime", "抓拍时间", datetimerangeConfig)
+    ]
+})
+
+const paramsColums = reactive([
+    { column: "fpiId", field: "projectId" },
+    { column: "licensePlate" },
+    { column: "carType" },
+    { column: "alarmType" },
+    { column: "beginCaptureTime", field: "createTime[0]" },
+    { column: "endCaptureTime", field: "createTime[1]" }
+])
+
+const columns = reactive([
+    { type: "seq", width: 60 },
+    { type: "html", field: "captureTime", title: "抓拍时间", minWidth: 160, sortable: true },
+    { type: "html", field: "enterTime", title: "车辆入场时间", minWidth: 160, sortable: true },
+    { type: "html", field: "leaveTime", title: "车辆出场时间", minWidth: 160, sortable: true },
+    { type: "html", field: "licensePlate", title: "车牌号", minWidth: 120, sortable: true },
+    { type: "html", field: "licensePlateColor", title: "车牌颜色", minWidth: 120, sortable: true, formatter: ({ cellValue }) => XEUtils.get(carWashDic.plateColor, cellValue, cellValue) },
+    { type: "html", field: "carType", title: "车辆类型", minWidth: 120, sortable: true, formatter: ({ cellValue }) => XEUtils.get(carWashDic.carType, cellValue, cellValue) },
+    { type: "html", field: "alarmType", title: "识别结果", minWidth: 100, sortable: true, formatter: ({ cellValue }) => XEUtils.get(carWashDic.alarmType, cellValue, cellValue) },
+    { field: "carrinse/attach", title: "车身清洗图片", minWidth: 110, align: "center", slots: { default: "default_imgUrl" } },
+    { field: "carrinse/side", title: "后盖密闭图片", minWidth: 110, align: "center", slots: { default: "default_imgUrl" } },
+    { title: "操作", fixed: "right", width: 140, align: "center", slots: { default: "action" } }
+])
+
+// 显示隐藏 筛选表单
+const xGridTable = ref();
+const toggleFormEnabled = () => xGridTable.value.toggleFormEnabled();
+
+const refreshTable = () => {
+    xGridTable.value.reloadColumn(columns);
+    xGridTable.value.searchData();
+}
+
+const table_stop = row => {
+    ElMessageBox.confirm("是否确认终止该任务?", "提示", {
+        type: "warning",
+        confirmButtonText: "确定",
+        cancelButtonText: "取消"
+    }).then(() => {
+        API.common.opsTask.stop({ id: row.id }).then(() => {
+            ElMessage.success("操作成功");
+            refreshTable();
+        });
+    });
+}
+</script>

+ 0 - 171
src/views/dataMock/carwash/components/table.vue

@@ -1,171 +0,0 @@
-<template>
-    <scTable ref="xGridTable" :apiObj="XEUtils.get($API.carwash.records, props.apiKey)" :toolbarConfig="toolbarConfig" :formConfig="formConfig" :paramsColums="XEUtils.get(paramsColums, props.apiKey)" :columns="XEUtils.get(columns, props.apiKey)" :options="props.options">
-        <template #default_imgUrl="{ row, column }">
-            <vxe-image v-if="formatUrl(row, column)" :src="'/api/folder/' + formatUrl(row, column)" width="40" height="40"></vxe-image>
-        </template>
-
-        <template #default_videoUrl="{ row, column }">
-            <video v-if="XEUtils.get(row, 'videoName')" :src="'/minio' + XEUtils.get(row, 'videoName')" width="40" height="40" :controls="false" @click="viewer.show = true, viewer.url = '/minio' + XEUtils.get(row, 'videoName')"></video>
-            <video v-else-if="formatUrl(row, column)" :src="'/api/folder/' + formatUrl(row, column)" width="40" height="40" :controls="false" @click="viewer.show = true, viewer.url = '/api/folder/' + formatUrl(row, column)"></video>
-        </template>
-
-        <template #action="{ row }">
-            <el-button type="primary" link @click="table_edit(row)">
-                <template #icon><sc-iconify icon="ant-design:edit-outlined"></sc-iconify></template>修改
-            </el-button>
-
-            <el-button type="primary" link @click="table_edit(row)">
-                <template #icon><sc-iconify icon="material-symbols:step-over"></sc-iconify></template>覆盖
-            </el-button>
-        </template>
-    </scTable>
-
-	<video-viewer v-if="viewer.show" :videoUrl="viewer.url" hideOnModal @close="viewer.show = false, viewer.url = null"></video-viewer>
-</template>
-
-<script setup>
-import moment from "moment";
-import XEUtils from "xe-utils";
-import API from "@/api";
-import TOOL from "@/utils/tool";
-import { carWashDic } from "@/utils/basicDic";
-import { mapFormItemInput, mapFormItemSelect, mapFormItemDatePicker } from "@/components/scTable/helper";
-import videoViewer from "@/components/scUpload/videoViewer.vue";
-
-const formatCarwashGate = row => XEUtils.find(TOOL.data.get("CARWASH_GATE"), item => item.deviceNum === XEUtils.get(row, "deviceSn", XEUtils.get(row, "deviceNum")));
-const formatAIGate = row => XEUtils.find(TOOL.data.get("UGLIAI_GATE"), item => XEUtils.findIndexOf(item.devices, d => d.device_num === XEUtils.get(row, "deviceSn", XEUtils.get(row, "deviceNum"))) !== -1);
-
-const getCarwashGate = async fpiId => {
-    const res =  await API.carwash.gate.get({ current: 1, size: 100, fpiId });
-    TOOL.data.set("CARWASH_GATE", XEUtils.uniq((TOOL.data.get("CARWASH_GATE") || []).concat(res.content || []), "id"));
-}
-
-const props = defineProps({
-    apiKey: { type: String, default: "temp" },
-    checked: { type: Boolean, default: false },
-    hideProject: { type: Boolean, default: false },
-    options: { type: Object, default: () => {} }
-})
-
-const route = useRoute()
-
-const proConfig = reactive({
-    visibleMethod: () => !props.hideProject,
-    storageKey: "PROJECT",
-    resetValue: props.hideProject ? 73 : XEUtils.toNumber(XEUtils.get(route, "query.projectId", 73)),
-    props: { clearable: false },
-    optionProps: { label: "projectName", value: "fpiId" },
-    events: {
-        change: data => XEUtils.assign(formConfig.data, data)
-    }
-})
-
-const resultConfig = reactive({
-    visibleMethod: () => props.apiKey == "temp",
-    options: carWashDic.alarmType.slice(1),
-    optionProps: { label: ({ data }) => data, value: ({ index }) => index + 1 },
-    events: {
-        change: data => XEUtils.assign(formConfig.data, data)
-    }
-})
-
-const daterangeConfig = reactive({
-    visibleMethod: () => props.apiKey == "temp",
-    span: 9,
-    props: {
-        type: "datetimerange",
-        startPlaceholder: "开始时间",
-        endPlaceholder: "结束时间",
-        format: "YYYY-MM-DD HH:mm"
-    }
-})
-
-const toolbarConfig = reactive({
-    enabled: true,
-    print: false
-})
-
-const formConfig = reactive({
-    data: {
-        projectID: props.hideProject ? 73 : XEUtils.toNumber(XEUtils.get(route, "query.projectId", 73))
-    },
-    items: [
-        mapFormItemSelect("projectID", "所属项目", proConfig),
-        mapFormItemInput("licensePlate", "车牌号", { visibleMethod: () => props.apiKey == "temp" }),
-        mapFormItemSelect("alarmType", "监测结果", resultConfig),
-        mapFormItemInput("deviceNum", "设备Id", { visibleMethod: () => props.apiKey == "camera" }),
-        mapFormItemDatePicker("createTime", "监测时间", daterangeConfig)
-    ]
-})
-
-const paramsColums = reactive({
-    temp: [
-        { column: "fpiId", field: "projectID" },
-        { column: "licensePlate" },
-        { column: "alarmType" },
-        { column: "beginCaptureTime", field: "createTime[0]" },
-        { column: "endCaptureTime", field: "createTime[1]" }
-    ],
-    camera: [
-        { column: "projectId", field: "projectID" },
-        { column: "deviceNum" }
-    ]
-});
-
-const columns = reactive({
-    temp: [
-        { visible: props.checked, type: "checkbox", fixed: "left", title: "", width: 40 },
-        { type: "seq", fixed: "left", width: 60 },
-        { type: "html", field: "deviceSn", title: "设备唯一标识", fixed: "left", minWidth: 150, sortable: true },
-        { type: "html", field: "projectName", title: "所属项目", minWidth: 200, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(XEUtils.find(TOOL.data.get("PROJECT"), item => item.fpiId === formConfig.data.projectID), "projectName") },
-        { type: "html", field: "carwashGroundName", title: "工地场区", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatCarwashGate(row), "groundName") },
-        { type: "html", field: "carwashMountedName", title: "安装点名称", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatCarwashGate(row), "mountedName") },
-        { type: "html", field: "aIGroundName", title: "AI工地场区", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatAIGate(row), "ground.groundName") },
-        { type: "html", field: "aIMountedName", title: "AI安装点名称", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatAIGate(row), "mountedName") },
-        { type: "html", field: "captureTime", title: "抓拍时间", minWidth: 160, sortable: true },
-        { type: "html", field: "enterTime", title: "车辆入场时间", minWidth: 160, sortable: true },
-        { type: "html", field: "leaveTime", title: "车辆出场时间", minWidth: 160, sortable: true },
-        { type: "html", field: "carType", title: "车型", minWidth: 110, sortable: true, formatter: ({ cellValue }) => XEUtils.get(carWashDic.carType, cellValue, cellValue) },
-        { type: "html", field: "licensePlate", title: "车牌号", minWidth: 110, sortable: true },
-        { type: "html", field: "licensePlateColor", title: "车牌颜色", minWidth: 110, sortable: true, formatter: ({ cellValue }) => XEUtils.get(carWashDic.carColor, cellValue, cellValue) },
-        { type: "html", field: "alarmType", title: "监测结果", minWidth: 110, sortable: true, formatter: ({ cellValue }) => XEUtils.get(carWashDic.alarmType, cellValue, cellValue) },
-        { field: "carrinse/attach", title: "车身清洗图片", minWidth: 110, align: "center", slots: { default: "default_imgUrl" } },
-        { field: "carrinse/side", title: "后盖密闭图片", minWidth: 110, align: "center", slots: { default: "default_imgUrl" } },
-        { visible: props.checked, title: "操作", fixed: "right", minWidth: 140, slots: { default: "action" } }
-    ],
-    camera:[
-        { visible: props.checked, type: "checkbox", fixed: "left", title: "", width: 40 },
-        { type: "seq", fixed: "left", width: 60 },
-        { type: "html", field: "deviceNum", title: "设备唯一标识", fixed: "left", minWidth: 150, sortable: true },
-        { type: "html", field: "projectName", title: "所属项目", minWidth: 200, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(XEUtils.find(TOOL.data.get("PROJECT"), item => item.fpiId === formConfig.data.projectID), "projectName") },
-        { type: "html", field: "carwashGroundName", title: "工地场区", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatCarwashGate(row), "groundName") },
-        { type: "html", field: "carwashMountedName", title: "安装点名称", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatCarwashGate(row), "mountedName") },
-        { type: "html", field: "aIGroundName", title: "AI工地场区", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatAIGate(row), "ground.groundName") },
-        { type: "html", field: "aIMountedName", title: "AI安装点名称", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatAIGate(row), "mountedName") },
-        { type: "html", field: "cameraName", title: "摄像机名称", minWidth: 150, sortable: true },
-        { type: "html", field: "taskTime", title: "创建时间", minWidth: 160, sortable: true, formatter: ({ cellValue, row }) => cellValue || TOOL.dateFormat(row.createTime) },
-        { type: "html", field: "videoType", title: "视频格式", minWidth: 100 },
-        { field: "carrinse/camera", title: "视频预览", minWidth: 110, align: "center", slots: { default: "default_videoUrl" } },
-        { visible: props.checked, title: "操作", fixed: "right", minWidth: 140, slots: { default: "action" } }
-    ]
-})
-
-const formatUrl = (row, { field }) => XEUtils.get(row, `folders.${field}.entities[0].path`);
-const viewer = ref({
-    show: false,
-    url: null
-});
-
-watch(formConfig.data, value => getCarwashGate(value.projectID), { immediate: true, deep: true });
-
-// 显示隐藏 筛选表单
-const xGridTable = ref();
-const refreshTable = e => {
-    xGridTable.value.reloadColumn(XEUtils.get(columns, e));
-    xGridTable.value.resetData();
-}
-
-defineExpose({
-    refreshTable
-})
-</script>

+ 0 - 24
src/views/dataMock/carwash/components/template.vue

@@ -1,24 +0,0 @@
-<template>
-    <el-tabs v-model="tabsConfig.modelValue" @tab-change="refreshTable">
-        <el-tab-pane v-for="(label, key) in tabsConfig.options" :key="key" :label="label" :name="key"></el-tab-pane>
-    </el-tabs>
-    
-    <data-table ref="tableRef" :apiKey="tabsConfig.modelValue" hideProject></data-table>
-</template>
-
-<script setup>
-import dataTable from "./table.vue";
-
-const tabsConfig = reactive({
-    modelValue: "temp",
-    options: { temp: "设备监控", camera: "视频监控" }
-});
-
-// 显示隐藏 筛选表单
-const tableRef = ref();
-const refreshTable = e => tableRef.value.refreshTable(e);
-</script>
-
-<style lang="scss" scoped>
-.el-tabs {padding: 0 12px;background: #fff;}
-</style>

+ 0 - 119
src/views/dataMock/carwash/detail.vue

@@ -1,119 +0,0 @@
-<template>
-    <el-dialog v-model="visible" title="数据模拟" width="870" :close-on-click-modal="false" @closed="$emit('closed')">
-        <el-form ref="formRef" :model="form" :rules="rules" label-width="110px">
-            <el-row>
-                <el-col :md="12" :xs="24">
-                    <el-form-item label="时间范围" prop="dateRange">
-                        <el-date-picker v-model="form.dateRange" type="daterange" :clearable="false" :shortcuts="shortcuts" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
-                    </el-form-item>
-                </el-col>
-                <el-col :md="12" :xs="24">
-                    <el-form-item label="数据处理" prop="handler">
-                        <el-radio-group v-model="form.handler">
-                            <el-radio value="copy">重复新增</el-radio>
-                            <el-radio value="cover">数据覆盖</el-radio>
-                            <el-radio value="partly">部分覆盖</el-radio>
-                        </el-radio-group>
-                    </el-form-item>
-                </el-col>
-                <el-col :md="12" :xs="24">
-                    <el-form-item label="数据模式" prop="checkList">
-                        <el-checkbox-group v-model="form.checkList">
-                            <el-checkbox label="模版项目" value="template" />
-                            <el-checkbox label="选择项目" value="select" />
-                        </el-checkbox-group>
-                    </el-form-item>
-                </el-col>
-            </el-row>
-
-            <template v-if="form.checkList.includes('select')">
-                <data-table ref="tableRef" :apiKey="form.apiKey" checked :options="tableOptions"></data-table>
-            </template>
-        </el-form>
-
-        <template #footer>
-            <el-button :loading="isSaving" type="primary" auto-insert-space @click="submit">保存</el-button>
-            <el-button auto-insert-space @click="dialog = false">取消</el-button>
-        </template>
-    </el-dialog>
-</template>
-
-<script setup>
-import moment from "moment";
-import XEUtils from "xe-utils";
-import dataTable from "./components/table";
-
-const $emit = defineEmits(["success", "closed"]);
-const visible = ref(false);
-const isSaving = ref(false);
-
-const shortcuts = [
-    { text: "上周", value: () => [moment().subtract(1, "week"), moment()] },
-    { text: "上月", value: () => [moment().subtract(1, "month"), moment()] },
-    { text: "去年", value: () => [moment().subtract(1, "year"), moment()] }
-]
-
-const form = ref({
-    id: null,
-    dateRange: null,
-    timeStepType: "minute",
-    timeStep: 3,
-    precision: null,
-    handler: "copy",
-    checkList: ["select"],
-    apiKey: "temp"
-});
-
-const rules = reactive({
-    dateRange: [{ required: true, message: "请选择时间范围" }],
-    timeStep: [{ required: true, message: "请输入时间步长" }],
-    precision: [{ required: true, message: "请输入精度偏差" }],
-    checkList: [{ required: true, message: "请选择生成模式" }],
-    apiKey: [{ required: true }]
-})
-
-const open = () => visible.value = true;
-const formRef = ref();
-const tableRef = ref();
-
-const tableOptions = reactive({
-    height: 392,
-    checkboxConfig: {
-        highlight: true,
-        labelField: ""
-    }
-})
-
-const refreshTable = e => tableRef.value.refreshTable(e);
-
-const submit = () => {
-    formRef.value.validate(valid => {
-        if (valid) {
-        } else {
-            return false;
-        }
-    });
-}
-
-defineExpose({
-    open
-})
-</script>
-
-<style lang="scss" scoped>
-.el-form {padding-right: calc(var(--el-dialog-padding-primary) + var(--el-message-close-size, 16px));}
-
-.el-form-item .el-input-number {width: 100%;}
-.el-form-item .el-input-number :deep(.el-input__prefix) {margin-right: 8px;}
-.el-form-item .el-input-number :deep(.el-input__inner) {text-align: unset;}
-.el-form-item .el-input-number + .el-form-item {margin-left: 20px;}
-    
-.el-form-item.step-item {
-    .el-input-number {flex: 1;}
-    .el-form-item {width: 100px;}
-}
-
-.el-form-item .el-radio-group {flex-wrap: nowrap;}
-
-.el-form :deep(.el-main) {padding-right: 0;padding-bottom: 0;}
-</style>

+ 3 - 17
src/views/dataMock/carwash/index.vue

@@ -1,34 +1,20 @@
 <template>
 	<el-container class="is-vertical">
-        <sc-page-header @add="dock_add"></sc-page-header>
+        <sc-page-header></sc-page-header>
 
         <el-tabs v-model="activeName">
-            <el-tab-pane v-for="(label, key) in XEUtils.omit(workerStates, ['threshold', 'calendar'])" :key="key" :label="label" :name="key"></el-tab-pane>
-            <el-tab-pane label="模版项目" name="template"></el-tab-pane>
+            <el-tab-pane v-for="(label, key) in workerStates" :key="key" :label="label" :name="key"></el-tab-pane>
         </el-tabs>
 
         <component :is="allcomp[activeName]" />
 	</el-container>
-
-    <mock-detail v-if="dialog" ref="mockRef" @success="refreshState" @closed="dialog = false"></mock-detail>
 </template>
 
 <script setup>
-import XEUtils from "xe-utils";
-import { workerStates } from "../main";
+import { workerStates } from "./main";
 import allcomp from "./components";
-import mockDetail from "./detail";
 
 const activeName = ref("monos");
-const refreshState = () => {}
-
-const mockRef = ref();
-const dialog = ref(false);
-
-const dock_add = () => {
-    dialog.value = true;
-    nextTick(() => mockRef.value?.open());
-}
 </script>
 
 <style lang="scss" scoped>

+ 23 - 0
src/views/dataMock/carwash/main.js

@@ -0,0 +1,23 @@
+export const workerStates = {
+    monos: "任务中心",
+    info: "车辆信息",
+    record: "设备监控",
+    form: "数据模拟"
+}
+
+export const carWashDic = {
+    carType: ["未知", "大型客车", "货车", "轿车", "面包车", "小货车", "行人", "二轮车", "三轮车", "SUV/MPV", "中型客车", "机动车", "非机动车", "小型轿车", "微型轿车", "皮卡车"],
+    carColor: ["未知", "白色", "银色", "灰色", "黑色", "红色", "深蓝色", "蓝色", "黄色", "绿色", "棕色", "粉色", "紫色", "深灰色", "青色", "橙色"],
+    plateColor: [null, "蓝色", "黄色", "黑色", "白色", "绿色"],
+    dataSources: ["三方系统推送", "其他"],
+    isSupervise: [{ label: "使用中", value: true }, { label: "已拆除", value: false }],
+    alarmType: [null, "未冲洗", "冲洗不足", "冲洗完成", "绕道未冲洗", "其他"]
+}
+
+export const taskStateDic = {
+    inactive: "等待执行",
+    active: "执行中",
+    success: "已完成",
+    cancel: "已取消",
+    fail: "执行失败"
+}

+ 0 - 47
src/views/dataMock/carwash/mono/index.vue

@@ -1,47 +0,0 @@
-<template>
-    <el-dialog v-model="visible" title="任务详情" width="80%" @closed="$emit('closed')">
-        <!-- <scTable :apiObj="formConfig.data.deviceCode && $API.passqrcode.online" min-height="108" max-height="600" framework="zeroLite" :formConfig="formConfig" :paramsColums="paramsColums" :columns="columns"></scTable> -->
-        <scTable :maxHeight="600" :options="options">
-            <template #expand_content="{ row }">
-                <table-expand :rowData="row.monos"></table-expand>
-            </template>
-            <template #progress_content="{ row }">
-                <el-progress text-inside :stroke-width="16" :status="row.status == '已完成' ? 'success' : row.status == '执行失败' ? 'exception' : ''" :percentage="row.progress" />
-            </template>
-        </scTable>
-    </el-dialog>
-</template>
-
-<script setup>
-import XEUtils from "xe-utils";
-import TOOL from "@/utils/tool";
-import { defaultGroups } from "../../main";
-
-import tableExpand from "./tableExpand";
-
-const visible = ref(false);
-const open = data => {
-    visible.value = true;
-    options.data = data ? [XEUtils.first(XEUtils.get(defaultGroups, "groups.000.monos"))] : XEUtils.get(defaultGroups, "groups.000.monos")
-}
-
-const options = reactive({
-    minHeight: 108,
-    data: [],
-    formConfig: { enabled: false },
-    pagerConfig: { enabled: false },
-    columns: [
-        { type: "expand", fixed: "left", width: 50, align: "center", slots: { content: "expand_content" } },
-        { field: "option", title: "任务类型", minWidth: 100 },
-        { field: "createTime", title: "触发时间", minWidth: 160, formatter: ({ cellValue }) => TOOL.dateFormat(cellValue) },
-        { field: "status", title: "任务状态", minWidth: 100 },
-        { field: "executeTimes", title: "执行次数", minWidth: 80 },
-        { field: "progress", title: "执行进度", minWidth: 250, slots: { default: 'progress_content' } },
-        { field: "reason", title: "失败原因", minWidth: 160 }
-    ]
-})
-
-defineExpose({
-    open
-})
-</script>

+ 0 - 33
src/views/dataMock/carwash/mono/tableExpand.vue

@@ -1,33 +0,0 @@
-<template>
-    <scTable :maxHeight="192" :options="options">
-        <template #progress_content="{ row }">
-            <el-progress text-inside :stroke-width="16" :status="row.status == '已完成' ? 'success' : row.status == '执行失败' ? 'exception' : ''" :percentage="row.progress" />
-        </template>
-    </scTable>
-</template>
-
-<script setup>
-import TOOL from "@/utils/tool";
-
-const options = reactive({
-    minHeight: 108,
-    data: useAttrs().rowData,
-    formConfig: { enabled: false },
-    pagerConfig: { enabled: false },
-    columns: [
-        { field: "dateRange", title: "时间范围", minWidth: 160 },
-        { field: "timeStep", title: "时间步长", minWidth: 100 },
-        { field: "precision", title: "精度偏差", minWidth: 100 },
-        { field: "handler", title: "数据处理", minWidth: 80 },
-        { field: "createTime", title: "触发时间", minWidth: 160, formatter: ({ cellValue }) => TOOL.dateFormat(cellValue) },
-        { field: "status", title: "任务状态", minWidth: 100 },
-        { field: "executeTimes", title: "执行次数", minWidth: 80 },
-        { field: "progress", title: "执行进度", minWidth: 250, slots: { default: 'progress_content' } },
-        { field: "reason", title: "失败原因", minWidth: 160 }
-    ]
-})
-</script>
-
-<style scoped>
-    .el-main {padding: 0;}
-</style>

+ 1 - 1
src/views/dataMock/env/detail.vue

@@ -63,7 +63,7 @@
 
         <template #footer>
             <el-button :loading="isSaving" type="primary" auto-insert-space @click="submit">保存</el-button>
-            <el-button auto-insert-space @click="dialog = false">取消</el-button>
+            <el-button auto-insert-space @click="visible = false">取消</el-button>
         </template>
     </el-dialog>
 </template>

+ 1 - 1
src/views/dataMock/standard/detail.vue

@@ -62,7 +62,7 @@
 
         <template #footer>
             <el-button :loading="isSaving" type="primary" auto-insert-space @click="submit">保存</el-button>
-            <el-button auto-insert-space @click="dialog = false">取消</el-button>
+            <el-button auto-insert-space @click="visible = false">取消</el-button>
         </template>
     </el-dialog>
 </template>

+ 1 - 1
src/views/dataMock/ugliAi/detail.vue

@@ -56,7 +56,7 @@
 
         <template #footer>
             <el-button :loading="isSaving" type="primary" auto-insert-space @click="submit">保存</el-button>
-            <el-button auto-insert-space @click="dialog = false">取消</el-button>
+            <el-button auto-insert-space @click="visible = false">取消</el-button>
         </template>
     </el-dialog>
 </template>

+ 0 - 92
src/views/env/monitor/index.vue

@@ -1,92 +0,0 @@
-<template>
-	<el-container class="is-vertical">
-        <sc-page-header @filter="toggleFormEnabled"></sc-page-header>
-
-        <scTable ref="xGridTable" :apiObj="$API.env.records" framework="zeroLiteOld" :toolbarConfig="toolbarConfig" :formConfig="formConfig" :paramsColums="paramsColums" :columns="columns"></scTable>
-	</el-container>
-</template>
-
-<script setup>
-import moment from "moment";
-import XEUtils from "xe-utils";
-import TOOL from "@/utils/tool";
-import { envWarningDic } from "@/utils/basicDic";
-import { mapFormItemSelect, mapFormItemDatePicker } from "@/components/scTable/helper";
-
-const formatGate = row => XEUtils.find(TOOL.data.get("ENV_GATE"), item => item.id == XEUtils.get(row, "mounted.id"));
-const formatWarn = ({ row, $rowIndex, column }) => {
-    const max = XEUtils.isEmpty(XEUtils.get(formatGate(row), `features.max${column.field}`)) && XEUtils.get(envWarningDic, `max${column.field}`) || XEUtils.get(formatGate(row), `features.max${column.field}`);
-    return XEUtils.divide(XEUtils.get(row, column.field.toLowerCase()), 10000) > max ? "tower-warning-column" : ""
-}
-
-const mountedConfig = reactive({
-    storageKey: "ENV_GATE",
-    slot: {
-        style: { float: "right", paddingLeft: "6px", color: "#8492a6" }
-    },
-    props: { multiple: true, collapseTags: true, collapseTagsTooltip: true },
-    optionProps: { label: "mountedName", value: "id", slot: ({ data }) => XEUtils.get(XEUtils.find(TOOL.data.get("PROJECT"), item => item.fpiId === XEUtils.get(XEUtils.first(data.devices), "project_id")), "projectName") },
-    events: {
-        change: data => XEUtils.assign(formConfig.data, data)
-    }
-})
-
-const daterangeConfig = reactive({
-    span: 9,
-    resetValue: [moment().startOf("day").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")],
-    props: {
-        type: "datetimerange",
-        clearable: false,
-        startPlaceholder: "开始时间",
-        endPlaceholder: "结束时间",
-        format: "YYYY-MM-DD HH:mm"
-    }
-})
-
-const toolbarConfig = reactive({
-    enabled: true,
-    print: false
-})
-
-const formConfig = reactive({
-    data: {
-        createTime: [moment().subtract(30, "minute").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")]
-        // createTime: [moment().startOf("day").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")]
-    },
-    items: [
-        mapFormItemSelect("mountedID", "设备安装点", mountedConfig),
-        mapFormItemDatePicker("createTime", "监测时间", daterangeConfig)
-    ]
-})
-
-const paramsColums = reactive([
-    { type: "relation", column: "mountedID", symbol: "or" },
-    { type: "relation", column: "createTime", symbol: "eqgt", field: "createTime[0]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "relation", column: "createTime", symbol: "eqlt", field: "createTime[1]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "orderby", column: "createTime", symbol: "desc" }
-]);
-
-const columns = reactive([
-    { type: "seq", fixed: "left", width: 60 },
-    { type: "html", field: "device.device_num", title: "设备唯一标识", fixed: "left", minWidth: 150, sortable: true },
-    { type: "html", field: "projectName", title: "所属项目", minWidth: 200, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(XEUtils.find(TOOL.data.get("PROJECT"), item => item.fpiId === XEUtils.get(row, "device.project_id")), "projectName") },
-    { type: "html", field: "mounted.ground.groundName", title: "工地场区", minWidth: 150, sortable: true },
-    { type: "html", field: "mounted.mountedName", title: "安装点名称", minWidth: 150, sortable: true },
-    { type: "html", field: "taskTime", title: "监测时间", minWidth: 160, sortable: true, formatter: ({ cellValue, row }) => cellValue || TOOL.dateFormat(row.createTime) },
-    { className: formatWarn, type: "html", field: "PM2_5", title: "pm2.5", minWidth: 110, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.divide(XEUtils.get(row, "pm2_5"), 10000) + "μg/m³" },
-    { className: formatWarn, type: "html", field: "PM10", title: "pm10", minWidth: 110, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.divide(XEUtils.get(row, "pm10"), 10000) + "μg/m³" },
-    { className: formatWarn, type: "html", field: "PM100", title: "pm100", minWidth: 110, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.divide(XEUtils.get(row, "pm100"), 10000) + "μg/m³" },
-    { type: "html", field: "windPower", title: "风力", minWidth: 100, sortable: true, formatter: ({ cellValue }) => (XEUtils.isNumber(cellValue) && XEUtils.divide(cellValue, 10000) + "级") || cellValue },
-    { type: "html", field: "windSpeed", title: "风速", minWidth: 100, sortable: true, formatter: ({ cellValue }) => (XEUtils.isNumber(cellValue) && XEUtils.divide(cellValue, 10000) + "m/s") || cellValue },
-    { className: formatWarn, type: "html", field: "noise", title: "噪声", minWidth: 100, sortable: true, formatter: ({ cellValue }) => (XEUtils.isNumber(cellValue) && XEUtils.divide(cellValue, 10000) + "dB") || cellValue },
-    { className: formatWarn, type: "html", field: "Temperature", title: "温度", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.divide(XEUtils.get(row, "temperature"), 10000) + "℃" },
-    { className: formatWarn, type: "html", field: "Humidity", title: "湿度", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.divide(XEUtils.get(row, "humidity"), 10000) + "%" }
-])
-
-// 显示隐藏 筛选表单
-const xGridTable = ref();
-const toggleFormEnabled = () => xGridTable.value.toggleFormEnabled();
-
-onMounted(() => window.addEventListener("setItemEvent", ({ key, newValue }) => key === "ENV_GATE" && newValue && xGridTable.value?.reloadColumn(columns)));
-onUnmounted(() => window.removeEventListener("setItemEvent", () => {}));
-</script>

src/views/env/device/index.vue → src/views/equipment/env.vue


+ 5 - 47
src/views/facerec/device/index.vue

@@ -6,26 +6,8 @@
             </template>
         </sc-page-header>
 
-        <scTable ref="xGridTable" :apiObj="$API.system.device" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns">
-            <template #action="{ row }">
-                <el-button type="primary" link @click="table_record(row)">
-                    <template #icon><sc-iconify icon="lets-icons:sort-list"></sc-iconify></template>上线/离线记录
-                </el-button>
-
-                <el-button type="primary" link @click="table_fluxs(row)">
-                    <template #icon><sc-iconify icon="ant-design:unordered-list-outlined"></sc-iconify></template>Fluxs任务
-                </el-button>
-
-                 <el-button type="primary" link @click="table_groups(row)">
-                    <template #icon><sc-iconify icon="mdi:format-list-group"></sc-iconify></template>Groups任务
-                </el-button>
-            </template>
-        </scTable>
+        <scTable ref="xGridTable" :apiObj="$API.system.device" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns"></scTable>
 	</el-container>
-
-    <log-detail v-if="dialogLog" ref="LogDetail" @closed="dialogLog = false"></log-detail>
-    <fluxs-detail v-if="dialogFluxs" ref="FluxsDetail" @closed="dialogFluxs = false"></fluxs-detail>
-    <groups-detail v-if="dialogGroups" ref="GroupsDetail" @closed="dialogGroups = false"></groups-detail>
 </template>
 
 <script setup>
@@ -35,9 +17,6 @@ import API from "@/api";
 import TOOL from "@/utils/tool";
 import { gateTypeDic, deviceStateDic } from "@/utils/basicDic";
 import { mapFormItemInput, mapFormItemSelect } from "@/components/scTable/helper";
-import logDetail from "@/views/facerec/device/logDetail";
-import fluxsDetail from "@/views/facerec/device/fluxs";
-import groupsDetail from "@/views/facerec/device/groups";
 
 const deviceState = ref({});
 const refreshTime = ref(moment().add(20, "second").valueOf());
@@ -84,7 +63,7 @@ const columns = reactive([
     { type: "html", field: "gateName", title: "闸口名称", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatGate(row), "gateName") },
     { type: "html", field: "gateType", title: "闸口类型", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(gateTypeDic, XEUtils.get(formatGate(row), "gateType")) },
     { field: "state", title: "在线状态", minWidth: 100, align: "center", formatter: ({ row }) => XEUtils.get(deviceStateDic, XEUtils.get(deviceState.value, row.deviceNum), "离线"), editRender: { name: "$cell-tag" } },
-    { title: "操作", fixed: "right", minWidth: 310, slots: { default: "action" } }
+    // { title: "操作", fixed: "right", minWidth: 310, slots: { default: "action" } }
 ])
 
 // 显示隐藏 筛选表单
@@ -92,8 +71,8 @@ const xGridTable = ref();
 const toggleFormEnabled = () => xGridTable.value.toggleFormEnabled();
 
 
-const tableLength = computed(() => xGridTable.value?.getTableData().fullData.length);
-watch(tableLength, value => value > 0 && getDeviceState());
+// const tableLength = computed(() => xGridTable.value?.getTableData().fullData.length);
+// watch(tableLength, value => value > 0 && getDeviceState());
 
 onMounted(() => window.addEventListener("setItemEvent", ({ key, newValue }) => key == "FACEREC_GATE" && newValue && xGridTable.value?.reloadColumn(columns)));
 onUnmounted(() => window.removeEventListener("setItemEvent", () => {}));
@@ -127,27 +106,6 @@ onUnmounted(() => {
 
 const refreshState = () => {
     if (moment().diff(refreshTime.value, "second") < 0) return ElMessage.warning("请勿重复刷新!(间隔时间至少20秒)");
-    getDeviceState();
-}
-
-const dialogLog = ref(false);
-const dialogFluxs = ref(false);
-const dialogGroups = ref(false);
-const LogDetail = ref();
-const FluxsDetail = ref();
-const GroupsDetail = ref();
-const table_record = row => {
-    dialogLog.value = true;
-    nextTick(() => LogDetail.value.open(row.deviceNum));
-}
-
-const table_fluxs = row => {
-    dialogFluxs.value = true;
-    nextTick(() => FluxsDetail.value.open(row.deviceNum));
-}
-
-const table_groups = row => {
-    dialogGroups.value = true;
-    nextTick(() => GroupsDetail.value.open(row.deviceNum));
+    // getDeviceState();
 }
 </script>

+ 5 - 61
src/views/passqrcode/device/index.vue

@@ -6,31 +6,8 @@
             </template>
         </sc-page-header>
 
-        <scTable ref="xGridTable" :apiObj="$API.system.device" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns">
-            <template #action="{ row }">
-                <el-button type="primary" link @click="table_state(row)">
-                    <template #icon><sc-iconify icon="ant-design:unordered-list-outlined"></sc-iconify></template>设备状态
-                </el-button>
-
-                <el-button type="primary" link @click="table_record(row)">
-                    <template #icon><sc-iconify icon="lets-icons:sort-list"></sc-iconify></template>上线/离线记录
-                </el-button>
-
-                <el-button type="primary" link @click="table_fluxs(row)">
-                    <template #icon><sc-iconify icon="ant-design:unordered-list-outlined"></sc-iconify></template>Fluxs任务
-                </el-button>
-
-                 <el-button type="primary" link @click="table_groups(row)">
-                    <template #icon><sc-iconify icon="mdi:format-list-group"></sc-iconify></template>Groups任务
-                </el-button>
-            </template>
-        </scTable>
+        <scTable ref="xGridTable" :apiObj="$API.system.device" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns"></scTable>
 	</el-container>
-
-    <state-dev v-if="dialogState" ref="StateDev" @closed="dialogState = false"></state-dev>
-    <log-detail v-if="dialogLog" ref="LogDetail" @closed="dialogLog = false"></log-detail>
-    <fluxs-detail v-if="dialogFluxs" ref="FluxsDetail" @closed="dialogFluxs = false"></fluxs-detail>
-    <groups-detail v-if="dialogGroups" ref="GroupsDetail" @closed="dialogGroups = false"></groups-detail>
 </template>
 
 <script setup>
@@ -40,10 +17,6 @@ import API from "@/api";
 import TOOL from "@/utils/tool";
 import { gateTypeDic, deviceStateDic } from "@/utils/basicDic";
 import { mapFormItemInput, mapFormItemSelect } from "@/components/scTable/helper";
-import stateDev from "@/views/passqrcode/device/stateDev";
-import logDetail from "@/views/passqrcode/device/logDetail";
-import fluxsDetail from "@/views/passqrcode/device/fluxs";
-import groupsDetail from "@/views/passqrcode/device/groups";
 
 const deviceState = ref({});
 const refreshTime = ref(moment().add(20, "second").valueOf());
@@ -90,7 +63,7 @@ const columns = reactive([
     { type: "html", field: "gateName", title: "闸口名称", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatGate(row), "gateName") },
     { type: "html", field: "gateType", title: "闸口类型", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(gateTypeDic, XEUtils.get(formatGate(row), "gateType")) },
     { field: "state", title: "在线状态", minWidth: 100, align: "center", formatter: ({ row }) => XEUtils.get(deviceStateDic, XEUtils.get(deviceState.value, row.deviceNum), "离线"), editRender: { name: "$cell-tag" } },
-    { title: "操作", fixed: "right", minWidth: 400, slots: { default: "action" } }
+    // { title: "操作", fixed: "right", minWidth: 400, slots: { default: "action" } }
 ])
 
 // 显示隐藏 筛选表单
@@ -98,8 +71,8 @@ const xGridTable = ref();
 const toggleFormEnabled = () => xGridTable.value.toggleFormEnabled();
 
 
-const tableLength = computed(() => xGridTable.value?.getTableData().fullData.length);
-watch(tableLength, value => value > 0 && getDeviceState());
+// const tableLength = computed(() => xGridTable.value?.getTableData().fullData.length);
+// watch(tableLength, value => value > 0 && getDeviceState());
 
 onMounted(() => window.addEventListener("setItemEvent", ({ key, newValue }) => key == "PASSQRCODE_GATE" && newValue && xGridTable.value?.reloadColumn(columns)));
 onUnmounted(() => window.removeEventListener("setItemEvent", () => {}));
@@ -133,35 +106,6 @@ onUnmounted(() => {
 
 const refreshState = () => {
     if (moment().diff(refreshTime.value, "second") < 0) return ElMessage.warning("请勿重复刷新!(间隔时间至少20秒)");
-    getDeviceState();
-}
-
-const dialogState = ref(false);
-const dialogLog = ref(false);
-const dialogFluxs = ref(false);
-const dialogGroups = ref(false);
-const StateDev = ref();
-const LogDetail = ref();
-const FluxsDetail = ref();
-const GroupsDetail = ref();
-
-const table_state = row => {
-    dialogState.value = true;
-    nextTick(() => StateDev.value.open(row.deviceNum));
-}
-
-const table_record = row => {
-    dialogLog.value = true;
-    nextTick(() => LogDetail.value.open(row.deviceNum));
-}
-
-const table_fluxs = row => {
-    dialogFluxs.value = true;
-    nextTick(() => FluxsDetail.value.open(row.deviceNum));
-}
-
-const table_groups = row => {
-    dialogGroups.value = true;
-    nextTick(() => GroupsDetail.value.open(row.deviceNum));
+    // getDeviceState();
 }
 </script>

src/views/tower/device/index.vue → src/views/equipment/tower.vue


+ 0 - 96
src/views/facerec/device/fluxs.vue

@@ -1,96 +0,0 @@
-<template>
-    <el-dialog v-model="visible" :title="`${formConfig.data.deviceCode} - Fluxs任务`" width="80%" @closed="$emit('closed')">
-        <scTable :apiObj="formConfig.data.deviceCode && $API.facerec.monohistory" min-height="108" max-height="600" framework="zeroLite" :formConfig="formConfig" :paramsColums="paramsColums" :columns="columns"></scTable>
-    </el-dialog>
-</template>
-
-<script setup>
-import moment from "moment";
-import XEUtils from "xe-utils";
-import TOOL from "@/utils/tool";
-import { monoStatusDic, monoOptionDic, objectToArray } from "@/utils/basicDic";
-import { mapFormItemDatePicker, mapFormItemInput, mapFormItemSelect } from "@/components/scTable/helper";
-
-const visible = ref(false);
-const open = deviceCode => {
-    visible.value = true;
-    formConfig.data.deviceCode = deviceCode;
-}
-
-const monthConfig = reactive({
-    span: 3,
-    showTitle: false,
-    props: {
-        type: "month",
-        valueFormat: "YYYY-MM-01",
-        clearable: false
-    },
-    resetValue: moment().format("YYYY-MM-01"),
-    events: {
-        change: ({ field, data }) => XEUtils.merge(formConfig.data, { ...data, createTime: [moment(data.zone).startOf("month").format("YYYY-MM-DD HH:mm:ss"), moment(data.zone).endOf("month").format("YYYY-MM-DD HH:mm:ss")] })
-    }
-})
-
-const daterangeConfig = reactive({
-    span: 9,
-    showTitle: false,
-    resetValue: [moment().startOf("month").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")],
-    props: {
-        type: "datetimerange",
-        startPlaceholder: "开始时间",
-        endPlaceholder: "结束时间",
-        format: "YYYY-MM-DD HH:mm",
-        disabledDate: date => moment(date).diff(moment(formConfig.data.zone).endOf("month")) > 0 || moment(formConfig.data.zone).startOf("month").diff(moment(date)) > 0
-    }
-})
-
-const selectConfig = reactive({
-    showTitle: false,
-    options: objectToArray(monoOptionDic.facerec),
-    events: {
-        change: data => XEUtils.merge(formConfig.data, data)
-    }
-})
-
-const formConfig = reactive({
-    data: {
-        deviceCode: null,
-        zone: moment().format("YYYY-MM-01"),
-        createTime: [moment().startOf("month").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")]
-    },
-    items: [
-        mapFormItemDatePicker("zone", "", monthConfig),
-        mapFormItemDatePicker("createTime", "", daterangeConfig),
-        mapFormItemInput("fname", "人员名称", { showTitle: false }),
-        mapFormItemInput("idCard", "身份证", { showTitle: false }),
-        mapFormItemSelect("option", "任务类型", selectConfig),
-        mapFormItemSelect("status", "任务状态", { ...selectConfig, options: objectToArray(monoStatusDic) })
-    ]
-})
-
-const paramsColums = reactive([
-    { type: "relation", column: "deviceCode", symbol: "eq" },
-    { type: "relation", column: "fname", symbol: "like", formatValue: value => `%${value}%` },
-    { type: "relation", column: "idCard", symbol: "like", formatValue: value => `%${value}%` },
-    { type: "relation", column: "option", symbol: "eq" },
-    { type: "relation", column: "status", symbol: "eq" },
-    { type: "relation", column: "createTime", symbol: "eqgt", field: "createTime[0]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "relation", column: "createTime", symbol: "eqlt", field: "createTime[1]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "orderby", column: "createTime", symbol: "desc" },
-    { type: "expands", column: "zone" }
-]);
-
-const columns = reactive([
-    { field: "fname", title: "人员姓名", minWidth: 120 },
-    { field: "idCard", title: "身份证", minWidth: 160 },
-    { field: "option", title: "任务类型", minWidth: 120, formatter: ({ cellValue }) => XEUtils.get(monoOptionDic.facerec, cellValue, cellValue) },
-    { field: "createTime", title: "触发时间", minWidth: 160, formatter: ({ cellValue }) => TOOL.dateFormat(cellValue) },
-    { field: "status", title: "任务状态", minWidth: 100, align: "center", formatter: ({ cellValue }) => XEUtils.get(monoStatusDic, cellValue, cellValue), editRender: { name: "$cell-tag" } },
-    { field: "executeTimes", title: "执行次数", minWidth: 100 },
-    { field: "reason", title: "失败原因", minWidth: 250, editRender: { name: "$cell-tag", props: { effect: "dark", type: "custom", color: "#f50" } } }
-])
-
-defineExpose({
-    open
-})
-</script>

+ 0 - 98
src/views/facerec/device/groups/index.vue

@@ -1,98 +0,0 @@
-<template>
-    <el-dialog v-model="visible" :title="`${formConfig.data.deviceCode} - Groups任务`" width="80%" @closed="$emit('closed')">
-        <scTable :apiObj="formConfig.data.deviceCode && $API.facerec.grouphistory" min-height="108" max-height="600" framework="zeroLite" :formConfig="formConfig" :paramsColums="paramsColums" :columns="columns">
-            <template #expand_content="{ row }">
-                <table-expand :rowData="row.monos"></table-expand>
-            </template>
-        </scTable>
-    </el-dialog>
-</template>
-
-<script setup>
-import moment from "moment";
-import XEUtils from "xe-utils";
-import TOOL from "@/utils/tool";
-import { monogroupOptionDic, monogroupStatusDic, objectToArray } from "@/utils/basicDic";
-import { mapFormItemDatePicker, mapFormItemInput, mapFormItemSelect } from "@/components/scTable/helper";
-import tableExpand from "./tableExpand";
-
-const visible = ref(false);
-const open = deviceCode => {
-    visible.value = true;
-    formConfig.data.deviceCode = deviceCode;
-}
-
-const monthConfig = reactive({
-    span: 3,
-    showTitle: false,
-    props: {
-        type: "month",
-        valueFormat: "YYYY-MM-01",
-        clearable: false
-    },
-    resetValue: moment().format("YYYY-MM-01"),
-    events: {
-        change: ({ field, data }) => XEUtils.merge(formConfig.data, { ...data, createTime: [moment(data.zone).startOf("month").format("YYYY-MM-DD HH:mm:ss"), moment(data.zone).endOf("month").format("YYYY-MM-DD HH:mm:ss")] })
-    }
-})
-
-const daterangeConfig = reactive({
-    span: 9,
-    showTitle: false,
-    resetValue: [moment().startOf("month").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")],
-    props: {
-        type: "datetimerange",
-        startPlaceholder: "开始时间",
-        endPlaceholder: "结束时间",
-        format: "YYYY-MM-DD HH:mm",
-        disabledDate: date => moment(date).diff(moment(formConfig.data.zone).endOf("month")) > 0 || moment(formConfig.data.zone).startOf("month").diff(moment(date)) > 0
-    }
-})
-
-const selectConfig = reactive({
-    showTitle: false,
-    options: objectToArray(monogroupOptionDic.facerec),
-    events: {
-        change: data => XEUtils.merge(formConfig.data, data)
-    }
-})
-
-const formConfig = reactive({
-    data: {
-        deviceCode: null,
-        options: "all",
-        zone: moment().format("YYYY-MM-01"),
-        createTime: [moment().startOf("month").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")]
-    },
-    items: [
-        mapFormItemDatePicker("zone", "", monthConfig),
-        mapFormItemDatePicker("createTime", "", daterangeConfig),
-        mapFormItemSelect("option", "任务类型", selectConfig),
-        mapFormItemSelect("status", "任务状态", { ...selectConfig, options: objectToArray(monogroupStatusDic) })
-    ]
-})
-
-const paramsColums = reactive([
-    { type: "relation", column: "deviceCode", symbol: "eq" },
-    { type: "relation", column: "option", symbol: "eq" },
-    { type: "relation", column: "status", symbol: "eq" },
-    { type: "relation", column: "createTime", symbol: "eqgt", field: "createTime[0]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "relation", column: "createTime", symbol: "eqlt", field: "createTime[1]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "orderby", column: "createTime", symbol: "desc" },
-    { type: "expands", column: "zone" },
-    { type: "expands", column: "options" }
-]);
-
-const columns = reactive([
-    { type: "expand", fixed: "left", width: 50, align: "center", slots: { content: "expand_content" } },
-    { field: "option", title: "任务类型", minWidth: 150, formatter: ({ cellValue }) => XEUtils.get(monogroupOptionDic.facerec, cellValue, cellValue) },
-    { field: "monoLen", title: "任务总数", minWidth: 100 },
-    { field: "createTime", title: "触发时间", minWidth: 160, formatter: ({ cellValue }) => TOOL.dateFormat(cellValue) },
-    { field: "status", title: "任务状态", minWidth: 100, align: "center", formatter: ({ cellValue }) => XEUtils.get(monogroupStatusDic, cellValue, cellValue), editRender: { name: "$cell-tag" } },
-    { field: "reason", title: "失败原因", minWidth: 250, editRender: { name: "$cell-tag", props: { effect: "dark", type: "custom", color: "#f50" } } },
-])
-
-defineExpose({
-    open
-})
-</script>

+ 0 - 31
src/views/facerec/device/groups/tableExpand.vue

@@ -1,31 +0,0 @@
-<template>
-    <scTable :options="options" min-height="108" maxHeight="600">
-        <template #top>
-            <el-divider content-position="left">任务清单</el-divider>
-        </template>
-    </scTable>
-</template>
-
-<script setup>
-import XEUtils from "xe-utils";
-import { monoOptionDic } from "@/utils/basicDic";
-
-const options = reactive({
-    layouts: [["Top", "Table"]],
-    data: useAttrs().rowData,
-    formConfig: { enabled: false },
-    pagerConfig: { enabled: false },
-    columns: [
-        { field: "fname", title: "人员姓名", minWidth: 120 },
-        { field: "idCard", title: "身份证", minWidth: 160 },
-        { field: "option", title: "任务类型", minWidth: 120, formatter: ({ cellValue }) => XEUtils.get(monoOptionDic.facerec, cellValue, cellValue) },
-        { field: "features.reason", title: "失败原因", minWidth: 250, editRender: { name: "$cell-tag", props: { effect: "dark", type: "custom", color: "#f50" } } }
-    ]
-})
-</script>
-
-<style scoped>
-    .el-main {padding: 0;}
-    .el-divider {margin-top: 12px;}
-    .el-divider :deep(.el-divider__text) {left: 0;padding-left: 12px;}
-</style>

+ 0 - 57
src/views/facerec/device/logDetail.vue

@@ -1,57 +0,0 @@
-<template>
-    <el-dialog v-model="visible" :title="`${formConfig.data.deviceCode} - 上线/离线记录`" width="80%" @closed="$emit('closed')">
-        <scTable :apiObj="formConfig.data.deviceCode && $API.facerec.online" min-height="108" max-height="600" framework="zeroLiteOld" :formConfig="formConfig" :paramsColums="paramsColums" :columns="columns"></scTable>
-    </el-dialog>
-</template>
-
-<script setup>
-import moment from "moment";
-import XEUtils from "xe-utils";
-import TOOL from "@/utils/tool";
-import { deviceRecordStateDic } from "@/utils/basicDic";
-import { mapFormItemDatePicker } from "@/components/scTable/helper";
-
-const visible = ref(false);
-const open = deviceCode => {
-    visible.value = true;
-    formConfig.data.deviceCode = deviceCode;
-}
-
-const daterangeConfig = reactive({
-    span: 9,
-    showTitle: false,
-    resetValue: [moment().startOf("day").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")],
-    props: {
-        type: "datetimerange",
-        startPlaceholder: "开始时间",
-        endPlaceholder: "结束时间",
-        format: "YYYY-MM-DD HH:mm"
-    }
-})
-
-const formConfig = reactive({
-    data: {
-        deviceCode: null,
-        createTime: [moment().startOf("day").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")]
-    },
-    items: [
-        mapFormItemDatePicker("createTime", "", daterangeConfig)
-    ]
-})
-
-const paramsColums = reactive([
-    { type: "relation", column: "deviceCode", symbol: "eq" },
-    { type: "relation", column: "createTime", symbol: "eqgt", field: "createTime[0]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "relation", column: "createTime", symbol: "eqlt", field: "createTime[1]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "orderby", column: "createTime", symbol: "desc" }
-]);
-
-const columns = reactive([
-    { field: "status", title: "上线/离线", minWidth: 100, align: "center", formatter: ({ cellValue }) => XEUtils.get(deviceRecordStateDic, cellValue, cellValue) },
-    { field: "createTime", title: "触发时间", minWidth: 160, align: "center", formatter: ({ cellValue }) => TOOL.dateFormat(cellValue) }
-])
-
-defineExpose({
-    open
-})
-</script>

+ 0 - 116
src/views/facerec/platform/push/index.vue

@@ -1,116 +0,0 @@
-<template>
-	<el-container class="is-vertical">
-        <sc-page-header @filter="toggleFormEnabled"></sc-page-header>
-
-        <scTable ref="xGridTable" :apiObj="$API.facerec.notify" framework="zeroLiteOld" :toolbarConfig="toolbarConfig" :formConfig="formConfig" :paramsColums="paramsColums" :columns="columns">
-            <template #action="{ row }">
-                <el-button type="primary" link @click="table_origin(row)">
-                    <template #icon><sc-iconify icon="lucide:file-json"></sc-iconify></template>查看原始数据
-                </el-button>
-            </template>
-        </scTable>
-	</el-container>
-
-    <json-model v-if="dialog" ref="JsonModel" @closed="dialog = false"></json-model>
-</template>
-
-<script setup>
-import moment from "moment";
-import XEUtils from "xe-utils";
-import TOOL from "@/utils/tool";
-import { gateTypeDic, platformTypeDic, platformStatusDic, objectToArray } from "@/utils/basicDic";
-import { mapFormItemInput, mapFormItemSelect, mapFormItemDatePicker } from "@/components/scTable/helper";
-import jsonModel from "@/views/facerec/platform/task/json";
-
-const formatGate = ({ deviceCode }) => XEUtils.find(TOOL.data.get("FACEREC_GATE"), item => XEUtils.findIndexOf(item.devices, d => d.device_num === deviceCode) !== -1);
-
-const deviceConfig = reactive({
-    api: {
-        key: "system.device",
-        query: { current:1, size: 999999, categoryId: 1, modelId: 1 }
-    },
-    slot: {
-        style: { float: "right", paddingLeft: "6px", color: "#8492a6" }
-    },
-    props: { multiple: true, collapseTags: true, collapseTagsTooltip: true },
-    optionProps: { label: "deviceNum", value: "deviceNum", slot: ({ data }) => XEUtils.get(XEUtils.find(TOOL.data.get("PROJECT"), item => item.fpiId === data.projectId), "projectName") },
-    events: {
-        change: data => XEUtils.assign(formConfig.data, data)
-    }
-})
-
-const selectConfig = reactive({
-    options: objectToArray(platformTypeDic),
-    events: {
-        change: data => XEUtils.merge(formConfig.data, data)
-    }
-})
-
-const daterangeConfig = reactive({
-    span: 9,
-    resetValue: [moment().startOf("day").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")],
-    props: {
-        type: "datetimerange",
-        startPlaceholder: "开始时间",
-        endPlaceholder: "结束时间",
-        format: "YYYY-MM-DD HH:mm"
-    }
-})
-
-const toolbarConfig = reactive({
-    enabled: true,
-    print: false
-})
-
-const formConfig = reactive({
-    data: {
-        createTime: [moment().startOf("day").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")]
-    },
-    items: [
-        mapFormItemSelect("deviceCode", "设备唯一标识", deviceConfig),
-        mapFormItemInput("idCard", "身份证"),
-        mapFormItemSelect("type", "三方平台类型", selectConfig),
-        mapFormItemSelect("status", "任务状态", { ...selectConfig, options: objectToArray(platformStatusDic) }),
-        mapFormItemDatePicker("createTime", "触发时间", daterangeConfig)
-    ]
-})
-
-const paramsColums = reactive([
-    { type: "relation", column: "deviceCode", symbol: "or" },
-    { type: "relation", column: "idCard", symbol: "like" },
-    { type: "relation", column: "type", symbol: "eq" },
-    { type: "relation", column: "status", symbol: "eq" },
-    { type: "relation", column: "createTime", symbol: "eqgt", field: "createTime[0]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "relation", column: "createTime", symbol: "eqlt", field: "createTime[1]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "orderby", column: "createTime", symbol: "desc" }
-]);
-
-const columns = reactive([
-    { type: "seq", fixed: "left", width: 60 },
-    { type: "html", field: "deviceCode", title: "设备唯一标识", fixed: "left", minWidth: 150, sortable: true },
-    { type: "html", field: "projectName", title: "所属项目", minWidth: 200, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(XEUtils.find(TOOL.data.get("PROJECT"), item => item.fpiId === XEUtils.get(XEUtils.get(formatGate(row), "devices", []).find(d => d.device_num === row.deviceCode), "project_id")), "projectName") },
-    { type: "html", field: "groundName", title: "工地场区", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatGate(row), "ground.groundName") },
-    { type: "html", field: "gateName", title: "闸口名称", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatGate(row), "gateName") },
-    { type: "html", field: "gateType", title: "闸口类型", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(gateTypeDic, XEUtils.get(formatGate(row), "gateType")) },
-    { type: "html", field: "fname", title: "人员姓名", minWidth: 120, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(row, "features.request.data.UserName", "/") },
-    { type: "html", field: "idcard", title: "身份证号", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || row.idCard && row.idCard.length === 18 ? row.idCard : "/" },
-    { type: "html", field: "type", title: "三方平台类型", minWidth: 120, sortable: true, formatter: ({ cellValue }) => XEUtils.get(platformTypeDic, cellValue, cellValue) },
-    { type: "html", field: "taskTime", title: "触发时间", minWidth: 160, sortable: true, formatter: ({ cellValue, row }) => cellValue || TOOL.dateFormat(row.createTime) },
-    { field: "status", title: "任务状态", minWidth: 100, align: "center", formatter: ({ cellValue }) => XEUtils.get(platformStatusDic, cellValue, cellValue), editRender: { name: "$cell-tag" } },
-    { title: "操作", fixed: "right", minWidth: 130, slots: { default: "action" } }
-])
-
-// 显示隐藏 筛选表单
-const xGridTable = ref();
-const toggleFormEnabled = () => xGridTable.value.toggleFormEnabled();
-
-onMounted(() => window.addEventListener("setItemEvent", ({ key, newValue }) => key == "FACEREC_GATE" && newValue && xGridTable.value?.reloadColumn(columns)));
-onUnmounted(() => window.removeEventListener("setItemEvent", () => {}));
-
-const dialog = ref(false);
-const JsonModel = ref();
-const table_origin = row => {
-    dialog.value = true;
-    nextTick(() => JsonModel.value.open(XEUtils.get(row, "features", {})));
-}
-</script>

+ 0 - 120
src/views/facerec/platform/task/index.vue

@@ -1,120 +0,0 @@
-<template>
-	<el-container class="is-vertical">
-        <sc-page-header @filter="toggleFormEnabled"></sc-page-header>
-
-        <scTable ref="xGridTable" :apiObj="$API.facerec.cmttask" framework="zeroLiteOld" :toolbarConfig="toolbarConfig" :formConfig="formConfig" :paramsColums="paramsColums" :columns="columns">
-            <template #action="{ row }">
-                <el-button type="primary" link @click="table_origin(row)">
-                    <template #icon><sc-iconify icon="lucide:file-json"></sc-iconify></template>查看原始数据
-                </el-button>
-            </template>
-        </scTable>
-	</el-container>
-
-    <json-model v-if="dialog" ref="JsonModel" @closed="dialog = false"></json-model>
-</template>
-
-<script setup>
-import moment from "moment";
-import XEUtils from "xe-utils";
-import TOOL from "@/utils/tool";
-import { gateTypeDic, platformTypeDic, platformStatusDic, objectToArray } from "@/utils/basicDic";
-import { mapFormItemInput, mapFormItemSelect, mapFormItemDatePicker } from "@/components/scTable/helper";
-import jsonModel from "@/views/facerec/platform/task/json";
-
-const formatGate = ({ deviceCode }) => XEUtils.find(TOOL.data.get("FACEREC_GATE"), item => XEUtils.findIndexOf(item.devices, d => d.device_num === deviceCode) !== -1);
-
-const deviceConfig = reactive({
-    api: {
-        key: "system.device",
-        query: { current:1, size: 999999, categoryId: 1, modelId: 1 }
-    },
-    slot: {
-        style: { float: "right", paddingLeft: "6px", color: "#8492a6" }
-    },
-    props: { multiple: true, collapseTags: true, collapseTagsTooltip: true },
-    optionProps: { label: "deviceNum", value: "deviceNum", slot: ({ data }) => XEUtils.get(XEUtils.find(TOOL.data.get("PROJECT"), item => item.fpiId === data.projectId), "projectName") },
-    events: {
-        change: data => XEUtils.assign(formConfig.data, data)
-    }
-})
-
-const selectConfig = reactive({
-    options: objectToArray(platformTypeDic),
-    events: {
-        change: data => XEUtils.merge(formConfig.data, data)
-    }
-})
-
-const daterangeConfig = reactive({
-    span: 9,
-    resetValue: [moment().startOf("day").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")],
-    props: {
-        type: "datetimerange",
-        startPlaceholder: "开始时间",
-        endPlaceholder: "结束时间",
-        format: "YYYY-MM-DD HH:mm"
-    }
-})
-
-const toolbarConfig = reactive({
-    enabled: true,
-    print: false
-})
-
-const formConfig = reactive({
-    data: {
-        createTime: [moment().startOf("day").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")]
-    },
-    items: [
-        mapFormItemSelect("deviceCode", "设备唯一标识", deviceConfig),
-        mapFormItemInput("monoID", "任务ID"),
-        mapFormItemInput("requestID", "三方平台任务ID"),
-        mapFormItemSelect("type", "三方平台类型", selectConfig),
-        mapFormItemSelect("status", "任务状态", { ...selectConfig, options: objectToArray(platformStatusDic) }),
-        mapFormItemDatePicker("createTime", "触发时间", daterangeConfig)
-    ]
-})
-
-const paramsColums = reactive([
-    { type: "relation", column: "deviceCode", symbol: "or" },
-    { type: "relation", column: "monoID", symbol: "like" },
-    { type: "relation", column: "requestID", symbol: "like" },
-    { type: "relation", column: "type", symbol: "eq" },
-    { type: "relation", column: "status", symbol: "eq" },
-    { type: "relation", column: "createTime", symbol: "eqgt", field: "createTime[0]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "relation", column: "createTime", symbol: "eqlt", field: "createTime[1]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "orderby", column: "createTime", symbol: "desc" }
-]);
-
-const columns = reactive([
-    { type: "seq", fixed: "left", width: 60 },
-    { type: "html", field: "deviceCode", title: "设备唯一标识ttt", fixed: "left", minWidth: 150, sortable: true },
-    { type: "html", field: "projectName", title: "所属项目", minWidth: 200, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(XEUtils.find(TOOL.data.get("PROJECT"), item => item.fpiId === XEUtils.get(XEUtils.get(formatGate(row), "devices", []).find(d => d.device_num === row.deviceCode), "project_id")), "projectName") },
-    { type: "html", field: "groundName", title: "工地场区", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatGate(row), "ground.groundName") },
-    { type: "html", field: "gateName", title: "闸口名称", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatGate(row), "gateName") },
-    { type: "html", field: "gateType", title: "闸口类型", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(gateTypeDic, XEUtils.get(formatGate(row), "gateType")) },
-    { type: "html", field: "fname", title: "人员姓名", minWidth: 120, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(row, "features.origin.userName", XEUtils.get(row, "features.origin.name", "/")) },
-    { type: "html", field: "taskId", title: "任务唯一标识", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(row, "features.origin.id", XEUtils.get(row, "features.origin.userNo", "/")) },
-    { type: "html", field: "monoId", title: "任务ID", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || !row.monoID || row.monoID.length === 36 ? "/" : row.monoID },
-    { type: "html", field: "requestID", title: "三方平台任务ID", minWidth: 150, sortable: true },
-    { type: "html", field: "type", title: "三方平台类型", minWidth: 120, sortable: true, formatter: ({ cellValue }) => XEUtils.get(platformTypeDic, cellValue, cellValue) },
-    { type: "html", field: "taskTime", title: "触发时间", minWidth: 160, sortable: true, formatter: ({ cellValue, row }) => cellValue || TOOL.dateFormat(row.createTime) },
-    { field: "status", title: "任务状态", minWidth: 100, align: "center", formatter: ({ cellValue }) => XEUtils.get(platformStatusDic, cellValue, cellValue), editRender: { name: "$cell-tag" } },
-    { title: "操作", fixed: "right", minWidth: 130, slots: { default: "action" } }
-])
-
-// 显示隐藏 筛选表单
-const xGridTable = ref();
-const toggleFormEnabled = () => xGridTable.value.toggleFormEnabled();
-
-onMounted(() => window.addEventListener("setItemEvent", ({ key, newValue }) => key == "FACEREC_GATE" && newValue && xGridTable.value?.reloadColumn(columns)));
-onUnmounted(() => window.removeEventListener("setItemEvent", () => {}));
-
-const dialog = ref(false);
-const JsonModel = ref();
-const table_origin = row => {
-    dialog.value = true;
-    nextTick(() => JsonModel.value.open(XEUtils.get(row, "features", {})));
-}
-</script>

+ 0 - 19
src/views/facerec/platform/task/json.vue

@@ -1,19 +0,0 @@
-<template>
-    <el-dialog v-model="visible" title="原始数据" width="70%" @closed="$emit('closed')">
-        <sc-code-editor v-model="json" height="500" readOnly></sc-code-editor>
-    </el-dialog>
-</template>
-
-<script setup>
-const visible = ref(false);
-const json = ref("");
-
-const open = data => {
-    visible.value = true;
-    nextTick(() => json.value = JSON.stringify(data, null, 4));
-}
-
-defineExpose({
-    open
-})
-</script>

+ 83 - 138
src/views/home/index.vue

@@ -13,105 +13,84 @@
             </el-row>
         </el-card>
 
-        <template v-for="(cardItem, key) in menuCard" v-bind:key="key">
-            <el-card class="home-menu" shadow="never">
-                <template #header>{{ cardItem.header }}
-                    <template v-if="XEUtils.has(cardItem, 'formData')">
-                        <el-input class="m-l-10" v-model="cardItem.formData.title" placeholder="输入名称"></el-input>
-                        <el-select class="m-l-10" v-model="cardItem.formData.projectId" filterable placeholder="选择项目">
-                            <el-option v-for="item in $TOOL.data.get('PROJECT')" :key="item.fpiId" :label="item.projectName" :value="item.fpiId"></el-option>
-                        </el-select>
-                    </template>
-                </template>
-
-                <el-row :gutter="40">
-                    <el-col :md="6" :xs="24" v-for="item in filterCardList(cardItem)" :key="item.name">
-                        <el-dropdown v-if="item.children" placement="bottom-end" @command="command">
-                            <div class="menu-item">
-                                <sc-iconify :icon="item.meta.icon" size="26"></sc-iconify>
-                                <div class="menu-item__tooltip"><scTooltip :content="item.meta?.title"></scTooltip></div>
-                                <el-badge :value="36" :max="99"></el-badge>
-                            </div>
-
-                            <template #dropdown>
-                                <el-dropdown-menu>
-                                    <el-dropdown-item v-for="child in item.children" :key="child.path" :command="child.path">
-                                        {{ child.meta?.title }}
-                                        <el-badge style="margin-left: 6px;" :value="12" :max="99" :offset="[0, 6]"></el-badge>
-                                    </el-dropdown-item>
-                                </el-dropdown-menu>
-                            </template>
-                        </el-dropdown>
-                        
-                        <div v-else class="menu-item" @click="showMenu(cardItem, item)">
-                            <sc-iconify :icon="item.meta.icon" size="26"></sc-iconify>
-                            <div class="menu-item__tooltip"><scTooltip :content="item.meta?.title"></scTooltip></div>
-                            <el-popover popper-class="mock-detail-popover" placement="right" :width="400" trigger="click" @hide="popoverHide(item)">
-                                <template #reference>
-                                    <el-badge :value="200" :max="99" @click.stop></el-badge>
-                                </template>
-                                <div v-if="XEUtils.has(item, 'tags')" class="warn-popover-content">
-                                    <el-form @submit.prevent>
-                                        <el-form-item label="数据时间">2023-10-24~2024-09-16</el-form-item>
-                                        <el-form-item label="监测数据量">7594条</el-form-item>
-                                        <el-form-item label="生成数据量">135217条</el-form-item>
-                                        <el-form-item label="失败数据量">4681条</el-form-item>
-                                        <el-form-item class="nowrap-item">
-                                            <el-input v-model="item.tags.formData.name" placeholder="输入内容">
-                                                <template v-if="item.tags.formData.type" #prefix>
-                                                    <el-tag :type="item.tags.formData.type" effect="dark" round></el-tag>
-                                                </template>
-                                            </el-input>
-                                            <el-button class="m-l-10" type="primary" @click="tag_add(item)">确定</el-button>
-                                        </el-form-item>
-                                        <el-form-item>
-                                            <el-tag v-for="color in colorList" :key="color" :type="color" effect="dark" round @click="item.tags.formData.type = color"></el-tag>
-                                        </el-form-item>
-                                    </el-form>
-                                    <el-tag v-for="(tagItem, tagIndex) in item.tags.list" :key="tagIndex" :type="tagItem.type" effect="plain" round>{{ tagItem.name }}</el-tag>
-                                </div>
-                            </el-popover>
+        <el-card class="home-menu" shadow="never">
+            <template #header>设备预警</template>
+
+            <el-row :gutter="40">
+                <el-col :md="6" :xs="24" v-for="item in equipmentItem" :key="item.name">
+                    <div class="menu-item" @click="$router.push(item.path)">
+                        <sc-iconify :icon="item.meta.icon" size="26"></sc-iconify>
+                        <div class="menu-item__tooltip"><scTooltip :content="item.title"></scTooltip></div>
+                        <el-badge :value="36" :max="99"></el-badge>
+                    </div>
+                </el-col>
+            </el-row>
+        </el-card>
+
+        <el-card class="home-menu data-mock-card" shadow="never">
+            <template #header>数据管理与模拟
+                <el-input v-model="formData.title" placeholder="输入名称"></el-input>
+                <el-select v-model="formData.projectId" filterable placeholder="选择项目" @change="projectChange">
+                    <el-option v-for="item in projects" :key="item.fpiId" :label="item.projectName" :value="item.fpiId"></el-option>
+                </el-select>
+            </template>
+
+            <el-row :gutter="40">
+                <el-col :md="6" :xs="24" v-for="item in filterAcceptItem" :key="item.id">
+                    <div :class="['menu-item', item.beginTime && 'has-time']" @click="dataMockRoute(item.name) && $router.push(dataMockRoute(item.name).path)">
+                        <div class="menu-item__content">
+                            <sc-iconify :icon="XEUtils.get(dataMockRoute(item.name), 'meta.icon')" size="26"></sc-iconify>
+                            <div class="menu-item__tooltip"><scTooltip :content="item.name"></scTooltip></div>
                         </div>
-                    </el-col>
-                </el-row>
-            </el-card>
-        </template>
+                        <div v-if="item.beginTime" class="diagnosis">{{ TOOL.dateFormat(item.beginTime, "YY.M.D") }}<span>-{{ item.endTime && TOOL.dateFormat(item.endTime, "YY.M.D") || "至今" }}</span></div>
+                    </div>
+                    <div class="tags-bottom">
+                        <!-- 新增标签 -->
+                        <el-scrollbar height="36px">
+                            <el-tag v-for="(tagItem, tagIndex) in XEUtils.get(diagnosisDic, `${XEUtils.get(dataMockRoute(item.name), 'name')}`, [])" :key="tagIndex" :type="tagItem.type" effect="plain" round>{{ tagItem.name }}</el-tag>
+                        </el-scrollbar>
+                    </div>
+                </el-col>
+            </el-row>
+        </el-card>
     </el-main>
 </template>
 
 <script setup>
-import TOOL from "@/utils/tool";
-import { mockData } from "@/mock/mock";
-import moment from "moment";
 import XEUtils from "xe-utils";
+import API from "@/api";
+import TOOL from "@/utils/tool";
+import userRoutes from "@/config/route";
+import { diagnosisDic } from "./main"; // 诊断对象
+import { nextTick } from "vue";
 
 const day = ref("");
 const time = ref("");
 const week = ref("");
 
-const menuCard = ref({
-    warning: {
-        header: "设备预警",
-        list: mockData.warning
-    },
-    toDo: {
-        header: "我的待办",
-        list: mockData.toDo
-    },
-    dataMock: {
-        header: "数据模拟",
-        formData: {
-            title: null,
-            projectId: 11
-        },
-        list: mockData.dataMock
-    }
-})
+const projects = ref(TOOL.data.get("PROJECT"));
+const formData = ref({
+    title: "",
+    projectId: TOOL.data.get("PROJECT_ID")
+});
+
+// 数据管理与模拟路由
+const acceptItem = ref([]);
+const dataMockRoute = name => XEUtils.get(XEUtils.findTree(userRoutes, item => XEUtils.last(XEUtils.get(item, "meta.title").split("-")) == name), "item")
+const filterAcceptItem = computed(() => XEUtils.filter(acceptItem.value, item => item.name.includes(formData.value.title)));
+
+// 设备预警 --> 路由
+const equipmentItem = ref(XEUtils.map(XEUtils.get(userRoutes.find(item => item.name == "equipment"), "children", []), item => ({ ...item, title: XEUtils.last(XEUtils.get(item, "meta.title", "").split("-")) })));
 
 onMounted(() => {
     showTime();
     setInterval(() => showTime(), 1000);
-})
+    window.addEventListener("setItemEvent", ({ key, newValue }) => {
+        if (key == "PROJECT" && newValue) projects.value = JSON.parse(newValue).content;
+    });
+    projectChange();
+});
+onUnmounted(() => window.removeEventListener("setItemEvent", () => {}));
 
 const showTime = () => {
     day.value = TOOL.dateFormat(new Date(), "YYYY年MM月DD日");
@@ -119,65 +98,45 @@ const showTime = () => {
     week.value = TOOL.dateFormat(new Date(), "dddd");
 }
 
-const filterCardList = cardItem => {
-    if (XEUtils.has(cardItem, "formData")) {
-        if (XEUtils.get(cardItem, "formData.title")) {
-            return XEUtils.filter(cardItem.list, item => XEUtils.get(item, "meta.title").includes(XEUtils.get(cardItem, "formData.title")))
-        }
-        // projectId: 
-    }
-    return cardItem.list;
-}
-
-const colorList = ["danger", "orange", "warning", "success", "processing", "purple", "default"]
-const tag_add = item => {
-    item.tags.list.push(XEUtils.clone(item.tags.formData, true))
-}
-const popoverHide = item => {
-    item.tags.formData = {
-        name: "",
-        type: ""
-    }
-}
+const projectChange = async projectId => {
+    projectId && TOOL.data.set("PROJECT_ID", projectId);
 
-const router = useRouter();
-const command = e => console.log("command", e)
-const showMenu = (cardItem, item) => {
-    if (XEUtils.has(cardItem, "formData")) {
-        router.push({
-            path: item.path,
-            query: {
-                projectId: XEUtils.get(cardItem, "formData.projectId")
-            }
-        })
-    } else router.push(item.path)
+    // 先查询当前项目的验收清单
+    const infoRes = await API.system.project.bindItem.get({ projectId: formData.value.projectId });
+    const sortArr = XEUtils.orderBy(XEUtils.get(infoRes, "data", []), [["item.itemCategory", "asc"], ["item.createTime", "asc"]]);
+    acceptItem.value = XEUtils.map(sortArr, item => ({ ...item, name: XEUtils.get(item, "item.acceptItem", "") }));
+    // 再查询每一项验收清单的诊断结果
 }
 </script>
 
 <style lang="scss" scoped>
-.m-l-10 {margin-left: 10px;}
-
 .home-container {
     height: 100%;padding: calc(var(--el-main-padding) + 15px);background:#f5f7f9;
 
     .el-card {
         background: transparent;margin-bottom: 0;border: none;
-        :deep(.el-card__header) {padding: 22px 0 10px;background: transparent;border-bottom: none;font-size: 20px;}
+        :deep(.el-card__header) {display: flex;align-items: center;padding: 22px 0 10px;background: transparent;border-bottom: none;font-size: 20px;}
+        .el-input, .el-select {width: 180px;margin-left: 10px;}
     }
 
-    .el-card.home-menu :deep(.el-card__header) {display: flex;align-items: center;
-        .el-input, .el-select {width: 180px;}
-    }
-
-    .el-card.home-menu :deep(.el-card__body) {padding: 0;
+    .home-menu :deep(.el-card__body) {padding: 0;
         .el-col {margin-bottom: 20px;}
         .el-dropdown {width: 100%;}
         .menu-item {cursor: pointer;position: relative;display: flex;align-items: center;width: calc(100% - 2px);padding: 25px;background-color: #fff;border-radius: 10px;font-size: 16px;}
         .menu-item:hover {box-shadow: 1px 0 #dbdbdb, 0 1px #dbdbdb, 1px 1px #dbdbdb, 1px 0 #dbdbdb inset, 0 1px #dbdbdb inset;}
         .sc-iconify-icon {margin-right: 20px;color: var(--el-color-primary);}
         .menu-item__tooltip {display: flex;width: calc(100% - 26px - 20px);}
-        .el-badge {right: -20px;top: -20px;}
+        .el-badge {position: absolute;right: 5px;top: 5px;}
+        .tags-bottom {margin-top: 10px;white-space: nowrap;overflow-x: auto;}
+        .el-tag + .el-tag {margin-left: 6px;}
+    }
+
+    .data-mock-card :deep(.el-card__body) .menu-item {
+        flex-direction: column;align-items: unset;padding: 25px;
+        .menu-item__content {display: flex;align-items: center;}
+        .diagnosis {margin-top: 13px;line-height: 1;font-size: 12px;color: #999;}
     }
+    .data-mock-card :deep(.el-card__body) .menu-item.has-time {padding: 15px 25px 10px;}
 
     .home-top {
         padding: 12px;background: linear-gradient(to right, #8E54E9, #4776E6);border-radius: 15px;color: #fff;
@@ -191,20 +150,6 @@ const showMenu = (cardItem, item) => {
     }
 }
 
-.mock-detail-popover {
-    .el-tag + .el-tag {margin-left: 6px;}
-    .el-tag--dark.el-tag--default {height: 20px;--el-tag-bg-color: #d9d9d9;--el-tag-border-color: #d9d9d9;}
-    .el-tag--dark.el-tag--danger {height: 20px;--el-tag-bg-color: #ff4d4f;--el-tag-border-color: #ff4d4f;}
-    .el-tag--dark.el-tag--orange {height: 20px;--el-tag-bg-color: #d46b08;--el-tag-border-color: #d46b08;}
-    .el-tag--dark.el-tag--warning {height: 20px;--el-tag-bg-color: #ffe58f;--el-tag-border-color: #ffe58f;}
-    .el-tag--dark.el-tag--success {height: 20px;--el-tag-bg-color: #52c41a;--el-tag-border-color: #52c41a;}
-    .el-tag--dark.el-tag--processing {height: 20px;--el-tag-bg-color: #1677ff;--el-tag-border-color: #1677ff;}
-    .el-tag--dark.el-tag--purple {height: 20px;--el-tag-bg-color: #531dab;--el-tag-border-color: #531dab;}
-
-    .el-form-item {margin-bottom: 10px;}
-    .el-form-item.nowrap-item :deep(.el-form-item__content) {flex-wrap: nowrap;}
-}
-
 @media (max-width: 992px) {
     .home-container .home-top .el-col:first-child {align-items: unset;}
     .home-container .home-top .el-col + .el-col {margin-top: 20px;}

+ 20 - 0
src/views/home/main.js

@@ -0,0 +1,20 @@
+export const diagnosisDic = {
+    envMock: [
+        { name: "数量过多", type: "danger" },
+        { name: "分布不均", type: "danger" },
+        { name: "数量不足", type: "danger" },
+        { name: "时间段缺失", type: "danger" }
+    ],
+    standardMock: [
+        { name: "数量不足", type: "danger" },
+        { name: "时间段缺失", type: "danger" }
+    ],
+    carwashMock: [
+        { name: "数量过多", type: "danger" },
+        { name: "分布不均", type: "danger" }
+    ],
+    ugliAiMock: [
+        { name: "数量不足", type: "danger" },
+        { name: "时间段缺失", type: "danger" }
+    ]
+}

+ 1 - 5
src/views/login/index.vue

@@ -29,11 +29,7 @@
                     <el-input v-model="form.password" prefix-icon="el-icon-lock" show-password :placeholder="$t('login.PWPlaceholder')"></el-input>
                 </el-form-item>
                 <el-form-item class="login-form__code-item" prop="code">
-					<el-input v-model="form.code" prefix-icon="sc-icon-valid-code" :placeholder="$t('login.codePlaceholder')">
-                        <template #suffix>
-                            <sc-iconify icon="validCode" size="26"></sc-iconify>
-                        </template>
-                    </el-input>
+					<el-input v-model="form.code" prefix-icon="sc-icon-valid-code" :placeholder="$t('login.codePlaceholder')"></el-input>
 					<div class="code-image" @click="getCode">
 						<el-image :src="codeUrl">
 							<template #placeholder>

+ 0 - 93
src/views/passqrcode/device/fluxs.vue

@@ -1,93 +0,0 @@
-<template>
-    <el-dialog v-model="visible" :title="`${formConfig.data.deviceCode} - Fluxs任务`" width="80%" @closed="$emit('closed')">
-        <scTable :apiObj="formConfig.data.deviceCode && $API.passqrcode.monohistory" min-height="108" max-height="600" framework="zeroLite" :formConfig="formConfig" :paramsColums="paramsColums" :columns="columns"></scTable>
-    </el-dialog>
-</template>
-
-<script setup>
-import moment from "moment";
-import XEUtils from "xe-utils";
-import TOOL from "@/utils/tool";
-import { monoStatusDic, monoOptionDic, objectToArray } from "@/utils/basicDic";
-import { mapFormItemDatePicker, mapFormItemInput, mapFormItemSelect } from "@/components/scTable/helper";
-
-const visible = ref(false);
-const open = deviceCode => {
-    visible.value = true;
-    formConfig.data.deviceCode = deviceCode;
-}
-
-const monthConfig = reactive({
-    span: 3,
-    showTitle: false,
-    props: {
-        type: "month",
-        valueFormat: "YYYY-MM-01",
-        clearable: false
-    },
-    resetValue: moment().format("YYYY-MM-01"),
-    events: {
-        change: ({ field, data }) => XEUtils.merge(formConfig.data, { ...data, createTime: [moment(data.zone).startOf("month").format("YYYY-MM-DD HH:mm:ss"), moment(data.zone).endOf("month").format("YYYY-MM-DD HH:mm:ss")] })
-    }
-})
-
-const daterangeConfig = reactive({
-    span: 9,
-    showTitle: false,
-    resetValue: [moment().startOf("month").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")],
-    props: {
-        type: "datetimerange",
-        startPlaceholder: "开始时间",
-        endPlaceholder: "结束时间",
-        format: "YYYY-MM-DD HH:mm",
-        disabledDate: date => moment(date).diff(moment(formConfig.data.zone).endOf("month")) > 0 || moment(formConfig.data.zone).startOf("month").diff(moment(date)) > 0
-    }
-})
-
-const selectConfig = reactive({
-    showTitle: false,
-    options: objectToArray(monoOptionDic.passqrcode),
-    events: {
-        change: data => XEUtils.merge(formConfig.data, data)
-    }
-})
-
-const formConfig = reactive({
-    data: {
-        deviceCode: null,
-        zone: moment().format("YYYY-MM-01"),
-        createTime: [moment().startOf("month").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")]
-    },
-    items: [
-        mapFormItemDatePicker("zone", "", monthConfig),
-        mapFormItemDatePicker("createTime", "", daterangeConfig),
-        mapFormItemSelect("option", "任务类型", selectConfig),
-        mapFormItemSelect("status", "任务状态", { ...selectConfig, options: objectToArray(monoStatusDic) })
-    ]
-})
-
-const paramsColums = reactive([
-    { type: "relation", column: "deviceCode", symbol: "eq" },
-    { type: "relation", column: "option", symbol: "eq" },
-    { type: "relation", column: "status", symbol: "eq" },
-    { type: "relation", column: "createTime", symbol: "eqgt", field: "createTime[0]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "relation", column: "createTime", symbol: "eqlt", field: "createTime[1]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "orderby", column: "createTime", symbol: "desc" },
-    { type: "expands", column: "zone" }
-]);
-
-const columns = reactive([
-    { field: "option", title: "任务类型", minWidth: 120, formatter: ({ cellValue }) => XEUtils.get(monoOptionDic.passqrcode, cellValue, cellValue) },
-    { field: "beginNo", title: "起始卡号", minWidth: 100 },
-    { field: "endNo", title: "结束卡号", minWidth: 100 },
-    { field: "availableTimes", title: "可用次数", minWidth: 100 },
-    { field: "createTime", title: "触发时间", minWidth: 160, formatter: ({ cellValue }) => TOOL.dateFormat(cellValue) },
-    { field: "status", title: "任务状态", minWidth: 100, align: "center", formatter: ({ cellValue }) => XEUtils.get(monoStatusDic, cellValue, cellValue), editRender: { name: "$cell-tag" } },
-    { field: "executeTimes", title: "执行次数", minWidth: 100 },
-    { field: "reason", title: "失败原因", minWidth: 250, editRender: { name: "$cell-tag", props: { effect: "dark", type: "custom", color: "#f50" } } }
-])
-
-defineExpose({
-    open
-})
-</script>

+ 0 - 99
src/views/passqrcode/device/groups/index.vue

@@ -1,99 +0,0 @@
-<template>
-    <el-dialog v-model="visible" :title="`${formConfig.data.deviceCode} - Groups任务`" width="80%" @closed="$emit('closed')">
-        <scTable :apiObj="formConfig.data.deviceCode && $API.passqrcode.grouphistory" min-height="108" max-height="600" framework="zeroLite" :formConfig="formConfig" :paramsColums="paramsColums" :columns="columns">
-            <template #expand_content="{ row }">
-                <table-expand :rowData="row.monos"></table-expand>
-            </template>
-        </scTable>
-    </el-dialog>
-</template>
-
-<script setup>
-import moment from "moment";
-import XEUtils from "xe-utils";
-import TOOL from "@/utils/tool";
-import { monogroupOptionDic, monogroupStatusDic, objectToArray } from "@/utils/basicDic";
-import { mapFormItemDatePicker, mapFormItemInput, mapFormItemSelect } from "@/components/scTable/helper";
-import tableExpand from "./tableExpand";
-
-const visible = ref(false);
-const open = deviceCode => {
-    visible.value = true;
-    formConfig.data.deviceCode = deviceCode;
-}
-
-const monthConfig = reactive({
-    span: 3,
-    showTitle: false,
-    props: {
-        type: "month",
-        valueFormat: "YYYY-MM-01",
-        clearable: false
-    },
-    resetValue: moment().format("YYYY-MM-01"),
-    events: {
-        change: ({ field, data }) => XEUtils.merge(formConfig.data, { ...data, createTime: [moment(data.zone).startOf("month").format("YYYY-MM-DD HH:mm:ss"), moment(data.zone).endOf("month").format("YYYY-MM-DD HH:mm:ss")] })
-    }
-})
-
-const daterangeConfig = reactive({
-    span: 9,
-    showTitle: false,
-    resetValue: [moment().startOf("month").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")],
-    props: {
-        type: "datetimerange",
-        startPlaceholder: "开始时间",
-        endPlaceholder: "结束时间",
-        format: "YYYY-MM-DD HH:mm",
-        disabledDate: date => moment(date).diff(moment(formConfig.data.zone).endOf("month")) > 0 || moment(formConfig.data.zone).startOf("month").diff(moment(date)) > 0
-    }
-})
-
-const selectConfig = reactive({
-    showTitle: false,
-    options: objectToArray(monogroupOptionDic.passqrcode),
-    events: {
-        change: data => XEUtils.merge(formConfig.data, data)
-    }
-})
-
-const formConfig = reactive({
-    data: {
-        deviceCode: null,
-        options: "all",
-        zone: moment().format("YYYY-MM-01"),
-        createTime: [moment().startOf("month").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")]
-    },
-    items: [
-        mapFormItemDatePicker("zone", "", monthConfig),
-        mapFormItemDatePicker("createTime", "", daterangeConfig),
-        mapFormItemSelect("option", "任务类型", selectConfig),
-        mapFormItemSelect("status", "任务状态", { ...selectConfig, options: objectToArray(monogroupStatusDic) })
-    ]
-})
-
-const paramsColums = reactive([
-    { type: "relation", column: "deviceCode", symbol: "eq" },
-    { type: "relation", column: "option", symbol: "eq" },
-    { type: "relation", column: "status", symbol: "eq" },
-    { type: "relation", column: "createTime", symbol: "eqgt", field: "createTime[0]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "relation", column: "createTime", symbol: "eqlt", field: "createTime[1]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "orderby", column: "createTime", symbol: "desc" },
-    { type: "expands", column: "zone" },
-    { type: "expands", column: "options" }
-]);
-
-const columns = reactive([
-    { type: "expand", fixed: "left", width: 50, align: "center", slots: { content: "expand_content" } },
-    { field: "option", title: "任务类型", minWidth: 150, formatter: ({ cellValue }) => XEUtils.get(monogroupOptionDic.passqrcode, cellValue, cellValue) },
-    { field: "preReserved", title: "前置保留数量", minWidth: 100 },
-    { field: "posReserved", title: "后置保留数量", minWidth: 100 },
-    { field: "createTime", title: "触发时间", minWidth: 160, formatter: ({ cellValue }) => TOOL.dateFormat(cellValue) },
-    { field: "status", title: "任务状态", minWidth: 100, align: "center", formatter: ({ cellValue }) => XEUtils.get(monogroupStatusDic, cellValue, cellValue), editRender: { name: "$cell-tag" } },
-    { field: "reason", title: "失败原因", minWidth: 250, editRender: { name: "$cell-tag", props: { effect: "dark", type: "custom", color: "#f50" } } },
-])
-
-defineExpose({
-    open
-})
-</script>

+ 0 - 34
src/views/passqrcode/device/groups/tableExpand.vue

@@ -1,34 +0,0 @@
-<template>
-    <scTable :options="options" min-height="108" maxHeight="600">
-        <template #top>
-            <el-divider content-position="left">任务清单</el-divider>
-        </template>
-    </scTable>
-</template>
-
-<script setup>
-import XEUtils from "xe-utils";
-import { monoOptionDic, monoStatusDic } from "@/utils/basicDic";
-
-const options = reactive({
-    layouts: [["Top", "Table"]],
-    data: useAttrs().rowData,
-    formConfig: { enabled: false },
-    pagerConfig: { enabled: false },
-    columns: [
-        { field: "option", title: "任务类型", minWidth: 120, formatter: ({ cellValue }) => XEUtils.get(monoOptionDic.passqrcode, cellValue, cellValue) },
-        { field: "beginNo", title: "起始卡号", minWidth: 100 },
-        { field: "endNo", title: "结束卡号", minWidth: 100 },
-        { field: "availableTimes", title: "可用次数", minWidth: 100 },
-        { field: "status", title: "任务状态", minWidth: 100, align: "center", formatter: ({ cellValue }) => XEUtils.get(monoStatusDic, cellValue, cellValue), editRender: { name: "$cell-tag" } },
-        { field: "executeTimes", title: "执行次数", minWidth: 100 },
-        { field: "features.reason", title: "失败原因", minWidth: 250, editRender: { name: "$cell-tag", props: { effect: "dark", type: "custom", color: "#f50" } } }
-    ]
-})
-</script>
-
-<style scoped>
-    .el-main {padding: 0;}
-    .el-divider {margin-top: 12px;}
-    .el-divider :deep(.el-divider__text) {left: 0;padding-left: 12px;}
-</style>

+ 0 - 57
src/views/passqrcode/device/logDetail.vue

@@ -1,57 +0,0 @@
-<template>
-    <el-dialog v-model="visible" :title="`${formConfig.data.deviceCode} - 上线/离线记录`" width="80%" @closed="$emit('closed')">
-        <scTable :apiObj="formConfig.data.deviceCode && $API.passqrcode.online" min-height="108" max-height="600" framework="zeroLite" :formConfig="formConfig" :paramsColums="paramsColums" :columns="columns"></scTable>
-    </el-dialog>
-</template>
-
-<script setup>
-import moment from "moment";
-import XEUtils from "xe-utils";
-import TOOL from "@/utils/tool";
-import { deviceRecordStateDic } from "@/utils/basicDic";
-import { mapFormItemDatePicker } from "@/components/scTable/helper";
-
-const visible = ref(false);
-const open = deviceCode => {
-    visible.value = true;
-    formConfig.data.deviceCode = deviceCode;
-}
-
-const daterangeConfig = reactive({
-    span: 9,
-    showTitle: false,
-    resetValue: [moment().startOf("day").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")],
-    props: {
-        type: "datetimerange",
-        startPlaceholder: "开始时间",
-        endPlaceholder: "结束时间",
-        format: "YYYY-MM-DD HH:mm"
-    }
-})
-
-const formConfig = reactive({
-    data: {
-        deviceCode: null,
-        createTime: [moment().startOf("day").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")]
-    },
-    items: [
-        mapFormItemDatePicker("createTime", "", daterangeConfig)
-    ]
-})
-
-const paramsColums = reactive([
-    { type: "relation", column: "deviceCode", symbol: "eq" },
-    { type: "relation", column: "createTime", symbol: "eqgt", field: "createTime[0]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "relation", column: "createTime", symbol: "eqlt", field: "createTime[1]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "orderby", column: "createTime", symbol: "desc" }
-]);
-
-const columns = reactive([
-    { field: "status", title: "上线/离线", minWidth: 100, align: "center", formatter: ({ cellValue }) => XEUtils.get(deviceRecordStateDic, cellValue, cellValue) },
-    { field: "createTime", title: "触发时间", minWidth: 160, align: "center", formatter: ({ cellValue }) => TOOL.dateFormat(cellValue) }
-])
-
-defineExpose({
-    open
-})
-</script>

+ 0 - 117
src/views/passqrcode/device/stateDev.vue

@@ -1,117 +0,0 @@
-<template>
-    <el-dialog v-model="visible" :title="`${formConfig.data.deviceCode} - 设备状态`" width="80%" @closed="$emit('closed')">
-        <el-tabs v-model="activeName" @tab-change="tabChange">
-            <el-tab-pane label="设备信息" name="deviceInfo">
-                <el-descriptions :column="3" label-width="120">
-                    <el-descriptions-item class-name="calculator-content" label="排序区起始卡号:">{{ XEUtils.get(deviceData, "beginNo", 0) }}</el-descriptions-item>
-                    <el-descriptions-item class-name="calculator-content" label="排序区结束卡号:">{{ XEUtils.get(deviceData, "endNo", 0) }}</el-descriptions-item>
-                    <el-descriptions-item class-name="calculator-content" label="排序区可用次数:">{{ XEUtils.get(deviceData, "availableTimes", 0) }}</el-descriptions-item>
-                    <el-descriptions-item class-name="calculator-content" label="保留起始卡号:">{{ XEUtils.get(deviceData, "reservedBeginNo", 0) }}</el-descriptions-item>
-                    <el-descriptions-item class-name="calculator-content" label="保留结束卡号:">{{ XEUtils.get(deviceData, "reservedEndNo", 0) }}</el-descriptions-item>
-                    <el-descriptions-item class-name="calculator-content" label="保留卡号使用次数:">{{ XEUtils.get(deviceData, "reservedTimes", 0) }}</el-descriptions-item>
-                    <el-descriptions-item label="创建时间:">{{ $TOOL.dateFormat(XEUtils.get(deviceData, "createTime")) || "/" }}</el-descriptions-item>
-                    <el-descriptions-item label="设备时间:">{{ $TOOL.dateFormat(XEUtils.get(deviceData, "deviceTime")) || "/" }}</el-descriptions-item>
-                    <el-descriptions-item label="校准时间:">{{ $TOOL.dateFormat(XEUtils.get(deviceData, "proofreadLast")) || "/" }}</el-descriptions-item>
-                    <el-descriptions-item label="卡号记录更新时间:" :span="3">{{ $TOOL.dateFormat(XEUtils.get(deviceData, "features.fetchLast")) || "/" }}</el-descriptions-item>
-                
-                    <el-descriptions-item :span="3">
-                        <el-divider content-position="left">非排序区卡号</el-divider>
-                    </el-descriptions-item>
-                    <template v-for="(item, index) in XEUtils.get(deviceData, 'unsequences', [])" v-bind:key="index">
-                        <el-descriptions-item class-name="calculator-content" label="保留起始卡号:">{{ XEUtils.get(item, "beginNo", 0) }}</el-descriptions-item>
-                        <el-descriptions-item class-name="calculator-content" label="保留结束卡号:">{{ XEUtils.get(item, "endNo", 0) }}</el-descriptions-item>
-                        <el-descriptions-item class-name="calculator-content" label="保留卡号使用次数:">{{ XEUtils.get(item, "times", 0) }}</el-descriptions-item>
-                    </template>
-                </el-descriptions>
-            </el-tab-pane>
-
-            <el-tab-pane label="卡号信息" name="cardNoInfo">
-                <scTable ref="xGridTable" :apiObj="formConfig.data.deviceId && $API.passqrcode.cardinfo" min-height="108" max-height="600" framework="zeroLite" :formConfig="formConfig" :paramsColums="paramsColums" :columns="columns"></scTable>
-            </el-tab-pane>
-        </el-tabs>
-    </el-dialog>
-</template>
-
-<script setup>
-import XEUtils from "xe-utils";
-import API from "@/api";
-import TOOL from "@/utils/tool";
-import { cardinfoZoneDic, cardinfoStatusDic, cardinfoOpenTime, objectToArray } from "@/utils/basicDic";
-import { mapFormItemInput, mapFormItemSelect } from "@/components/scTable/helper";
-
-const visible = ref(false);
-const open = deviceCode => {
-    visible.value = true;
-    formConfig.data.deviceCode = deviceCode;
-    getDeviceStatus(deviceCode);
-}
-
-const activeName = reactive("deviceInfo");
-const deviceData = ref({});
-
-const getDeviceStatus = value => {
-    const query = {
-        limit: { start: 0, length: 1 },
-        orderby: [{ column: "createTime", seq: "DESC" }],
-        condition: { column: "deviceCode", symbol: "EQ", value }
-    }
-    API.passqrcode.devstat.get({ querys: [query] }).then(res => deviceData.value = XEUtils.merge(XEUtils.first(res.datas)));
-}
-
-const selectConfig = reactive({
-    options: objectToArray(cardinfoZoneDic),
-    events: {
-        change: data => XEUtils.merge(formConfig.data, data)
-    }
-})
-
-const formConfig = reactive({
-    data: {
-        deviceCode: null,
-        deviceId: null
-    },
-    items: [
-        mapFormItemInput("cardNo", "卡号"),
-        mapFormItemSelect("zone", "区域", selectConfig)
-    ]
-})
-
-const paramsColums = reactive([
-    { type: "relation", column: "deviceId", symbol: "eq" },
-    { type: "relation", column: "cardNo", symbol: "like" },
-    { type: "relation", column: "zone", symbol: "eq" },
-    { type: "orderby", column: "cardNo", symbol: "asc" }
-]);
-
-const columns = reactive([
-    { field: "cardNo", title: "卡号", minWidth: 100 },
-    { field: "zone", title: "区域", minWidth: 100, formatter: ({ cellValue }) => XEUtils.get(cardinfoZoneDic, cellValue, cellValue) },
-    { field: "times", title: "可用次数", minWidth: 100 },
-    { field: "openTime", title: "开门时段", minWidth: 200, formatter: ({ cellValue }) => XEUtils.map(cardinfoOpenTime(cellValue).split(","), (item, index) => `读头编号${index + 1}:开门时段${item.split("")[1] || ""}`).join() },
-    { field: "auths", title: "认证策略", minWidth: 100 },
-    { field: "holiday", title: "假日策略", minWidth: 100 },
-    { field: "status", title: "状态", minWidth: 100, formatter: ({ cellValue }) => XEUtils.get(cardinfoStatusDic, cellValue, cellValue) },
-    { field: "createTime", title: "创建时间", minWidth: 160, formatter: ({ cellValue }) => TOOL.dateFormat(cellValue) },
-    { field: "effectiveTime", title: "有效时间", minWidth: 160, formatter: ({ cellValue }) => TOOL.dateFormat(cellValue) },
-    { field: "last", title: "最后刷卡时间", minWidth: 160, formatter: ({ cellValue }) => TOOL.dateFormat(XEUtils.toStringDate(`20${cellValue}`, "yyyyMMddHHmmss")) || "/" }
-])
-
-const xGridTable = ref();
-const tabChange = name => {
-    if (name == "deviceInfo") getDeviceStatus(formConfig.data.deviceCode);
-    if (name == "cardNoInfo") {
-        formConfig.data.deviceId = deviceData.value.id;
-        nextTick(() => xGridTable.value.resetData());
-    }
-}
-
-defineExpose({
-    open
-})
-</script>
-
-<style scoped>
-    .el-descriptions :deep(.el-descriptions__label) {margin-right: 12px;text-align: right;font-weight: 700;color: var(--vxe-ui-font-color);}
-    .el-descriptions :deep(.el-descriptions__content).calculator-content {position: relative;top: 3px;font: 22px calculator-all;color: var(--el-color-primary);letter-spacing: 2px;}
-    .el-divider {margin-bottom: 12px;}
-</style>

+ 0 - 112
src/views/passqrcode/platform/push/index.vue

@@ -1,112 +0,0 @@
-<template>
-	<el-container class="is-vertical">
-        <sc-page-header @filter="toggleFormEnabled"></sc-page-header>
-
-        <scTable ref="xGridTable" :apiObj="$API.passqrcode.notify" framework="zeroLite" :toolbarConfig="toolbarConfig" :formConfig="formConfig" :paramsColums="paramsColums" :columns="columns">
-            <template #action="{ row }">
-                <el-button type="primary" link @click="table_origin(row)">
-                    <template #icon><sc-iconify icon="lucide:file-json"></sc-iconify></template>查看原始数据
-                </el-button>
-            </template>
-        </scTable>
-	</el-container>
-
-    <json-model v-if="dialog" ref="JsonModel" @closed="dialog = false"></json-model>
-</template>
-
-<script setup>
-import moment from "moment";
-import XEUtils from "xe-utils";
-import TOOL from "@/utils/tool";
-import { gateTypeDic, platformTypeDic, platformStatusDic, objectToArray } from "@/utils/basicDic";
-import { mapFormItemInput, mapFormItemSelect, mapFormItemDatePicker } from "@/components/scTable/helper";
-import jsonModel from "@/views/facerec/platform/task/json";
-
-const formatGate = ({ deviceCode }) => XEUtils.find(TOOL.data.get("PASSQRCODE_GATE"), item => XEUtils.findIndexOf(item.devices, d => d.device_num === deviceCode) !== -1);
-
-const deviceConfig = reactive({
-    api: {
-        key: "system.device",
-        query: { current:1, size: 999999, categoryId: 22, modelId: 29 }
-    },
-    slot: {
-        style: { float: "right", paddingLeft: "6px", color: "#8492a6" }
-    },
-    props: { multiple: true, collapseTags: true, collapseTagsTooltip: true },
-    optionProps: { label: "deviceNum", value: "deviceNum", slot: ({ data }) => XEUtils.get(XEUtils.find(TOOL.data.get("PROJECT"), item => item.fpiId === data.projectId), "projectName") },
-    events: {
-        change: data => XEUtils.assign(formConfig.data, data)
-    }
-})
-
-const selectConfig = reactive({
-    options: objectToArray(platformTypeDic),
-    events: {
-        change: data => XEUtils.merge(formConfig.data, data)
-    }
-})
-
-const daterangeConfig = reactive({
-    resetValue: [moment().startOf("day").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")],
-    props: {
-        type: "datetimerange",
-        startPlaceholder: "开始时间",
-        endPlaceholder: "结束时间",
-        format: "YY-MM-DD HH:mm"
-    }
-})
-
-const toolbarConfig = reactive({
-    enabled: true,
-    print: false
-})
-
-const formConfig = reactive({
-    data: {
-        createTime: [moment().startOf("day").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")]
-    },
-    items: [
-        mapFormItemSelect("deviceCode", "设备唯一标识", deviceConfig),
-        mapFormItemSelect("type", "三方平台类型", selectConfig),
-        mapFormItemSelect("status", "任务状态", { ...selectConfig, options: objectToArray(platformStatusDic) }),
-        mapFormItemDatePicker("createTime", "触发时间", daterangeConfig)
-    ]
-})
-
-const paramsColums = reactive([
-    { type: "relation", column: "deviceCode", symbol: "or" },
-    { type: "relation", column: "type", symbol: "eq" },
-    { type: "relation", column: "status", symbol: "eq" },
-    { type: "relation", column: "createTime", symbol: "eqgt", field: "createTime[0]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "relation", column: "createTime", symbol: "eqlt", field: "createTime[1]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "orderby", column: "createTime", symbol: "desc" }
-]);
-
-const columns = reactive([
-    { type: "seq", fixed: "left", width: 60 },
-    { type: "html", field: "deviceCode", title: "设备唯一标识", fixed: "left", minWidth: 150, sortable: true },
-    { type: "html", field: "projectName", title: "所属项目", minWidth: 200, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(XEUtils.find(TOOL.data.get("PROJECT"), item => item.fpiId === XEUtils.get(XEUtils.get(formatGate(row), "devices", []).find(d => d.device_num === row.deviceCode), "project_id")), "projectName") },
-    { type: "html", field: "groundName", title: "工地场区", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatGate(row), "ground.groundName") },
-    { type: "html", field: "gateName", title: "闸口名称", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatGate(row), "gateName") },
-    { type: "html", field: "gateType", title: "闸口类型", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(gateTypeDic, XEUtils.get(formatGate(row), "gateType")) },
-    { type: "html", field: "cardNo", title: "卡号", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(row, "features.request.data.CardNo") },
-    { type: "html", field: "type", title: "三方平台类型", minWidth: 120, sortable: true, formatter: ({ cellValue }) => XEUtils.get(platformTypeDic, cellValue, cellValue) },
-    { type: "html", field: "taskTime", title: "触发时间", minWidth: 160, sortable: true, formatter: ({ cellValue, row }) => cellValue || TOOL.dateFormat(row.createTime) },
-    { field: "status", title: "任务状态", minWidth: 100, align: "center", formatter: ({ cellValue }) => XEUtils.get(platformStatusDic, cellValue, cellValue), editRender: { name: "$cell-tag" } },
-    { title: "操作", fixed: "right", minWidth: 130, slots: { default: "action" } }
-])
-
-// 显示隐藏 筛选表单
-const xGridTable = ref();
-const toggleFormEnabled = () => xGridTable.value.toggleFormEnabled();
-
-onMounted(() => window.addEventListener("setItemEvent", ({ key, newValue }) => key == "PASSQRCODE_GATE" && newValue && xGridTable.value?.reloadColumn(columns)));
-onUnmounted(() => window.removeEventListener("setItemEvent", () => {}));
-
-const dialog = ref(false);
-const JsonModel = ref();
-const table_origin = row => {
-    dialog.value = true;
-    nextTick(() => JsonModel.value.open(XEUtils.get(row, "features", {})));
-}
-</script>

+ 0 - 100
src/views/passqrcode/platform/task/index.vue

@@ -1,100 +0,0 @@
-<template>
-	<el-container class="is-vertical">
-        <sc-page-header @filter="toggleFormEnabled"></sc-page-header>
-
-        <scTable ref="xGridTable" :apiObj="$API.passqrcode.cardSync" framework="zeroLite" :toolbarConfig="toolbarConfig" :formConfig="formConfig" :paramsColums="paramsColums" :columns="columns">
-            <template #action="{ row }">
-                <el-button type="primary" link @click="table_sync(row)">
-                    <template #icon><sc-iconify icon="hugeicons:database-sync-01"></sc-iconify></template>同步设备
-                </el-button>
-
-                <el-button type="primary" link @click="table_mono(row)">
-                    <template #icon><sc-iconify icon="ant-design:unordered-list-outlined"></sc-iconify></template>同步任务
-                </el-button>
-            </template>
-        </scTable>
-	</el-container>
-
-    <sync-detail v-if="dialogSync" ref="SyncDetail" @closed="dialogSync = false"></sync-detail>
-    <mono-detail v-if="dialogMono" ref="MonoDetail" @closed="dialogMono = false"></mono-detail>
-</template>
-
-<script setup>
-import moment from "moment";
-import XEUtils from "xe-utils";
-import TOOL from "@/utils/tool";
-import { synModeDic, synStatusDic, objectToArray } from "@/utils/basicDic";
-import { mapFormItemInput, mapFormItemSelect, mapFormItemDatePicker } from "@/components/scTable/helper";
-import syncDetail from "@/views/passqrcode/platform/task/sync";
-import monoDetail from "@/views/passqrcode/platform/task/mono";
-
-const selectConfig = reactive({
-    span: 5,
-    options: objectToArray(synModeDic),
-    events: {
-        change: data => XEUtils.merge(formConfig.data, data)
-    }
-})
-
-const daterangeConfig = reactive({
-    span: 8,
-    resetValue: [moment().startOf("day").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")],
-    props: {
-        type: "datetimerange",
-        startPlaceholder: "开始时间",
-        endPlaceholder: "结束时间",
-        format: "YYYY-MM-DD HH:mm"
-    }
-})
-
-const toolbarConfig = reactive({
-    enabled: true,
-    print: false
-})
-
-const formConfig = reactive({
-    data: {
-        createTime: [moment().startOf("day").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")]
-    },
-    items: [
-        mapFormItemSelect("mode", "任务类型", selectConfig),
-        mapFormItemSelect("status", "任务状态", { ...selectConfig, options: objectToArray(synStatusDic) }),
-        mapFormItemDatePicker("createTime", "触发时间", daterangeConfig)
-    ]
-})
-
-const paramsColums = reactive([
-    { type: "relation", column: "mode", symbol: "eq" },
-    { type: "relation", column: "status", symbol: "eq" },
-    { type: "relation", column: "createTime", symbol: "eqgt", field: "createTime[0]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "relation", column: "createTime", symbol: "eqlt", field: "createTime[1]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "orderby", column: "createTime", symbol: "desc" }
-]);
-
-const columns = reactive([
-    { type: "seq", fixed: "left", width: 60 },
-    { type: "html", field: "mode", title: "任务类型", minWidth: 120, sortable: true, formatter: ({ cellValue }) => XEUtils.get(synModeDic, cellValue, cellValue) },
-    { type: "html", field: "taskTime", title: "触发时间", minWidth: 160, sortable: true, formatter: ({ cellValue, row }) => cellValue || TOOL.dateFormat(row.createTime) },
-    { field: "status", title: "任务状态", minWidth: 100, align: "center", formatter: ({ cellValue }) => XEUtils.get(synStatusDic, cellValue, cellValue), editRender: { name: "$cell-tag" } },
-    { field: "reason", title: "失败原因", minWidth: 250, editRender: { name: "$cell-tag", props: { effect: "dark", type: "custom", color: "#f50" } } },
-    { title: "操作", fixed: "right", minWidth: 180, slots: { default: "action" } }
-])
-
-// 显示隐藏 筛选表单
-const xGridTable = ref();
-const toggleFormEnabled = () => xGridTable.value.toggleFormEnabled();
-
-const dialogSync = ref(false);
-const dialogMono = ref(false);
-const SyncDetail = ref();
-const MonoDetail = ref();
-const table_sync = row => {
-    dialogSync.value = true;
-    nextTick(() => SyncDetail.value.open(row));
-}
-
-const table_mono = row => {
-    dialogMono.value = true;
-    nextTick(() => MonoDetail.value.open(row.id));
-}
-</script>

+ 0 - 51
src/views/passqrcode/platform/task/mono/index.vue

@@ -1,51 +0,0 @@
-<template>
-    <el-dialog v-model="visible" title="同步任务" width="80%" @closed="$emit('closed')">
-        <scTable :apiObj="formConfig.data.workerID && $API.passqrcode.cmttask" min-height="108" max-height="600" framework="zeroLite" :formConfig="formConfig" :paramsColums="paramsColums" :columns="columns">
-            <template #expand_content="{ row }">
-                <table-expand :rowData="row"></table-expand>
-            </template>
-        </scTable>
-    </el-dialog>
-</template>
-
-<script setup>
-import XEUtils from "xe-utils";
-import TOOL from "@/utils/tool";
-import { gateTypeDic, synStatusDic } from "@/utils/basicDic";
-import tableExpand from "./tableExpand";
-
-const formatGate = ({ deviceCode }) => XEUtils.find(TOOL.data.get("PASSQRCODE_GATE"), item => XEUtils.findIndexOf(item.devices, d => d.device_num === deviceCode) !== -1);
-
-const visible = ref(false);
-const open = id => {
-    visible.value = true;
-    formConfig.data.workerID = id;
-}
-
-const formConfig = reactive({
-    data: {
-        workerID: null
-    }
-})
-
-const paramsColums = reactive([
-    { type: "relation", column: "workerID", symbol: "eq" },
-    { type: "orderby", column: "createTime", symbol: "desc" }
-]);
-
-const columns = reactive([
-    { type: "expand", fixed: "left", width: 50, align: "center", slots: { content: "expand_content" } },
-    { field: "deviceCode", title: "设备唯一标识", fixed: "left", minWidth: 150 },
-    { field: "projectName", title: "所属项目", minWidth: 200, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(XEUtils.find(TOOL.data.get("PROJECT"), item => item.fpiId === formatGate(row).devices.find(d => d.device_num === row.deviceCode).project_id), "projectName") },
-    { field: "groundName", title: "工地场区", minWidth: 150, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatGate(row), "ground.groundName") },
-    { field: "gateName", title: "闸口名称", minWidth: 150, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatGate(row), "gateName") },
-    { field: "gateType", title: "闸口类型", minWidth: 150, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(gateTypeDic, XEUtils.get(formatGate(row), "gateType")) },
-    { field: "groupID", title: "任务ID", minWidth: 150 },
-    { field: "taskTime", title: "触发时间", minWidth: 160, formatter: ({ cellValue, row }) => cellValue || TOOL.dateFormat(row.createTime) },
-    { field: "status", title: "任务状态", minWidth: 100, align: "center", formatter: ({ cellValue }) => XEUtils.get(synStatusDic, cellValue, cellValue), editRender: { name: "$cell-tag" } }
-])
-
-defineExpose({
-    open
-})
-</script>

+ 0 - 70
src/views/passqrcode/platform/task/mono/tableExpand.vue

@@ -1,70 +0,0 @@
-<template>
-    <scTable :options="permOptions" min-height="108" maxHeight="600">
-        <template #top>
-            <el-divider style="margin-top: 12px;" content-position="center">永久卡号</el-divider>
-        </template>
-    </scTable>
-
-    <template v-if="props.rowData.temporary.currentWiegand">
-        <scTable :options="seqOptions" min-height="108" maxHeight="600">
-            <template #top>
-                <el-divider content-position="center">临时卡号</el-divider>
-            </template>
-        </scTable>
-    </template>
-</template>
-
-<script setup>
-import XEUtils from "xe-utils";
-import { synExecStatusDic } from "@/utils/basicDic";
-
-const props = defineProps({
-    rowData: { type: Object, default: () => {} }
-})
-
-// 青岛地铁同步任务原始数据
-const wiegandDIc = {
-    currentWiegand: "添加卡号",
-    removeWiegand: "删除卡号"
-}
-
-// 永久卡号
-const permOptions = reactive({
-    layouts: [["Top", "Table"]],
-    data: XEUtils.map(XEUtils.get(props, "rowData.permanent"), item => ({ ...XEUtils.pick(props.rowData, "features"), ...item })),
-    formConfig: { enabled: false },
-    pagerConfig: { enabled: false },
-    columns: [
-        { field: "beginNumber", title: "起始卡号", minWidth: 100 },
-        { field: "endNumber", title: "结束卡号", minWidth: 100 },
-        { field: "features.permanentNotify", title: "推送状态", minWidth: 100 },
-        { field: "features.perm", title: "执行状态", minWidth: 100, formatter: ({ cellValue }) => XEUtils.get(synExecStatusDic, cellValue, cellValue) },
-        { field: "features.permRequestErr", title: "失败原因", minWidth: 250, editRender: { name: "$cell-tag", props: { effect: "dark", type: "custom", color: "#f50" } } }
-    ]
-})
-
-// 临时卡号
-const seqOptions = reactive({
-    layouts: [["Top", "Table"]],
-    data: XEUtils.map(
-        XEUtils.keys(XEUtils.pick(XEUtils.get(props, "rowData.temporary"), ["currentWiegand", "removeWiegand"])),
-        key => ({ ...XEUtils.pick(props.rowData, ["temporary", "features"]), option: XEUtils.get(wiegandDIc, key, key), ...XEUtils.get(props, `rowData.temporary.${key}`) })
-    ),
-    formConfig: { enabled: false },
-    pagerConfig: { enabled: false },
-    columns: [
-        { field: "option", minWidth: 100 },
-        { field: "beginNumber", title: "起始卡号", minWidth: 100 },
-        { field: "endNumber", title: "结束卡号", minWidth: 100 },
-        { field: "temporary.effectCount", title: "有效次数", minWidth: 100 },
-        { field: "temporary.lineCount", title: "保留字段", minWidth: 100 },
-        { field: "features.temporaryNotify", title: "推送状态", minWidth: 100 },
-        { field: "features.seq", title: "执行状态", minWidth: 100, formatter: ({ cellValue }) => XEUtils.get(synExecStatusDic, cellValue, cellValue) },
-        { field: "features.seqRequestErr", title: "失败原因", minWidth: 250, editRender: { name: "$cell-tag", props: { effect: "dark", type: "custom", color: "#f50" } } }
-    ]
-})
-</script>
-
-<style scoped>
-    .el-main {padding: 0;}
-</style>

+ 0 - 38
src/views/passqrcode/platform/task/sync.vue

@@ -1,38 +0,0 @@
-<template>
-    <el-dialog v-model="visible" title="同步设备" width="80%" @closed="$emit('closed')">
-        <scTable :options="options" min-height="108" maxHeight="600"></scTable>
-    </el-dialog>
-</template>
-
-<script setup>
-import XEUtils from "xe-utils";
-import TOOL from "@/utils/tool";
-import { gateTypeDic } from "@/utils/basicDic";
-
-const formatGate = ({ deviceCode }) => XEUtils.find(TOOL.data.get("PASSQRCODE_GATE"), item => XEUtils.findIndexOf(item.devices, d => d.device_num === deviceCode) !== -1);
-
-const visible = ref(false);
-const open = row => {
-    visible.value = true;
-    options.data = XEUtils.map(row.devices, deviceCode => ({ deviceCode, requestFaileds: XEUtils.get(row, "features.requestFaileds") })) ;
-}
-
-const options = reactive({
-    data: [],
-    formConfig: { enabled: false },
-    pagerConfig: { enabled: false },
-    columns: [
-        { field: "deviceCode", title: "设备唯一标识", fixed: "left", minWidth: 150 },
-        { field: "projectName", title: "所属项目", minWidth: 200, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(XEUtils.find(TOOL.data.get("PROJECT"), item => item.fpiId === formatGate(row).devices.find(d => d.device_num === row.deviceCode).project_id), "projectName") },
-        { field: "groundName", title: "工地场区", minWidth: 150, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatGate(row), "ground.groundName") },
-        { field: "gateName", title: "闸口名称", minWidth: 150, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatGate(row), "gateName") },
-        { field: "gateType", title: "闸口类型", minWidth: 150, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(gateTypeDic, XEUtils.get(formatGate(row), "gateType")) },
-        { field: "permFailed", title: "获取永久卡号失败原因", minWidth: 250, formatter: ({ row }) => XEUtils.get(row, `requestFaileds.${row.deviceCode}.perm`), editRender: { name: "$cell-tag", props: { effect: "dark", type: "custom", color: "#f50" } } },
-        { field: "seqFailed", title: "获取临时卡号失败原因", minWidth: 250, formatter: ({ row }) => XEUtils.get(row, `requestFaileds.${row.deviceCode}.seq`), editRender: { name: "$cell-tag", props: { effect: "dark", type: "custom", color: "#f50" } } }
-    ]
-})
-
-defineExpose({
-    open
-})
-</script>

+ 0 - 95
src/views/tower/monitor/index.vue

@@ -1,95 +0,0 @@
-<template>
-	<el-container class="is-vertical">
-        <sc-page-header @filter="toggleFormEnabled"></sc-page-header>
-
-        <scTable ref="xGridTable" :apiObj="$API.tower.records" :toolbarConfig="toolbarConfig" :formConfig="formConfig" :paramsColums="paramsColums" :columns="columns"></scTable>
-	</el-container>
-</template>
-
-<script setup>
-import moment from "moment";
-import XEUtils from "xe-utils";
-import TOOL from "@/utils/tool";
-import { mapFormItemInput, mapFormItemSelect, mapFormItemDatePicker } from "@/components/scTable/helper";
-
-const formatGate = ({ mountedId }) => XEUtils.find(TOOL.data.get("TOWER_GATE"), item => item.id === mountedId);
-const formatGateFeatures = row => XEUtils.toStringJSON(XEUtils.get(row, "features", "{}"));
-const formatTilt = row => {
-    const feaTilt = XEUtils.divide(XEUtils.get(formatGateFeatures(row), "tiltAngle"), 100);
-    const tilt = feaTilt > 0 ? feaTilt : XEUtils.add(360, feaTilt);
-    return tilt >= 180 ? XEUtils.subtract(360, tilt) : tilt;
-};
-
-const mountedConfig = reactive({
-    storageKey: "TOWER_GATE",
-    slot: {
-        style: { float: "right", paddingLeft: "6px", color: "#8492a6" }
-    },
-    props: { multiple: true, collapseTags: true, collapseTagsTooltip: true },
-    optionProps: { label: "mountedName", value: "id", slot: ({ data }) => XEUtils.get(XEUtils.find(TOOL.data.get("PROJECT"), item => item.fpiId === XEUtils.get(XEUtils.first(data.devices), "project_id")), "projectName") },
-    events: {
-        change: data => XEUtils.assign(formConfig.data, data)
-    }
-})
-
-const daterangeConfig = reactive({
-    span: 9,
-    resetValue: [moment().startOf("day").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")],
-    props: {
-        type: "datetimerange",
-        clearable: false,
-        startPlaceholder: "开始时间",
-        endPlaceholder: "结束时间",
-        format: "YYYY-MM-DD HH:mm"
-    }
-})
-
-const toolbarConfig = reactive({
-    enabled: true,
-    print: false
-})
-
-const formConfig = reactive({
-    data: {
-        createTime: [moment().startOf("day").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")],
-        orderBy: "createTime_desc"
-    },
-    items: [
-        mapFormItemSelect("mountedID", "设备安装点", mountedConfig),
-        mapFormItemDatePicker("createTime", "监测时间", daterangeConfig)
-    ]
-})
-
-const paramsColums = reactive([
-    { column: "mountedIdIn", field: "mountedID" },
-    { column: "createTimeBegin", field: "createTime[0]" },
-    { column: "createTimeEnd", field: "createTime[1]" },
-    { column: "orderBy" }
-]);
-
-const columns = reactive([
-    { type: "seq", fixed: "left", width: 60 },
-    { type: "html", field: "deviceCode", title: "设备唯一标识", fixed: "left", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatGateFeatures(row), "device") },
-    { type: "html", field: "projectName", title: "所属项目", minWidth: 200, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(XEUtils.find(TOOL.data.get("PROJECT"), item => item.fpiId === XEUtils.get(XEUtils.find(XEUtils.get(formatGate(row), "devices", []), item => item.device_num == XEUtils.get(formatGateFeatures(row), "device")), "project_id")), "projectName") },
-    { type: "html", field: "groundName", title: "工地场区", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatGate(row), "ground.groundName") },
-    { type: "html", field: "mountedName", title: "安装点名称", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatGate(row), "mountedName") },
-    { type: "html", field: "taskTime", title: "监测时间", minWidth: 160, sortable: true, formatter: ({ cellValue, row }) => cellValue || TOOL.dateFormat(row.createTime) },
-    { type: "html", field: "weight", title: "起重量(kg)", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatGateFeatures(row), "weight") },
-    { type: "html", field: "weightRate", title: "载重百分比", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatGateFeatures(row), "weightRate") },
-    { type: "html", field: "height", title: "高度(m)", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.divide(XEUtils.get(formatGateFeatures(row), "height"), 10) },
-    { type: "html", field: "powerRate", title: "力矩百分比", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(formatGateFeatures(row), "powerRate") },
-    { type: "html", field: "rotationAngle", title: "回转角度(°)", minWidth: 110, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.divide(XEUtils.get(formatGateFeatures(row), "rotationAngle"), 10) },
-    { type: "html", field: "tiltAngle", title: "倾角(°)", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || formatTilt(row) },
-    { type: "html", field: "tiltAngleRate", title: "倾角百分比", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.toInteger(XEUtils.multiply(XEUtils.divide(formatTilt(row), XEUtils.divide(XEUtils.get(formatGate(row), "features.maxTilt"), 10)), 100)) },
-    { type: "html", field: "windSpeed", title: "风力(级)", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.divide(XEUtils.get(formatGateFeatures(row), "windSpeed"), 10) },
-    { type: "html", field: "windSpeedRate", title: "风力百分比", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.toInteger(XEUtils.multiply(XEUtils.divide(XEUtils.get(formatGateFeatures(row), "windSpeed"), XEUtils.get(formatGate(row), "features.maxWindSpeed")), 100)) },
-    { type: "html", field: "workAngle", title: "幅度(m)", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.divide(XEUtils.get(formatGateFeatures(row), "workAngle"), 10) }
-])
-
-// 显示隐藏 筛选表单
-const xGridTable = ref();
-const toggleFormEnabled = () => xGridTable.value.toggleFormEnabled();
-
-onMounted(() => window.addEventListener("setItemEvent", ({ key, newValue }) => key === "TOWER_GATE" && newValue && xGridTable.value?.reloadColumn(columns)));
-onUnmounted(() => window.removeEventListener("setItemEvent", () => {}));
-</script>

+ 0 - 98
src/views/tower/warning/index.vue

@@ -1,98 +0,0 @@
-<template>
-	<el-container class="is-vertical">
-        <sc-page-header @filter="toggleFormEnabled"></sc-page-header>
-
-        <scTable ref="xGridTable" :apiObj="$API.tower.warnings" framework="zeroLiteOld" :toolbarConfig="toolbarConfig" :formConfig="formConfig" :paramsColums="paramsColums" :columns="columns"></scTable>
-	</el-container>
-</template>
-
-<script setup>
-import moment from "moment";
-import XEUtils from "xe-utils";
-import TOOL from "@/utils/tool";
-import { towerWarningDic } from "@/utils/basicDic";
-import { mapFormItemInput, mapFormItemSelect, mapFormItemDatePicker } from "@/components/scTable/helper";
-
-const formatGate = row => XEUtils.find(TOOL.data.get("TOWER_GATE"), item => item.id === XEUtils.get(row, "record.mounted.id"));
-// const formatGateFeatures = row => XEUtils.get(row, "record.features", {});
-const formatTilt = row => {
-    const feaTilt = XEUtils.divide(XEUtils.get(XEUtils.get(row, "record.features"), "tiltAngle"), 100);
-    const tilt = feaTilt > 0 ? feaTilt : XEUtils.add(360, feaTilt);
-    return tilt >= 180 ? XEUtils.subtract(360, tilt) : tilt;
-};
-const formatWarn = ({ row, column }) => XEUtils.includes(XEUtils.get(row, "features.warnings", []), `WARNING_${column.field}`) ? "tower-warning-column" : "";
-
-
-const mountedConfig = reactive({
-    storageKey: "TOWER_GATE",
-    slot: {
-        style: { float: "right", paddingLeft: "6px", color: "#8492a6" }
-    },
-    props: { multiple: true, collapseTags: true, collapseTagsTooltip: true },
-    optionProps: { label: "mountedName", value: "id", slot: ({ data }) => XEUtils.get(XEUtils.find(TOOL.data.get("PROJECT"), item => item.fpiId === XEUtils.get(XEUtils.first(data.devices), "project_id")), "projectName") },
-    events: {
-        change: data => XEUtils.assign(formConfig.data, data)
-    }
-})
-
-const daterangeConfig = reactive({
-    span: 9,
-    resetValue: [moment().subtract(1, "hour").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")],
-    props: {
-        type: "datetimerange",
-        clearable: false,
-        startPlaceholder: "开始时间",
-        endPlaceholder: "结束时间",
-        format: "YYYY-MM-DD HH:mm"
-    }
-})
-
-const toolbarConfig = reactive({
-    enabled: true,
-    print: false
-})
-
-const formConfig = reactive({
-    data: {
-        createTime: [moment().subtract(30, "minute").format("YYYY-MM-DD HH:mm:ss"), moment().format("YYYY-MM-DD HH:mm:ss")]
-    },
-    items: [
-        mapFormItemSelect("mountedID", "设备安装点", mountedConfig),
-        mapFormItemDatePicker("createTime", "告警时间", daterangeConfig)
-    ]
-})
-
-const paramsColums = reactive([
-    { type: "relation", column: "mountedID", symbol: "eq" },
-    { type: "relation", column: "createTime", symbol: "eqgt", field: "createTime[0]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "relation", column: "createTime", symbol: "eqlt", field: "createTime[1]", formatValue: value => moment(value).format("YYYY-MM-DDTHH:mm:ss[Z]") },
-    { type: "orderby", column: "createTime", symbol: "desc" }
-]);
-
-const columns = reactive([
-    { type: "seq", fixed: "left", width: 60 },
-    { type: "html", field: "deviceCode", title: "设备唯一标识", fixed: "left", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(row, "record.features.device") },
-    { type: "html", field: "projectName", title: "所属项目", minWidth: 200, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(XEUtils.find(TOOL.data.get("PROJECT"), item => item.fpiId === XEUtils.get(XEUtils.find(XEUtils.get(formatGate(row), "devices", []), item => item.device_num == XEUtils.get(row, "record.features.device")), "project_id")), "projectName") },
-    { type: "html", field: "groundName", title: "工地场区", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(row, "record.mounted.ground.groundName") },
-    { type: "html", field: "mountedName", title: "安装点名称", minWidth: 150, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(row, "record.mounted.mountedName") },
-    { type: "html", field: "taskTime", title: "告警时间", minWidth: 160, sortable: true, formatter: ({ cellValue, row }) => cellValue || TOOL.dateFormat(row.createTime) },
-    { type: "html", field: "alarmType", title: "告警类型", minWidth: 130, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.map(XEUtils.get(row, "features.warnings", []), item => XEUtils.get(towerWarningDic, item, item)).join() },
-    { className: formatWarn, type: "html", field: "WEIGHT", title: "起重量(kg)", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(XEUtils.get(row, "record.features"), "weight") },
-    { type: "html", field: "weightRate", title: "载重百分比", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(XEUtils.get(row, "record.features"), "weightRate") },
-    { type: "html", field: "height", title: "高度(m)", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.divide(XEUtils.get(XEUtils.get(row, "record.features"), "height"), 10) },
-    { className: formatWarn, type: "html", field: "POWER", title: "力矩百分比", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(XEUtils.get(row, "record.features"), "powerRate") },
-    { type: "html", field: "rotationAngle", title: "回转角度(°)", minWidth: 110, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.divide(XEUtils.get(XEUtils.get(row, "record.features"), "rotationAngle"), 10) },
-    { className: formatWarn, type: "html", field: "TILT", title: "倾角(°)", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || formatTilt(row) },
-    { type: "html", field: "tiltAngleRate", title: "倾角百分比", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.toInteger(XEUtils.multiply(XEUtils.divide(formatTilt(row), XEUtils.divide(XEUtils.get(formatGate(row), "features.maxTilt"), 10)), 100)) },
-    { className: formatWarn, type: "html", field: "WIND_SPEED", title: "风力(级)", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.divide(XEUtils.get(XEUtils.get(row, "record.features"), "windSpeed"), 10) },
-    { type: "html", field: "windSpeedRate", title: "风力百分比", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.toInteger(XEUtils.multiply(XEUtils.divide(XEUtils.get(XEUtils.get(row, "record.features"), "windSpeed"), XEUtils.get(formatGate(row), "features.maxWindSpeed")), 100)) },
-    { type: "html", field: "workAngle", title: "幅度(m)", minWidth: 100, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.divide(XEUtils.get(XEUtils.get(row, "record.features"), "workAngle"), 10) }
-])
-
-// 显示隐藏 筛选表单
-const xGridTable = ref();
-const toggleFormEnabled = () => xGridTable.value.toggleFormEnabled();
-
-onMounted(() => window.addEventListener("setItemEvent", ({ key, newValue }) => key === "TOWER_GATE" && newValue && xGridTable.value?.reloadColumn(columns)));
-onUnmounted(() => window.removeEventListener("setItemEvent", () => {}));
-</script>

+ 3 - 3
src/views/userCenter/index.vue

@@ -37,7 +37,7 @@
         </el-card>
         
         <el-card header="基本资料">
-            <el-form ref="userForm" :model="form" :rules="rules" label-width="120px">
+            <el-form ref="userForm" :model="form" :rules="rules" label-width="110">
                 <el-form-item label="用户昵称:" prop="nickName">
                     <el-input v-model="form.nickName"></el-input>
                 </el-form-item>
@@ -54,9 +54,9 @@
                     </el-radio-group>
                 </el-form-item>
                 
-                <el-form-item>
+                <!-- <el-form-item>
                     <el-button type="primary" @click="submit">保存</el-button>
-                </el-form-item>
+                </el-form-item> -->
             </el-form>
         </el-card>
     </el-main>