Jelajahi Sumber

路由重构

zhuangyunsheng 2 bulan lalu
induk
melakukan
865ee932c7
53 mengubah file dengan 761 tambahan dan 284 penghapusan
  1. 5 13
      src/api/model/system.js
  2. 64 0
      src/components/scImage/index.vue
  3. 6 1
      src/components/scTable/index.vue
  4. 2 1
      src/components/scUpload/fileViewer.vue
  5. 3 16
      src/components/scUpload/index.vue
  6. 3 1
      src/components/scUpload/multiple.vue
  7. 31 31
      src/components/scUpload/videoViewer.vue
  8. 189 0
      src/components/scUploadMinio/file.vue
  9. 7 42
      src/components/scUpload/minio.vue
  10. 89 67
      src/config/route.js
  11. 0 0
      src/views/dataDiagnosis/tag/detail.vue
  12. 0 0
      src/views/dataDiagnosis/tag/index.vue
  13. 1 1
      src/views/dataMock/aihazard/components/record/detail.vue
  14. 1 6
      src/views/dataMock/aihazard/components/record/index.vue
  15. 2 2
      src/views/dataMock/aihazard/detail.vue
  16. 2 2
      src/views/dataMock/broadcast/detail.vue
  17. 1 6
      src/views/dataMock/carwash/components/record/index.vue
  18. 2 2
      src/views/dataMock/carwash/detail.vue
  19. 2 2
      src/views/dataMock/edgeprot/detail.vue
  20. 2 2
      src/views/dataMock/elevator/detail.vue
  21. 2 2
      src/views/dataMock/env/detail.vue
  22. 1 1
      src/views/dataMock/parking/components/record/detail.vue
  23. 1 7
      src/views/dataMock/parking/components/record/index.vue
  24. 2 2
      src/views/dataMock/parking/detail.vue
  25. 1 1
      src/views/dataMock/perimeter/components/record/detail.vue
  26. 1 6
      src/views/dataMock/perimeter/components/record/index.vue
  27. 2 2
      src/views/dataMock/perimeter/detail.vue
  28. 2 2
      src/views/dataMock/smoke/detail.vue
  29. 2 2
      src/views/dataMock/spray/detail.vue
  30. 2 2
      src/views/dataMock/standard/detail.vue
  31. 2 2
      src/views/dataMock/tower/detail.vue
  32. 2 2
      src/views/dataMock/warehouse/detail.vue
  33. 4 4
      src/views/home/index.vue
  34. 0 0
      src/views/milestone/main.js
  35. 0 0
      src/views/milestone/matters/components/index.js
  36. 85 0
      src/views/milestone/matters/components/record/detail.vue
  37. 32 9
      src/views/system/todoTask/components/record/index.vue
  38. 0 0
      src/views/milestone/matters/components/template.vue
  39. 63 0
      src/views/milestone/matters/detail.vue
  40. 13 4
      src/views/system/todoTask/index.vue
  41. 0 0
      src/views/milestone/plan/components/index.js
  42. 6 6
      src/views/system/milestone/components/record/detail.vue
  43. 9 5
      src/views/system/milestone/components/record/index.vue
  44. 0 0
      src/views/milestone/plan/components/template.vue
  45. 77 0
      src/views/milestone/plan/detail.vue
  46. 13 4
      src/views/system/milestone/index.vue
  47. 9 9
      src/views/system/acceptItems/detail.vue
  48. 10 9
      src/views/system/acceptItems/index.vue
  49. 0 0
      src/views/project/acceptItems/main.js
  50. 0 0
      src/views/project/maintenance/detail.vue
  51. 2 2
      src/views/basic/project/index.vue
  52. 6 6
      src/views/basic/project/items.vue
  53. 0 0
      src/views/project/maintenance/main.js

+ 5 - 13
src/api/model/system.js

