Hu Cheng před 2 roky
rodič
revize
c1cfecbc7f
38 změnil soubory, kde provedl 1959 přidání a 1824 odebrání
  1. 1 1
      .env.development
  2. 3 1
      package.json
  3. 68 0
      pnpm-lock.yaml
  4. 0 9
      src/api/modules/TaskData.js
  5. binární
      src/assets/1.pdf
  6. 9 0
      src/common/index.js
  7. 0 111
      src/layout/components/menu/index.vue
  8. 1 0
      src/layout/index.vue
  9. 2 2
      src/login/index.vue
  10. 1 1
      src/main.js
  11. 0 0
      src/plugins/highcharts.js
  12. 0 0
      src/plugins/naive-ui.js
  13. 9 9
      src/router/index.js
  14. 0 0
      src/stores/task.js
  15. 1 1
      src/utils/axios.js
  16. 207 0
      src/views/certificate/index.vue
  17. 9 22
      src/views/data/checkout/index.vue
  18. 17 62
      src/views/data/edit/AddVue.vue
  19. 14 41
      src/views/data/edit/ExportVue.vue
  20. 20 49
      src/views/data/edit/ImportPlatform.vue
  21. 75 0
      src/views/data/edit/ImportVue.vue
  22. 48 0
      src/views/data/edit/SetVue.vue
  23. 173 0
      src/views/data/edit/TemplateForm.vue
  24. 91 83
      src/views/data/edit/index.vue
  25. 198 0
      src/views/data/source/index.vue
  26. 76 124
      src/views/equipment/index.vue
  27. 0 205
      src/views/project/certificate/index.vue
  28. 0 69
      src/views/project/data/edit/SetVue.vue
  29. 0 231
      src/views/project/data/source/index.vue
  30. 26 6
      src/views/project/index.vue
  31. 0 245
      src/views/project/report/create/index.vue
  32. 0 480
      src/views/project/scheme/index.vue
  33. 40 33
      src/views/report/audit/TabFour.vue
  34. 82 0
      src/views/report/audit/TabThree.vue
  35. 27 36
      src/views/report/audit/index.vue
  36. 371 0
      src/views/report/create/index.vue
  37. 387 0
      src/views/scheme/index.vue
  38. 3 3
      vite.config.js

+ 1 - 1
.env.development

@@ -1,3 +1,3 @@
 VITE_API_BASE_URL="https://coldverify.coldbaozhida.com/api"
-VITE_API_LOCAL_BASE_URL="http://192.168.1.23:6400"
+VITE_API_LOCAL_BASE_URL="http://192.168.11.23:6400"
 VITE_API_COLD_BASE_URL="https://cold.coldbaozhida.com/api"

+ 3 - 1
package.json

@@ -24,11 +24,13 @@
     "qiniu-js": "^3.4.1",
     "vue": "^3.2.45",
     "vue-pdf-embed": "^1.1.5",
-    "vue-router": "^4.1.6"
+    "vue-router": "^4.1.6",
+    "xlsx": "^0.18.5"
   },
   "devDependencies": {
     "@rushstack/eslint-patch": "^1.1.4",
     "@vicons/antd": "^0.12.0",
+    "@vicons/ionicons5": "^0.12.0",
     "@vitejs/plugin-vue": "^4.0.0",
     "@vue/eslint-config-prettier": "^7.0.0",
     "cz-git": "^1.4.1",

+ 68 - 0
pnpm-lock.yaml

@@ -3,6 +3,7 @@ lockfileVersion: 5.4
 specifiers:
   '@rushstack/eslint-patch': ^1.1.4
   '@vicons/antd': ^0.12.0
+  '@vicons/ionicons5': ^0.12.0
   '@vitejs/plugin-vue': ^4.0.0
   '@vue/eslint-config-prettier': ^7.0.0
   '@vueuse/core': ^9.13.0
@@ -26,6 +27,7 @@ specifiers:
   vue-pdf-embed: ^1.1.5
   vue-router: ^4.1.6
   windicss: ^3.5.6
+  xlsx: ^0.18.5
 
 dependencies:
   '@vueuse/core': 9.13.0_vue@3.2.47
@@ -38,10 +40,12 @@ dependencies:
   vue: 3.2.47
   vue-pdf-embed: 1.1.5_vue@3.2.47
   vue-router: 4.1.6_vue@3.2.47
+  xlsx: 0.18.5
 
 devDependencies:
   '@rushstack/eslint-patch': 1.2.0
   '@vicons/antd': 0.12.0
+  '@vicons/ionicons5': 0.12.0
   '@vitejs/plugin-vue': 4.0.0_vite@4.1.1+vue@3.2.47
   '@vue/eslint-config-prettier': 7.0.0_kcm2pbg4372aakdkvxabn7z2ri
   cz-git: 1.4.1
@@ -683,6 +687,10 @@ packages:
     resolution: {integrity: sha512-C0p6aO1EmGG1QHrqgUWQS1No20934OdWSRQshM5NIDK5H1On6tC26U0hT6Rmp40KfUsvhvX5YW8BoWJdNFifPg==}
     dev: true
 
+  /@vicons/ionicons5/0.12.0:
+    resolution: {integrity: sha512-Iy1EUVRpX0WWxeu1VIReR1zsZLMc4fqpt223czR+Rpnrwu7pt46nbnC2ycO7ItI/uqDLJxnbcMC7FujKs9IfFA==}
+    dev: true
+
   /@vitejs/plugin-vue/4.0.0_vite@4.1.1+vue@3.2.47:
     resolution: {integrity: sha512-e0X4jErIxAB5oLtDqbHvHpJe/uWNkdpYV83AOG2xo2tEVSzCzewgJMtREZM30wXnM5ls90hxiOtAuVU6H5JgbA==}
     engines: {node: ^14.18.0 || >=16.0.0}
@@ -843,6 +851,11 @@ packages:
     hasBin: true
     dev: true
 
+  /adler-32/1.3.1:
+    resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==}
+    engines: {node: '>=0.8'}
+    dev: false
+
   /agent-base/6.0.2:
     resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
     engines: {node: '>= 6.0.0'}
@@ -978,6 +991,14 @@ packages:
     resolution: {integrity: sha512-XY7UbUpGRatZzoRft//5xOa69/1iGJRBlrieH6QYrkKLIFn3m7OVEJ81dSrKoy2BnKsdbX5cLrOispZNYo9v2w==}
     dev: true
 
+  /cfb/1.2.2:
+    resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
+    engines: {node: '>=0.8'}
+    dependencies:
+      adler-32: 1.3.1
+      crc-32: 1.2.2
+    dev: false
+
   /chalk/2.4.2:
     resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
     engines: {node: '>=4'}
@@ -1019,6 +1040,11 @@ packages:
     engines: {node: '>=10'}
     dev: true
 
+  /codepage/1.15.0:
+    resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==}
+    engines: {node: '>=0.8'}
+    dev: false
+
   /color-convert/1.9.3:
     resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
     dependencies:
@@ -1073,6 +1099,12 @@ packages:
     resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
     dev: true
 
+  /crc-32/1.2.2:
+    resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
+    engines: {node: '>=0.8'}
+    hasBin: true
+    dev: false
+
   /create-require/1.1.1:
     resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
     dev: true
@@ -1502,6 +1534,11 @@ packages:
       mime-types: 2.1.35
     dev: false
 
+  /frac/1.1.2:
+    resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==}
+    engines: {node: '>=0.8'}
+    dev: false
+
   /fs-minipass/2.1.0:
     resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
     engines: {node: '>= 8'}
@@ -2339,6 +2376,13 @@ packages:
     resolution: {integrity: sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==}
     dev: false
 
+  /ssf/0.11.2:
+    resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
+    engines: {node: '>=0.8'}
+    dependencies:
+      frac: 1.1.2
+    dev: false
+
   /std-env/3.3.2:
     resolution: {integrity: sha512-uUZI65yrV2Qva5gqE0+A7uVAvO40iPo6jGhs7s8keRfHCmtg+uB2X6EiLGCI9IgL1J17xGhvoOqSz79lzICPTA==}
     dev: true
@@ -2717,15 +2761,39 @@ packages:
     hasBin: true
     dev: true
 
+  /wmf/1.0.2:
+    resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==}
+    engines: {node: '>=0.8'}
+    dev: false
+
   /word-wrap/1.2.3:
     resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==}
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /word/0.3.0:
+    resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==}
+    engines: {node: '>=0.8'}
+    dev: false
+
   /wrappy/1.0.2:
     resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
     dev: true
 
+  /xlsx/0.18.5:
+    resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==}
+    engines: {node: '>=0.8'}
+    hasBin: true
+    dependencies:
+      adler-32: 1.3.1
+      cfb: 1.2.2
+      codepage: 1.15.0
+      crc-32: 1.2.2
+      ssf: 0.11.2
+      wmf: 1.0.2
+      word: 0.3.0
+    dev: false
+
   /xml-name-validator/4.0.0:
     resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
     engines: {node: '>=12'}

+ 0 - 9
src/api/modules/TaskData.js

@@ -37,15 +37,6 @@ export const getTaskDataList = (data) => {
   });
 };
 
