Bladeren bron

feat(all): update

HuCheng 2 jaren geleden
bovenliggende
commit
c2037e560c

+ 2 - 1
package.json

@@ -7,7 +7,7 @@
     "build": "vite build",
     "preview": "vite preview",
     "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
-    "commit": "git add . && git cz"
+    "commit": "git add . && git cz && git push"
   },
   "config": {
     "commitizen": {
@@ -19,6 +19,7 @@
     "highcharts": "^10.3.3",
     "highcharts-vue": "^1.4.0",
     "md5": "^2.3.0",
+    "qiniu-js": "^3.4.1",
     "vue": "^3.2.45",
     "vue-pdf-embed": "^1.1.5",
     "vue-router": "^4.1.6"

+ 38 - 0
pnpm-lock.yaml

@@ -14,6 +14,7 @@ specifiers:
   md5: ^2.3.0
   naive-ui: ^2.34.3
   prettier: ^2.7.1
+  qiniu-js: ^3.4.1
   sass: ^1.58.0
   unplugin-auto-import: ^0.14.2
   unplugin-vue-components: ^0.23.0
@@ -29,6 +30,7 @@ dependencies:
   highcharts: 10.3.3
   highcharts-vue: 1.4.0_x2ttk5sq3pjxzxl7cjqodvt53e
   md5: 2.3.0
+  qiniu-js: 3.4.1
   vue: 3.2.47
   vue-pdf-embed: 1.1.5_vue@3.2.47
   vue-router: 4.1.6_vue@3.2.47
@@ -219,6 +221,14 @@ packages:
     dependencies:
       '@babel/types': 7.20.7
 
+  /@babel/runtime-corejs2/7.21.0:
+    resolution: {integrity: sha512-hVFDLYkuthnvQwWoOniPSq+RWyQTiimVdMXQJujoiSX8maFh/62+qRImGkRpeRflsVXXSMFS4HgNe3X9fuw5ww==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      core-js: 2.6.12
+      regenerator-runtime: 0.13.11
+    dev: false
+
   /@babel/standalone/7.20.15:
     resolution: {integrity: sha512-B3LmZ1NHlTb2eFEaw8rftZc730Wh9MlmsH8ubb6IjsNoIk9+SQ2aAA0nrm/1806+PftPRAACPClmKTu8PG7Tew==}
     engines: {node: '>=6.9.0'}
@@ -1024,6 +1034,12 @@ packages:
     resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
     dev: true
 
+  /core-js/2.6.12:
+    resolution: {integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==}
+    deprecated: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.
+    requiresBuild: true
+    dev: false
+
   /core-util-is/1.0.3:
     resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
     dev: true
@@ -2115,6 +2131,20 @@ packages:
     engines: {node: '>=6'}
     dev: true
 
+  /qiniu-js/3.4.1:
+    resolution: {integrity: sha512-8vxrLqDPlJUk3fUAaTozh3TAT3ww9B5KqGogmGuTiFHnewXDoMxTCSY5z8Ab5UNdrCo6ZxDM07G/o++CICRUFw==}
+    dependencies:
+      '@babel/runtime-corejs2': 7.21.0
+      querystring: 0.2.1
+      spark-md5: 3.0.2
+    dev: false
+
+  /querystring/0.2.1:
+    resolution: {integrity: sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==}
+    engines: {node: '>=0.4.x'}
+    deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
+    dev: false
+
   /queue-microtask/1.2.3:
     resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
     dev: true
@@ -2146,6 +2176,10 @@ packages:
       picomatch: 2.3.1
     dev: true
 
+  /regenerator-runtime/0.13.11:
+    resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
+    dev: false
+
   /regexpp/3.2.0:
     resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==}
     engines: {node: '>=8'}
@@ -2255,6 +2289,10 @@ packages:
     resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
     deprecated: Please use @jridgewell/sourcemap-codec instead
 
+  /spark-md5/3.0.2:
+    resolution: {integrity: sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==}
+    dev: false
+
   /std-env/3.3.2:
     resolution: {integrity: sha512-uUZI65yrV2Qva5gqE0+A7uVAvO40iPo6jGhs7s8keRfHCmtg+uB2X6EiLGCI9IgL1J17xGhvoOqSz79lzICPTA==}
     dev: true

+ 9 - 60
src/api/index.js

@@ -1,4 +1,7 @@
 import service from "@/utils/axios";
+export * from "./modules/equipment";
+export * from "./modules/data";
+export * from "./modules/report";
 
 // 登录
 export const login = (data, role) => {
@@ -10,74 +13,20 @@ export const login = (data, role) => {
   });
 };
 
-// 任务管理(列表)
-export const getTaskData = (data) => {
-  return service.request({
-    method: "POST",
-    url: "/Task/List",
-    data,
-  });
-};
-
-// 设备分类-设备列表(列表)
-export const getDeviceClassListData = (data) => {
-  return service.request({
-    method: "POST",
-    url: "/DeviceClassList/List",
-    data,
-  });
-};
-
-// 设备分类-设备列表(添加)
-export const addDeviceClassListData = (data) => {
-  return service.request({
-    method: "POST",
-    url: "/DeviceClassList/Add",
-    data,
-  });
-};
-
-// 设备分类-设备列表(编辑)
-export const editDeviceClassListData = (data) => {
+// 获取上传token
+export const getUpFileTokenData = (data) => {
   return service.request({
     method: "POST",
-    url: "/DeviceClassList/Up",
+    url: "/UpFileToken",
     data,
   });
 };
 
-// 设备分类-设备列表(删除)
-export const deleteDeviceClassListData = (data) => {
-  return service.request({
-    method: "POST",
-    url: "/DeviceClassList/Del",
-    data,
-  });
-};
-
-// 设备管理(列表)
-export const getDeviceData = (data) => {
-  return service.request({
-    method: "POST",
-    url: "/Device/List",
-    data,
-  });
-};
-
-// 设备分类(获取)
-export const getDeviceClass = (data) => {
-  return service.request({
-    method: "POST",
-    url: "/DeviceClass/Get",
-    data,
-  });
-};
-
-// 设备分类(编辑)
-export const editDeviceClass = (data) => {
+// 任务管理(列表)
+export const getTaskData = (data) => {
   return service.request({
     method: "POST",
-    url: "/DeviceClass/Up",
+    url: "/Task/List",
     data,
   });
 };

+ 0 - 0
src/api/modules/data.js


+ 64 - 0
src/api/modules/equipment.js

@@ -0,0 +1,64 @@
+import service from "@/utils/axios";
+
+// 设备分类-设备列表(列表)
+export const getDeviceClassListData = (data) => {
+  return service.request({
+    method: "POST",
+    url: "/DeviceClassList/List",
+    data,
+  });
+};
+
+// 设备分类-设备列表(添加)
+export const addDeviceClassListData = (data) => {
+  return service.request({
+    method: "POST",
+    url: "/DeviceClassList/Add",
+    data,
+  });
+};
+
+// 设备分类-设备列表(编辑)
+export const editDeviceClassListData = (data) => {
+  return service.request({
+    method: "POST",
+    url: "/DeviceClassList/Up",
+    data,
+  });
+};
+
+// 设备分类-设备列表(删除)
+export const deleteDeviceClassListData = (data) => {
+  return service.request({
+    method: "POST",
+    url: "/DeviceClassList/Del",
+    data,
+  });
+};
+
+// 设备管理(列表)
+export const getDeviceData = (data) => {
+  return service.request({
+    method: "POST",
+    url: "/Device/List",
+    data,
+  });
+};
+
+// 设备分类(获取)
+export const getDeviceClass = (data) => {
+  return service.request({
+    method: "POST",
+    url: "/DeviceClass/Get",
+    data,
+  });
+};
+
+// 设备分类(编辑)
+export const editDeviceClass = (data) => {
+  return service.request({
+    method: "POST",
+    url: "/DeviceClass/Up",
+    data,
+  });
+};

+ 10 - 0
src/api/modules/report.js

@@ -0,0 +1,10 @@
+import service from "@/utils/axios";
+
+// 任务(获取)
+export const getTask = (data) => {
+  return service.request({
+    method: "POST",
+    url: "/Task/Get",
+    data,
+  });
+};

+ 1 - 1
src/layout/index.vue

@@ -15,7 +15,7 @@
       <MenuComponent />
     </n-layout-sider>
     <n-layout>
-      <n-layout-header class="h-16 px-6 shadow">
+      <n-layout-header class="h-16 px-6" bordered>
         <n-space justify="end" align="center" class="h-full">
           <n-dropdown trigger="hover" :options="options" @select="handleSelect">
             <n-avatar round class="cursor-pointer"> 用户名 </n-avatar>

+ 53 - 44
src/login/index.vue

@@ -1,50 +1,58 @@
 <template>
   <div
-    class="absolute top-0 right-0 bottom-0 left-0 bg-[#f5f7f9] flex justify-center items-center overflow-auto"
+    class="absolute top-0 right-0 bottom-0 left-0 bg-[#2d3a4b] flex justify-center items-center overflow-auto"
   >
-    <n-card class="w-[500px] shadow rounded-lg" :bordered="false">
-      <template #header>
-        <h2 class="text-center">冷链验证报告生成系统</h2>
-      </template>
-      <n-form
-        ref="formRef"
-        :rules="rules"
-        :model="formValue"
-        size="large"
-        label-placement="left"
-      >
-        <n-form-item path="username">
-          <n-input v-model:value="formValue.username" :maxlength="8" clearable>
-            <template #prefix>
-              <n-icon :component="UserOutlined" />
-            </template>
-          </n-input>
-        </n-form-item>
-        <n-form-item path="password">
-          <n-input
-            v-model:value="formValue.password"
-            type="password"
-            show-password-on="click"
-            :maxlength="8"
-            clearable
-          >
-            <template #prefix>
-              <n-icon :component="LockOutlined" />
-            </template>
-          </n-input>
-        </n-form-item>
-        <n-form-item path="role">
-          <n-select
-            v-model:value="formValue.role"
-            :options="generalOptions"
-            clearable
-          />
-        </n-form-item>
-        <n-form-item>
-          <n-button type="primary" block @click="handleSubmit">登录</n-button>
-        </n-form-item>
-      </n-form>
-    </n-card>
+    <n-config-provider :theme="darkTheme">
+      <n-card class="w-[500px] bg-transparent" :bordered="false">
+        <template #header>
+          <h2 class="text-center">冷链验证报告生成系统</h2>
+        </template>
+        <n-form
+          ref="formRef"
+          :rules="rules"
+          :model="formValue"
+          size="large"
+          label-placement="left"
+        >
+          <n-form-item path="username">
+            <n-input
+              v-model:value="formValue.username"
+              :maxlength="8"
+              clearable
+            >
+              <template #prefix>
+                <n-icon :component="UserOutlined" />
+              </template>
+            </n-input>
+          </n-form-item>
+          <n-form-item path="password">
+            <n-input
+              v-model:value="formValue.password"
+              type="password"
+              show-password-on="click"
+              :maxlength="8"
+              clearable
+            >
+              <template #prefix>
+                <n-icon :component="LockOutlined" />
+              </template>
+            </n-input>
+          </n-form-item>
+          <n-form-item path="role">
+            <n-select
+              v-model:value="formValue.role"
+              :options="generalOptions"
+              clearable
+            />
+          </n-form-item>
+          <n-form-item>
+            <n-button type="primary" block @click="handleSubmit"
+              ><span class="text-white">立即登录</span></n-button
+            >
+          </n-form-item>
+        </n-form>
+      </n-card>
+    </n-config-provider>
   </div>
 </template>
 
@@ -53,6 +61,7 @@ import { UserOutlined, LockOutlined } from "@vicons/antd";
 import { login } from "@/api";
 import md5 from "md5";
 import { setToken } from "@/utils/storage/sessionToken";
+import { darkTheme } from "naive-ui";
 
 const loadingBar = useLoadingBar();
 

+ 4 - 2
src/router/index.js

@@ -37,11 +37,13 @@ const router = createRouter({
           component: () => import("@/views/project/report/create/index.vue"),
         },
         {
-          path: "/report_edit",
+          path: "/report_edit/:taskId",
+          props: true,
           component: () => import("@/views/project/report/edit/index.vue"),
         },
         {
-          path: "/report_audit",
+          path: "/report_audit/:taskId",
+          props: true,
           component: () => import("@/views/project/report/audit/index.vue"),
         },
       ],

+ 1 - 0
src/utils/axios.js

@@ -48,6 +48,7 @@ service.interceptors.response.use(
   function (error) {
     // 超出 2xx 范围的状态码都会触发该函数。
     // 对响应错误做点什么
+    console.log(error);
     return Promise.reject(error);
   }
 );

+ 29 - 29
src/views/project/equipment/index.vue

@@ -8,38 +8,38 @@
           <n-breadcrumb-item>设备管理</n-breadcrumb-item>
         </n-breadcrumb>
       </template>
+      <template #extra>
+        <n-button type="primary" @click="showAddModal">批量导入</n-button>
+      </template>
       <n-space justify="space-between">
-        <n-space>
+        <n-input-group>
+          <n-input
+            style="width: 300px"
+            v-model:value="queryData.T_sn"
+            @clear="handleClear"
+            clearable
+            placeholder="请输入SN"
+          />
+          <n-button type="primary" @click="getDeviceClassListDataList">
+            搜索
+          </n-button>
+        </n-input-group>
+        <n-space align="center">
+          <span>分类名称 </span>
           <n-input-group>
             <n-input
               style="width: 300px"
-              v-model:value="queryData.T_sn"
-              @clear="handleClear"
-              clearable
-              placeholder="请输入SN"
+              v-model:value="formValue.T_name"
+              placeholder="请输入分类名称"
             />
-            <n-button type="primary" @click="getDeviceClassListDataList">
-              搜索
-            </n-button>
+            <n-popconfirm @positive-click="editDeviceClassInfo">
+              <template #trigger>
+                <n-button type="primary">修改分类</n-button>
+              </template>
+              是否确认编辑?
+            </n-popconfirm>
           </n-input-group>
-          <n-space align="center">
-            <span>分类名称 </span>
-            <n-input-group>
-              <n-input
-                style="width: 300px"
-                v-model:value="formValue.T_name"
-                placeholder="请输入分类名称"
-              />
-              <n-popconfirm @positive-click="editDeviceClassInfo">
-                <template #trigger>
-                  <n-button type="primary">编辑分类</n-button>
-                </template>
-                是否确认编辑?
-              </n-popconfirm>
-            </n-input-group>
-          </n-space>
         </n-space>
-        <n-button type="primary" @click="showAddModal">批量导入</n-button>
       </n-space>
     </n-page-header>
     <n-data-table
@@ -139,8 +139,8 @@ const columns = [
         {},
         {
           default: () =>
-            ["编辑编号", "删除"].map((item) => {
-              if (item === "编辑编号") {
+            ["修改编号", "删除"].map((item) => {
+              if (item === "修改编号") {
                 return h(
                   NButton,
                   {
@@ -223,7 +223,7 @@ const submitCallback = () => {
 
 // 显示编辑
 const showEditModal = (row) => {
-  modal.title = "编辑编号";
+  modal.title = "修改编号";
   modal.showModal = true;
   Object.assign(formValue, row);
 };
@@ -232,7 +232,7 @@ const showEditModal = (row) => {
 const showAddModal = () => {
   modal.title = "批量导入";
   modal.showModal = true;
-  Object.keys(formValue).forEach((key) => (formValue[key] = null));
+  formValue.T_sn = null;
 };
 
 // 删除

+ 5 - 5
src/views/project/index.vue

@@ -83,15 +83,15 @@ const columns = [
                     } else if (item === "数据来源") {
                       router.push("/data_source");
                     } else if (item === "数据编辑") {
-                      router.push("/data_source");
+                      router.push("/data_edit");
                     } else if (item === "数据校验") {
-                      router.push("/data_source");
+                      router.push("/data_checkout");
                     } else if (item === "报告生成") {
-                      router.push("/data_source");
+                      router.push("/report_create");
                     } else if (item === "报告编辑") {
-                      router.push("/data_source");
+                      router.push(`/report_edit/${row.T_task_id}`);
                     } else {
-                      router.push("/data_source");
+                      router.push(`/report_audit/${row.T_task_id}`);
                     }
                   },
                 },

+ 145 - 83
src/views/project/report/audit/index.vue

@@ -1,18 +1,52 @@
 <template>
   <n-space vertical>
-    <n-space justify="space-between">
-      <n-input-group>
-        <n-input style="width: 300px" placeholder="请输入项目名称" />
-        <n-button type="primary"> 搜索 </n-button>
-      </n-input-group>
-      <n-button type="primary" @click="handleAdd">新增</n-button>
-    </n-space>
-    <n-data-table
-      :columns="columns"
-      :data="data"
-      :pagination="pagination"
-      :bordered="false"
-    />
+    <n-page-header @back="() => $router.back()">
+      <template #title> 返回上一级 </template>
+      <template #header>
+        <n-breadcrumb>
+          <n-breadcrumb-item>任务管理</n-breadcrumb-item>
+          <n-breadcrumb-item>报告审核</n-breadcrumb-item>
+        </n-breadcrumb>
+      </template>
+      <template #extra>
+        <n-button type="primary">审核</n-button>
+      </template>
+      <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>
+    </n-page-header>
+    <n-tabs type="segment">
+      <n-tab-pane name="chap1" 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: 400px" trigger="none">
+          <vue-pdf-embed
+            ref="pdfRef"
+            source="https://pure-admin.github.io/pure-admin-doc/pdf/Cookie%E5%92%8CSession%E5%8C%BA%E5%88%AB%E7%94%A8%E6%B3%95.pdf"
+          />
+        </n-scrollbar>
+      </n-tab-pane>
+      <n-tab-pane name="chap2" tab="验证设备证书">
+        “威尔!着火了!快来帮忙!”我听到女朋友大喊。现在一个难题在我面前——是恢复一个重要的
+        Amazon 服务,还是救公寓的火。<br /><br />
+        我的脑海中忽然出现了 Amazon
+        著名的领导力准则”客户至上“,有很多的客户还依赖我们的服务,我不能让他们失望!所以着火也不管了,女朋友喊我也无所谓,我开始
+        debug 这个线上问题。
+      </n-tab-pane>
+    </n-tabs>
   </n-space>
   <n-modal
     :show-icon="false"
@@ -24,99 +58,127 @@
     @positive-click="submitCallback"
   >
     <n-form
-      :model="form"
+      ref="formRef"
+      :model="formValue"
       label-placement="left"
       label-width="auto"
-      require-mark-placement="right-hanging"
+      :rules="rules"
     >
-      <n-form-item label="项目名称" path="inputValue">
-        <n-input v-model:value="form.inputValue" />
+      <n-form-item label="名称" path="T_name">
+        <n-input v-model:value="formValue.T_name" />
       </n-form-item>
-      <n-form-item label="描述" path="textareaValue">
-        <n-input
-          v-model:value="form.textareaValue"
-          type="textarea"
-          :autosize="{
-            minRows: 3,
-            maxRows: 5,
-          }"
-        />
+      <n-form-item label="预览" path="T_img" required>
+        <n-space>
+          <template v-if="modal.title === '编辑'">
+            <n-image width="100" :src="formValue.T_img" />
+          </template>
+          <n-upload
+            :default-upload="false"
+            list-type="image-card"
+            :max="1"
+            @change="handleChange"
+          >
+            <template v-if="modal.title === '添加'"> 点击上传 </template>
+            <template v-else> 重新上传 </template>
+          </n-upload>
+        </n-space>
       </n-form-item>
     </n-form>
   </n-modal>
 </template>
 
 <script setup>
-import { h } from "vue";
-import { NButton, NSpace } from "naive-ui";
+import VuePdfEmbed from "vue-pdf-embed";
+import { getTask, getUpFileTokenData } from "@/api";
+import * as qiniu from "qiniu-js";
+import { PrinterOutlined as PrinterIcon } from "@vicons/antd";
 
-const createColumns = ({ goToView }) => {
-  return [
-    {
-      title: "项目名称",
-      key: "no",
-    },
-    {
-      title: "描述",
-      key: "title",
-    },
-    {
-      title: "时间",
-      key: "length",
-    },
-    {
-      title: "操作",
-      key: "actions",
-      render() {
-        return h(
-          NSpace,
-          {},
-          {
-            default: () =>
-              ["删除"].map((item) =>
-                h(
-                  NButton,
-                  {
-                    type: "error",
-                    size: "small",
-                    onClick: () => {},
-                  },
-                  { default: () => item }
-                )
-              ),
-          }
-        );
-      },
-    },
-  ];
-};
+const message = useMessage();
+
+const props = defineProps({
+  taskId: String,
+});
 
-const data = [
-  { no: 3, title: "Wonderwall", length: "4:18" },
-  { no: 4, title: "Don't Look Back in Anger", length: "4:48" },
-  { no: 12, title: "Champagne Supernova", length: "7:27" },
-];
+const pdfRef = ref(null);
 
-const columns = createColumns({});
-const pagination = ref(false);
+// 任务信息
+const taskInfo = ref({});
 
+// 表单信息
+const formValue = reactive({});
+
+// 模态框数据源
 const modal = reactive({
   title: "",
   showModal: false,
 });
 
+// 验证表项的规则
+const rules = {
+  T_name: { required: true, message: "不能为空", trigger: "blur" },
+};
+
+// 打印pdf
+const onPrint = () => {
+  pdfRef.value.print();
+};
+
+// 组件状态变化的回调
+const handleChange = async (options) => {
+  const token = await getUpFileToken(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_img = res.key;
+    },
+  });
+};
+
+// 执行 positive 时执行的回调函数
 const submitCallback = () => {
-  console.log("确定");
+  if (modal.title === "添加") {
+    console.log("add");
+  } else {
+    console.log("edit");
+  }
+};
+
+// 显示上传
+const showUploadModal = () => {};
+
+// 获取上传token
+const getUpFileToken = async () => {
+  const { data: res } = await getUpFileTokenData({
+    T_suffix: "pdf",
+  });
+  return res.Data;
 };
-const handleAdd = () => {
-  modal.showModal = true;
-  modal.title = "新增";
+
+// 任务(获取)
+const getTaskInfo = async () => {
+  const { data: res } = await getTask({
+    T_task_id: props.taskId,
+  });
+  taskInfo.value = res.Data;
 };
 
-const form = reactive({
-  inputValue: "",
-  textareaValue: "",
-});
+getUpFileToken();
+getTaskInfo();
 </script>
 
 <style scoped></style>

+ 189 - 4
src/views/project/report/edit/index.vue

@@ -1,12 +1,197 @@
 <template>
-  <n-scrollbar style="max-height: 750px" trigger="none">
-    <vue-pdf-embed :source="pdfSource" />
-  </n-scrollbar>
+  <n-space vertical>
+    <n-page-header @back="() => $router.back()">
+      <template #title> 返回上一级 </template>
+      <template #header>
+        <n-breadcrumb>
+          <n-breadcrumb-item>任务管理</n-breadcrumb-item>
+          <n-breadcrumb-item>报告编辑</n-breadcrumb-item>
+        </n-breadcrumb>
+      </template>
+      <template #extra>
+        <n-space>
+          <n-button
+            type="primary"
+            @click="showUploadModal"
+            :color="taskInfo.T_pdf2 ? '#888' : ''"
+          >上传验证报告内容</n-button
+          >
+          <n-button
+            type="primary"
+            @click="showUploadModal"
+            :color="taskInfo.T_pdf3 ? '#888' : ''"
+          >上传验证设备证书</n-button
+          >
+        </n-space>
+      </template>
+      <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>
+    </n-page-header>
+    <n-tabs type="segment">
+      <n-tab-pane name="chap1" 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: 400px" trigger="none">
+          <vue-pdf-embed
+            ref="pdfRef"
+            source="https://pure-admin.github.io/pure-admin-doc/pdf/Cookie%E5%92%8CSession%E5%8C%BA%E5%88%AB%E7%94%A8%E6%B3%95.pdf"
+          />
+        </n-scrollbar>
+      </n-tab-pane>
+      <n-tab-pane name="chap2" tab="验证设备证书">
+        “威尔!着火了!快来帮忙!”我听到女朋友大喊。现在一个难题在我面前——是恢复一个重要的
+        Amazon 服务,还是救公寓的火。<br /><br />
+        我的脑海中忽然出现了 Amazon
+        著名的领导力准则”客户至上“,有很多的客户还依赖我们的服务,我不能让他们失望!所以着火也不管了,女朋友喊我也无所谓,我开始
+        debug 这个线上问题。
+      </n-tab-pane>
+    </n-tabs>
+  </n-space>
+  <n-modal
+    :show-icon="false"
+    v-model:show="modal.showModal"
+    preset="dialog"
+    :title="modal.title"
+    positive-text="提交"
+    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_name">
+        <n-input v-model:value="formValue.T_name" />
+      </n-form-item>
+      <n-form-item label="预览" path="T_img" required>
+        <n-space>
+          <template v-if="modal.title === '编辑'">
+            <n-image width="100" :src="formValue.T_img" />
+          </template>
+          <n-upload
+            :default-upload="false"
+            list-type="image-card"
+            :max="1"
+            @change="handleChange"
+          >
+            <template v-if="modal.title === '添加'"> 点击上传 </template>
+            <template v-else> 重新上传 </template>
+          </n-upload>
+        </n-space>
+      </n-form-item>
+    </n-form>
+  </n-modal>
 </template>
 
 <script setup>
 import VuePdfEmbed from "vue-pdf-embed";
-import pdfSource from "@/assets/1.pdf";
+import { getTask, getUpFileTokenData } from "@/api";
+import * as qiniu from "qiniu-js";
+import { PrinterOutlined as PrinterIcon } from "@vicons/antd";
+
+const message = useMessage();
+
+const props = defineProps({
+  taskId: String,
+});
+
+const pdfRef = ref(null);
+
+// 任务信息
+const taskInfo = ref({});
+
+// 表单信息
+const formValue = reactive({});
+
+// 模态框数据源
+const modal = reactive({
+  title: "",
+  showModal: false,
+});
+
+// 验证表项的规则
+const rules = {
+  T_name: { required: true, message: "不能为空", trigger: "blur" },
+};
+
+// 打印pdf
+const onPrint = () => {
+  pdfRef.value.print();
+};
+
+// 组件状态变化的回调
+const handleChange = async (options) => {
+  const token = await getUpFileToken(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_img = res.key;
+    },
+  });
+};
+
+// 执行 positive 时执行的回调函数
+const submitCallback = () => {
+  if (modal.title === "添加") {
+    console.log("add");
+  } else {
+    console.log("edit");
+  }
+};
+
+// 显示上传
+const showUploadModal = () => {};
+
+// 获取上传token
+const getUpFileToken = async () => {
+  const { data: res } = await getUpFileTokenData({
+    T_suffix: "pdf",
+  });
+  return res.Data;
+};
+
+// 任务(获取)
+const getTaskInfo = async () => {
+  const { data: res } = await getTask({
+    T_task_id: props.taskId,
+  });
+  taskInfo.value = res.Data;
+};
+
+getUpFileToken();
+getTaskInfo();
 </script>
 
 <style scoped></style>