@@ -35,7 +35,7 @@ export default {
 
         bindItem: {
             url: `${config.API_URL}/ops/acceptanceProject`,
-            name: "项目绑定验收清单项",
+            name: "项目绑定应用项",
             get: async function (data = {}) {
                 return await http.post(`${this.url}/getList`, data);
             },
@@ -51,7 +51,7 @@ export default {
 	},
 
     acceptItems: {
-		name: "获取验收清单项",
+		name: "获取应用项",
 		url: `${config.API_URL}/ops/acceptanceItems`,
         get: async function (data = {}) {
 			return await http.post(`${this.url}/getPage`, data);
@@ -91,11 +91,6 @@ export default {
 
         del: async function (data = {}) {
 			return await http.post(`${this.url}/remove`, data);
-		},
-        
-        // 一键复制
-        copyData: async function (data = {}) {
-			return await http.post(`${this.url}/remove`, data);
 		}
     },
 
@@ -110,16 +105,13 @@ export default {
 			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);
+        // 一键复制
+        copyData: async function (data = {}) {
+			return await http.post(`${this.url}/copyData`, data);
 		}
     },
 

+ 64 - 0
src/components/scImage/index.vue

@@ -0,0 +1,64 @@
+<!--
+ * @Descripttion: xgplayer二次封装
+ * @version: 1.1
+ * @Date: 2021年11月29日12:10:06
+ * @LastEditTime: 2023年12月22日12:02:50
+-->
+
+<template>
+    <vxe-image class="sc-image" :src="formatUrl" :show-preview="false" @click="showPreview"></vxe-image>
+</template>
+
+<script setup>
+import { VxeUI } from "vxe-pc-ui";
+import API from "@/api";
+
+const props = defineProps({
+    prefix: { type: String, default: "folder" },
+    fileName: { type: String, default: "抓拍图片" },
+    path: { type: String, default: "" }
+})
+
+const formatUrl = computed(() => {
+    if (props.prefix == "folder") return "/api/folder/" + props.path;
+    return props.path.startsWith(`/${props.prefix}`) ? "/minio" + props.path : "data:image/jpeg;base64," + props.path;
+})
+
+const showPreview = () => {
+    VxeUI.previewImage({
+        showDownloadButton: true,
+        urlList: [formatUrl.value],
+        downloadMethod: () => handleDownload()
+    });
+}
+
+const handleDownload = () => {
+    if (props.prefix == "folder" || props.path.startsWith(`/${props.prefix}`)) {
+        const key = props.prefix == "folder" ? "folder" : "minio";
+
+        API.common[key].download(props.path).then(res => {
+            const a = document.createElement("a");
+            const blob = new Blob([res], { type: "image/jpeg" });
+            a.download = props.fileName;
+            a.href = URL.createObjectURL(blob);
+            a.click();
+        });
+    } else  {
+        const binaryData = atob(props.path);
+        const length = binaryData.length;
+        const uint8Array = new Uint8Array(length);
+        for (let i = 0; i < length; i++) {
+            uint8Array[i] = binaryData.charCodeAt(i);
+        }
+        const a = document.createElement("a");
+        const blob = new Blob([uint8Array], { type: "image/jpeg" });
+        a.download = props.fileName;
+        a.href = URL.createObjectURL(blob);
+        a.click();
+    }
+}
+
+defineExpose({
+    handleDownload
+})
+</script>

+ 6 - 1
src/components/scTable/index.vue

@@ -203,8 +203,13 @@ const resizeTable = () => {
 
 // 获取数据
 const getData = () => {
-    if (!props.apiObj) return;
     nextTick(() => {
+        if (!props.apiObj) {
+            gridOptions.value.data = [];
+            gridOptions.value.pagerConfig.total = 0;
+            return;
+        }
+
         gridOptions.value.loading = true;
         const reqData = config.framework[props.framework].queryData(gridOptions.value, props.paramsColums);
         const query = props.apiObj.url == "/ops/projectInfo" ? XEUtils.omit(reqData, "orderBy") : reqData;

+ 2 - 1
src/components/scUpload/fileViewer.vue

@@ -70,7 +70,8 @@ export default {
                 if (this.fileType == "image") {
                     VxeUI.previewImage({
                         showDownloadButton: true,
-                        urlList: ["/api/folder/" + uploadFile.path]
+                        urlList: ["/api/folder/" + uploadFile.path],
+                        downloadMethod: () => this.downloadFile()
                     })
                 } else if (this.fileType == "video") this.showVideoViewer = true;
                 else {

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

@@ -7,7 +7,7 @@
 			<el-image class="image" :src="file.tempFile" fit="cover"></el-image>
 		</div>
 		<div v-if="file && file.status=='success'" class="sc-upload__img">
-            <vxe-image v-if="isImage(file.mineType)" class="image" :src="'/api/folder/' + file.path" :toolbar-config="imageToolbar"></vxe-image>
+            <sc-image v-if="isImage(file.mineType)" ref="imageRef" class="image" :fileName="file.name" :path="file.path"></sc-image>
 			<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">
@@ -68,7 +68,7 @@ export default {
         width: { type: Number, default: 148 },
         height: { type: Number, default: 148 },
         title: { type: String, default: "" },
-        accept: { type: String, default: "image/gif, image/jpeg, image/png, video/mp4 , video/avi" },
+        accept: { type: String, default: "image/gif, image/jpeg, image/png, video/mp4, video/avi" },
         icon: { type: String, default: "el-icon-plus" },
         maxSize: { type: Number, default: 50 },
         drag: { type: Boolean, default: false },
@@ -82,11 +82,6 @@ export default {
 
     data() {
         return {
-            imageToolbar: {
-                print: false,
-                download: true
-            },
-
             value: "{}",
             file: null,
             style: {
@@ -252,15 +247,7 @@ export default {
         },
 
         handleDownload() {
-            const file = JSON.parse(this.value);
-            
-            this.$API.common.folder.download(file.path).then(res => {
-                const a = document.createElement("a");
-                const blob = new Blob([res], { type: "image/jpeg" });
-                a.download = `/api/folder/${file.path}`.replaceAll("/", "_");
-                a.href = URL.createObjectURL(blob);
-                a.click();
-            });
+            this.$refs.imageRef.handleDownload();
         }
     }
 }

+ 3 - 1
src/components/scUpload/multiple.vue

@@ -3,7 +3,7 @@
 		<el-upload ref="uploader" list-type="picture-card"
 			v-model:file-list="defaultFileList"
 			action=""
-			accept="image/gif, image/jpeg, image/png, video/mp4 , video/avi"
+			:accept="accept"
 			:limit="limit"
 			:multiple="multiple"
 			:disabled="disabled"
@@ -54,6 +54,7 @@ import { fileTypes } from "./main";
 export default {
     props: {
         modelValue: { type: Array, default: () => [] },
+        accept: { type: String, default: "image/gif, image/jpeg, image/png, video/mp4, video/avi" },
         tip: { type: String, default: "" },
         maxSize: { type: Number, default: 50 },
         limit: { type: Number, default: 0 },
@@ -169,6 +170,7 @@ export default {
         request(param) {
             const data = new FormData();
             data.append(param.filename, param.file);
+            
             this.$API.common.folder.up(data, {
                 onUploadProgress: e => {
                     const percent = parseInt(((e.loaded / e.total) * 100) | 0, 10)

+ 31 - 31
src/components/scUpload/videoViewer.vue

@@ -14,40 +14,40 @@
 </template>
 
 <script>
- 	import "element-plus/es/components/image-viewer/style/css"
-
-	export default {
-		emits: ["close"],
-
-		props: {
-			hideOnModal: { type: Boolean, default: false }, // 是否支持通过点击遮罩层关闭
-			hideOnEscape: { type: Boolean, default: false }, // 是否支持通过按下ESC关闭
-			videoUrl: { type: String, default: "" },
-		},
-
-		data() {
-			return {}
-		},
-
-		created() {
-			document.addEventListener("keydown", this.keydownHandler);
-		},
-
-		beforeUnmount() {
-			document.removeEventListener("keydown", this.keydownHandler);
-		},
-
-		methods: {
-			keydownHandler(e) {
-				this.hideOnEscape && e.code == "Escape" && this.$emit("close");
-				e.preventDefault();
-			}
-		}
-	}
+import "element-plus/es/components/image-viewer/style/css"
+
+export default {
+    emits: ["close"],
+
+    props: {
+        hideOnModal: { type: Boolean, default: false }, // 是否支持通过点击遮罩层关闭
+        hideOnEscape: { type: Boolean, default: false }, // 是否支持通过按下ESC关闭
+        videoUrl: { type: String, default: "" }
+    },
+
+    data() {
+        return {}
+    },
+
+    created() {
+        document.addEventListener("keydown", this.keydownHandler);
+    },
+
+    beforeUnmount() {
+        document.removeEventListener("keydown", this.keydownHandler);
+    },
+
+    methods: {
+        keydownHandler(e) {
+            this.hideOnEscape && e.code == "Escape" && this.$emit("close");
+            e.preventDefault();
+        }
+    }
+}
 </script>
 
 <style lang="scss" scoped>
 .el-image-viewer__canvas {
-  padding: 12%;
+    padding: 12%;
 }
 </style>

+ 189 - 0
src/components/scUploadMinio/file.vue

@@ -0,0 +1,189 @@
+<template>
+	<div class="sc-upload-file">
+		<el-upload ref="uploader"
+			:class="hideAdd && 'el-upload-hide-add'"
+			v-model:file-list="defaultFileList"
+			action=""
+			:accept="accept"
+			:limit="limit"
+			:multiple="multiple"
+			:disabled="disabled"
+			show-file-list
+			:http-request="request"
+			:before-upload="before"
+			:before-remove="beforeRemove"
+			:on-success="success"
+			:on-error="error"
+			:on-preview="handlePreview"
+			:on-exceed="handleExceed">
+			<slot>
+				<el-button type="primary" :disabled="disabled">Click to upload</el-button>
+			</slot>
+			<template #tip>
+				<div v-if="tip" class="el-upload__tip">{{ tip }}</div>
+			</template>
+		</el-upload>
+		<span style="display: none!important;"><el-input v-model="value"></el-input></span>
+	</div>
+
+    <file-viewer v-if="showViewer" ref="fileViewer" @closed="showViewer = false"></file-viewer>
+</template>
+
+<script>
+export default {
+    props: {
+        modelValue: { type: Array, default: () => [] },
+        tip: { type: String, default: "" },
+        accept: { type: String, default: "" },
+        maxSize: { type: Number, default: 50 },
+        limit: { type: Number, default: 0 },
+        multiple: { type: Boolean, default: true },
+        disabled: { type: Boolean, default: false },
+        hideAdd: { type: Boolean, default: false },
+        onSuccess: { type: Function, default: () => { return true } }
+    },
+
+    data() {
+        return {
+            value: "",
+            defaultFileList: [],
+            
+            showViewer: false
+        }
+    },
+
+    watch: {
+        modelValue(val) {
+            if (JSON.stringify(val) != JSON.stringify(this.formatArr(this.defaultFileList))) {
+                this.defaultFileList = val;
+                this.value = val;
+            }
+        },
+
+        defaultFileList: {
+            deep: true,
+            handler(val) {
+                this.$emit("update:modelValue", this.formatArr(val));
+                this.value = val.map(v => v.path).join(",");
+            }
+        }
+    },
+
+    mounted() {
+        this.defaultFileList = this.modelValue;
+        this.value = this.modelValue;
+    },
+
+    methods: {
+        // 格式化数组值
+        formatArr(arr) {
+            return arr.map(item => ({ id: item.id, name: item.name, mineType: item.mineType, path: item.path }));
+        },
+
+        before(file) {
+            const maxSize = file.size / 1024 / 1024 < this.maxSize;
+            if (!maxSize) {
+                this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`);
+                return false;
+            }
+        },
+
+        success(res, file) {
+            let os = this.onSuccess(res, file);
+            if (os != undefined && os == false) return false;
+            
+            file.name = res.fileName;
+            file.path = res.path;
+            file.mineType = res.mineType;
+        },
+
+        error(message) {
+            this.$notify.error({ title: "上传文件未成功", message });
+        },
+
+        beforeRemove({ id, name, path }) {
+            return this.$confirm(`是否移除 ${name}? 此操作不可逆!`, "提示", {
+                type: "warning",
+                confirmButtonText: "移除"
+            }).then(() => {
+                const entityID = id || path;
+                this.$API.common.folder.rm(entityID).then(res => {
+                    if (res.code == 200) return true;
+                    else return false;
+                }).catch(() => {
+                    return false;
+                });
+            }).catch(() => {
+                return false;
+            });
+        },
+
+        handleExceed() {
+            this.$message.warning(`当前设置最多上传 ${this.limit} 个文件,请移除后上传!`);
+        },
+
+        handlePreview(uploadFile) {
+            this.showViewer = true;
+            nextTick(() => this.$refs.fileViewer.init(uploadFile));
+        },
+
+        request(param) {
+            const data = new FormData();
+            data.append(param.filename, param.file);
+            
+            this.$API.common.folder.up(data, {
+                onUploadProgress: e => {
+                    const percent = parseInt(((e.loaded / e.total) * 100) | 0, 10);
+                    param.onProgress({ percent });
+                },
+                transformRequest: [function (data, headers) {
+                    // 移除 Axios 可能自动设置的 Content-Type,让浏览器自动设置
+                    delete headers["Content-Type"];
+                    return data;
+                }]
+            }).then(res => {
+                if (res.code == 200) param.onSuccess({ path: res.expands.file, fileName: param.file.name, mineType: param.file.type })
+                else param.onError(res.message || "未知错误");
+            }).catch(err => param.onError(err));
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+.el-form-item.is-error .sc-upload-file:deep(.el-upload-dragger) {
+  border-color: var(--el-color-danger);
+}
+
+.sc-upload-file {
+  width: 100%;
+
+  :deep(.el-upload-list__item) {
+    transition: none !important;
+  }
+
+  .el-upload-hide-add {
+    :deep(.el-upload) {
+      display: none;
+    }
+
+    :deep(.el-upload-list) {
+      margin-top: 0;
+
+      .el-upload-list__item {
+        &:hover {
+          background-color: transparent;
+        }
+
+        .el-upload-list__item-info {
+          width: 100%;
+        }
+
+        .el-upload-list__item-status-label {
+          display: none;
+        }
+      }
+    }
+  }
+}
+</style>

+ 7 - 42
src/components/scUpload/minio.vue

@@ -7,7 +7,7 @@
 			<el-image class="image" :src="file.tempFile" fit="cover"></el-image>
 		</div>
 		<div v-if="file && file.status=='success'" class="sc-upload__img">
-            <vxe-image class="image" :src="formatUrl(file.path)" :toolbar-config="imageToolbar"></vxe-image>
+            <sc-image ref="imageRef" class="image" :prefix="apiKey" :fileName="fileName" :path="file.path"></sc-image>
 			
 			<div class="sc-upload__img-actions" v-if="!disabled">
                 <el-button type="primary" @click="handleDownload">
@@ -59,6 +59,8 @@
 </template>
 
 <script>
+import { VxeUI } from "vxe-pc-ui";
+
 export default {
     props: {
         modelValue: { type: Object, default: () => {} },
@@ -68,6 +70,7 @@ export default {
         title: { type: String, default: "" },
         accept: { type: String, default: "image/jpeg, image/png" },
         icon: { type: String, default: "el-icon-plus" },
+        fileName: { type: String, default: "抓拍图片" },
         maxSize: { type: Number, default: 50 },
         drag: { type: Boolean, default: false },
         disabled: { type: Boolean, default: false },
@@ -80,11 +83,6 @@ export default {
 
     data() {
         return {
-            imageToolbar: {
-                print: false,
-                download: true
-            },
-
             value: "{}",
             file: null,
             style: {
@@ -117,10 +115,6 @@ export default {
     },
     
     methods: {
-        formatUrl(path) {
-            return path.startsWith(`/${this.apiKey}`) ? "/minio" + path : "data:image/jpeg;base64," + path;
-        },
-
         newFile(data) {
             this.file = Object.keys(data).length ? { status: "success", ...data } : null;
         },
@@ -146,9 +140,8 @@ export default {
 
         handleRemove() {
             const file = JSON.parse(this.value)
-            const fileName = this.formatUrl(file.path).replaceAll("/", "_");
             
-            this.$confirm(`是否移除 ${fileName}? 此操作不可逆!`, "提示", {
+            this.$confirm(`是否移除 ${this.fileName}? 此操作不可逆!`, "提示", {
                 type: "warning",
                 confirmButtonText: "移除"
             }).then(() => {
@@ -219,7 +212,7 @@ export default {
         request(param) {
             const data = new FormData();
             data.append(param.filename, param.file);
-
+            
             this.$API.common.minio[this.apiKey](data, {
                 onUploadProgress: e => {
                     const percent = parseInt(((e.loaded / e.total) * 100) | 0, 10);
@@ -231,36 +224,8 @@ export default {
             }).catch(err => param.onError(err));
         },
 
-        videoPlay() {
-            this.showViewer = true;
-            nextTick(() => this.$refs.fileViewer.init(this.file));
-        },
-
         handleDownload() {
-            const file = JSON.parse(this.value);
-            const fileName = this.formatUrl(file.path).replaceAll("/", "_");
-            
-            if (file.path.startsWith(`/${this.apiKey}`)) {
-                this.$API.common.minio.download(file.path).then(res => {
-                    const a = document.createElement("a");
-                    const blob = new Blob([res], { type: "image/jpeg" });
-                    a.download = fileName;
-                    a.href = URL.createObjectURL(blob);
-                    a.click();
-                });
-            } else  {
-                const binaryData = atob(file.path);
-                const length = binaryData.length;
-                const uint8Array = new Uint8Array(length);
-                for (let i = 0; i < length; i++) {
-                    uint8Array[i] = binaryData.charCodeAt(i);
-                }
-                const a = document.createElement("a");
-                const blob = new Blob([uint8Array], { type: "image/jpeg" });
-                a.download = fileName;
-                a.href = URL.createObjectURL(blob);
-                a.click();
-            }
+            this.$refs.imageRef.handleDownload();
         }
     }
 }

+ 89 - 67
src/config/route.js

@@ -18,60 +18,42 @@ const routes = [
         component: "userCenter"
     },
     {
-        name: "basic",
-        path: "/basic",
-        meta: { title: "基础数据管理", icon: "ri:apps-line" },
-        redirect: "/basic/project",
+        name: "project",
+        path: "/project",
+        meta: { title: "项目管理", icon: "ix:projects" },
+        redirect: "/project/maintenance",
         children: [
             {
-                name: "project",
-                path: "/basic/project",
-                meta: { title: "项目管理", icon: "ix:projects" },
-                component: "basic/project"
-            },
-            {
-                name: "supplier",
-                path: "/basic/supplier",
-                meta: { title: "供应商管理", icon: "ant-design:phone-outlined" },
-                component: "basic/supplier"
+                name: "projectMaintenance",
+                path: "/project/maintenance",
+                meta: { title: "项目维护", icon: "ant-design:cloud-upload-outlined" },
+                component: "project/maintenance"
             },
             {
-                name: "customer",
-                path: "/basic/customer",
-                meta: { title: "客户管理", icon: "garden:customer-lists-fill-26" },
-                component: "basic/customer"
+                name: "acceptItems",
+                path: "/project/acceptItems",
+                meta: { title: "应用项管理", icon: "pajamas:list-bulleted" },
+                component: "project/acceptItems"
             }
         ]
     },
     {
-        name: "equipment",
-        path: "/equipment",
-        meta: { title: "设备管理", icon: "ant-design:code-sandbox-outlined" },
-        redirect: "/equipment/facerec",
+        name: "milestone",
+        path: "/milestone",
+        meta: { title: "里程碑管理", icon: "heroicons-outline:flag" },
+        redirect: "/milestone/plan",
         children: [
             {
-                name: "equipmentFacerec",
-                path: "/equipment/facerec",
-                meta: { title: "实名制人脸识别设备", icon: "tabler:face-id" },
-                component: "equipment/facerec"
+                name: "nodePlan",
+                path: "/milestone/plan",
+                meta: { title: "节点规划", icon: "streamline:business-progress-bar-2-remix" },
+                component: "milestone/plan"
             },
             {
-                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: "nodeMatters",
+                path: "/milestone/matters",
+                meta: { title: "节点事项", icon: "streamline-ultimate:task-list-pin" },
+                component: "milestone/matters"
             }
         ]
     },
@@ -161,6 +143,20 @@ const routes = [
             }
         ]
     },
+    {
+        name: "dataDiagnosis",
+        path: "/dataDiagnosis",
+        meta: { title: "数据诊断与分析", icon: "icon-park-outline:analysis" },
+        redirect: "/dataDiagnosis/tag",
+        children: [
+            {
+                name: "tag",
+                path: "/dataDiagnosis/tag",
+                meta: { title: "标签管理", icon: "mingcute:tag-line" },
+                component: "dataDiagnosis/tag"
+            }
+        ]
+    },
     {
         name: "EasyRun",
         path: "/easyRun",
@@ -181,6 +177,38 @@ const routes = [
             }
         ]
     },
+    {
+        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: "warranty",
         path: "/warranty",
@@ -195,39 +223,33 @@ const routes = [
         redirect: "/afterSales/afterSales",
         children: []
     },
-
     {
-        name: "system",
-        path: "/system",
-        meta: { title: "系统管理", icon: "fluent:apps-settings-16-filled" },
-        redirect: "/system/milestone",
+        name: "basic",
+        path: "/basic",
+        meta: { title: "基础数据管理", icon: "ri:apps-line" },
+        redirect: "/basic/supplier",
         children: [
             {
-                name: "milestone",
-                path: "/system/milestone",
-                meta: { title: "里程碑管理", icon: "streamline:business-progress-bar-2-remix" },
-                component: "system/milestone"
-            },
-            {
-                name: "todoTask",
-                path: "/system/todoTask",
-                meta: { title: "待办任务", icon: "streamline-ultimate:task-list-pin" },
-                component: "system/todoTask"
-            },
-            {
-                name: "acceptItems",
-                path: "/system/acceptItems",
-                meta: { title: "验收清单项", icon: "pajamas:list-bulleted" },
-                component: "system/acceptItems"
+                name: "supplier",
+                path: "/basic/supplier",
+                meta: { title: "供应商管理", icon: "ant-design:phone-outlined" },
+                component: "basic/supplier"
             },
             {
-                name: "tag",
-                path: "/system/tag",
-                meta: { title: "标签管理", icon: "mingcute:tag-line" },
-                component: "system/tag"
+                name: "customer",
+                path: "/basic/customer",
+                meta: { title: "客户管理", icon: "garden:customer-lists-fill-26" },
+                component: "basic/customer"
             }
         ]
     },
+    {
+        name: "system",
+        path: "/system",
+        meta: { title: "系统管理", icon: "fluent:apps-settings-16-filled" },
+        redirect: "/system/index",
+        children: []
+    }
 ]
 
 export default routes;

src/views/system/tag/detail.vue → src/views/dataDiagnosis/tag/detail.vue


src/views/system/tag/index.vue → src/views/dataDiagnosis/tag/index.vue


+ 1 - 1
src/views/dataMock/aihazard/components/record/detail.vue

@@ -38,7 +38,7 @@ import XEUtils from "xe-utils";
 import API from "@/api";
 import TOOL from "@/utils/tool";
 import { aiTypeDic } from "@/utils/basicDic";
-import scUpload from "@/components/scUpload/minio";
+import scUpload from "@/components/scUploadMinio/index";
 
 const $emit = defineEmits(["success", "closed"]);
 const props = defineProps({

+ 1 - 6
src/views/dataMock/aihazard/components/record/index.vue

@@ -2,7 +2,7 @@
     <scTable ref="xGridTable" batchDel :apiObj="$API.aihazard.record" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns" v-bind="props.options">
         <template #default_imgUrl="{ row }">
             <template v-if="XEUtils.get(XEUtils.toStringJSON(row.features), 'bigImage.image')">
-                <vxe-image style="cursor: pointer;" :src="'/minio' + XEUtils.get(XEUtils.toStringJSON(row.features), 'bigImage.image')" width="40" height="40" :toolbar-config="imageToolbar"></vxe-image>
+                <sc-image style="cursor: pointer;" prefix="airecord" :path="XEUtils.get(XEUtils.toStringJSON(row.features), 'bigImage.image')" width="40" height="40"></sc-image>
             </template>
         </template>
         
@@ -128,11 +128,6 @@ const columns = reactive([
     { visible: !props.hideHandler, title: "操作", fixed: "right", width: 140, align: "center", slots: { default: "action" } }
 ])
 
-const imageToolbar = reactive({
-    print: false,
-    download: true
-})
-
 // 显示隐藏 筛选表单
 const xGridTable = ref();
 const getTableTotal = () => xGridTable.value.getTableData().tableData.length;

+ 2 - 2
src/views/dataMock/aihazard/detail.vue

@@ -16,9 +16,9 @@
                 </el-col>
                 <el-col v-if="form.targetProjectId" :md="12" :xs="24">
                     <el-form-item label="数据时间范围">
-                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置验收清单,<el-button type="primary" link @click="$router.push('/system/acceptItems')">去配置</el-button></template>
+                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置应用项,<el-button type="primary" link @click="$router.push('/project/acceptItems')">去配置</el-button></template>
                         <template v-else-if="acceptItem.beginTime">{{ $TOOL.dateFormat(acceptItem.beginTime, "YY.M.D") }}<span>-{{ acceptItem.endTime && $TOOL.dateFormat(acceptItem.endTime, "YY.M.D") || "至今" }}</span></template>
-                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/basic/project')">去配置</el-button></template>
+                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/project/maintenance')">去配置</el-button></template>
                     </el-form-item>
                 </el-col>
                 <el-col :md="12" :xs="24">

+ 2 - 2
src/views/dataMock/broadcast/detail.vue

@@ -16,9 +16,9 @@
                 </el-col>
                 <el-col v-if="form.targetProjectId" :md="12" :xs="24">
                     <el-form-item label="数据时间范围">
-                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置验收清单,<el-button type="primary" link @click="$router.push('/system/acceptItems')">去配置</el-button></template>
+                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置应用项,<el-button type="primary" link @click="$router.push('/project/acceptItems')">去配置</el-button></template>
                         <template v-else-if="acceptItem.beginTime">{{ $TOOL.dateFormat(acceptItem.beginTime, "YY.M.D") }}<span>-{{ acceptItem.endTime && $TOOL.dateFormat(acceptItem.endTime, "YY.M.D") || "至今" }}</span></template>
-                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/basic/project')">去配置</el-button></template>
+                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/project/maintenance')">去配置</el-button></template>
                     </el-form-item>
                 </el-col>
                 <el-col :md="12" :xs="24">

+ 1 - 6
src/views/dataMock/carwash/components/record/index.vue

@@ -2,7 +2,7 @@
     <scTable ref="xGridTable" batchDel :apiObj="$API.car_rinse.record" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns" v-bind="props.options">
         <template #default_imgUrl="{ row, column }">
             <template v-if="XEUtils.get(row, `folders.${column.field}.entities[0].path`)">
-                <vxe-image style="cursor: pointer;" :src="'/api/folder/' + XEUtils.get(row, `folders.${column.field}.entities[0].path`)" width="40" height="40" :toolbar-config="imageToolbar"></vxe-image>
+                <sc-image style="cursor: pointer;" :fileName="XEUtils.get(row, `folders.${column.field}.entities[0].name`)" :path="XEUtils.get(row, `folders.${column.field}.entities[0].path`)" width="40" height="40"></sc-image>
             </template>
         </template>
         
@@ -113,11 +113,6 @@ const columns = reactive([
     { visible: !props.hideHandler, title: "操作", fixed: "right", width: 140, align: "center", slots: { default: "action" } }
 ])
 
-const imageToolbar = reactive({
-    print: false,
-    download: true
-})
-
 // 显示隐藏 筛选表单
 const xGridTable = ref();
 const getTableTotal = () => xGridTable.value.getTableData().tableData.length;

+ 2 - 2
src/views/dataMock/carwash/detail.vue

@@ -16,9 +16,9 @@
                 </el-col>
                 <el-col v-if="form.targetProjectId" :md="12" :xs="24">
                     <el-form-item label="数据时间范围">
-                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置验收清单,<el-button type="primary" link @click="$router.push('/system/acceptItems')">去配置</el-button></template>
+                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置应用项,<el-button type="primary" link @click="$router.push('/project/acceptItems')">去配置</el-button></template>
                         <template v-else-if="acceptItem.beginTime">{{ $TOOL.dateFormat(acceptItem.beginTime, "YY.M.D") }}<span>-{{ acceptItem.endTime && $TOOL.dateFormat(acceptItem.endTime, "YY.M.D") || "至今" }}</span></template>
-                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/basic/project')">去配置</el-button></template>
+                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/project/maintenance')">去配置</el-button></template>
                     </el-form-item>
                 </el-col>
                 <el-col :md="12" :xs="24">

+ 2 - 2
src/views/dataMock/edgeprot/detail.vue

@@ -16,9 +16,9 @@
                 </el-col>
                 <el-col v-if="form.targetProjectId" :md="12" :xs="24">
                     <el-form-item label="数据时间范围">
-                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置验收清单,<el-button type="primary" link @click="$router.push('/system/acceptItems')">去配置</el-button></template>
+                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置应用项,<el-button type="primary" link @click="$router.push('/project/acceptItems')">去配置</el-button></template>
                         <template v-else-if="acceptItem.beginTime">{{ $TOOL.dateFormat(acceptItem.beginTime, "YY.M.D") }}<span>-{{ acceptItem.endTime && $TOOL.dateFormat(acceptItem.endTime, "YY.M.D") || "至今" }}</span></template>
-                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/basic/project')">去配置</el-button></template>
+                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/project/maintenance')">去配置</el-button></template>
                     </el-form-item>
                 </el-col>
                 <el-col :md="12" :xs="24">

+ 2 - 2
src/views/dataMock/elevator/detail.vue

@@ -22,9 +22,9 @@
                 </el-col>
                 <el-col v-if="form.targetProjectId" :md="12" :xs="24">
                     <el-form-item label="数据时间范围">
-                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置验收清单,<el-button type="primary" link @click="$router.push('/system/acceptItems')">去配置</el-button></template>
+                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置应用项,<el-button type="primary" link @click="$router.push('/project/acceptItems')">去配置</el-button></template>
                         <template v-else-if="acceptItem.beginTime">{{ $TOOL.dateFormat(acceptItem.beginTime, "YY.M.D") }}<span>-{{ acceptItem.endTime && $TOOL.dateFormat(acceptItem.endTime, "YY.M.D") || "至今" }}</span></template>
-                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/basic/project')">去配置</el-button></template>
+                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/project/maintenance')">去配置</el-button></template>
                     </el-form-item>
                 </el-col>
                 <el-col :md="12" :xs="24">

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

@@ -16,9 +16,9 @@
                 </el-col>
                 <el-col v-if="form.targetProjectId" :md="12" :xs="24">
                     <el-form-item label="数据时间范围">
-                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置验收清单,<el-button type="primary" link @click="$router.push('/system/acceptItems')">去配置</el-button></template>
+                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置应用项,<el-button type="primary" link @click="$router.push('/project/acceptItems')">去配置</el-button></template>
                         <template v-else-if="acceptItem.beginTime">{{ $TOOL.dateFormat(acceptItem.beginTime, "YY.M.D") }}<span>-{{ acceptItem.endTime && $TOOL.dateFormat(acceptItem.endTime, "YY.M.D") || "至今" }}</span></template>
-                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/basic/project')">去配置</el-button></template>
+                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/project/maintenance')">去配置</el-button></template>
                     </el-form-item>
                 </el-col>
                 <el-col :md="12" :xs="24">

+ 1 - 1
src/views/dataMock/parking/components/record/detail.vue

@@ -64,7 +64,7 @@ import XEUtils from "xe-utils";
 import API from "@/api";
 import TOOL from "@/utils/tool";
 import { plateColorDic, eventDic, formatMeta } from "@/views/dataMock/parking/main";
-import scUpload from "@/components/scUpload/minio";
+import scUpload from "@/components/scUploadMinio/index";
 
 const $emit = defineEmits(["success", "closed"]);
 const props = defineProps({

+ 1 - 7
src/views/dataMock/parking/components/record/index.vue

@@ -2,8 +2,7 @@
     <scTable ref="xGridTable" batchDel :apiObj="$API.parking.record" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns" v-bind="props.options">
         <template #default_imgUrl="{ row }">
             <template v-if="formatMeta(row).url">
-                <vxe-image v-if="XEUtils.startsWith(formatMeta(row).url, '/parking')" style="cursor: pointer;" :src="'/minio' + formatMeta(row).url" width="40" height="40" :toolbar-config="imageToolbar"></vxe-image>
-                <vxe-image v-else style="cursor: pointer;" :src="'data:image/jpeg;base64,' + formatMeta(row).url" width="40" height="40" :toolbar-config="imageToolbar"></vxe-image>
+                <sc-image style="cursor: pointer;" prefix="parking" :path="formatMeta(row).url" width="40" height="40"></sc-image>
             </template>
         </template>
         
@@ -132,11 +131,6 @@ const columns = reactive([
     { visible: !props.hideHandler, title: "操作", fixed: "right", width: 140, align: "center", slots: { default: "action" } }
 ])
 
-const imageToolbar = reactive({
-    print: false,
-    download: true
-})
-
 // 显示隐藏 筛选表单
 const xGridTable = ref();
 const getTableTotal = () => xGridTable.value.getTableData().tableData.length;

+ 2 - 2
src/views/dataMock/parking/detail.vue

@@ -16,9 +16,9 @@
                 </el-col>
                 <el-col v-if="form.targetProjectId" :md="12" :xs="24">
                     <el-form-item label="数据时间范围">
-                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置验收清单,<el-button type="primary" link @click="$router.push('/system/acceptItems')">去配置</el-button></template>
+                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置应用项,<el-button type="primary" link @click="$router.push('/project/acceptItems')">去配置</el-button></template>
                         <template v-else-if="acceptItem.beginTime">{{ $TOOL.dateFormat(acceptItem.beginTime, "YY.M.D") }}<span>-{{ acceptItem.endTime && $TOOL.dateFormat(acceptItem.endTime, "YY.M.D") || "至今" }}</span></template>
-                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/basic/project')">去配置</el-button></template>
+                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/project/maintenance')">去配置</el-button></template>
                     </el-form-item>
                 </el-col>
                 <el-col :md="12" :xs="24">

+ 1 - 1
src/views/dataMock/perimeter/components/record/detail.vue

@@ -32,7 +32,7 @@
 import XEUtils from "xe-utils";
 import API from "@/api";
 import TOOL from "@/utils/tool";
-import scUpload from "@/components/scUpload/minio";
+import scUpload from "@/components/scUploadMinio/index";
 
 const $emit = defineEmits(["success", "closed"]);
 const props = defineProps({

+ 1 - 6
src/views/dataMock/perimeter/components/record/index.vue

@@ -2,7 +2,7 @@
     <scTable ref="xGridTable" batchDel :apiObj="$API.aihazard.record" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns" v-bind="props.options">
         <template #default_imgUrl="{ row }">
             <template v-if="XEUtils.get(XEUtils.toStringJSON(row.features), 'bigImage.image')">
-                <vxe-image style="cursor: pointer;" :src="'/minio' + XEUtils.get(XEUtils.toStringJSON(row.features), 'bigImage.image')" width="40" height="40" :toolbar-config="imageToolbar"></vxe-image>
+                <sc-image style="cursor: pointer;" prefix="airecord" :path="XEUtils.get(XEUtils.toStringJSON(row.features), 'bigImage.image')" width="40" height="40"></sc-image>
             </template>
         </template>
         
@@ -115,11 +115,6 @@ const columns = reactive([
     { visible: !props.hideHandler, title: "操作", fixed: "right", width: 140, align: "center", slots: { default: "action" } }
 ])
 
-const imageToolbar = reactive({
-    print: false,
-    download: true
-})
-
 // 显示隐藏 筛选表单
 const xGridTable = ref();
 const getTableTotal = () => xGridTable.value.getTableData().tableData.length;

+ 2 - 2
src/views/dataMock/perimeter/detail.vue

@@ -16,9 +16,9 @@
                 </el-col>
                 <el-col v-if="form.targetProjectId" :md="12" :xs="24">
                     <el-form-item label="数据时间范围">
-                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置验收清单,<el-button type="primary" link @click="$router.push('/system/acceptItems')">去配置</el-button></template>
+                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置应用项,<el-button type="primary" link @click="$router.push('/project/acceptItems')">去配置</el-button></template>
                         <template v-else-if="acceptItem.beginTime">{{ $TOOL.dateFormat(acceptItem.beginTime, "YY.M.D") }}<span>-{{ acceptItem.endTime && $TOOL.dateFormat(acceptItem.endTime, "YY.M.D") || "至今" }}</span></template>
-                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/basic/project')">去配置</el-button></template>
+                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/project/maintenance')">去配置</el-button></template>
                     </el-form-item>
                 </el-col>
                 <el-col :md="12" :xs="24">

+ 2 - 2
src/views/dataMock/smoke/detail.vue

@@ -16,9 +16,9 @@
                 </el-col>
                 <el-col v-if="form.targetProjectId" :md="12" :xs="24">
                     <el-form-item label="数据时间范围">
-                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置验收清单,<el-button type="primary" link @click="$router.push('/system/acceptItems')">去配置</el-button></template>
+                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置应用项,<el-button type="primary" link @click="$router.push('/project/acceptItems')">去配置</el-button></template>
                         <template v-else-if="acceptItem.beginTime">{{ $TOOL.dateFormat(acceptItem.beginTime, "YY.M.D") }}<span>-{{ acceptItem.endTime && $TOOL.dateFormat(acceptItem.endTime, "YY.M.D") || "至今" }}</span></template>
-                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/basic/project')">去配置</el-button></template>
+                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/project/maintenance')">去配置</el-button></template>
                     </el-form-item>
                 </el-col>
                 <el-col :md="12" :xs="24">

+ 2 - 2
src/views/dataMock/spray/detail.vue

@@ -16,9 +16,9 @@
                 </el-col>
                 <el-col v-if="form.targetProjectId" :md="12" :xs="24">
                     <el-form-item label="数据时间范围">
-                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置验收清单,<el-button type="primary" link @click="$router.push('/system/acceptItems')">去配置</el-button></template>
+                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置应用项,<el-button type="primary" link @click="$router.push('/project/acceptItems')">去配置</el-button></template>
                         <template v-else-if="acceptItem.beginTime">{{ $TOOL.dateFormat(acceptItem.beginTime, "YY.M.D") }}<span>-{{ acceptItem.endTime && $TOOL.dateFormat(acceptItem.endTime, "YY.M.D") || "至今" }}</span></template>
-                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/basic/project')">去配置</el-button></template>
+                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/project/maintenance')">去配置</el-button></template>
                     </el-form-item>
                 </el-col>
                 <el-col :md="12" :xs="24">

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

@@ -16,9 +16,9 @@
                 </el-col>
                 <el-col v-if="form.targetProjectId" :md="12" :xs="24">
                     <el-form-item label="数据时间范围">
-                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置验收清单,<el-button type="primary" link @click="$router.push('/system/acceptItems')">去配置</el-button></template>
+                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置应用项,<el-button type="primary" link @click="$router.push('/project/acceptItems')">去配置</el-button></template>
                         <template v-else-if="acceptItem.beginTime">{{ $TOOL.dateFormat(acceptItem.beginTime, "YY.M.D") }}<span>-{{ acceptItem.endTime && $TOOL.dateFormat(acceptItem.endTime, "YY.M.D") || "至今" }}</span></template>
-                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/basic/project')">去配置</el-button></template>
+                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/project/maintenance')">去配置</el-button></template>
                     </el-form-item>
                 </el-col>
                 <el-col :md="12" :xs="24">

+ 2 - 2
src/views/dataMock/tower/detail.vue

@@ -22,9 +22,9 @@
                 </el-col>
                 <el-col v-if="form.targetProjectId" :md="12" :xs="24">
                     <el-form-item label="数据时间范围">
-                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置验收清单,<el-button type="primary" link @click="$router.push('/system/acceptItems')">去配置</el-button></template>
+                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置应用项,<el-button type="primary" link @click="$router.push('/project/acceptItems')">去配置</el-button></template>
                         <template v-else-if="acceptItem.beginTime">{{ $TOOL.dateFormat(acceptItem.beginTime, "YY.M.D") }}<span>-{{ acceptItem.endTime && $TOOL.dateFormat(acceptItem.endTime, "YY.M.D") || "至今" }}</span></template>
-                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/basic/project')">去配置</el-button></template>
+                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/project/maintenance')">去配置</el-button></template>
                     </el-form-item>
                 </el-col>
                 <el-col :md="12" :xs="24">

+ 2 - 2
src/views/dataMock/warehouse/detail.vue

@@ -16,9 +16,9 @@
                 </el-col>
                 <el-col v-if="form.targetProjectId" :md="12" :xs="24">
                     <el-form-item label="数据时间范围">
-                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置验收清单,<el-button type="primary" link @click="$router.push('/system/acceptItems')">去配置</el-button></template>
+                        <template v-if="XEUtils.isEmpty(acceptItem)">该项目未配置应用项,<el-button type="primary" link @click="$router.push('/project/acceptItems')">去配置</el-button></template>
                         <template v-else-if="acceptItem.beginTime">{{ $TOOL.dateFormat(acceptItem.beginTime, "YY.M.D") }}<span>-{{ acceptItem.endTime && $TOOL.dateFormat(acceptItem.endTime, "YY.M.D") || "至今" }}</span></template>
-                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/basic/project')">去配置</el-button></template>
+                        <template v-else>该项目未配置数据时间范围,<el-button type="primary" link @click="$router.push('/project/maintenance')">去配置</el-button></template>
                     </el-form-item>
                 </el-col>
                 <el-col :md="12" :xs="24">

+ 4 - 4
src/views/home/index.vue

@@ -34,8 +34,8 @@
         
             <el-row v-if="$store.state.project.projectId" v-loading="loading" :gutter="40">
                 <el-col v-if="!acceptItem.length" :xs="24">
-                    <el-empty description="暂无验收项">
-                        <el-button type="primary" @click="$router.push('/basic/project')">维护验收清单</el-button>
+                    <el-empty description="暂无应用项">
+                        <el-button type="primary" @click="$router.push('/project/maintenance')">维护应用项</el-button>
                     </el-empty>
                 </el-col>
 
@@ -117,14 +117,14 @@ const projectChange = projectId => {
 }
 
 const getItems = projectId => {
-    // 先查询当前项目的验收清单
+    // 先查询当前项目的应用项
     loading.value = true;
     API.system.project.bindItem.get({ projectId }).then(res => {
         loading.value = false;
         const sortArr = XEUtils.orderBy(XEUtils.get(res, "data", []), [["item.itemCategory", "asc"], ["item.createTime", "asc"]]);
         acceptItem.value = XEUtils.map(sortArr, item => ({ ...item, name: XEUtils.get(item, "item.acceptItem", "") }));
     }).catch(() => loading.value = false);
-    // 再查询每一项验收清单的诊断结果
+    // 再查询每一项应用项的诊断结果
 }
 
 onMounted(() => {

src/views/system/milestone/main.js → src/views/milestone/main.js


src/views/system/milestone/components/index.js → src/views/milestone/matters/components/index.js


+ 85 - 0
src/views/milestone/matters/components/record/detail.vue

@@ -0,0 +1,85 @@
+<template>
+    <el-dialog v-model="visible" title="关联里程碑" :width="480" :close-on-click-modal="false" @closed="$emit('closed')">
+        <el-form ref="formRef" :model="form" label-width="120">
+            <el-form-item label="里程碑节点">
+                <el-tree-select v-model="form.planId" v-bind="treeSelectProps"></el-tree-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";
+
+const $emit = defineEmits(["success", "closed"]);
+const visible = ref(false);
+const isSaving = ref(false);
+
+const form = ref({
+    id: null,
+    planId: null
+})
+
+const treeSelectProps = reactive({
+    nodeKey: "id",
+    popperClass: "vxe-table-slot--popper",
+    data: [],
+    filterable: true,
+    clearable: true,
+    checkStrictly: true,
+    placeholder: "请选择里程碑节点",
+    defaultExpandedKeys: [],
+    props: { label: "nodeName" }
+})
+
+const getTreeData = async projectId => {
+    const planRes = await API.system.milestone.all({ projectId });
+    treeSelectProps.data = XEUtils.toArrayTree(planRes, { parentKey: "parentId", key: "id", sortKey: "nodeSeq" });
+    treeSelectProps.defaultExpandedKeys = XEUtils.map(XEUtils.toTreeArray(XEUtils.searchTree(treeSelectProps.data, item => item.id === form.value.planId)), item => item.id);
+}
+
+const open = data => visible.value = true;
+
+const setData = data => {
+    open();
+    getTreeData(data.projectId);
+    XEUtils.set(form.value, "id", data.id);
+    data.planId != "-" && XEUtils.set(form.value, "planId", data.planId);
+}
+
+const formRef = ref();
+const submit = () => {
+    formRef.value.validate(valid => {
+        if (valid) {
+            const data = XEUtils.clone(form.value, true);
+            !form.value.planId && XEUtils.set(data, "planId", "-");
+
+            isSaving.value = true;
+            API.system.todoTask.edit(data).then(() => {
+                isSaving.value = false;
+                ElMessage.success("操作成功");
+                visible.value = false;
+                $emit("success");
+            }).catch(() => isSaving.value = false);
+        } else {
+            return false;
+        }
+    });
+}
+
+defineExpose({
+    setData
+})
+</script>
+
+<style scoped>
+.el-form {
+    padding-right: calc(var(--el-dialog-padding-primary) + var(--el-message-close-size, 16px));
+}
+</style>

+ 32 - 9
src/views/system/todoTask/components/record/index.vue

@@ -1,11 +1,13 @@
 <template>
-    <scTable ref="xGridTable" :apiObj="$API.system.todoTask" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns" :pagerConfig="pagerConfig">
+    <scTable ref="xGridTable" :apiObj="hasAPI && $API.system.todoTask" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns" :pagerConfig="pagerConfig">
         <template #action="{ row }">
             <el-button type="primary" link @click="table_bind(row)">
                 <template #icon><sc-iconify icon="mdi:relation-only-one-to-one"></sc-iconify></template>关联里程碑
             </el-button>
         </template>
     </scTable>
+
+    <record-detail v-if="dialog" ref="recordRef" @success="refreshTable" @closed="dialog = false"></record-detail>
 </template>
 
 <script setup>
@@ -14,17 +16,18 @@ import API from "@/api";
 import TOOL from "@/utils/tool";
 import { mapFormItemInput, mapFormItemSelect, mapFormItemDatePicker } from "@/components/scTable/helper";
 import { objectToArray } from "@/utils/basicDic";
-import { nodeStatusDic } from "@/views/system/milestone/main";
+import { nodeStatusDic } from "@/views/milestone/main";
+import recordDetail from "./detail";
 
 const props = defineProps({
     isTemp: { type: Boolean, default: false }
 })
 const visible = computed(() => !props.isTemp);
+const hasAPI = computed(() => props.isTemp || formConfig.data.projectId);
 
 const proConfig = reactive({
     visible,
     storageKey: "PROJECT",
-    props: { clearable: false },
     resetValue: TOOL.data.get("PROJECT_ID"),
     optionProps: { label: "projectName", value: "fpiId" },
     events: {
@@ -33,6 +36,7 @@ const proConfig = reactive({
 })
 
 const selectConfig = reactive({
+    span: 5,
     visible,
     options: objectToArray(nodeStatusDic),
     events: {
@@ -59,14 +63,14 @@ const toolbarConfig = reactive({
 
 const formConfig = reactive({
     data: {
-        orderBy: "createTime_asc",
+        orderBy: "sortNum_asc",
         projectId: TOOL.data.get("PROJECT_ID"),
         projectIdNot: 1
     },
     items: [
         mapFormItemSelect("projectId", "所属项目", proConfig),
-        mapFormItemInput("content", "任务内容"),
-        mapFormItemSelect("status", "任务状态", selectConfig),
+        mapFormItemInput("content", "事项内容"),
+        mapFormItemSelect("status", "事项状态", selectConfig),
         mapFormItemDatePicker("finishTime", "完成时间", datetimerangeConfig),
     ]
 })
@@ -84,10 +88,29 @@ const paramsColums = reactive([
 const columns = reactive([
     { type: "seq", width: 60 },
     { visible, type: "html", field: "projectName", title: "项目名称", minWidth: 160, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(XEUtils.find(TOOL.data.get("PROJECT"), item => item.fpiId == row.projectId), "projectName") },
-    { type: "html", field: "content", title: "任务内容", minWidth: 200, sortable: true },
-    { visible, field: "status", title: "任务状态", minWidth: 100, align: "center", editRender: { name: "$cell-tag" }, formatter: ({ cellValue }) => XEUtils.get(nodeStatusDic, cellValue, cellValue) },
+    { type: "html", field: "content", title: "事项内容", minWidth: 200, sortable: true },
+    { visible, field: "status", title: "事项状态", minWidth: 100, align: "center", editRender: { name: "$cell-tag" }, formatter: ({ cellValue }) => XEUtils.get(nodeStatusDic, cellValue, cellValue) },
     { visible, type: "html", field: "finishTime", title: "完成时间", minWidth: 160, sortable: true },
     { visible, type: "html", field: "deadlineTime", title: "截止时间", minWidth: 160, sortable: true },
-    { title: "操作", fixed: "right", width: 230, align: "center", slots: { default: "action" } }
+    { title: "操作", fixed: "right", width: 140, align: "center", slots: { default: "action" } }
 ])
+
+// 显示隐藏 筛选表单
+const xGridTable = ref();
+const refreshTable = () => {
+    xGridTable.value.reloadColumn(columns);
+    xGridTable.value.searchData("bind");
+}
+
+const recordRef = ref();
+const dialog = ref(false);
+
+const table_bind = row => {
+    dialog.value = true;
+    nextTick(() => recordRef.value?.setData(row));
+}
+
+defineExpose({
+    refreshTable
+})
 </script>

src/views/system/todoTask/components/template.vue → src/views/milestone/matters/components/template.vue


+ 63 - 0
src/views/milestone/matters/detail.vue

@@ -0,0 +1,63 @@
+<template>
+    <el-dialog v-model="visible" title="任务初始化" :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="targetProjectId">
+                <el-select v-model="form.targetProjectId" 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>
+            </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";
+
+const $emit = defineEmits(["success", "closed"]);
+const visible = ref(false);
+const isSaving = ref(false);
+
+const form = ref({
+    targetProjectId: TOOL.data.get("PROJECT_ID")
+});
+
+const rules = reactive({
+    targetProjectId: [{ required: true, message: "请选择所属项目" }]
+})
+
+const open = () => visible.value = true;
+
+const formRef = ref();
+const submit = key => {
+    formRef.value.validate(valid => {
+        if (valid) {
+            isSaving.value = true;
+            API.system.todoTask.copyData(form.value).then(() => {
+                isSaving.value = false;
+                ElMessage.success("操作成功");
+                visible.value = false;
+                $emit("success");
+            }).catch(() => isSaving.value = false);
+        } 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));
+}
+</style>

+ 13 - 4
src/views/system/todoTask/index.vue

@@ -2,8 +2,8 @@
 	<el-container class="is-vertical">
         <sc-page-header>
             <template #extra-right>
-                <el-button type="primary" @click="table_copy">
-                    <template #icon><sc-iconify icon="ant-design:copy-outlined"></sc-iconify></template>一键复制
+                <el-button v-if="activeName == 'record'" type="primary" @click="table_copy">
+                    <template #icon><sc-iconify icon="mdi:file-document-refresh-outline"></sc-iconify></template>任务初始化
                 </el-button>
             </template>
         </sc-page-header>
@@ -14,19 +14,28 @@
 
         <component ref="componentRef" :is="allcomp[activeName]" />
 	</el-container>
+
+    <mock-detail v-if="dialog" ref="mockRef" @success="table_refresh" @closed="dialog = false"></mock-detail>
 </template>
 
 <script setup>
-import { workerStates } from "@/views/system/milestone/main";
+import { workerStates } from "@/views/milestone/main";
 import allcomp from "./components";
+import mockDetail from "./detail";
 
 const activeName = ref("record");
 
 const componentRef = ref();
+const mockRef = ref();
+const dialog = ref(false);
 
 const table_add = () => componentRef.value.table_add();
+const table_refresh = () => componentRef.value.refreshTable();
 
-const table_copy = () => {};
+const table_copy = () => {
+    dialog.value = true;
+    nextTick(() => mockRef.value?.open());
+}
 </script>
 
 <style lang="scss" scoped>

src/views/system/todoTask/components/index.js → src/views/milestone/plan/components/index.js


+ 6 - 6
src/views/system/milestone/components/record/detail.vue

@@ -52,8 +52,8 @@
                     </el-form-item>
                 </el-col>
                 <el-col :xs="24">
-                    <el-form-item label="可选任务">
-                        <el-select v-model="form.todoList" filterable clearable multiple collapse-tags collapse-tags-tooltip :max-collapse-tags="2" placeholder="请选择待办任务">
+                    <el-form-item label="可选事项">
+                        <el-select v-model="form.todoIdList" filterable clearable multiple collapse-tags collapse-tags-tooltip :max-collapse-tags="2" placeholder="请选择可选事项">
                             <el-option v-for="item in filterTodoL" :key="item.id" :label="item.content" :value="item.id"></el-option>
                         </el-select>
                     </el-form-item>
@@ -77,7 +77,7 @@
 import XEUtils from "xe-utils";
 import API from "@/api";
 import TOOL from "@/utils/tool";
-import { nodeStatusDic } from "@/views/system/milestone/main";
+import { nodeStatusDic } from "@/views/milestone/main";
 
 const $emit = defineEmits(["success", "closed"]);
 const props = defineProps({
@@ -105,7 +105,7 @@ const form = ref({
     planFinishTime: null,
     actualStartTime: null,
     actualFinishTime: null,
-    todoList: [],
+    todoIdList: [],
     remark: null
 })
 const rules = reactive({
@@ -126,12 +126,12 @@ const treeSelectProps = reactive({
 })
 
 const todoTasks = ref([]);
-const filterTodoL = computed(() => mode.value == "edit" ? todoTasks.value : XEUtils.filter(todoTasks.value, item => item.planId == "-"));
+const filterTodoL = computed(() => XEUtils.filter(todoTasks.value, item => item.planId == "-" || item.planId == form.value.id));
 const getTreeData = async projectId => {
     const planRes = await API.system.milestone.all({ orderBy: "nodeSeq_asc", projectId });
     treeSelectProps.data = XEUtils.toArrayTree([{ id: "0", nodeName: "根目录" }, ...planRes], { parentKey: "parentId", key: "id" });
     
-    const taskRes = await API.system.todoTask.all({ orderBy: "createTime_asc", projectId });
+    const taskRes = await API.system.todoTask.all({ orderBy: "sortNum_asc", projectId });
     todoTasks.value = taskRes;
 }
 

+ 9 - 5
src/views/system/milestone/components/record/index.vue

@@ -1,5 +1,5 @@
 <template>
-    <scTable ref="xGridTable" :apiObj="hasAPI && $API.system.milestone" apiKey="all" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns" :pagerConfig="pagerConfig" :options="options">
+    <scTable ref="xGridTable" :apiObj="hasAPI && $API.system.milestone" apiKey="all" :formConfig="formConfig" :paramsColums="paramsColums" :toolbarConfig="toolbarConfig" :columns="columns" :pagerConfig="pagerConfig" :options="options" v-bind="props.options">
         <template #action="{ $grid, rowid }">
             <el-button type="primary" link @click="table_add($grid.getData(XEUtils.findIndexOf($grid.getData(), item => item.id == rowid)))">
                 <template #icon><sc-iconify icon="ant-design:cloud-upload-outlined"></sc-iconify></template>新增子节点
@@ -22,11 +22,13 @@ import API from "@/api";
 import TOOL from "@/utils/tool";
 import { mapFormItemInput, mapFormItemSelect, mapFormItemDatePicker } from "@/components/scTable/helper";
 import { objectToArray } from "@/utils/basicDic";
-import { nodeStatusDic } from "@/views/system/milestone/main";
+import { nodeStatusDic } from "@/views/milestone/main";
 import recordDetail from "./detail";
 
 const props = defineProps({
-    isTemp: { type: Boolean, default: false }
+    options: { type: Object, default: () => {} },
+    isTemp: { type: Boolean, default: false },
+    hideHandler: { type: Boolean, default: false }
 })
 const visible = computed(() => !props.isTemp);
 const hasAPI = computed(() => props.isTemp || formConfig.data.projectId);
@@ -93,6 +95,7 @@ const paramsColums = reactive([
 ])
 
 const columns = reactive([
+    { visible: props.hideHandler, type: "checkbox", fixed: "left", width: 40 },
     { type: "seq", title: "序号", fixed: "left", width: 70 },
     { type: "html", field: "nodeName", title: "节点名称", minWidth: 200, sortable: true, treeNode: true },
     { visible, type: "html", field: "projectName", title: "项目名称", minWidth: 160, sortable: true, formatter: ({ cellValue, row }) => cellValue || XEUtils.get(XEUtils.find(TOOL.data.get("PROJECT"), item => item.fpiId == row.projectId), "projectName") },
@@ -101,7 +104,7 @@ const columns = reactive([
     { type: "html", field: "actualStartTime", title: "实际开始时间", minWidth: 160, sortable: true },
     { type: "html", field: "actualFinishTime", title: "实际完成时间", minWidth: 160, sortable: true },
     { field: "nodeStatus", title: "节点状态", fixed: "right", minWidth: 100, align: "center", editRender: { name: "$cell-tag" }, formatter: ({ cellValue }) => XEUtils.get(nodeStatusDic, cellValue, cellValue) },
-    { title: "操作", fixed: "right", width: 230, align: "center", slots: { default: "action" } }
+    { visible: !props.hideHandler, title: "操作", fixed: "right", width: 230, align: "center", slots: { default: "action" } }
 ])
 
 const options = reactive({
@@ -146,6 +149,7 @@ const table_del = ({ id }) => {
 
 defineExpose({
     table_add,
-    table_expand
+    table_expand,
+    refreshTable
 })
 </script>

src/views/system/milestone/components/template.vue → src/views/milestone/plan/components/template.vue


+ 77 - 0
src/views/milestone/plan/detail.vue

@@ -0,0 +1,77 @@
+<template>
+    <el-dialog v-model="visible" title="一键复制" fullscreen :close-on-click-modal="false" @closed="$emit('closed')">
+
+        <el-form ref="formRef" :model="form" :rules="rules" label-width="126">
+            <data-table ref="tableRef" isTemp hideHandler :options="tableOptions"></data-table>
+        </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 TOOL from "@/utils/tool";
+import dataTable from "./components/record";
+
+const $emit = defineEmits(["success", "closed"]);
+const visible = ref(false);
+const isSaving = ref(false);
+
+const form = ref({
+    targetProjectId: TOOL.data.get("PROJECT_ID"),
+    orderBy: "nodeSeq_asc",
+    projectIdNot: 1
+});
+
+const rules = reactive({
+    targetProjectId: [{ required: true, message: "请选择模拟项目" }],
+})
+
+const tableRef = ref();
+const tableOptions = reactive({
+    maxHeight: 1048,
+    toolbarConfig: { enabled: true, print: false, zoom: false },
+    formConfig: { enabled: false, data: form }
+})
+const refreshTable = () => tableRef.value.refreshTable();
+
+const open = () => {
+    visible.value = true;
+}
+
+const formRef = ref();
+const submit = key => {
+    formRef.value.validate(valid => {
+        if (valid) {
+            // if (tableRef.value?.getTableTotal() == 0) return ElMessage.warning("暂无相关数据,请调整条件后重试。");
+            // const data = XEUtils.omit(form.value, "sourceProjectId", "sourceProjectIdNot", "source", "sourceTime", "recordNum");
+            // XEUtils.set(data, "sourceBeginTime", XEUtils.first(form.value.sourceTime));
+            // XEUtils.set(data, "sourceEndTime", XEUtils.last(form.value.sourceTime));
+            // isSaving.value = true;
+            // API.aihazard.dataMock[apiKey.value](data).then(() => {
+            //     isSaving.value = false;
+            //     ElMessage.success("操作成功");
+            //     visible.value = false;
+            //     $emit("success");
+            // }).catch(() => isSaving.value = false);
+        } else {
+            return false;
+        }
+    });
+}
+
+defineExpose({
+    open
+})
+</script>
+
+<style lang="scss" scoped>
+.el-form {margin-top: 5px;padding-right: var(--el-message-close-size, 16px);}
+.el-form :deep(.el-main) {padding-right: 0;padding-bottom: 0;}
+</style>

+ 13 - 4
src/views/system/milestone/index.vue

@@ -2,9 +2,9 @@
 	<el-container class="is-vertical">
         <sc-page-header addText="新增节点" @add="table_add" @expand="table_expand">
             <template #extra-right>
-                <el-button v-if="activeName == 'record'" type="primary" @click="table_copy">
+                <!-- <el-button v-if="activeName == 'record'" type="primary" @click="table_copy">
                     <template #icon><sc-iconify icon="ant-design:copy-outlined"></sc-iconify></template>一键复制
-                </el-button>
+                </el-button> -->
             </template>
         </sc-page-header>
 
@@ -14,20 +14,29 @@
 
         <component ref="componentRef" :is="allcomp[activeName]" />
 	</el-container>
+
+    <mock-detail v-if="dialog" ref="mockRef" @success="table_refresh" @closed="dialog = false"></mock-detail>
 </template>
 
 <script setup>
-import { workerStates } from "./main";
+import { workerStates } from "../main";
 import allcomp from "./components";
+import mockDetail from "./detail";
 
 const activeName = ref("record");
 
 const componentRef = ref();
+const mockRef = ref();
+const dialog = ref(false);
 
 const table_add = () => componentRef.value.table_add();
 const table_expand = () => componentRef.value.table_expand();
+const table_refresh = () => componentRef.value.refreshTable();
 
-const table_copy = () => {};
+const table_copy = () => {
+    dialog.value = true;
+    nextTick(() => mockRef.value?.open());
+}
 </script>
 
 <style lang="scss" scoped>

+ 9 - 9
src/views/system/acceptItems/detail.vue

@@ -1,16 +1,16 @@
 <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-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 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-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>
@@ -47,9 +47,9 @@ const form = ref({
 });
 const rules = reactive({
     projectType: [{ required: true }],
-    itemCategory: [{ required: true, message: "请选择验收项类别" }],
-    acceptItem: [{ required: true, message: "请输入验收项名称" }],
-    acceptItemType: [{ required: true, message: "请选择验收项级别" }]
+    itemCategory: [{ required: true, message: "请选择应用项类别" }],
+    acceptItem: [{ required: true, message: "请输入应用项名称" }],
+    acceptItemType: [{ required: true, message: "请选择应用项级别" }]
 })
 
 const open = () => visible.value = true;

+ 10 - 9
src/views/system/acceptItems/index.vue

@@ -1,6 +1,7 @@
 <template>
 	<el-container class="is-vertical">
-        <sc-page-header @add="table_add"></sc-page-header>
+        <sc-page-header></sc-page-header>
+        <!-- <sc-page-header @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 }">
@@ -41,9 +42,9 @@ const formConfig = reactive({
         orderBy: "itemCategory_asc,createTime_asc"
     },
     items: [
-        mapFormItemSelect("itemCategory", "验收项类别", selectConfig),
-        mapFormItemInput("acceptItemLike", "验收项名称"),
-        mapFormItemSelect("acceptItemType", "验收项级别", { ...selectConfig, options: levelDic.map(label => ({ label, value: label })) })
+        mapFormItemSelect("itemCategory", "应用项类别", selectConfig),
+        mapFormItemInput("acceptItemLike", "应用项名称"),
+        mapFormItemSelect("acceptItemType", "应用项级别", { ...selectConfig, options: levelDic.map(label => ({ label, value: label })) })
     ]
 });
 
@@ -56,10 +57,10 @@ const paramsColums = reactive([
 
 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" } }
+    { 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({
@@ -103,7 +104,7 @@ const table_edit = row => {
 }
 
 const table_del = ({ id }) => {
-    ElMessageBox.confirm("是否确认删除该验收项?", "删除警告", {
+    ElMessageBox.confirm("是否确认删除该应用项?", "删除警告", {
         type: "warning",
         confirmButtonText: "确定",
         cancelButtonText: "取消"

src/views/system/acceptItems/main.js → src/views/project/acceptItems/main.js


src/views/basic/project/detail.vue → src/views/project/maintenance/detail.vue


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

@@ -9,7 +9,7 @@
             
             <template #action="{ row }">
                 <el-button type="primary" link @click="table_items(row)">
-                    <template #icon><sc-iconify icon="pajamas:list-bulleted"></sc-iconify></template>验收清单
+                    <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>修改
@@ -73,7 +73,7 @@ const columns = reactive([
     { 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" } }
+    { title: "操作", fixed: "right", width: 230, align: "center", slots: { default: "action" } }
 ])
 
 // 获取组织树

+ 6 - 6
src/views/basic/project/items.vue

@@ -1,5 +1,5 @@
 <template>
-    <el-dialog v-model="visible" :title="`${form.projectName}-验收清单`" fullscreen :close-on-click-modal="false" @closed="$emit('closed')">
+    <el-dialog v-model="visible" :title="`${form.projectName}-应用项管理`" fullscreen :close-on-click-modal="false" @closed="$emit('closed')">
         <el-form ref="formRef" :model="form" label-width="120">
             <el-form-item required>
                 <template #label>
@@ -63,7 +63,7 @@
 import moment from "moment";
 import XEUtils from "xe-utils";
 import API from "@/api";
-import { categoryDic, statisticTemp } from "@/views/system/acceptItems/main";
+import { categoryDic, statisticTemp } from "@/views/project/acceptItems/main";
 
 const $emit = defineEmits(["success", "closed"]);
 const visible = ref(false);
@@ -96,11 +96,11 @@ const options = reactive({
 })
 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: "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" } },
+    { 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" } },
 ])

src/views/basic/project/main.js → src/views/project/maintenance/main.js