-// 打包本地数据
-export const importTaskData = (data) => {
-  return service.request({
-    method: "POST",
-    url: `${localBaseUrl}/TaskData/Import_TaskData`,
-    data,
-  });
-};
-
 // 更新线上数据
 export const updateTaskData = (data) => {
   return service.request({

binární
src/assets/1.pdf


+ 9 - 0
src/common/index.js

@@ -0,0 +1,9 @@
+import { getUpFileToken } from "@/api";
+
+// 获取上传token
+export const getFileToken = async (T_suffix) => {
+  const { data: res } = await getUpFileToken({
+    T_suffix,
+  });
+  return res.Data;
+};

+ 0 - 111
src/layout/components/menu/index.vue

@@ -1,111 +0,0 @@
-<template>
-  <n-menu inverted :options="menuOptions" :value="$route.path" />
-</template>
-
-<script setup>
-import { h } from "vue";
-import { RouterLink } from "vue-router";
-import { NIcon } from "naive-ui";
-import { UnorderedListOutlined } from "@vicons/antd";
-
-function renderIcon(icon) {
-  return () => h(NIcon, null, { default: () => h(icon) });
-}
-
-// 菜单的数据
-const menuOptions = [
-  {
-    label: () =>
-      h(
-        RouterLink,
-        {
-          to: "/project",
-        },
-        { default: () => "任务管理" }
-      ),
-    key: "/project",
-    icon: renderIcon(UnorderedListOutlined),
-    // children: [
-    //   {
-    //     label: () =>
-    //       h(
-    //         RouterLink,
-    //         {
-    //           to: "/equipment",
-    //         },
-    //         { default: () => "设备管理" }
-    //       ),
-    //     key: "/equipment",
-    //   },
-    //   {
-    //     label: () =>
-    //       h(
-    //         RouterLink,
-    //         {
-    //           to: "/data_source",
-    //         },
-    //         { default: () => "数据来源" }
-    //       ),
-    //     key: "/data_source",
-    //   },
-    //   {
-    //     label: () =>
-    //       h(
-    //         RouterLink,
-    //         {
-    //           to: "/data_edit",
-    //         },
-    //         { default: () => "数据编辑" }
-    //       ),
-    //     key: "/data_edit",
-    //   },
-    //   {
-    //     label: () =>
-    //       h(
-    //         RouterLink,
-    //         {
-    //           to: "/data_checkout",
-    //         },
-    //         { default: () => "数据校验" }
-    //       ),
-    //     key: "/data_checkout",
-    //   },
-    //   {
-    //     label: () =>
-    //       h(
-    //         RouterLink,
-    //         {
-    //           to: "/report_create",
-    //         },
-    //         { default: () => "报告生成" }
-    //       ),
-    //     key: "/report_create",
-    //   },
-    //   {
-    //     label: () =>
-    //       h(
-    //         RouterLink,
-    //         {
-    //           to: "/report_edit",
-    //         },
-    //         { default: () => "报告编辑" }
-    //       ),
-    //     key: "/report_edit",
-    //   },
-    //   {
-    //     label: () =>
-    //       h(
-    //         RouterLink,
-    //         {
-    //           to: "/report_audit",
-    //         },
-    //         { default: () => "报告审核" }
-    //       ),
-    //     key: "/report_audit",
-    //   },
-    // ],
-  },
-];
-</script>
-
-<style scoped></style>

+ 1 - 0
src/layout/index.vue

@@ -15,6 +15,7 @@
       <n-card
         :bordered="false"
         style="height: calc(100vh - 112px); min-height: 600px"
+        :content-style="{ position: 'relative' }"
       >
         <RouterView />
       </n-card>

+ 2 - 2
src/login/index.vue

@@ -99,8 +99,8 @@ const handleLogin = async () => {
       if (res.Code === 200) {
         router.replace("/");
         notification.success({
-          content: `好,${formValue.username}`,
-          meta: "欢迎登录",
+          content: `好,${formValue.username}`,
+          title: "登录成功",
           duration: 2500,
           keepAliveOnHover: true,
         });

+ 1 - 1
src/main.js

@@ -2,7 +2,7 @@ import { createApp } from "vue";
 import { createPinia } from "pinia";
 import App from "./App.vue";
 import router from "./router";
-import "./plugin/highcharts";
+import "./plugins/highcharts";
 import "virtual:windi.css";
 import "./assets/main.css";
 

+ 0 - 0
src/plugin/highcharts.js → src/plugins/highcharts.js


+ 0 - 0
src/plugin/naive-ui.js → src/plugins/naive-ui.js


+ 9 - 9
src/router/index.js

@@ -2,7 +2,7 @@ import { createRouter, createWebHashHistory } from "vue-router";
 import LayoutView from "../layout/index.vue";
 import LoginView from "../login/index.vue";
 import { getToken } from "@/utils/storage/sessionToken";
-import { loadingBar } from "@/plugin/naive-ui";
+import { loadingBar } from "@/plugins/naive-ui";
 
 const router = createRouter({
   history: createWebHashHistory(import.meta.env.BASE_URL),
@@ -18,35 +18,35 @@ const router = createRouter({
         },
         {
           path: "/certificate",
-          component: () => import("@/views/project/certificate/index.vue"),
+          component: () => import("@/views/certificate/index.vue"),
         },
         {
           path: "/scheme",
-          component: () => import("@/views/project/scheme/index.vue"),
+          component: () => import("@/views/scheme/index.vue"),
         },
         {
           path: "/equipment",
-          component: () => import("@/views/project/equipment/index.vue"),
+          component: () => import("@/views/equipment/index.vue"),
         },
         {
           path: "/data_source",
-          component: () => import("@/views/project/data/source/index.vue"),
+          component: () => import("@/views/data/source/index.vue"),
         },
         {
           path: "/data_edit",
-          component: () => import("@/views/project/data/edit/index.vue"),
+          component: () => import("@/views/data/edit/index.vue"),
         },
         {
           path: "/data_checkout",
-          component: () => import("@/views/project/data/checkout/index.vue"),
+          component: () => import("@/views/data/checkout/index.vue"),
         },
         {
           path: "/report_create",
-          component: () => import("@/views/project/report/create/index.vue"),
+          component: () => import("@/views/report/create/index.vue"),
         },
         {
           path: "/report_audit",
-          component: () => import("@/views/project/report/audit/index.vue"),
+          component: () => import("@/views/report/audit/index.vue"),
         },
       ],
     },

+ 0 - 0
src/store/task.js → src/stores/task.js


+ 1 - 1
src/utils/axios.js

@@ -1,7 +1,7 @@
 import axios from "axios";
 import { getToken } from "@/utils/storage/sessionToken";
 import { TOKEN } from "@/constant";
-import { loadingBar, message } from "@/plugin/naive-ui";
+import { loadingBar, message } from "@/plugins/naive-ui";
 
 // 显示消息
 const showMessage = (res) => {

+ 207 - 0
src/views/certificate/index.vue

@@ -0,0 +1,207 @@
+<template>
+  <div class="h-full flex flex-col gap-y-3">
+    <n-page-header @back="$router.back">
+      <template #title> 校准证书 </template>
+    </n-page-header>
+    <div class="flex-1 grid grid-cols-4 gap-x-3">
+      <n-card>
+        <n-list class="h-full">
+          <template #header>
+            <n-space justify="space-between">
+              <h2>校准证书</h2>
+              <n-button type="primary" @click="showAddModal">添加</n-button>
+            </n-space>
+          </template>
+          <n-scrollbar
+            :style="{ maxHeight: `${height - 300}px` }"
+            trigger="none"
+          >
+            <template v-for="item of dataList" :key="item">
+              <n-list-item>
+                <template #suffix>
+                  <n-popconfirm>
+                    <template #trigger>
+                      <n-button type="error">删除</n-button>
+                    </template>
+                    是否确认删除?
+                  </n-popconfirm>
+                </template>
+                <n-card
+                  embedded
+                  :bordered="false"
+                  :content-style="{ padding: '5px', textAlign: 'center' }"
+                  style="cursor: pointer"
+                  @click="handleView(item)"
+                >
+                  {{ item.slice(item.lastIndexOf("-") + 1) }}
+                </n-card>
+              </n-list-item>
+            </template>
+          </n-scrollbar>
+        </n-list>
+      </n-card>
+      <n-card class="col-span-3">
+        <div class="flex flex-col gap-y-3">
+          <n-descriptions bordered>
+            <n-descriptions-item label="报告名称">
+              {{ taskInfo.T_name }}
+            </n-descriptions-item>
+            <n-descriptions-item label="创建时间">
+              {{ taskInfo.CreateTime }}
+            </n-descriptions-item>
+            <n-descriptions-item label="修改时间">
+              {{ taskInfo.UpdateTime }}
+            </n-descriptions-item>
+          </n-descriptions>
+          <div class="flex justify-end">
+            <n-button text style="font-size: 24px" @click="onPrint">
+              <n-icon>
+                <printer-icon />
+              </n-icon>
+            </n-button>
+          </div>
+          <n-scrollbar
+            :style="{ maxHeight: `${height - 380}px` }"
+            trigger="none"
+          >
+            <vue-pdf-embed ref="pdfRef" :source="source" />
+          </n-scrollbar>
+        </div>
+      </n-card>
+    </div>
+  </div>
+  <n-modal
+    :show-icon="false"
+    v-model:show="modal.showModal"
+    preset="dialog"
+    :title="modal.title"
+    positive-text="确认"
+    negative-text="取消"
+    @positive-click="editTaskInfo"
+  >
+    <n-form :model="formValue" label-width="auto" show-require-mark>
+      <n-form-item label="名称" path="T_name">
+        <n-input v-model:value="formValue.T_name" />
+      </n-form-item>
+      <n-form-item label="证书" path="T_pdf3">
+        <n-upload :default-upload="false" :max="1" @change="handleChange">
+          <n-button> 上传文件 </n-button>
+        </n-upload>
+      </n-form-item>
+    </n-form>
+  </n-modal>
+</template>
+
+<script setup>
+import VuePdfEmbed from "vue-pdf-embed";
+import { PrinterOutlined as PrinterIcon } from "@vicons/antd";
+import { editTask, getTask } from "@/api";
+import * as qiniu from "qiniu-js";
+import { getFileToken } from "@/common";
+import { useWindowSize } from "@vueuse/core";
+
+const { height } = useWindowSize();
+
+const task = window.sessionStorage.getItem("task")
+  ? JSON.parse(window.sessionStorage.getItem("task"))
+  : {};
+
+const message = useMessage();
+
+const pdfRef = ref(null);
+
+//
+const source = ref("");
+
+// 任务信息
+const taskInfo = ref({});
+
+//
+const dataList = ref([]);
+
+// 模态框数据源
+const modal = reactive({
+  title: "",
+  showModal: false,
+});
+
+// 表单数据
+const formValue = reactive({
+  T_name: null,
+  T_pdf3: null,
+});
+
+// 打印pdf
+const onPrint = () => {
+  pdfRef.value.print();
+};
+
+//
+const handleView = (row) => {
+  source.value = row.slice(0, row.lastIndexOf("-"));
+};
+
+//
+const handleChange = async ({ file }) => {
+  const type = file.type.split("/")[1];
+  const token = await getFileToken(type);
+  const observable = qiniu.upload(
+    file.file,
+    file.name,
+    token,
+    {},
+    {
+      useCdnDomain: true,
+    }
+  );
+  observable.subscribe({
+    next: (result) => {
+      // 主要用来展示进度
+      console.warn(result);
+    },
+    error: () => {
+      message.error("上传失败");
+    },
+    complete: (res) => {
+      formValue.T_pdf3 = `${res.key}-${formValue.T_name}|`;
+    },
+  });
+};
+
+// 显示添加
+const showAddModal = () => {
+  modal.title = "新增";
+  modal.showModal = true;
+};
+
+// 添加
+const editTaskInfo = async () => {
+  try {
+    const { data: res } = await editTask({
+      T_task_id: task.T_task_id,
+      T_pdf3: taskInfo.value.T_pdf3 + formValue.T_pdf3,
+    });
+    message.success(res.Msg);
+    getTaskInfo();
+  } catch (e) {
+    console.log(e);
+  }
+};
+
+// 任务(获取)
+const getTaskInfo = async () => {
+  try {
+    const { data: res } = await getTask({
+      T_task_id: task.T_task_id,
+    });
+    taskInfo.value = res.Data || {};
+    dataList.value = res.Data.T_pdf3.split("|").filter((item) => item);
+  } catch (e) {
+    console.log(e);
+  }
+};
+
+getTaskInfo();
+</script>
+
+<style scoped></style>

+ 9 - 22
src/views/project/data/checkout/index.vue → src/views/data/checkout/index.vue

@@ -1,32 +1,23 @@
 <template>
-  <n-space vertical>
-    <n-space>
-      <n-button text style="font-size: 24px" @click="$router.back">
-        <n-icon>
-          <ArrowLeftOutlined />
-        </n-icon>
-      </n-button>
-      <n-breadcrumb>
-        <n-breadcrumb-item>任务管理</n-breadcrumb-item>
-        <n-breadcrumb-item>数据校验</n-breadcrumb-item>
-      </n-breadcrumb>
-    </n-space>
+  <div class="h-full flex flex-col gap-y-3">
+    <n-page-header @back="$router.back">
+      <template #title> 数据校验 </template>
+    </n-page-header>
     <n-data-table
       remote
       :columns="columns"
       :data="dataList"
       :bordered="false"
-      :row-key="(row) => row.T_sn"
-      :max-height="650"
+      flex-height
+      class="flex-1"
     />
-  </n-space>
+  </div>
 </template>
 
 <script setup>
 import { h } from "vue";
 import { checkTaskData } from "@/api";
 import { NTag } from "naive-ui";
-import { ArrowLeftOutlined } from "@vicons/antd";
 
 const task = window.sessionStorage.getItem("task")
   ? JSON.parse(window.sessionStorage.getItem("task"))
@@ -40,11 +31,6 @@ const queryData = reactive({
 // 需要展示的列
 const columns = [
   {
-    type: "expand",
-    expandable: (row) => row.Result !== 200,
-    renderExpand: (row) => row.Result_str,
-  },
-  {
     title: "编号",
     key: "T_id",
   },
@@ -71,6 +57,7 @@ const columns = [
   {
     title: "结果",
     key: "Result",
+    width: 700,
     render(row) {
       return h(
         NTag,
@@ -79,7 +66,7 @@ const columns = [
           bordered: false,
           size: "large",
         },
-        { default: () => (row.Result === 200 ? "OK" : "错误") }
+        { default: () => row.Result_str }
       );
     },
   },

+ 17 - 62
src/views/project/data/edit/AddVue.vue → src/views/data/edit/AddVue.vue

@@ -9,20 +9,14 @@
     negative-text="取消"
     @positive-click="addTask"
   >
-    <n-form
-      ref="formRef"
-      :model="formValue"
-      :rules="rules"
-      label-placement="left"
-      label-width="auto"
-    >
+    <n-form :model="formValue" label-width="auto" show-require-mark>
       <n-form-item label="测点" path="T_id">
         <n-select
           v-model:value="formValue.T_id"
           label-field="T_sn"
           value-field="T_id"
           @update:value="handleUpdateValue"
-          :options="options"
+          :options="classList"
         />
       </n-form-item>
       <n-form-item label="温度" path="T_t">
@@ -49,7 +43,7 @@
 </template>
 
 <script setup>
-import { getTaskDataClassList, addTaskData } from "@/api";
+import { addTaskData } from "@/api";
 
 // const emit = defineEmits(["submit"]);
 
@@ -58,15 +52,14 @@ const props = defineProps({
     required: true,
     default: {},
   },
+  classList: {
+    required: true,
+    default: [],
+  },
 });
 
 const message = useMessage();
 
-const formRef = ref(null);
-
-// 设备列表
-const options = ref([]);
-
 // 是否展示 Modal
 const showModal = ref(false);
 
@@ -79,31 +72,6 @@ const formValue = reactive({
   T_time: null,
 });
 
-// 表单规则
-const rules = {
-  T_id: {
-    type: "number",
-    required: true,
-    trigger: ["blur", "change"],
-    message: "不能为空",
-  },
-  T_t: {
-    required: true,
-    trigger: ["blur"],
-    message: "不能为空",
-  },
-  T_rh: {
-    required: true,
-    trigger: ["blur"],
-    message: "不能为空",
-  },
-  T_time: {
-    required: true,
-    trigger: ["blur", "change"],
-    message: "不能为空",
-  },
-};
-
 // 值更新时执行的回调
 const handleUpdateValue = (value, option) => {
   formValue.T_id = option.T_id;
@@ -117,31 +85,18 @@ const showAddModal = () => {
 
 // 获取设备列表
 const addTask = async () => {
-  formRef.value.validate(async (errors) => {
-    if (!errors) {
-      const { data: res } = await addTaskData({
-        T_task_id: props.task.T_task_id,
-        ...formValue,
-      });
-      if (res.Code === 200) {
-        message.success(res.Msg);
-      }
-    } else {
-      message.error("验证失败");
-      showModal.value = true;
+  try {
+    const { data: res } = await addTaskData({
+      T_task_id: props.task.T_task_id,
+      ...formValue,
+    });
+    if (res.Code === 200) {
+      message.success(res.Msg);
     }
-  });
+  } catch (e) {
+    console.log(e);
+  }
 };
-
-// 获取设备列表
-const getClassList = async () => {
-  const { data: res } = await getTaskDataClassList({
-    T_task_id: props.task.T_task_id,
-  });
-  options.value = res.Data !== null ? res.Data : [];
-};
-
-getClassList();
 </script>
 
 <style lang="scss" scoped></style>

+ 14 - 41
src/views/project/data/edit/ExportVue.vue → src/views/data/edit/ExportVue.vue

@@ -10,13 +10,7 @@
     negative-text="取消"
     @positive-click="exportTask"
   >
-    <n-form
-      ref="formRef"
-      :model="formValue"
-      :rules="rules"
-      label-placement="left"
-      label-width="auto"
-    >
+    <n-form :model="formValue" label-width="auto" show-require-mark>
       <n-form-item label="SN" path="T_sn_list">
         <n-select
           v-model:value="formValue.T_sn_list"
@@ -25,7 +19,8 @@
           :options="options"
           multiple
           clearable
-        />
+        >
+        </n-select>
       </n-form-item>
       <n-form-item label="开始时间" path="Time_start">
         <n-date-picker
@@ -67,12 +62,6 @@ const options = computed(() => {
   return props.classList;
 });
 
-console.log(options);
-
-const message = useMessage();
-
-const formRef = ref(null);
-
 // 是否展示 Modal
 const showModal = ref(false);
 
@@ -83,39 +72,23 @@ const formValue = reactive({
   Time_end: null,
 });
 
-// 表单规则
-const rules = {
-  Time_start: {
-    required: true,
-    trigger: ["blur", "change"],
-    message: "不能为空",
-  },
-  Time_end: {
-    required: true,
-    trigger: ["blur", "change"],
-    message: "不能为空",
-  },
-  T_sn_list: {
-    type: "array",
-    required: true,
-    trigger: ["blur", "change"],
-    message: "不能为空",
-  },
-};
-
 // 显示导入
 const showImportModal = () => {
   showModal.value = true;
 };
 
 const exportTask = async () => {
-  const { data: res } = await exportTaskData({
-    T_task_id: props.task.T_task_id,
-    T_sn_list: formValue.T_sn_list.join(),
-    Time_start: formValue.Time_start,
-    Time_end: formValue.Time_end,
-  });
-  location.href = res.Data;
+  try {
+    const { data: res } = await exportTaskData({
+      T_task_id: props.task.T_task_id,
+      T_sn_list: formValue.T_sn_list.join(),
+      Time_start: formValue.Time_start,
+      Time_end: formValue.Time_end,
+    });
+    location.href = res.Data;
+  } catch (e) {
+    console.log(e);
+  }
 };
 </script>
 

+ 20 - 49
src/views/project/data/edit/ImportVue.vue → src/views/data/edit/ImportPlatform.vue

@@ -1,5 +1,7 @@
 <template>
-  <n-button type="primary" @click="showImportModal">导入数据</n-button>
+  <n-button type="primary" @click="showImportModal"
+    >导入数据(冷链平台)</n-button
+  >
   <n-modal
     style="width: 30%"
     v-model:show="showModal"
@@ -10,11 +12,10 @@
     <n-space vertical>
       <n-card>
         <n-form
-          ref="formRef"
           :model="formValue"
-          :rules="rules"
           label-placement="left"
           label-width="auto"
+          show-require-mark
         >
           <n-form-item label="开始时间" path="Time_start">
             <n-date-picker
@@ -82,14 +83,10 @@ import { getV3DataList, addTaskData } from "@/api";
 const props = defineProps({
   task: {
     required: true,
-    default: {}
-  }
+    default: {},
+  },
 });
 
-const message = useMessage();
-
-const formRef = ref(null);
-
 // 是否展示 Modal
 const showModal = ref(false);
 
@@ -109,30 +106,6 @@ const formValue = reactive({
   page_z: 9999,
 });
 
-// 表单规则
-const rules = {
-  Time_start: {
-    required: true,
-    trigger: ["blur", "change"],
-    message: "不能为空",
-  },
-  Time_end: {
-    required: true,
-    trigger: ["blur", "change"],
-    message: "不能为空",
-  },
-  T_sn: {
-    required: true,
-    trigger: "blur",
-    message: "不能为空",
-  },
-  T_id: {
-    required: true,
-    trigger: "blur",
-    message: "不能为空",
-  },
-};
-
 // 显示导入
 const showImportModal = () => {
   showModal.value = true;
@@ -159,22 +132,20 @@ const addTask = async (item) => {
 
 //
 const getData = async () => {
-  show.value = true;
-  formRef.value.validate(async (errors) => {
-    if (!errors) {
-      const { data: res } = await getV3DataList({
-        Time_start: formValue.Time_start,
-        Time_end: formValue.Time_end,
-        T_snid: `${formValue.T_sn},${formValue.T_id}|`,
-        page: 1,
-        page_z: 9999,
-      });
-      show.value = false;
-      data.value = res.Data ? res.Data : {};
-    } else {
-      message.error("验证失败");
-    }
-  });
+  try {
+    show.value = true;
+    const { data: res } = await getV3DataList({
+      Time_start: formValue.Time_start,
+      Time_end: formValue.Time_end,
+      T_snid: `${formValue.T_sn},${formValue.T_id}|`,
+      page: 1,
+      page_z: 9999,
+    });
+    show.value = false;
+    data.value = res.Data || {};
+  } catch (e) {
+    console.log(e);
+  }
 };
 </script>
 

+ 75 - 0
src/views/data/edit/ImportVue.vue

@@ -0,0 +1,75 @@
+<template>
+  <n-button type="primary" @click="showImportModal">导入数据</n-button>
+  <n-modal
+    style="width: 30%"
+    v-model:show="showModal"
+    :show-icon="false"
+    preset="dialog"
+    title="导入"
+    positive-text="确认"
+    negative-text="取消"
+    @positive-click="addTask"
+  >
+    <n-form :model="formValue" label-width="auto" show-require-mark>
+      <n-form-item label="数据">
+        <n-upload :default-upload="false" @change="handleChange">
+          <n-button>上传文件</n-button>
+        </n-upload>
+      </n-form-item>
+    </n-form>
+  </n-modal>
+</template>
+
+<script setup>
+import { addTaskData } from "@/api";
+import { read } from "xlsx";
+
+const props = defineProps({
+  task: {
+    required: true,
+    default: {},
+  },
+});
+
+const message = useMessage();
+
+// 是否展示 Modal
+const showModal = ref(false);
+
+// 组件状态变化的回调
+const handleChange = (options) => {
+  console.log(options);
+  const data = read(options.file.file, { type: "File" });
+  console.log(data);
+};
+
+// 表单数据
+const formValue = reactive({
+  Time_start: null,
+  Time_end: null,
+  T_sn: "",
+  T_id: "",
+  page: 1,
+  page_z: 9999,
+});
+
+// 显示导入
+const showImportModal = () => {
+  showModal.value = true;
+};
+
+//
+const addTask = async (item) => {
+  const { data: res } = await addTaskData({
+    T_task_id: props.task.T_task_id,
+    T_sn: formValue.T_sn,
+    T_id: item.T_id,
+    T_t: item.T_t,
+    T_rh: item.T_rh,
+    T_time: item.T_time,
+  });
+  console.log(res);
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 48 - 0
src/views/data/edit/SetVue.vue

@@ -0,0 +1,48 @@
+<template>
+  <n-button type="primary" @click="showSetModal">温湿度上下限设置</n-button>
+  <n-modal
+    v-model:show="showModal"
+    :show-icon="false"
+    preset="dialog"
+    title="温湿度上下限设置"
+  >
+    <n-form :model="formValue" label-width="auto" show-require-mark>
+      <n-divider title-placement="center"> 温度 </n-divider>
+      <n-form-item label="上限" path="value">
+        <n-input v-model:value="formValue.value" />
+      </n-form-item>
+      <n-form-item label="下限" path="value">
+        <n-input v-model:value="formValue.value" />
+      </n-form-item>
+      <n-divider title-placement="center"> 湿度 </n-divider>
+      <n-form-item label="上限" path="value">
+        <n-input v-model:value="formValue.value" />
+      </n-form-item>
+      <n-form-item label="下限" path="value">
+        <n-input v-model:value="formValue.value" />
+      </n-form-item>
+      <div class="flex justify-end">
+        <n-button type="primary">确定</n-button>
+      </div>
+    </n-form>
+  </n-modal>
+</template>
+
+<script setup>
+// 是否展示 Modal
+const showModal = ref(false);
+
+// 表单数据
+const formValue = reactive({
+  value: null,
+  datetimeValue: null,
+  selectValue: null,
+});
+
+// 显示设置
+const showSetModal = () => {
+  showModal.value = true;
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 173 - 0
src/views/data/edit/TemplateForm.vue

@@ -0,0 +1,173 @@
+<template>
+  <n-scrollbar x-scrollable trigger="none">
+    <div class="flex items-center gap-x-3 mb-5" style="min-width: 2000px">
+      <template v-for="item of templateDataList" :key="item.T_label">
+        <n-space :wrap="false" align="center" v-if="item.T_label === 101">
+          <span>{{ item.T_name }}</span>
+          <n-date-picker
+            v-model:formatted-value="item.T_value"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            type="datetime"
+            clearable
+            class="w-[300px]"
+          />
+        </n-space>
+        <n-space :wrap="false" align="center" v-else-if="item.T_label === 102">
+          <span>{{ item.T_name }}</span>
+          <n-date-picker
+            v-model:formatted-value="item.T_value"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            type="datetimerange"
+            clearable
+            class="w-[300px]"
+          />
+        </n-space>
+        <n-space :wrap="false" align="center" v-else-if="item.T_label === 103">
+          <span>{{ item.T_name }}</span>
+          <n-select
+            v-model:value="item.T_value"
+            :options="options"
+            style="width: 300px"
+          />
+        </n-space>
+        <n-space :wrap="false" align="center" v-else-if="item.T_label === 104">
+          <span>{{ item.T_name }}</span>
+          <n-popselect
+            multiple
+            :options="options"
+            :render-label="renderLabel"
+            v-model:value="item.T_value"
+            style="width: 300px"
+          >
+            <n-button>
+              {{
+                Array.isArray(item.T_value) && item.T_value.length
+                  ? item.T_value.join(" | ")
+                  : "选择"
+              }}
+            </n-button>
+          </n-popselect>
+        </n-space>
+      </template>
+      <n-button type="primary" @click="handleCreate">生成方案</n-button>
+    </div>
+  </n-scrollbar>
+</template>
+
+<script setup>
+import { getVerifyTemplateMapDataList, putVerifyTemplateMapData } from "@/api";
+import { NThing } from "naive-ui";
+import { getToken } from "@/utils/storage/sessionToken";
+import { getFileToken } from "@/common";
+import * as qiniu from "qiniu-js";
+
+const message = useMessage();
+
+const dialog = useDialog();
+
+const props = defineProps({
+  task: {
+    required: true,
+    default: {},
+  },
+});
+
+//
+let fileList = [];
+
+//
+const renderLabel = () => {
+  return h(
+    NThing,
+    {},
+    {
+      avatar: () => "avatar",
+      header: () => "title",
+      description: () => "description",
+    }
+  );
+};
+
+//
+const templateDataList = ref([]);
+
+//
+const options = [
+  {
+    label: "Everybody's Got Something to Hide Except Me and My Monkey",
+    value: "song0",
+  },
+  {
+    label: "Drive My Car",
+    value: "song1",
+  },
+];
+
+//
+const queryData = reactive({
+  T_source: 3,
+  T_task_id: props.task.T_task_id,
+  T_VerifyTemplate_id: props.task.T_VerifyTemplate_id,
+});
+
+// 显示生成方案
+const handleCreate = async () => {
+  fileList = toRaw(templateDataList.value);
+  dialog.info({
+    title: "提示",
+    content: "确认生成报告?",
+    positiveText: "确定",
+    negativeText: "取消",
+    onPositiveClick: () => {
+      putTemplateData();
+    },
+  });
+};
+
+// 模板标签数据添加或更新
+const putTemplateData = async () => {
+  try {
+    const VerifyTemplateMapData = fileList.map((item) => {
+      if (item.T_label === 102 || item.T_label === 104) {
+        return {
+          T_VerifyTemplateMap_id: item.T_VerifyTemplateMap_id,
+          T_value: item.T_value.join("|"),
+        };
+      } else {
+        return {
+          T_VerifyTemplateMap_id: item.T_VerifyTemplateMap_id,
+          T_value: item.T_value + "",
+        };
+      }
+    });
+    const token = getToken();
+    const { data: res } = await putVerifyTemplateMapData({
+      User_tokey: token,
+      T_source: queryData.T_source,
+      T_task_id: queryData.T_task_id,
+      T_VerifyTemplate_id: queryData.T_VerifyTemplate_id,
+
+      VerifyTemplateMapData,
+    });
+    message.success(res.Msg);
+  } catch (e) {
+    console.log(e);
+  }
+};
+
+//
+const getDataList = async () => {
+  const { data: res } = await getVerifyTemplateMapDataList(queryData);
+  templateDataList.value = res.Data;
+  templateDataList.value.forEach((item) => {
+    if (item.T_label === 104) {
+      item.T_value = [];
+    } else {
+      item.T_value = null;
+    }
+  });
+};
+getDataList();
+</script>
+
+<style lang="scss" scoped></style>

+ 91 - 83
src/views/project/data/edit/index.vue → src/views/data/edit/index.vue

@@ -1,60 +1,54 @@
 <template>
-  <n-space vertical>
-    <n-space>
-      <n-button text style="font-size: 24px" @click="$router.back">
-        <n-icon>
-          <ArrowLeftOutlined />
-        </n-icon>
-      </n-button>
-      <n-breadcrumb>
-        <n-breadcrumb-item>任务管理</n-breadcrumb-item>
-        <n-breadcrumb-item>数据编辑</n-breadcrumb-item>
-      </n-breadcrumb>
-    </n-space>
-    <n-grid x-gap="12" :cols="4">
-      <n-gi>
-        <n-card>
-          <n-list>
-            <template #header>
-              <n-checkbox label="全选" @update:checked="handleCheckAll" />
-            </template>
-            <n-scrollbar style="max-height: 600px" @scroll="onScroll">
-              <n-checkbox-group
-                @update:value="onUpdateValues"
-                v-model:value="checkValues"
-              >
-                <template v-for="item of classList" :key="item.T_id">
-                  <n-list-item class="mr-5">
-                    <template #prefix>
-                      <n-checkbox :value="item.T_id" />
-                    </template>
-                    <template #suffix>
-                      <n-button type="error" size="small" text>删除</n-button>
-                    </template>
-                    <n-thing>
-                      <template #header> ID:{{ item.T_id }} </template>
-                      <template #description> {{ item.T_sn }} </template>
-                    </n-thing>
-                  </n-list-item>
-                </template>
-              </n-checkbox-group>
-            </n-scrollbar>
-            <template #footer>
-              <n-gradient-text type="info">
-                传感器总数:{{ classList.length }}
-              </n-gradient-text>
-            </template>
-          </n-list>
-        </n-card>
-      </n-gi>
-      <n-gi :span="3">
-        <n-space vertical>
+  <div class="h-full flex flex-col gap-y-3">
+    <n-page-header @back="$router.back">
+      <template #title> 数据编辑 </template>
+    </n-page-header>
+    <div class="flex-1 grid grid-cols-4 gap-x-3">
+      <n-card>
+        <n-list>
+          <template #header>
+            <n-checkbox label="全选" @update:checked="handleCheckAll" />
+          </template>
+          <n-scrollbar
+            :style="{ maxHeight: `${height - 320}px` }"
+            trigger="none"
+            @scroll="onScroll"
+          >
+            <n-checkbox-group
+              @update:value="onUpdateValues"
+              v-model:value="checkValues"
+            >
+              <template v-for="item of classList" :key="item.T_id">
+                <n-list-item class="mr-5">
+                  <template #prefix>
+                    <n-checkbox :value="item.T_id" />
+                  </template>
+                  <template #suffix>
+                    <n-button type="error" size="small" text>删除</n-button>
+                  </template>
+                  <n-thing>
+                    <template #header> ID:{{ item.T_id }} </template>
+                    <template #description> {{ item.T_sn }} </template>
+                  </n-thing>
+                </n-list-item>
+              </template>
+            </n-checkbox-group>
+          </n-scrollbar>
+          <template #footer>
+            <n-gradient-text type="info">
+              传感器总数:{{ classList.length }}
+            </n-gradient-text>
+          </template>
+        </n-list>
+      </n-card>
+      <n-card class="h-full col-span-3">
+        <div class="flex flex-col gap-y-3">
           <n-space>
             <n-space align="center">
               <span>开始时间</span>
               <n-date-picker
                 v-model:formatted-value="queryData.Time_start"
-                value-format="yyyy.MM.dd HH:mm:ss"
+                value-format="yyyy-MM-dd HH:mm:ss"
                 type="datetime"
                 clearable
               />
@@ -63,35 +57,38 @@
               <span>结束时间</span>
               <n-date-picker
                 v-model:formatted-value="queryData.Time_end"
-                value-format="yyyy.MM.dd HH:mm:ss"
+                value-format="yyyy-MM-dd HH:mm:ss"
                 type="datetime"
                 clearable
               />
             </n-space>
             <n-button type="primary" @click="getDataList">搜索</n-button>
-            <ExportVue :task="task" :classList="classList" />
+            <ExportVue :task="task" :class-list="classList" />
             <ImportVue :task="task" />
-            <AddVue :task="task" />
+            <ImportPlatform :task="task" />
+            <AddVue :class-list="classList" :task="task" />
             <SetVue />
           </n-space>
-          <n-card>
-            <n-scrollbar style="max-height: 650px">
-              <Chart
-                ref="chart1"
-                constructor-type="stockChart"
-                :options="chartOptions1"
-              ></Chart>
-              <Chart
-                ref="chart2"
-                constructor-type="stockChart"
-                :options="chartOptions2"
-              ></Chart>
-            </n-scrollbar>
-          </n-card>
-        </n-space>
-      </n-gi>
-    </n-grid>
-  </n-space>
+          <TemplateForm :task="task" />
+          <n-scrollbar
+            :style="{ maxHeight: `${height - 320}px` }"
+            trigger="none"
+          >
+            <Chart
+              ref="chart1"
+              constructor-type="stockChart"
+              :options="chartOptions1"
+            ></Chart>
+            <Chart
+              ref="chart2"
+              constructor-type="stockChart"
+              :options="chartOptions2"
+            ></Chart>
+          </n-scrollbar>
+        </div>
+      </n-card>
+    </div>
+  </div>
   <n-modal
     v-model:show="modal.showModal"
     :title="modal.title"
@@ -130,9 +127,13 @@ import {
 } from "@/api";
 import AddVue from "./AddVue.vue";
 import ImportVue from "./ImportVue.vue";
+import ImportPlatform from "./ImportPlatform.vue";
 import SetVue from "./SetVue.vue";
 import ExportVue from "./ExportVue.vue";
-import { ArrowLeftOutlined } from "@vicons/antd";
+import TemplateForm from "./TemplateForm.vue";
+import { useWindowSize } from "@vueuse/core";
+
+const { height } = useWindowSize();
 
 const message = useMessage();
 
@@ -165,7 +166,7 @@ const queryData = reactive({
   Time_start: null,
   Time_end: null,
   page: 1,
-  page_z: 10,
+  page_z: 1000,
 });
 
 // 表单数据
@@ -268,8 +269,10 @@ const onUpdateValues = async (values, meta) => {
     new Date(item.T_time).getTime(),
     item.T_t,
   ]);
-  const data2 = dataList.value.map((item) => [new Date(item.T_time).getTime(), item.T_rh]);
-  console.log(data2);
+  const data2 = dataList.value.map((item) => [
+    new Date(item.T_time).getTime(),
+    item.T_rh,
+  ]);
   if (meta.actionType === "check") {
     chart1.value.chart.addSeries({
       id: meta.value,
@@ -435,17 +438,22 @@ const getClassList = async () => {
   const { data: res } = await getTaskDataClassList({
     T_task_id: queryData.T_task_id,
   });
-  classList.value = res.Data ? res.Data : [];
+  classList.value = res.Data || [];
 };
 
 // 获取任务数据列表
 const getDataList = async () => {
-  const { data: res } = await getTaskDataList({
-    ...queryData,
-    Time_start: queryData.Time_start ? queryData.Time_start : "",
-    Time_end: queryData.Time_end ? queryData.Time_end : "",
-  });
-  dataList.value = res.Data.List ? res.Data.List : [];
+  const data = {};
+  for (const key in queryData) {
+    if (queryData[key]) {
+      data[key] = queryData[key];
+    }
+  }
+  const { data: res } = await getTaskDataList(data);
+  dataList.value = res.Data.List || [];
+  if (queryData.page_z < res.Page_size) {
+    getDataList();
+  }
 };
 
 getClassList();

+ 198 - 0
src/views/data/source/index.vue

@@ -0,0 +1,198 @@
+<template>
+  <div class="h-full flex flex-col gap-y-3">
+    <n-page-header @back="$router.back">
+      <template #title> 数据来源 </template>
+    </n-page-header>
+    <n-space>
+      <n-input v-model:value="queryData.T_sn" placeholder="请输入SN" />
+      <n-input v-model:value="queryData.T_id" placeholder="请输入ID" />
+      <n-date-picker
+        v-model:formatted-value="queryData.Time_start"
+        value-format="yyyy-MM-dd HH:mm:ss"
+        type="datetime"
+        placeholder="请选择开始时间"
+        clearable
+      />
+      <n-date-picker
+        v-model:formatted-value="queryData.Time_end"
+        value-format="yyyy-MM-dd HH:mm:ss"
+        type="datetime"
+        placeholder="请选择结束时间"
+        clearable
+      />
+      <n-button type="primary" @click="getDataList">搜索</n-button>
+      <n-button type="primary" @click="handleReset">重置数据</n-button>
+    </n-space>
+    <n-data-table
+      remote
+      :columns="columns"
+      :data="taskList"
+      :pagination="pagination"
+      :bordered="false"
+      flex-height
+      class="flex-1"
+    />
+  </div>
+  <n-modal
+    v-model:show="showModal"
+    :mask-closable="false"
+    :show-icon="false"
+    :closable="false"
+    preset="dialog"
+    title="自定义时间"
+  >
+    <n-form
+      label-width="auto"
+      label-placement="top"
+      :model="queryData"
+      show-require-mark
+    >
+      <n-form-item label="开始时间" path="Time_start">
+        <n-date-picker
+          v-model:formatted-value="queryData.Time_start"
+          value-format="yyyy-MM-dd HH:mm:ss"
+          type="datetime"
+          clearable
+          class="w-full"
+        />
+      </n-form-item>
+      <n-form-item label="结束时间" path="Time_end">
+        <n-date-picker
+          v-model:formatted-value="queryData.Time_end"
+          value-format="yyyy-MM-dd HH:mm:ss"
+          type="datetime"
+          clearable
+          class="w-full"
+        />
+      </n-form-item>
+      <n-form-item>
+        <n-button type="primary" block @click="handleSearch">搜索</n-button>
+      </n-form-item>
+    </n-form>
+  </n-modal>
+</template>
+
+<script setup>
+import { NSpace } from "naive-ui";
+import { extractTaskData, getTaskDataList } from "@/api";
+import { message } from "@/plugins/naive-ui";
+
+const dialog = useDialog();
+
+const task = window.sessionStorage.getItem("task")
+  ? JSON.parse(window.sessionStorage.getItem("task"))
+  : {};
+
+// 查询数据
+const queryData = reactive({
+  T_task_id: task.T_task_id,
+  T_sn: "",
+  T_id: "",
+  Time_start: null,
+  Time_end: null,
+  page: 1,
+  page_z: 10,
+});
+
+// 列表
+const taskList = ref([]);
+
+// 需要展示的列
+const columns = [
+  {
+    title: "SN",
+    key: "T_sn",
+  },
+  {
+    title: "温度℃",
+    key: "T_t",
+  },
+  {
+    title: "湿度%",
+    key: "T_rh",
+  },
+  {
+    title: "记录时间",
+    key: "T_time",
+  },
+];
+
+// 分页数据源
+const pagination = reactive({
+  page: queryData.page,
+  pageSize: queryData.page_z,
+  itemCount: 0,
+  onChange: (page) => {
+    pagination.page = page;
+    queryData.page = page;
+    getDataList();
+  },
+});
+
+// modal数据源
+const showModal = ref(false);
+
+//
+const handleSearch = () => {
+  showModal.value = false;
+  queryData.T_id = "";
+  queryData.T_sn = "";
+  extractTask();
+};
+
+// 重置
+const handleReset = () => {
+  dialog.warning({
+    title: "警告",
+    content: "是否确认重置数据?",
+    positiveText: "确认",
+    negativeText: "取消",
+    onPositiveClick: () => {
+      extractTask();
+    },
+  });
+};
+
+//
+const extractTask = async () => {
+  try {
+    const { data: res } = await extractTaskData({
+      T_task_id: queryData.T_task_id,
+      Time_start: queryData.Time_start !== null ? queryData.Time_start : "",
+      Time_end: queryData.Time_end !== null ? queryData.Time_end : "",
+    });
+    if (res.Code === 200) {
+      message.success(res.Msg);
+      getDataList();
+    }
+  } catch (e) {
+    console.log(e);
+  }
+};
+
+// 获取列表
+const getDataList = async () => {
+  try {
+    const { data: res } = await getTaskDataList({
+      ...queryData,
+      Time_start: queryData.Time_start !== null ? queryData.Time_start : "",
+      Time_end: queryData.Time_end !== null ? queryData.Time_end : "",
+    });
+    pagination.itemCount = res.Data.Num;
+    if (!res.Data.List) {
+      message.info("数据为空");
+      queryData.Time_start = null;
+      queryData.Time_end = null;
+      showModal.value = true;
+    } else {
+      taskList.value = res.Data.List || [];
+    }
+  } catch (e) {
+    console.log(e);
+  }
+};
+
+getDataList();
+</script>
+
+<style scoped></style>

+ 76 - 124
src/views/project/equipment/index.vue → src/views/equipment/index.vue

@@ -1,16 +1,8 @@
 <template>
-  <n-space vertical>
-    <n-space>
-      <n-button text style="font-size: 24px" @click="$router.back">
-        <n-icon>
-          <ArrowLeftOutlined />
-        </n-icon>
-      </n-button>
-      <n-breadcrumb>
-        <n-breadcrumb-item>任务管理</n-breadcrumb-item>
-        <n-breadcrumb-item>设备管理</n-breadcrumb-item>
-      </n-breadcrumb>
-    </n-space>
+  <div class="h-full flex flex-col gap-y-3">
+    <n-page-header @back="$router.back">
+      <template #title> 设备管理 </template>
+    </n-page-header>
     <n-space justify="space-between">
       <n-input-group>
         <n-input
@@ -19,9 +11,9 @@
           @clear="handleClear"
           clearable
         />
-        <n-button type="primary" @click="getDeviceClassList"> 搜索 </n-button>
+        <n-button type="primary" @click="getDataList"> 搜索 </n-button>
       </n-input-group>
-      <n-button type="primary" @click="showAddModal">批量导入</n-button>
+      <n-button type="primary" @click="showAddModal">添加</n-button>
     </n-space>
     <n-data-table
       remote
@@ -30,11 +22,11 @@
       :data="data"
       :pagination="pagination"
       :bordered="false"
-      :max-height="550"
+      flex-height
+      class="flex-1"
     />
-  </n-space>
+  </div>
   <n-modal
-    :style="{ width: modal.title === '批量导入' ? '50%' : '' }"
     :show-icon="false"
     v-model:show="modal.showModal"
     preset="dialog"
@@ -43,23 +35,24 @@
     negative-text="取消"
     @positive-click="submitCallback"
   >
-    <n-form
-      ref="formRef"
-      :model="formValue"
-      label-placement="left"
-      label-width="auto"
-      :rules="rules"
-    >
-      <n-form-item
-        label="设备序列号"
-        path="T_sn"
-        v-if="modal.title === '批量导入'"
-      >
-        <n-transfer v-model:value="formValue.T_sn" :options="deviceOptions" />
-      </n-form-item>
-      <n-form-item label="编号" path="T_id" v-else>
-        <n-input v-model:value="formValue.T_id" />
-      </n-form-item>
+    <n-form :model="formValue" label-width="auto" show-require-mark>
+      <template v-if="modal.title === '添加'">
+        <n-form-item label="SN-ID" path="T_snid">
+          <n-input
+            v-model:value="formValue.T_snid"
+            type="textarea"
+            :autosize="{
+              minRows: 3,
+              maxRows: 5,
+            }"
+          />
+        </n-form-item>
+      </template>
+      <template v-else>
+        <n-form-item label="编号" path="T_id">
+          <n-input v-model:value="formValue.T_id" />
+        </n-form-item>
+      </template>
     </n-form>
   </n-modal>
 </template>
@@ -70,11 +63,9 @@ import { NButton, NSpace, NPopconfirm, NInput } from "naive-ui";
 import {
   getDeviceClassListList,
   addDeviceClassList,
-  getDeviceList,
   editDeviceClassList,
   deleteDeviceClassList,
 } from "@/api";
-import { ArrowLeftOutlined } from "@vicons/antd";
 
 const task = window.sessionStorage.getItem("task")
   ? JSON.parse(window.sessionStorage.getItem("task"))
@@ -82,23 +73,6 @@ const task = window.sessionStorage.getItem("task")
 
 const message = useMessage();
 
-const formRef = ref(null);
-
-// 验证表项的规则
-const rules = {
-  T_sn: {
-    required: true,
-    message: "不能为空",
-    trigger: "change",
-    type: "array",
-  },
-  T_id: {
-    required: true,
-    message: "不能为空",
-    trigger: "blur",
-  },
-};
-
 // 查询参数
 const queryData = reactive({
   T_sn: "",
@@ -165,10 +139,6 @@ const columns = [
 // 设备分类-设备列表
 const data = ref([]);
 
-// 设备列表
-const deviceList = ref([]);
-const deviceOptions = ref([]);
-
 // 分页数据源
 const pagination = reactive({
   page: queryData.page,
@@ -177,7 +147,7 @@ const pagination = reactive({
   onChange: (page) => {
     pagination.page = page;
     queryData.page = page;
-    getDeviceClassList();
+    getDataList();
   },
 });
 
@@ -189,27 +159,23 @@ const modal = reactive({
 
 // 获取表项中收集到的值的对象
 const formValue = reactive({
-  T_sn: null,
+  T_snid: "",
+  T_id: 0,
 });
 
 // 输入框点击清空按钮时触发
 const handleClear = () => {
   queryData.T_sn = "";
-  getDeviceClassList();
+  getDataList();
 };
 
+//
 const submitCallback = () => {
-  if (modal.title === "批量导入") {
-    formRef.value.validate(async (errors) => {
-      if (!errors) {
-        formValue.T_sn.forEach((T_sn) => {
-          const T_id = deviceList.value.find((item) => item.T_sn === T_sn).T_id;
-          addDeviceClass(T_sn, T_id);
-        });
-      } else {
-        modal.showModal = true;
-        message.error("验证失败,请填写完整信息");
-      }
+  if (modal.title === "添加") {
+    const arr = formValue.T_snid.split("\n");
+    arr.forEach((item) => {
+      const [T_sn, T_id] = item.split("-");
+      addDeviceClass(T_sn, T_id);
     });
   } else {
     editDeviceClass();
@@ -220,48 +186,45 @@ const submitCallback = () => {
 const showEditModal = (row) => {
   modal.title = "修改编号";
   modal.showModal = true;
-  Object.assign(formValue, row);
+  formValue.Id = row.Id;
+  formValue.T_id = `${row.T_id}`;
 };
 
 // 显示添加
 const showAddModal = () => {
-  modal.title = "批量导入";
+  modal.title = "添加";
   modal.showModal = true;
-  formValue.T_sn = null;
+  formValue.T_snid = "";
 };
 
 // 删除
 const deleteDeviceClass = async (row) => {
-  const { data: res } = await deleteDeviceClassList({
-    Id: row.Id,
-  });
-  message.success(res.Msg);
-  getDeviceClassList();
+  try {
+    const { data: res } = await deleteDeviceClassList({
+      Id: row.Id,
+    });
+    message.success(res.Msg);
+    getDataList();
+  } catch (e) {
+    console.log(e);
+  }
 };
 
 // 编辑编号
-const editDeviceClass = () => {
-  formRef.value.validate(async (errors) => {
-    if (!errors) {
-      try {
-        const { data: res } = await editDeviceClassList({
-          Id: formValue.Id,
-          T_id: formValue.T_id,
-        });
-        message.success(res.Msg);
-        getDeviceClassList();
-      } finally {
-        formRef.value.restoreValidation();
-        Object.keys(formValue).forEach((key) => (formValue[key] = ""));
-      }
-    } else {
-      modal.showModal = true;
-      message.error("验证失败,请填写完整信息");
-    }
-  });
+const editDeviceClass = async () => {
+  try {
+    const { data: res } = await editDeviceClassList({
+      Id: formValue.Id,
+      T_id: formValue.T_id,
+    });
+    message.success(res.Msg);
+    getDataList();
+  } catch (e) {
+    console.log(e);
+  }
 };
 
-// 批量导入
+// 添加
 const addDeviceClass = async (T_sn, T_id) => {
   try {
     const { data: res } = await addDeviceClassList({
@@ -269,38 +232,27 @@ const addDeviceClass = async (T_sn, T_id) => {
       T_sn,
       T_id,
     });
-    message.success(res.Msg);
-    getDeviceClassList();
-  } finally {
-    formRef.value.restoreValidation();
-    Object.keys(formValue).forEach((key) => (formValue[key] = ""));
+    if (res.Code === 200) {
+      message.success(res.Msg);
+      getDataList();
+    }
+  } catch (e) {
+    console.log(e);
   }
 };
 
-// 设备分类-设备列表(列表)
-const getDeviceClassList = async () => {
-  const { data: res } = await getDeviceClassListList(queryData);
-  pagination.itemCount = res.Data.Num;
-  data.value = res.Data.List;
-};
-
-// 设备管理(列表)
+// 获取列表
 const getDataList = async () => {
-  const { data: res } = await getDeviceList({
-    T_MSISDN: "",
-    T_sn: "",
-    page: "",
-    page_z: "",
-  });
-  deviceOptions.value = res.Data.List.map((item) => ({
-    label: item.T_sn,
-    value: item.T_sn,
-  }));
-  deviceList.value = res.Data.List;
+  try {
+    const { data: res } = await getDeviceClassListList(queryData);
+    pagination.itemCount = res.Data.Num;
+    data.value = res.Data.List || [];
+  } catch (e) {
+    console.log(e);
+  }
 };
 
 getDataList();
-getDeviceClassList();
 </script>
 
 <style scoped></style>

+ 0 - 205
src/views/project/certificate/index.vue

@@ -1,205 +0,0 @@
-<template>
-  <n-space vertical>
-    <n-space>
-      <n-button text style="font-size: 24px" @click="$router.back">
-        <n-icon>
-          <ArrowLeftOutlined />
-        </n-icon>
-      </n-button>
-      <n-breadcrumb>
-        <n-breadcrumb-item>任务管理</n-breadcrumb-item>
-        <n-breadcrumb-item>校准证书</n-breadcrumb-item>
-      </n-breadcrumb>
-    </n-space>
-    <n-grid x-gap="12" :cols="4">
-      <n-gi>
-        <n-card>
-          <n-list>
-            <template #header>
-              <n-button type="primary" @click="showAddModal">添加</n-button>
-            </template>
-            <template v-for="item of []" :key="item">
-              <n-list-item>
-                <template #suffix>
-                  <n-button type="error" size="small">删除</n-button>
-                </template>
-                123
-              </n-list-item>
-            </template>
-          </n-list>
-        </n-card>
-      </n-gi>
-      <n-gi :span="3">
-        <n-card>
-          <n-space vertical>
-            <n-descriptions bordered>
-              <n-descriptions-item label="报告名称">
-                {{ taskInfo.T_name }}
-              </n-descriptions-item>
-              <n-descriptions-item label="创建时间">
-                {{ taskInfo.CreateTime }}
-              </n-descriptions-item>
-              <n-descriptions-item label="修改时间">
-                {{ taskInfo.UpdateTime }}
-              </n-descriptions-item>
-            </n-descriptions>
-            <div class="flex justify-end">
-              <n-button text style="font-size: 24px" @click="onPrint">
-                <n-icon>
-                  <printer-icon />
-                </n-icon>
-              </n-button>
-            </div>
-            <n-scrollbar style="max-height: 550px" trigger="none">
-              <vue-pdf-embed ref="pdfRef" :source="taskInfo.T_pdf3" />
-            </n-scrollbar>
-          </n-space>
-        </n-card>
-      </n-gi>
-    </n-grid>
-  </n-space>
-  <n-modal
-    :show-icon="false"
-    v-model:show="modal.showModal"
-    preset="dialog"
-    :title="modal.title"
-    positive-text="提交"
-    negative-text="取消"
-    @positive-click="editTaskInfo"
-  >
-    <n-form
-      ref="formRef"
-      :model="model"
-      :rules="rules"
-      label-placement="left"
-      label-width="auto"
-      require-mark-placement="right-hanging"
-      :size="size"
-      :style="{
-        maxWidth: '640px',
-      }"
-    >
-      <n-form-item label="名称" path="T_name">
-        <n-input v-model:value="formValue.T_name" />
-      </n-form-item>
-    </n-form>
-    <n-form-item label="上传文件" required>
-      <n-upload :default-upload="false" :max="1" @change="handleChange">
-        <n-button>
-          <template #icon>
-            <n-icon><upload-icon /></n-icon>
-          </template>
-        </n-button>
-      </n-upload>
-    </n-form-item>
-  </n-modal>
-</template>
-
-<script setup>
-import VuePdfEmbed from "vue-pdf-embed";
-import {
-  PrinterOutlined as PrinterIcon,
-  ArrowLeftOutlined,
-  UploadOutlined as UploadIcon,
-} from "@vicons/antd";
-import { editTask, getTask, getUpFileToken } from "@/api";
-import * as qiniu from "qiniu-js";
-
-const task = window.sessionStorage.getItem("task")
-  ? JSON.parse(window.sessionStorage.getItem("task"))
-  : {};
-
-const message = useMessage();
-
-const pdfRef = ref(null);
-
-//
-const pdfList = ref([]);
-
-// 任务信息
-const taskInfo = ref({});
-
-// 模态框数据源
-const modal = reactive({
-  title: "",
-  showModal: false,
-});
-
-// 表单数据
-const formValue = reactive({
-  T_name: "",
-  T_pdf3: [],
-});
-
-// 打印pdf
-const onPrint = () => {
-  pdfRef.value.print();
-};
-
-const handleChange = async ({ file }) => {
-  const type = file.type.split("/")[1];
-  const token = await getFileToken(type);
-  const observable = qiniu.upload(
-    file.file,
-    file.name,
-    token,
-    {},
-    {
-      useCdnDomain: true,
-    }
-  );
-  observable.subscribe({
-    next: (result) => {
-      // 主要用来展示进度
-      console.warn(result);
-    },
-    error: () => {
-      message.error("上传失败");
-    },
-    complete: (res) => {
-      formValue.T_pdf3 = [`${res.key}-${formValue.T_name}`];
-      console.log(formValue);
-    },
-  });
-};
-
-// 显示添加
-const showAddModal = () => {
-  modal.title = "新增";
-  modal.showModal = true;
-};
-
-// 编辑
-const editTaskInfo = async () => {
-  const { data: res } = await editTask({
-    T_task_id: task.T_task_id,
-    T_pdf3: `${taskInfo.value.T_pdf3}|${formValue.T_pdf3}`,
-  });
-  message.success(res.Msg);
-  getTaskInfo();
-};
-
-// 任务(获取)
-const getTaskInfo = async () => {
-  const { data: res } = await getTask({
-    T_task_id: task.T_task_id,
-  });
-  taskInfo.value = res.Data;
-  pdfList.value = res.Data.T_pdf3.split("|");
-  const str1 =
-    "https://bzdcoldverifyoss.baozhida.cn/UpImage/1678870726ff1c8023-2cda-405f-945e-ffcaa4fa3abc.pdf-123";
-  console.log(str1.endsWith("pdf"));
-};
-
-// 获取上传token
-const getFileToken = async (T_suffix) => {
-  const { data: res } = await getUpFileToken({
-    T_suffix,
-  });
-  return res.Data;
-};
-
-getTaskInfo();
-</script>
-
-<style scoped></style>

+ 0 - 69
src/views/project/data/edit/SetVue.vue

@@ -1,69 +0,0 @@
-<template>
-  <n-button type="primary" @click="showSetModal">温湿度上下限设置</n-button>
-  <n-modal
-    v-model:show="showModal"
-    :show-icon="false"
-    preset="dialog"
-    title="温湿度上下限设置"
-  >
-      <n-form
-        ref="formRef"
-        :model="formValue"
-        :rules="rules"
-        label-placement="left"
-        label-width="auto"
-      >
-        <n-divider title-placement="center"> 温度 </n-divider>
-        <n-form-item label="上限" path="value">
-          <n-input v-model:value="formValue.value" />
-        </n-form-item>
-        <n-form-item label="下限" path="value">
-          <n-input v-model:value="formValue.value" />
-        </n-form-item>
-        <n-divider title-placement="center"> 湿度 </n-divider>
-        <n-form-item label="上限" path="value">
-          <n-input v-model:value="formValue.value" />
-        </n-form-item>
-        <n-form-item label="下限" path="value">
-          <n-input v-model:value="formValue.value" />
-        </n-form-item>
-        <div class="flex justify-end">
-          <n-button type="primary">确定</n-button>
-        </div>
-      </n-form>
-  </n-modal>
-</template>
-
-<script setup>
-// 是否展示 Modal
-const showModal = ref(false);
-
-// 表单数据
-const formValue = reactive({
-  value: null,
-  datetimeValue: null,
-  selectValue: null,
-});
-
-// 表单规则
-const rules = {
-  datetimeValue: {
-    type: "number",
-    required: true,
-    trigger: ["blur", "change"],
-    message: "不能为空",
-  },
-  value: {
-    required: true,
-    trigger: ["blur"],
-    message: "不能为空",
-  },
-};
-
-// 显示设置
-const showSetModal = () => {
-  showModal.value = true;
-};
-</script>
-
-<style lang="scss" scoped></style>

+ 0 - 231
src/views/project/data/source/index.vue

@@ -1,231 +0,0 @@
-<template>
-  <n-spin size="large" :show="pinShow">
-    <template #description>
-      {{ pinText }}
-    </template>
-    <n-space vertical>
-      <n-space>
-        <n-button text style="font-size: 24px" @click="$router.back">
-          <n-icon>
-            <ArrowLeftOutlined />
-          </n-icon>
-        </n-button>
-        <n-breadcrumb>
-          <n-breadcrumb-item>任务管理</n-breadcrumb-item>
-          <n-breadcrumb-item>数据编辑</n-breadcrumb-item>
-        </n-breadcrumb>
-      </n-space>
-      <n-space>
-        <n-form-item label="SN" label-placement="left">
-          <n-input v-model:value="queryData.T_sn" />
-        </n-form-item>
-        <n-form-item label="ID" label-placement="left">
-          <n-input v-model:value="queryData.T_id" />
-        </n-form-item>
-        <n-form-item label="开始时间" label-placement="left">
-          <n-date-picker
-            v-model:formatted-value="queryData.Time_start"
-            value-format="yyyy-MM-dd HH:mm:ss"
-            type="datetime"
-            clearable
-          />
-        </n-form-item>
-        <n-form-item label="结束时间" label-placement="left">
-          <n-date-picker
-            v-model:formatted-value="queryData.Time_end"
-            value-format="yyyy-MM-dd HH:mm:ss"
-            type="datetime"
-            clearable
-          />
-        </n-form-item>
-        <n-button type="primary" @click="getDataList">搜索</n-button>
-        <n-popconfirm @positive-click="extractTaskData">
-          <template #trigger>
-            <n-button type="primary">重置数据</n-button>
-          </template>
-          是否确定重置数据?
-        </n-popconfirm>
-      </n-space>
-      <n-data-table
-        remote
-        :columns="columns"
-        :data="taskList"
-        :pagination="pagination"
-        :bordered="false"
-        :max-height="600"
-      />
-    </n-space>
-  </n-spin>
-  <n-modal v-model:show="showModal" :mask-closable="false">
-    <n-card
-      title="自定义时间"
-      style="width: 30%"
-      :bordered="false"
-      size="huge"
-      role="dialog"
-      aria-modal="true"
-    >
-      <n-form label-width="auto" label-placement="top" :model="queryData">
-        <n-form-item label="开始时间" path="Time_start">
-          <n-date-picker
-            v-model:formatted-value="queryData.Time_start"
-            value-format="yyyy-MM-dd HH:mm:ss"
-            type="datetime"
-            clearable
-            class="w-full"
-          />
-        </n-form-item>
-        <n-form-item label="结束时间" path="Time_end">
-          <n-date-picker
-            v-model:formatted-value="queryData.Time_end"
-            value-format="yyyy-MM-dd HH:mm:ss"
-            type="datetime"
-            clearable
-            class="w-full"
-          />
-        </n-form-item>
-        <n-form-item>
-          <n-button
-            block
-            type="primary"
-            @click="handleSearch"
-            :disabled="!(queryData.Time_start && queryData.Time_end)"
-          >
-            搜索
-          </n-button>
-        </n-form-item>
-      </n-form>
-    </n-card>
-  </n-modal>
-</template>
-
-<script setup>
-import { NSpace } from "naive-ui";
-import { extractTaskData, getTaskDataList, importTaskData } from "@/api";
-import { message } from "@/plugin/naive-ui";
-import { ArrowLeftOutlined } from "@vicons/antd";
-
-const notification = useNotification();
-
-const task = window.sessionStorage.getItem("task")
-  ? JSON.parse(window.sessionStorage.getItem("task"))
-  : {};
-
-const pinShow = ref(false);
-const pinText = ref("");
-
-// 查询数据
-const queryData = reactive({
-  T_task_id: task.T_task_id,
-  T_sn: "",
-  T_id: "",
-  Time_start: null,
-  Time_end: null,
-  page: 1,
-  page_z: 10,
-});
-
-// 列表
-const taskList = ref([]);
-
-// 需要展示的列
-const columns = [
-  {
-    title: "SN",
-    key: "T_sn",
-  },
-  {
-    title: "温度℃",
-    key: "T_t",
-  },
-  {
-    title: "湿度%",
-    key: "T_rh",
-  },
-  {
-    title: "记录时间",
-    key: "T_time",
-  },
-];
-
-// 分页数据源
-const pagination = reactive({
-  page: queryData.page,
-  pageSize: queryData.page_z,
-  itemCount: 10,
-  onChange: (page) => {
-    pagination.page = page;
-    queryData.page = page;
-    getDataList();
-  },
-});
-
-// modal数据源
-const showModal = ref(true);
-
-//
-const handleSearch = () => {
-  showModal.value = false;
-  extractTaskInfo();
-};
-
-//
-const importTaskInfo = async () => {
-  pinText.value = "正在打包数据";
-  const { data: res } = await importTaskData({
-    T_task_id: queryData.T_task_id,
-  });
-  if (res.Code === 200) {
-    notification.success({
-      content: "打包数据成功",
-      duration: 2500,
-      keepAliveOnHover: true,
-    });
-    getDataList();
-  } else {
-    showModal.value = true;
-  }
-  pinShow.value = false;
-};
-
-//
-const extractTaskInfo = async () => {
-  pinShow.value = true;
-  pinText.value = "正在汇总数据";
-  const { data: res } = await extractTaskData({
-    T_task_id: queryData.T_task_id,
-    Time_start: queryData.Time_start !== null ? queryData.Time_start : "",
-    Time_end: queryData.Time_end !== null ? queryData.Time_end : "",
-  });
-  if (res.Code === 200) {
-    notification.success({
-      content: "汇总数据成功",
-      duration: 2500,
-      keepAliveOnHover: true,
-    });
-    importTaskInfo();
-  } else {
-    showModal.value = true;
-  }
-};
-
-// 获取列表
-const getDataList = async () => {
-  const { data: res } = await getTaskDataList({
-    ...queryData,
-    Time_start: queryData.Time_start !== null ? queryData.Time_start : "",
-    Time_end: queryData.Time_end !== null ? queryData.Time_end : "",
-  });
-  pagination.itemCount = res.Data.Num;
-  if (!res.Data.List) {
-    message.info("数据为空,请重新选择");
-    queryData.Time_start = null;
-    queryData.Time_end = null;
-    showModal.value = true;
-  } else {
-    taskList.value = res.Data.List;
-  }
-};
-</script>
-
-<style scoped></style>

+ 26 - 6
src/views/project/index.vue

@@ -1,9 +1,12 @@
 <template>
   <n-data-table
+    remote
     :columns="columns"
     :data="data"
     :pagination="pagination"
     :bordered="false"
+    flex-height
+    class="h-full flex flex-col justify-between"
   />
 </template>
 
@@ -31,14 +34,17 @@ const columns = [
   {
     title: "公司名称",
     key: "T_user_name",
+    width: 180,
   },
   {
     title: "报告名称",
     key: "T_name",
+    width: 180,
   },
   {
     title: "截止时间",
     key: "T_deadline",
+    width: 180,
   },
   {
     title: "流程",
@@ -55,6 +61,10 @@ const columns = [
             h(
               NCard,
               {
+                style: {
+                  width: "70px",
+                  height: "50px",
+                },
                 contentStyle: {
                   textAlign: "center",
                   padding: 0,
@@ -67,7 +77,7 @@ const columns = [
                     NTag,
                     {
                       class: "w-full",
-                      type: "success",
+                      type: row.T_scheme_state === 0 ? "warning" : "success",
                       bordered: false,
                     },
                     {
@@ -96,7 +106,12 @@ const columns = [
                     NTag,
                     {
                       class: "w-full",
-                      type: "success",
+                      type:
+                        row.T_collection_state === 0
+                          ? "warning"
+                          : row.T_collection_state === 1
+                          ? "success"
+                          : "info",
                       bordered: false,
                     },
                     {
@@ -125,7 +140,7 @@ const columns = [
                     NTag,
                     {
                       class: "w-full",
-                      type: "success",
+                      type: row.T_reporting_state === 0 ? "warning" : "success",
                       bordered: false,
                     },
                     {
@@ -148,13 +163,18 @@ const columns = [
                 },
               },
               {
-                default: () => row.T_collection_name,
+                default: () => row.T_delivery_name,
                 cover: () =>
                   h(
                     NTag,
                     {
                       class: "w-full",
-                      type: "success",
+                      type:
+                        row.T_delivery_state === 0
+                          ? "warning"
+                          : row.T_delivery_state === 1
+                            ? "success"
+                            : "info",
                       bordered: false,
                     },
                     {
@@ -234,7 +254,7 @@ const pagination = reactive({
 const getDataList = async () => {
   const { data: res } = await getTaskList(queryData);
   pagination.itemCount = res.Data.Num;
-  data.value = res.Data.List;
+  data.value = res.Data.List || [];
 };
 
 getDataList();

+ 0 - 245
src/views/project/report/create/index.vue

@@ -1,245 +0,0 @@
-<template>
-  <n-space vertical>
-    <n-space>
-      <n-button text style="font-size: 24px" @click="$router.back">
-        <n-icon>
-          <ArrowLeftOutlined />
-        </n-icon>
-      </n-button>
-      <n-breadcrumb>
-        <n-breadcrumb-item>任务管理</n-breadcrumb-item>
-        <n-breadcrumb-item>报告生成</n-breadcrumb-item>
-      </n-breadcrumb>
-    </n-space>
-    <n-scrollbar style="max-height: 650px">
-      <div class="w-1/2 mx-auto">
-        <n-form
-          ref="formRef"
-          :model="formValue"
-          :rules="rules"
-          label-placement="left"
-          label-width="auto"
-        >
-          <template
-            v-for="item of templateDataList"
-            :key="item.T_VerifyTemplateMap_id"
-          >
-            <n-form-item
-              required
-              :label="item.T_name"
-              :path="item.T_field"
-              v-if="item.T_label === 3"
-            >
-              <n-select
-                v-model:value="formValue.selectedValue"
-                :options="options"
-              />
-              <n-popover trigger="hover">
-                <template #trigger>
-                  <n-icon size="20" class="ml-3">
-                    <question-icon />
-                  </n-icon>
-                </template>
-                <n-image width="200" :src="item.T_text" />
-              </n-popover>
-            </n-form-item>
-            <n-form-item
-              required
-              :label="item.T_name"
-              :path="item.T_field"
-              v-else-if="item.T_label === 4"
-            >
-              <n-select
-                v-model:value="formValue.selectedValue"
-                :options="options"
-              />
-              <n-popover trigger="hover">
-                <template #trigger>
-                  <n-icon size="20" class="ml-3">
-                    <question-icon />
-                  </n-icon>
-                </template>
-                <n-image width="200" :src="item.T_text" />
-              </n-popover>
-            </n-form-item>
-            <n-divider v-else-if="item.T_label === 5" />
-            <n-form-item
-              required
-              :label="item.T_name"
-              :path="item.T_field"
-              v-else-if="item.T_label === 7"
-            >
-              <n-date-picker
-                class="w-full"
-                v-model:formatted-value="formValue.formattedValue"
-                value-format="yyyy-MM-dd HH:mm:ss"
-                type="datetime"
-                clearable
-              />
-              <n-popover trigger="hover">
-                <template #trigger>
-                  <n-icon size="20" class="ml-3">
-                    <question-icon />
-                  </n-icon>
-                </template>
-                <n-image width="200" :src="item.T_text" />
-              </n-popover>
-            </n-form-item>
-            <n-form-item
-              required
-              :label="item.T_name"
-              :path="item.T_field"
-              v-else-if="item.T_label === 9"
-            >
-              <n-date-picker
-                class="w-full"
-                v-model:formatted-value="formValue.formattedValue"
-                value-format="yyyy-MM-dd HH:mm:ss"
-                v-model:value="formValue.formattedValue"
-                type="datetimerange"
-                clearable
-              />
-              <n-popover trigger="hover">
-                <template #trigger>
-                  <n-icon size="20" class="ml-3">
-                    <question-icon />
-                  </n-icon>
-                </template>
-                <n-image width="200" :src="item.T_text" />
-              </n-popover>
-            </n-form-item>
-            <n-form-item
-              required
-              :label="item.T_name"
-              :path="item.T_field"
-              v-else-if="item.T_label === 10"
-            >
-              <n-upload :default-upload="false" list-type="image-card" :max="1">
-                点击上传
-              </n-upload>
-              <n-popover trigger="hover">
-                <template #trigger>
-                  <n-icon size="20" class="ml-3">
-                    <question-icon />
-                  </n-icon>
-                </template>
-                <n-image width="200" :src="item.T_text" />
-              </n-popover>
-            </n-form-item>
-            <n-form-item
-              required
-              :label="item.T_name"
-              :path="item.T_field"
-              v-else-if="item.T_label === 11"
-            >
-              <n-upload :default-upload="false" list-type="image-card" :max="1">
-                点击上传
-              </n-upload>
-              <n-popover trigger="hover">
-                <template #trigger>
-                  <n-icon size="20" class="ml-3">
-                    <question-icon />
-                  </n-icon>
-                </template>
-                <n-image width="200" :src="item.T_text" />
-              </n-popover>
-            </n-form-item>
-            <n-form-item :label="item.T_name" :path="item.T_field" v-else>
-              <n-input v-model:value="formValue[item.T_field]" />
-              <n-popover trigger="hover">
-                <template #trigger>
-                  <n-icon size="20" class="ml-3">
-                    <question-icon />
-                  </n-icon>
-                </template>
-                <n-image width="200" :src="item.T_text" />
-              </n-popover>
-            </n-form-item>
-          </template>
-        </n-form>
-      </div>
-    </n-scrollbar>
-  </n-space>
-  <n-modal
-    :show-icon="false"
-    v-model:show="modal.showModal"
-    preset="dialog"
-    :title="modal.title"
-    positive-text="提交"
-    negative-text="取消"
-    @positive-click="submitCallback"
-  >
-    123
-  </n-modal>
-</template>
-
-<script setup>
-import { getVerifyTemplateMapDataList } from "@/api";
-import { QuestionCircleOutlined as QuestionIcon, ArrowLeftOutlined } from "@vicons/antd";
-import { useTaskStore } from "@/store/task";
-
-const taskStore = useTaskStore();
-
-// 表单信息
-const formValue = reactive({
-  textValue: null,
-  formattedValue: null,
-  selectedValue: null,
-});
-
-// 模板标签数据列表
-const templateDataList = ref([]);
-
-// 	配置选项内容
-const options = [
-  {
-    label: "Everybody's Got Something to Hide Except Me and My Monkey",
-    value: "song0",
-    disabled: true,
-  },
-  {
-    label: "Drive My Car",
-    value: "song1",
-  },
-  {
-    label: "Norwegian Wood",
-    value: "song2",
-  },
-];
-
-// 模态框数据源
-const modal = reactive({
-  title: "",
-  showModal: false,
-});
-
-// 验证表项的规则
-const rules = {
-  T_name: { required: true, message: "不能为空", trigger: "blur" },
-  time: { required: true, message: "不能为空", trigger: ["blur", "change"] },
-  extent: { required: true, message: "不能为空", trigger: ["blur", "change"] },
-};
-
-// 执行 positive 时执行的回调函数
-const submitCallback = () => {
-  if (modal.title === "添加") {
-    console.log("add");
-  } else {
-    console.log("edit");
-  }
-};
-
-// 模板标签数据(列表)
-const getDataList = async () => {
-  const { data: res } = await getVerifyTemplateMapDataList({
-    T_source: 1,
-    T_task_id: taskStore.task.T_task_id,
-    T_VerifyTemplate_id: "rtxF",
-  });
-  templateDataList.value = res.Data;
-};
-
-getDataList();
-</script>
-
-<style scoped></style>

+ 0 - 480
src/views/project/scheme/index.vue

@@ -1,480 +0,0 @@
-<template>
-  <n-space vertical>
-    <n-space justify="space-between">
-      <n-space>
-        <n-button text style="font-size: 24px" @click="$router.back">
-          <n-icon>
-            <ArrowLeftOutlined />
-          </n-icon>
-        </n-button>
-        <n-breadcrumb>
-          <n-breadcrumb-item>任务管理</n-breadcrumb-item>
-          <n-breadcrumb-item>实施方案</n-breadcrumb-item>
-        </n-breadcrumb>
-      </n-space>
-      <n-space>
-        <n-button type="primary" @click="showPutModal">生成方案</n-button>
-        <n-button type="primary" @click="showUploadModal"
-          >上传验证实施方案</n-button
-        >
-      </n-space>
-    </n-space>
-    <n-scrollbar style="max-height: 700px">
-      <n-form
-        label-placement="left"
-        label-width="auto"
-        style="width: 40%; margin: 0 auto"
-      >
-        <template
-          v-for="item of templateDataList"
-          :key="item.T_VerifyTemplateMap_id"
-        >
-          <n-form-item
-            required
-            :label="item.T_name"
-            :path="item.T_field"
-            v-if="item.T_label === 1"
-          >
-            <n-input v-model:value="item.T_value" />
-            <n-popover trigger="hover">
-              <template #trigger>
-                <n-icon size="20" class="ml-3">
-                  <info-icon />
-                </n-icon>
-              </template>
-              <n-image width="200" :src="item.T_text" />
-            </n-popover>
-          </n-form-item>
-          <n-form-item
-            required
-            :label="item.T_name"
-            :path="item.T_field"
-            v-else-if="item.T_label === 2"
-          >
-            <n-input v-model:value="item.T_value" />
-            <n-popover trigger="hover">
-              <template #trigger>
-                <n-icon size="20" class="ml-3">
-                  <info-icon />
-                </n-icon>
-              </template>
-              <n-image width="200" :src="item.T_text" />
-            </n-popover>
-          </n-form-item>
-          <n-form-item
-            required
-            :label="item.T_name"
-            :path="item.T_field"
-            v-else-if="item.T_label === 3"
-          >
-            <!--            <div class="w-full">-->
-            <!--              <n-popselect-->
-            <!--                multiple-->
-            <!--                :options="options"-->
-            <!--                :render-label="renderLabel"-->
-            <!--                v-model:value="item.T_value"-->
-            <!--                class="w-full"-->
-            <!--              >-->
-            <!--                <n-button block>-->
-            <!--                  {{-->
-            <!--                    Array.isArray(item.T_value) && item.T_value.length-->
-            <!--                      ? item.T_value.join(" | ")-->
-            <!--                      : "选择"-->
-            <!--                  }}-->
-            <!--                </n-button>-->
-            <!--              </n-popselect>-->
-            <!--            </div>-->
-            <n-select
-              v-model:value="item.T_value"
-              multiple
-              label-field="T_sn"
-              value-field="T_id"
-              :options="taskDataClassList"
-            />
-            <n-popover trigger="hover">
-              <template #trigger>
-                <n-icon size="20" class="ml-3">
-                  <info-icon />
-                </n-icon>
-              </template>
-              <n-image width="200" :src="item.T_text" />
-            </n-popover>
-          </n-form-item>
-          <n-form-item
-            required
-            :label="item.T_name"
-            :path="item.T_field"
-            v-else-if="item.T_label === 4"
-          >
-            <n-select
-              v-model:value="item.T_value"
-              :options="taskDataClassList"
-              label-field="T_sn"
-              value-field="T_id"
-            />
-            <n-popover trigger="hover">
-              <template #trigger>
-                <n-icon size="20" class="ml-3">
-                  <info-icon />
-                </n-icon>
-              </template>
-              <n-image width="200" :src="item.T_text" />
-            </n-popover>
-          </n-form-item>
-          <n-divider v-else-if="item.T_label === 5" />
-          <n-form-item
-            required
-            :label="item.T_name"
-            :path="item.T_field"
-            v-else-if="item.T_label === 7"
-          >
-            <n-date-picker
-              v-model:formatted-value="item.T_value"
-              value-format="yyyy-MM-dd HH:mm:ss"
-              type="datetime"
-              clearable
-              class="w-full"
-            />
-            <n-popover trigger="hover">
-              <template #trigger>
-                <n-icon size="20" class="ml-3">
-                  <info-icon />
-                </n-icon>
-              </template>
-              <n-image width="200" :src="item.T_text" />
-            </n-popover>
-          </n-form-item>
-          <n-form-item
-            required
-            :label="item.T_name"
-            :path="item.T_field"
-            v-else-if="item.T_label === 9"
-          >
-            <n-date-picker
-              v-model:formatted-value="item.T_value"
-              value-format="yyyy-MM-dd HH:mm:ss"
-              type="datetimerange"
-              clearable
-              class="w-full"
-            />
-            <n-popover trigger="hover">
-              <template #trigger>
-                <n-icon size="20" class="ml-3">
-                  <info-icon />
-                </n-icon>
-              </template>
-              <n-image width="200" :src="item.T_text" />
-            </n-popover>
-          </n-form-item>
-          <n-form-item
-            required
-            :label="item.T_name"
-            :path="item.T_field"
-            v-else-if="item.T_label === 10"
-          >
-            <n-upload
-              :default-upload="false"
-              v-model:file-list="item.T_value"
-              list-type="image-card"
-              :max="1"
-            >
-              点击上传
-            </n-upload>
-            <n-popover trigger="hover">
-              <template #trigger>
-                <n-icon size="20" class="ml-3">
-                  <info-icon />
-                </n-icon>
-              </template>
-              <n-image width="200" :src="item.T_text" />
-            </n-popover>
-          </n-form-item>
-          <n-form-item
-            required
-            :label="item.T_name"
-            :path="item.T_field"
-            v-else-if="item.T_label === 11"
-          >
-            <n-upload
-              :default-upload="false"
-              v-model:file-list="item.T_value"
-              list-type="image-card"
-              :max="1"
-            >
-              点击上传
-            </n-upload>
-            <n-popover trigger="hover">
-              <template #trigger>
-                <n-icon size="20" class="ml-3">
-                  <info-icon />
-                </n-icon>
-              </template>
-              <n-image width="200" :src="item.T_text" />
-            </n-popover>
-          </n-form-item>
-        </template>
-      </n-form>
-    </n-scrollbar>
-  </n-space>
-  <n-modal
-    :style="{ width: modal.width }"
-    v-model:show="modal.showModal"
-    :show-icon="modal.title === '生成方案'"
-    preset="dialog"
-    :title="modal.title"
-    positive-text="确认"
-    negative-text="取消"
-    @positive-click="submitCallback"
-  >
-    <template v-if="modal.title === '生成方案'"> 确定生成报告? </template>
-    <template v-else>
-      <n-form
-        ref="formRef"
-        :model="formValue"
-        :rules="rules"
-        label-placement="top"
-      >
-        <n-grid :cols="12">
-          <n-form-item-gi :span="12" label="是否公开" path="T_Show">
-            <n-select
-              v-model:value="formValue.T_Show"
-              :options="generalOptions"
-            />
-          </n-form-item-gi>
-          <n-form-item-gi :span="12" label="实施方案" path="T_pdf1" required>
-            <n-upload :default-upload="false" :max="1" @change="handleChange">
-              <n-button>上传文件</n-button>
-            </n-upload>
-          </n-form-item-gi>
-        </n-grid>
-      </n-form>
-    </template>
-  </n-modal>
-</template>
-
-<script setup>
-import {
-  getTaskDataClassList,
-  getVerifyTemplateMapDataList,
-  getUpFileToken,
-  putVerifyTemplateMapData,
-  editTask,
-} from "@/api";
-import {
-  InfoCircleOutlined as InfoIcon,
-  ArrowLeftOutlined,
-} from "@vicons/antd";
-import * as qiniu from "qiniu-js";
-import { getToken } from "@/utils/storage/sessionToken";
-import { useTaskStore } from "@/store/task";
-
-const message = useMessage();
-
-const taskStore = useTaskStore();
-
-// 查询数据
-const queryData = reactive({
-  T_source: 1,
-  T_task_id: taskStore.task.T_task_id,
-  T_VerifyTemplate_id: taskStore.task.T_VerifyTemplate_id,
-});
-
-// 是否公开选项
-const generalOptions = ["隐藏", "公开"].map((v, i) => ({
-  label: v,
-  value: i,
-}));
-
-// const renderLabel = () => {
-//   return h(
-//     NThing,
-//     {},
-//     {
-//       avatar: () => "avatar",
-//       header: () => "title",
-//       description: () => "description",
-//     }
-//   );
-// };
-
-// 获取表项中收集到的值的对象
-const formValue = reactive({
-  T_Show: null,
-  T_pdf1: null,
-});
-
-// 验证表项的规则
-const rules = {
-  inputValue: {
-    required: true,
-    trigger: ["blur", "input"],
-    message: "请输入 inputValue",
-  },
-};
-
-// 模板标签数据列表
-const templateDataList = ref([]);
-
-// 设备列表
-const taskDataClassList = ref([]);
-
-// 上传数据
-let fileList = [];
-
-// 模态框数据源
-const modal = reactive({
-  title: "",
-  width: "",
-  showModal: false,
-});
-
-// 组件状态变化的回调
-const handleChange = async (options) => {
-  const token = await getFileToken(options.file.type.split("/")[1]);
-  const observable = qiniu.upload(
-    options.file.file,
-    options.file.name,
-    token,
-    {},
-    {
-      useCdnDomain: true,
-    }
-  );
-  observable.subscribe({
-    next: (result) => {
-      // 主要用来展示进度
-      console.warn(result);
-    },
-    error: () => {
-      message.error("上传图片失败");
-    },
-    complete: (res) => {
-      formValue.T_pdf1 = res.key;
-    },
-  });
-};
-
-// 执行 positive 时执行的回调函数
-const submitCallback = () => {
-  if (modal.title === "生成方案") {
-    putTemplateData();
-  } else {
-    editTaskInfo();
-  }
-};
-
-// 获取七牛云上传文件token
-const getFileToken = async (T_suffix) => {
-  const { data: res } = await getUpFileToken({
-    T_suffix,
-  });
-  return res.Data;
-};
-
-// 显示上传验证实施方案
-const showUploadModal = () => {
-  modal.title = "上传验证实施方案";
-  modal.showModal = true;
-  modal.width = "30%";
-};
-
-// 任务(编辑)
-const editTaskInfo = async () => {
-  const { data: res } = await editTask({
-    T_task_id: queryData.T_task_id,
-    T_pdf1: formValue.T_pdf1,
-  });
-  if (res.Code === 200) {
-    message.success(res.Msg);
-  }
-};
-
-// 显示生成方案
-const showPutModal = async () => {
-  modal.title = "生成方案";
-  modal.width = "";
-  modal.showModal = true;
-  fileList = toRaw(templateDataList.value);
-  fileList = fileList.filter((item) => item.T_label !== 5);
-  for (const item of fileList) {
-    const index = fileList.indexOf(item);
-    if (item.T_label === 10 || item.T_label === 11) {
-      const token = await getUpFileToken(item.T_value[0].type.split("/")[1]);
-      const observable = qiniu.upload(
-        item.T_value[0].file,
-        item.T_value[0].name,
-        token,
-        {},
-        {
-          useCdnDomain: true,
-        }
-      );
-      observable.subscribe({
-        next: (result) => {
-          // 主要用来展示进度
-          console.warn(result);
-        },
-        error: () => {
-          message.error("上传图片失败");
-        },
-        complete: (res) => {
-          fileList[index].T_value = res.key;
-        },
-      });
-    }
-  }
-};
-
-// 模板标签数据添加或更新
-const putTemplateData = async () => {
-  const VerifyTemplateMapData = fileList.map((item) => {
-    if (item.T_label === 3 || item.T_label === 9) {
-      return {
-        T_VerifyTemplateMap_id: item.T_VerifyTemplateMap_id,
-        T_value: item.T_value.join("|"),
-      };
-    } else {
-      return {
-        T_VerifyTemplateMap_id: item.T_VerifyTemplateMap_id,
-        T_value: item.T_value + "",
-      };
-    }
-  });
-  const token = getToken();
-  const { data: res } = await putVerifyTemplateMapData({
-    User_tokey: token,
-    T_source: queryData.T_source,
-    T_task_id: queryData.T_task_id,
-    T_VerifyTemplate_id: queryData.T_VerifyTemplate_id,
-
-    VerifyTemplateMapData,
-  });
-  console.log(res);
-};
-
-// 模板标签数据(列表)
-const getDataList = async () => {
-  const { data: res } = await getVerifyTemplateMapDataList(queryData);
-  templateDataList.value = res.Data;
-  templateDataList.value.forEach((item) => {
-    if (item.T_label === 10 || item.T_label === 11 || item.T_label === 3) {
-      item.T_value = [];
-    } else if (item.T_label === 7 || item.T_label === 9) {
-      item.T_value = null;
-    }
-  });
-};
-
-// 获取设备列表
-const getTaskClassList = async () => {
-  const { data: res } = await getTaskDataClassList({
-    T_task_id: queryData.T_task_id,
-  });
-  taskDataClassList.value = res.Data ? res.Data : [];
-};
-
-getTaskClassList();
-getDataList();
-</script>
-
-<style scoped></style>

+ 40 - 33
src/views/project/report/audit/tab-four/index.vue → src/views/report/audit/TabFour.vue

@@ -1,40 +1,42 @@
 <template>
-  <n-space vertical>
-    <n-space>
-      <n-space align="center">
-        <span>开始时间</span>
-        <n-date-picker
-          v-model:formatted-value="queryData.Time_start"
-          value-format="yyyy-MM-dd HH:mm:ss"
-          type="datetime"
-          clearable
-        />
-      </n-space>
-      <n-space align="center">
-        <span>结束时间</span>
-        <n-date-picker
-          v-model:formatted-value="queryData.Time_end"
-          value-format="yyyy-MM-dd HH:mm:ss"
-          type="datetime"
-          clearable
-        />
-      </n-space>
-      <n-button type="primary" @click="getDataList">搜索</n-button>
-    </n-space>
-    <n-data-table
-      remote
-      :columns="columns"
-      :data="dataList"
-      :pagination="pagination"
-      :bordered="false"
+  <n-space>
+    <n-input
+      v-model:value="queryData.T_sn"
+      type="text"
+      placeholder="请输入SN搜索"
     />
+    <n-input
+      v-model:value="queryData.T_id"
+      type="text"
+      placeholder="请输入设备编号搜索"
+    />
+    <n-date-picker
+      type="datetimerange"
+      @update:formatted-value="handleUpdateDate"
+      clearable
+    />
+    <n-button type="primary" @click="extractTask">搜索</n-button>
   </n-space>
+  <n-data-table
+    remote
+    :columns="columns"
+    :data="dataList"
+    :pagination="pagination"
+    :bordered="false"
+    flex-height
+    class="flex-1"
+  />
 </template>
 
 <script setup>
 import { extractTaskData } from "@/api";
 
-const props = defineProps(["task"]);
+const props = defineProps({
+  task: {
+    required: true,
+    default: {},
+  },
+});
 
 // 查询参数
 const queryData = reactive({
@@ -74,21 +76,26 @@ const pagination = reactive({
   onChange: (page) => {
     pagination.page = page;
     queryData.page = page;
-    getDataList();
   },
 });
 
+// 选择起止时间
+const handleUpdateDate = (value) => {
+  queryData.Time_start = value[0];
+  queryData.Time_end = value[1];
+};
+
 // 获取列表
-const getDataList = async () => {
+const extractTask = async () => {
   const { data: res } = await extractTaskData({
     ...queryData,
     Time_start: queryData.Time_start !== null ? queryData.Time_start : "",
     Time_end: queryData.Time_end !== null ? queryData.Time_end : "",
   });
-  dataList.value = res.Data ? res.Data : [];
+  console.log(res);
 };
 
-getDataList();
+extractTask();
 </script>
 
 <style scoped></style>

+ 82 - 0
src/views/report/audit/TabThree.vue

@@ -0,0 +1,82 @@
+<template>
+  <div class="h-full grid grid-cols-4 gap-x-3">
+    <n-card>
+      <n-list>
+        <template #header>
+          <h2>校准证书</h2>
+        </template>
+        <n-scrollbar :style="{ maxHeight: `${height - 450}px`, paddingRight: '20px' }" trigger="none">
+          <template
+            v-for="item of dataList"
+            :key="item"
+          >
+            <n-list-item>
+              <template #suffix>
+                <n-button type="primary" @click="handleView(item)"
+                  >查看</n-button
+                >
+              </template>
+              <n-card
+                embedded
+                :bordered="false"
+                :content-style="{ padding: '5px', textAlign: 'center' }"
+              >
+                {{ item.slice(item.lastIndexOf("-") + 1) }}
+              </n-card>
+            </n-list-item>
+          </template>
+        </n-scrollbar>
+      </n-list>
+    </n-card>
+    <n-card class="col-span-3">
+      <div class="flex justify-end">
+        <n-button text style="font-size: 24px" @click="onPrint">
+          <n-icon>
+            <printer-icon />
+          </n-icon>
+        </n-button>
+      </div>
+      <n-scrollbar :style="{ maxHeight: `${height - 430}px` }" trigger="none">
+        <vue-pdf-embed ref="pdfRef" :source="source" />
+      </n-scrollbar>
+    </n-card>
+  </div>
+</template>
+
+<script setup>
+import VuePdfEmbed from "vue-pdf-embed";
+import { PrinterOutlined as PrinterIcon } from "@vicons/antd";
+
+const props = defineProps({
+  taskInfo: {
+    required: true,
+    default: {},
+  },
+  height: {
+    required: true,
+    default: {},
+  },
+});
+
+//
+const source = ref("");
+
+//
+const handleView = (row) => {
+  source.value = row.slice(0, row.lastIndexOf("-"));
+};
+
+//
+const dataList = computed(() => {
+  return props.taskInfo.T_pdf3.split("|").filter((item) => item);
+});
+
+const pdfRef = ref(null);
+
+// 打印pdf
+const onPrint = () => {
+  pdfRef.value.print();
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 27 - 36
src/views/project/report/audit/index.vue → src/views/report/audit/index.vue

@@ -1,19 +1,11 @@
 <template>
-  <n-space vertical>
-    <n-space justify="space-between">
-      <n-space>
-        <n-button text style="font-size: 24px" @click="$router.back">
-          <n-icon>
-            <ArrowLeftOutlined />
-          </n-icon>
-        </n-button>
-        <n-breadcrumb>
-          <n-breadcrumb-item>任务管理</n-breadcrumb-item>
-          <n-breadcrumb-item>报告审核</n-breadcrumb-item>
-        </n-breadcrumb>
-      </n-space>
-      <n-button type="primary" @click="handleCheck">审核</n-button>
-    </n-space>
+  <div class="h-full flex flex-col gap-y-3">
+    <n-page-header @back="$router.back">
+      <template #title> 报告审核 </template>
+      <template #extra>
+        <n-button type="primary" @click="handleCheck">审核</n-button>
+      </template>
+    </n-page-header>
     <n-descriptions bordered>
       <n-descriptions-item label="报告名称">
         {{ taskInfo.T_name }}
@@ -25,8 +17,8 @@
         {{ taskInfo.UpdateTime }}
       </n-descriptions-item>
     </n-descriptions>
-    <n-tabs type="segment" animated size="large">
-      <n-tab-pane name="1" tab="验证方案">
+    <n-tabs class="flex-1" type="segment" animated size="large">
+      <n-tab-pane class="h-full flex flex-col gap-y-3" name="1" tab="验证方案">
         <n-space justify="end">
           <n-button text style="font-size: 24px" @click="onPrint">
             <n-icon>
@@ -34,11 +26,11 @@
             </n-icon>
           </n-button>
         </n-space>
-        <n-scrollbar style="max-height: 450px" trigger="none">
+        <n-scrollbar :style="{ maxHeight: `${height - 420}px` }" trigger="none">
           <vue-pdf-embed ref="pdfRef" :source="taskInfo.T_pdf1" />
         </n-scrollbar>
       </n-tab-pane>
-      <n-tab-pane name="2" tab="验证报告">
+      <n-tab-pane class="h-full flex flex-col gap-y-3" name="2" tab="验证报告">
         <n-space justify="end">
           <n-button text style="font-size: 24px" @click="onPrint">
             <n-icon>
@@ -46,34 +38,29 @@
             </n-icon>
           </n-button>
         </n-space>
-        <n-scrollbar style="max-height: 450px" trigger="none">
+        <n-scrollbar :style="{ maxHeight: `${height - 420}px` }" trigger="none">
           <vue-pdf-embed ref="pdfRef" :source="taskInfo.T_pdf2" />
         </n-scrollbar>
       </n-tab-pane>
-      <n-tab-pane name="3" tab="校准证书">
-        <n-space justify="end">
-          <n-button text style="font-size: 24px" @click="onPrint">
-            <n-icon>
-              <printer-icon />
-            </n-icon>
-          </n-button>
-        </n-space>
-        <n-scrollbar style="max-height: 450px" trigger="none">
-          <vue-pdf-embed ref="pdfRef" :source="taskInfo.T_pdf3" />
-        </n-scrollbar>
+      <n-tab-pane class="h-full flex flex-col gap-y-3" name="3" tab="校准证书">
+        <TabThree :taskInfo="taskInfo" :height="height" />
       </n-tab-pane>
-      <n-tab-pane name="4" tab="验证数据">
+      <n-tab-pane class="h-full flex flex-col gap-y-3" name="4" tab="验证数据">
         <TabFour :task="task" />
       </n-tab-pane>
     </n-tabs>
-  </n-space>
+  </div>
 </template>
 
 <script setup>
 import VuePdfEmbed from "vue-pdf-embed";
 import { getTask, updateTaskData } from "@/api";
-import { PrinterOutlined as PrinterIcon, ArrowLeftOutlined } from "@vicons/antd";
-import TabFour from "./tab-four/index.vue";
+import { PrinterOutlined as PrinterIcon } from "@vicons/antd";
+import TabThree from "./TabThree.vue";
+import TabFour from "./TabFour.vue";
+import { useWindowSize } from "@vueuse/core";
+
+const { height } = useWindowSize();
 
 const dialog = useDialog();
 
@@ -126,4 +113,8 @@ const getTaskInfo = async () => {
 getTaskInfo();
 </script>
 
-<style scoped lang="scss"></style>
+<style scoped lang="scss">
+:deep(.n-tabs-pane-wrapper) {
+  flex: 1;
+}
+</style>

+ 371 - 0
src/views/report/create/index.vue

@@ -0,0 +1,371 @@
+<template>
+  <div class="h-full flex flex-col gap-y-3">
+    <n-page-header @back="$router.back">
+      <template #title> 报告生成 </template>
+      <template #extra>
+        <n-space>
+          <n-button type="primary" @click="handleCreate">生成方案</n-button>
+          <n-button type="primary" @click="showUploadModal"
+          >上传验证实施方案</n-button
+          >
+        </n-space>
+      </template>
+    </n-page-header>
+    <n-form
+      label-placement="left"
+      label-width="auto"
+      size="large"
+      show-require-mark
+      :show-feedback="false"
+      class="w-2/5 mx-auto flex-1 flex flex-col justify-between"
+    >
+      <template v-for="item of dataList" :key="item.T_VerifyTemplateMap_id">
+        <n-form-item :label="item.T_name" v-if="item.T_label === 1">
+          <n-input v-model:value="item.T_value" />
+          <n-popover trigger="hover">
+            <template #trigger>
+              <n-icon size="24" class="ml-3">
+                <InformationCircleOutline />
+              </n-icon>
+            </template>
+            <n-image width="200" :src="item.T_text" />
+          </n-popover>
+        </n-form-item>
+        <n-form-item :label="item.T_name" v-else-if="item.T_label === 2">
+          <n-input v-model:value="item.T_value" />
+          <n-popover trigger="hover">
+            <template #trigger>
+              <n-icon size="24" class="ml-3">
+                <InformationCircleOutline />
+              </n-icon>
+            </template>
+            <n-image width="200" :src="item.T_text" />
+          </n-popover>
+        </n-form-item>
+        <n-form-item :label="item.T_name" v-else-if="item.T_label === 3">
+          <div class="w-full">
+            <n-popselect
+              multiple
+              :options="classList"
+              :render-label="renderLabel"
+              v-model:value="item.T_value"
+              class="w-full"
+            >
+              <n-button block>
+                {{
+                  Array.isArray(item.T_value) && item.T_value.length
+                    ? item.T_value.join(" | ")
+                    : "选择"
+                }}
+              </n-button>
+            </n-popselect>
+          </div>
+          <n-popover trigger="hover">
+            <template #trigger>
+              <n-icon size="24" class="ml-3">
+                <InformationCircleOutline />
+              </n-icon>
+            </template>
+            <n-image width="200" :src="item.T_text" />
+          </n-popover>
+        </n-form-item>
+        <n-form-item :label="item.T_name" v-else-if="item.T_label === 4">
+          <n-select v-model:value="item.T_value" :options="classList" />
+          <n-popover trigger="hover">
+            <template #trigger>
+              <n-icon size="24" class="ml-3">
+                <InformationCircleOutline />
+              </n-icon>
+            </template>
+            <n-image width="200" :src="item.T_text" />
+          </n-popover>
+        </n-form-item>
+        <n-divider v-else-if="item.T_label === 5" />
+        <n-form-item :label="item.T_name" v-else-if="item.T_label === 7">
+          <n-date-picker
+            v-model:formatted-value="item.T_value"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            type="datetime"
+            clearable
+            class="w-full"
+          />
+          <n-popover trigger="hover">
+            <template #trigger>
+              <n-icon size="24" class="ml-3">
+                <InformationCircleOutline />
+              </n-icon>
+            </template>
+            <n-image width="200" :src="item.T_text" />
+          </n-popover>
+        </n-form-item>
+        <n-form-item :label="item.T_name" v-else-if="item.T_label === 9">
+          <n-date-picker
+            v-model:formatted-value="item.T_value"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            type="datetimerange"
+            clearable
+            class="w-full"
+          />
+          <n-popover trigger="hover">
+            <template #trigger>
+              <n-icon size="24" class="ml-3">
+                <InformationCircleOutline />
+              </n-icon>
+            </template>
+            <n-image width="200" :src="item.T_text" />
+          </n-popover>
+        </n-form-item>
+        <n-form-item :label="item.T_name" v-else-if="item.T_label === 10">
+          <n-upload
+            v-model:file-list="item.T_value"
+            :default-upload="false"
+            list-type="image-card"
+            :max="1"
+          >
+            点击上传
+          </n-upload>
+          <n-popover trigger="hover">
+            <template #trigger>
+              <n-icon size="24" class="ml-3">
+                <InformationCircleOutline />
+              </n-icon>
+            </template>
+            <n-image width="200" :src="item.T_text" />
+          </n-popover>
+        </n-form-item>
+        <n-form-item :label="item.T_name" v-else-if="item.T_label === 11">
+          <n-upload
+            v-model:file-list="item.T_value"
+            :default-upload="false"
+            list-type="image-card"
+            :max="1"
+          >
+            点击上传
+          </n-upload>
+          <n-popover trigger="hover">
+            <template #trigger>
+              <n-icon size="24" class="ml-3">
+                <InformationCircleOutline />
+              </n-icon>
+            </template>
+            <n-image width="200" :src="item.T_text" />
+          </n-popover>
+        </n-form-item>
+      </template>
+    </n-form>
+  </div>
+  <n-modal
+    v-model:show="modal.showModal"
+    :show-icon="false"
+    preset="dialog"
+    title="上传验证实施方案"
+    positive-text="确认"
+    negative-text="取消"
+    @positive-click="editTaskInfo"
+  >
+    <n-form :model="formValue" label-width="auto" show-require-mark>
+      <n-form-item label="实施方案">
+        <n-upload :default-upload="false" :max="1" @change="handleChange">
+          <n-button> 上传文件 </n-button>
+        </n-upload>
+      </n-form-item>
+    </n-form>
+  </n-modal>
+</template>
+
+<script setup>
+import {
+  getTaskDataClassList,
+  getVerifyTemplateMapDataList,
+  putVerifyTemplateMapData,
+  editTask,
+} from "@/api";
+import { InformationCircleOutline } from "@vicons/ionicons5";
+import * as qiniu from "qiniu-js";
+import { getToken } from "@/utils/storage/sessionToken";
+import { NThing } from "naive-ui";
+import { getFileToken } from "@/common";
+
+const message = useMessage();
+
+const dialog = useDialog();
+
+const task = window.sessionStorage.getItem("task")
+  ? JSON.parse(window.sessionStorage.getItem("task"))
+  : {};
+
+// 查询数据
+const queryData = reactive({
+  T_source: 1,
+  T_task_id: task.T_task_id,
+  T_VerifyTemplate_id: task.T_VerifyTemplate_id,
+});
+
+//
+const renderLabel = () => {
+  return h(
+    NThing,
+    {},
+    {
+      avatar: () => "avatar",
+      header: () => "title",
+      description: () => "description",
+    }
+  );
+};
+
+// 获取表项中收集到的值的对象
+const formValue = reactive({
+  T_pdf1: null,
+});
+
+// 模板标签数据列表
+const dataList = ref([]);
+
+//
+let fileList = [];
+
+// 设备列表
+const classList = ref([]);
+
+// 模态框数据源
+const modal = reactive({
+  showModal: false,
+});
+
+// 组件状态变化的回调
+const handleChange = async (options) => {
+  const token = await getFileToken(options.file.type.split("/")[1]);
+  const observable = qiniu.upload(
+    options.file.file,
+    options.file.name,
+    token,
+    {},
+    {
+      useCdnDomain: true,
+    }
+  );
+  observable.subscribe({
+    next: (result) => {
+      // 主要用来展示进度
+      console.warn(result);
+    },
+    error: () => {
+      message.error("上传图片失败");
+    },
+    complete: (res) => {
+      formValue.T_pdf1 = res.key;
+    },
+  });
+};
+
+// 显示上传验证实施方案
+const showUploadModal = () => {
+  modal.title = "上传验证实施方案";
+  modal.showModal = true;
+};
+
+// 任务(编辑)
+const editTaskInfo = async () => {
+  const { data: res } = await editTask({
+    T_task_id: queryData.T_task_id,
+    T_pdf1: formValue.T_pdf1,
+  });
+  if (res.Code === 200) {
+    message.success(res.Msg);
+  }
+};
+
+// 显示生成方案
+const handleCreate = async () => {
+  fileList = toRaw(dataList.value);
+  for (const item of fileList) {
+    if (item.T_label === 10 || item.T_label === 11) {
+      const token = await getFileToken(item.T_value[0].type.split("/")[1]);
+      const observable = qiniu.upload(
+        item.T_value[0].file,
+        item.T_value[0].name,
+        token,
+        {},
+        {
+          useCdnDomain: true,
+        }
+      );
+      observable.subscribe({
+        next: (result) => {
+          // 主要用来展示进度
+          console.warn(result);
+        },
+        error: () => {
+          message.error("上传图片失败");
+        },
+        complete: (res) => {
+          item.T_value = res.key;
+        },
+      });
+    }
+  }
+  dialog.info({
+    title: "提示",
+    content: "确认生成报告?",
+    positiveText: "确定",
+    negativeText: "取消",
+    onPositiveClick: () => {
+      putTemplateData();
+    },
+  });
+};
+
+// 模板标签数据添加或更新
+const putTemplateData = async () => {
+  const VerifyTemplateMapData = fileList.map((item) => {
+    if (item.T_label === 3 || item.T_label === 9) {
+      return {
+        T_VerifyTemplateMap_id: item.T_VerifyTemplateMap_id,
+        T_value: item.T_value.join("|"),
+      };
+    } else {
+      return {
+        T_VerifyTemplateMap_id: item.T_VerifyTemplateMap_id,
+        T_value: item.T_value + "",
+      };
+    }
+  });
+  const token = getToken();
+  const { data: res } = await putVerifyTemplateMapData({
+    User_tokey: token,
+    T_source: queryData.T_source,
+    T_task_id: queryData.T_task_id,
+    T_VerifyTemplate_id: queryData.T_VerifyTemplate_id,
+
+    VerifyTemplateMapData,
+  });
+  message.success(res.Msg);
+};
+
+// 模板标签数据(列表)
+const getDataList = async () => {
+  const { data: res } = await getVerifyTemplateMapDataList(queryData);
+  dataList.value = res.Data;
+  dataList.value.forEach((item) => {
+    if (item.T_label === 10 || item.T_label === 11 || item.T_label === 3) {
+      item.T_value = [];
+    } else if (item.T_label === 7 || item.T_label === 9) {
+      item.T_value = null;
+    }
+  });
+};
+
+// 获取设备列表
+const getTaskClassList = async () => {
+  const { data: res } = await getTaskDataClassList({
+    T_task_id: queryData.T_task_id,
+  });
+  classList.value = res.Data || [];
+};
+
+getTaskClassList();
+getDataList();
+</script>
+
+<style scoped></style>

+ 387 - 0
src/views/scheme/index.vue

@@ -0,0 +1,387 @@
+<template>
+  <div class="h-full flex flex-col gap-y-3">
+    <n-page-header @back="$router.back">
+      <template #title> 实施方案 </template>
+      <template #extra>
+        <n-space>
+          <n-button type="primary" @click="handleCreate">生成方案</n-button>
+          <n-button type="primary" @click="showUploadModal"
+            >上传验证实施方案</n-button
+          >
+        </n-space>
+      </template>
+    </n-page-header>
+    <n-form
+      label-placement="left"
+      label-width="auto"
+      size="large"
+      show-require-mark
+      :show-feedback="false"
+      class="w-2/5 mx-auto flex-1 flex flex-col justify-between"
+    >
+      <template v-for="item of dataList" :key="item.T_VerifyTemplateMap_id">
+        <n-form-item :label="item.T_name" v-if="item.T_label === 1">
+          <n-input v-model:value="item.T_value" />
+          <n-popover trigger="hover">
+            <template #trigger>
+              <n-icon size="24" class="ml-3">
+                <InformationCircleOutline />
+              </n-icon>
+            </template>
+            <n-image width="200" :src="item.T_text" />
+          </n-popover>
+        </n-form-item>
+        <n-form-item :label="item.T_name" v-else-if="item.T_label === 2">
+          <n-input v-model:value="item.T_value" />
+          <n-popover trigger="hover">
+            <template #trigger>
+              <n-icon size="24" class="ml-3">
+                <InformationCircleOutline />
+              </n-icon>
+            </template>
+            <n-image width="200" :src="item.T_text" />
+          </n-popover>
+        </n-form-item>
+        <n-form-item :label="item.T_name" v-else-if="item.T_label === 3">
+          <div class="w-full">
+            <n-popselect
+              multiple
+              :options="classList"
+              :render-label="renderLabel"
+              v-model:value="item.T_value"
+              class="w-full"
+            >
+              <n-button block>
+                {{
+                  Array.isArray(item.T_value) && item.T_value.length
+                    ? item.T_value.join(" | ")
+                    : "选择"
+                }}
+              </n-button>
+            </n-popselect>
+          </div>
+          <n-popover trigger="hover">
+            <template #trigger>
+              <n-icon size="24" class="ml-3">
+                <InformationCircleOutline />
+              </n-icon>
+            </template>
+            <n-image width="200" :src="item.T_text" />
+          </n-popover>
+        </n-form-item>
+        <n-form-item :label="item.T_name" v-else-if="item.T_label === 4">
+          <n-select v-model:value="item.T_value" :options="classList" />
+          <n-popover trigger="hover">
+            <template #trigger>
+              <n-icon size="24" class="ml-3">
+                <InformationCircleOutline />
+              </n-icon>
+            </template>
+            <n-image width="200" :src="item.T_text" />
+          </n-popover>
+        </n-form-item>
+        <n-divider v-else-if="item.T_label === 5" />
+        <n-form-item :label="item.T_name" v-else-if="item.T_label === 7">
+          <n-date-picker
+            v-model:formatted-value="item.T_value"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            type="datetime"
+            clearable
+            class="w-full"
+          />
+          <n-popover trigger="hover">
+            <template #trigger>
+              <n-icon size="24" class="ml-3">
+                <InformationCircleOutline />
+              </n-icon>
+            </template>
+            <n-image width="200" :src="item.T_text" />
+          </n-popover>
+        </n-form-item>
+        <n-form-item :label="item.T_name" v-else-if="item.T_label === 9">
+          <n-date-picker
+            v-model:formatted-value="item.T_value"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            type="datetimerange"
+            clearable
+            class="w-full"
+          />
+          <n-popover trigger="hover">
+            <template #trigger>
+              <n-icon size="24" class="ml-3">
+                <InformationCircleOutline />
+              </n-icon>
+            </template>
+            <n-image width="200" :src="item.T_text" />
+          </n-popover>
+        </n-form-item>
+        <n-form-item :label="item.T_name" v-else-if="item.T_label === 10">
+          <n-upload
+            v-model:file-list="item.T_value"
+            :default-upload="false"
+            list-type="image-card"
+            :max="1"
+          >
+            点击上传
+          </n-upload>
+          <n-popover trigger="hover">
+            <template #trigger>
+              <n-icon size="24" class="ml-3">
+                <InformationCircleOutline />
+              </n-icon>
+            </template>
+            <n-image width="200" :src="item.T_text" />
+          </n-popover>
+        </n-form-item>
+        <n-form-item :label="item.T_name" v-else-if="item.T_label === 11">
+          <n-upload
+            v-model:file-list="item.T_value"
+            :default-upload="false"
+            list-type="image-card"
+            :max="1"
+          >
+            点击上传
+          </n-upload>
+          <n-popover trigger="hover">
+            <template #trigger>
+              <n-icon size="24" class="ml-3">
+                <InformationCircleOutline />
+              </n-icon>
+            </template>
+            <n-image width="200" :src="item.T_text" />
+          </n-popover>
+        </n-form-item>
+      </template>
+    </n-form>
+  </div>
+  <n-modal
+    v-model:show="modal.showModal"
+    :show-icon="false"
+    preset="dialog"
+    title="上传验证实施方案"
+    positive-text="确认"
+    negative-text="取消"
+    @positive-click="editTaskInfo"
+  >
+    <n-form :model="formValue" label-width="auto" show-require-mark>
+      <n-form-item label="实施方案">
+        <n-upload :default-upload="false" :max="1" @change="handleChange">
+          <n-button> 上传文件 </n-button>
+        </n-upload>
+      </n-form-item>
+    </n-form>
+  </n-modal>
+</template>
+
+<script setup>
+import {
+  getTaskDataClassList,
+  getVerifyTemplateMapDataList,
+  putVerifyTemplateMapData,
+  editTask,
+} from "@/api";
+import { InformationCircleOutline } from "@vicons/ionicons5";
+import * as qiniu from "qiniu-js";
+import { getToken } from "@/utils/storage/sessionToken";
+import { NThing } from "naive-ui";
+import { getFileToken } from "@/common";
+
+const message = useMessage();
+
+const dialog = useDialog();
+
+const task = window.sessionStorage.getItem("task")
+  ? JSON.parse(window.sessionStorage.getItem("task"))
+  : {};
+
+// 查询数据
+const queryData = reactive({
+  T_source: 1,
+  T_task_id: task.T_task_id,
+  T_VerifyTemplate_id: task.T_VerifyTemplate_id,
+});
+
+//
+const renderLabel = () => {
+  return h(
+    NThing,
+    {},
+    {
+      avatar: () => "avatar",
+      header: () => "title",
+      description: () => "description",
+    }
+  );
+};
+
+// 获取表项中收集到的值的对象
+const formValue = reactive({
+  T_pdf1: null,
+});
+
+// 模板标签数据列表
+const dataList = ref([]);
+
+//
+let fileList = [];
+
+// 设备列表
+const classList = ref([]);
+
+// 模态框数据源
+const modal = reactive({
+  showModal: false,
+});
+
+// 组件状态变化的回调
+const handleChange = async (options) => {
+  const token = await getFileToken(options.file.type.split("/")[1]);
+  const observable = qiniu.upload(
+    options.file.file,
+    options.file.name,
+    token,
+    {},
+    {
+      useCdnDomain: true,
+    }
+  );
+  observable.subscribe({
+    next: (result) => {
+      // 主要用来展示进度
+      console.warn(result);
+    },
+    error: () => {
+      message.error("上传图片失败");
+    },
+    complete: (res) => {
+      formValue.T_pdf1 = res.key;
+    },
+  });
+};
+
+// 显示上传验证实施方案
+const showUploadModal = () => {
+  modal.title = "上传验证实施方案";
+  modal.showModal = true;
+};
+
+// 任务(编辑)
+const editTaskInfo = async () => {
+  try {
+    const { data: res } = await editTask({
+      T_task_id: queryData.T_task_id,
+      T_pdf1: formValue.T_pdf1,
+    });
+    if (res.Code === 200) {
+      message.success(res.Msg);
+    }
+  } catch (e) {
+    console.log(e);
+  }
+};
+
+// 显示生成方案
+const handleCreate = async () => {
+  fileList = toRaw(dataList.value);
+  for (const item of fileList) {
+    if (item.T_label === 10 || item.T_label === 11) {
+      const token = await getFileToken(item.T_value[0].type.split("/")[1]);
+      const observable = qiniu.upload(
+        item.T_value[0].file,
+        item.T_value[0].name,
+        token,
+        {},
+        {
+          useCdnDomain: true,
+        }
+      );
+      observable.subscribe({
+        next: (result) => {
+          // 主要用来展示进度
+          console.warn(result);
+        },
+        error: () => {
+          message.error("上传图片失败");
+        },
+        complete: (res) => {
+          item.T_value = res.key;
+        },
+      });
+    }
+  }
+  dialog.info({
+    title: "提示",
+    content: "确认生成报告?",
+    positiveText: "确定",
+    negativeText: "取消",
+    onPositiveClick: () => {
+      putTemplateData();
+    },
+  });
+};
+
+// 模板标签数据添加或更新
+const putTemplateData = async () => {
+  try {
+    const VerifyTemplateMapData = fileList.map((item) => {
+      if (item.T_label === 3 || item.T_label === 9) {
+        return {
+          T_VerifyTemplateMap_id: item.T_VerifyTemplateMap_id,
+          T_value: item.T_value.join("|"),
+        };
+      } else {
+        return {
+          T_VerifyTemplateMap_id: item.T_VerifyTemplateMap_id,
+          T_value: item.T_value + "",
+        };
+      }
+    });
+    const token = getToken();
+    const { data: res } = await putVerifyTemplateMapData({
+      User_tokey: token,
+      T_source: queryData.T_source,
+      T_task_id: queryData.T_task_id,
+      T_VerifyTemplate_id: queryData.T_VerifyTemplate_id,
+
+      VerifyTemplateMapData,
+    });
+    message.success(res.Msg);
+  } catch (e) {
+    console.log(e);
+  }
+};
+
+// 模板标签数据(列表)
+const getDataList = async () => {
+  try {
+    const { data: res } = await getVerifyTemplateMapDataList(queryData);
+    dataList.value = res.Data || [];
+    dataList.value.forEach((item) => {
+      if (item.T_label === 10 || item.T_label === 11 || item.T_label === 3) {
+        item.T_value = [];
+      } else if (item.T_label === 7 || item.T_label === 9) {
+        item.T_value = null;
+      }
+    });
+  } catch (e) {
+    console.log(e);
+  }
+};
+
+// 获取设备列表
+const getTaskClassList = async () => {
+  try {
+    const { data: res } = await getTaskDataClassList({
+      T_task_id: queryData.T_task_id,
+    });
+    classList.value = res.Data || [];
+  } catch (e) {
+    console.log(e);
+  }
+};
+
+getTaskClassList();
+getDataList();
+</script>
+
+<style scoped></style>

+ 3 - 3
vite.config.js

@@ -8,9 +8,9 @@ import WindiCSS from "vite-plugin-windicss";
 
 // https://vitejs.dev/config/
 export default defineConfig({
-  server: {
-    host: true,
-  },
+  // server: {
+  //   host: true,
+  // },
   base: "./",
   plugins: [
     vue(),