From 8fa692c8c6693d9f8833c58ce84f6b61f6b6610e Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期六, 10 十月 2026 23:09:56 +0800
Subject: [PATCH] feat: 导入 添加导入按钮

---
 src/views/mes/qc/iqc/index.vue                    |   41 +++
 src/views/mes/qc/ipqc/data.ts                     |    6 
 src/views/mes/qc/iqc/data.ts                      |    6 
 src/views/crm/invoice/index.vue                   |   19 +
 src/api/mes/qc/ipqc/index.ts                      |   19 +
 src/views/mes/qc/oqc/data.ts                      |    6 
 src/api/mes/qc/rqc/index.ts                       |    1 
 src/views/mes/qc/ipqc/index.vue                   |   42 +++
 src/views/mes/qc/oqc/index.vue                    |   41 +++
 src/api/crm/invoice/index.ts                      |   20 +
 src/api/mes/qc/iqc/index.ts                       |   19 +
 src/api/mes/qc/oqc/index.ts                       |   19 +
 src/views/crm/invoice/modules/import-form.vue     |  157 ++++++++++++++
 src/views/mes/qc/components/import-form-modal.vue |  204 ++++++++++++++++++
 src/views/mes/qc/rqc/data.ts                      |    6 
 15 files changed, 603 insertions(+), 3 deletions(-)

diff --git a/src/api/crm/invoice/index.ts b/src/api/crm/invoice/index.ts
index 121f6ec..c567875 100644
--- a/src/api/crm/invoice/index.ts
+++ b/src/api/crm/invoice/index.ts
@@ -120,3 +120,23 @@
     `/crm/invoice/simple-list?contractId=${contractId}`,
   );
 }
+
+/** 寮�绁ㄥ鍏ョ粨鏋� */
+export interface InvoiceImportResult {
+  /** 鍒涘缓鎴愬姛鐨勫紑绁ㄧ紪鍙� */
+  createList?: string[];
+  /** 瀵煎叆澶辫触鐨勯泦鍚堬細key 涓恒�屽彂绁ㄥ彿鐮� xxx銆嶆垨銆岀 N 琛屻�� */
+  failureList?: Record<string, string>;
+}
+
+/** 涓嬭浇寮�绁ㄥ鍏ユā鏉� */
+export function importInvoiceTemplate() {
+  return requestClient.download('/crm/invoice/get-import-template');
+}
+
+/** 瀵煎叆寮�绁� */
+export function importInvoice(file: File) {
+  return requestClient.upload<InvoiceImportResult>('/crm/invoice/import', {
+    file,
+  });
+}
diff --git a/src/api/mes/qc/ipqc/index.ts b/src/api/mes/qc/ipqc/index.ts
index cd9390a..aa539ec 100644
--- a/src/api/mes/qc/ipqc/index.ts
+++ b/src/api/mes/qc/ipqc/index.ts
@@ -33,11 +33,18 @@
     majorQuantity?: number; // 涓ラ噸缂洪櫡鏁伴噺
     minorQuantity?: number; // 杞诲井缂洪櫡鏁伴噺
     checkResult?: number; // 妫�娴嬬粨鏋�
+    indicatorResultSummary?: string; // 妫�娴嬬粨鏋滐紙妫�楠岄」瀹炴祴鍊兼眹鎬伙紝濡傘�屾俯搴�:15锛岀數闃�:1銆嶏級
     inspectDate?: number; // 妫�娴嬫棩鏈�
     inspectorUserId?: number; // 妫�娴嬩汉鍛樼敤鎴� ID
     inspectorNickname?: string; // 妫�娴嬩汉鍛樻樀绉帮紙鍏宠仈鏌ヨ锛�
     status?: number; // 鐘舵��
     remark?: string; // 澶囨敞
+  }
+
+  /** 杩囩▼妫�楠屽崟瀵煎叆缁撴灉 */
+  export interface ImportResult {
+    createList?: string[]; // 鏂板鎴愬姛鐨勬楠屽崟缂栧彿
+    failureList?: Record<string, string>; // 瀵煎叆澶辫触鏄庣粏锛歬ey 涓恒�屾楠屽崟缂栧彿 X銆嶆垨銆岀 N 琛屻��
   }
 }
 
@@ -77,3 +84,15 @@
 export function exportIpqc(params: any) {
   return requestClient.download('/mes/qc/ipqc/export-excel', { params });
 }
+
+/** 涓嬭浇杩囩▼妫�楠屽崟瀵煎叆妯℃澘 */
+export function importIpqcTemplate() {
+  return requestClient.download('/mes/qc/ipqc/get-import-template');
+}
+
+/** 瀵煎叆杩囩▼妫�楠屽崟 */
+export function importIpqc(file: File) {
+  return requestClient.upload<MesQcIpqcApi.ImportResult>('/mes/qc/ipqc/import', {
+    file,
+  });
+}
diff --git a/src/api/mes/qc/iqc/index.ts b/src/api/mes/qc/iqc/index.ts
index cdb04cb..865bb0f 100644
--- a/src/api/mes/qc/iqc/index.ts
+++ b/src/api/mes/qc/iqc/index.ts
@@ -32,6 +32,7 @@
     majorQuantity?: number; // 涓ラ噸缂洪櫡鏁伴噺
     minorQuantity?: number; // 杞诲井缂洪櫡鏁伴噺
     checkResult?: number; // 妫�娴嬬粨鏋�
+    indicatorResultSummary?: string; // 妫�娴嬬粨鏋滐紙妫�楠岄」瀹炴祴鍊兼眹鎬伙紝濡傘�屾俯搴�:15锛岀數闃�:1銆嶏級
     receiveDate?: number; // 鏉ユ枡鏃ユ湡
     inspectDate?: number; // 妫�娴嬫棩鏈�
     inspector?: string; // 妫�娴嬩汉鍛橈紙鏄电О锛�
@@ -39,6 +40,12 @@
     inspectorNickname?: string; // 妫�娴嬩汉鍛樻樀绉帮紙鍏宠仈鏌ヨ锛�
     status?: number; // 鐘舵��
     remark?: string; // 澶囨敞
+  }
+
+  /** 鏉ユ枡妫�楠屽崟瀵煎叆缁撴灉 */
+  export interface ImportResult {
+    createList?: string[]; // 鏂板鎴愬姛鐨勬楠屽崟缂栧彿
+    failureList?: Record<string, string>; // 瀵煎叆澶辫触鏄庣粏锛歬ey 涓恒�屾楠屽崟缂栧彿 X銆嶆垨銆岀 N 琛屻��
   }
 }
 
@@ -78,3 +85,15 @@
 export function exportIqc(params: any) {
   return requestClient.download('/mes/qc/iqc/export-excel', { params });
 }
+
+/** 涓嬭浇鏉ユ枡妫�楠屽崟瀵煎叆妯℃澘 */
+export function importIqcTemplate() {
+  return requestClient.download('/mes/qc/iqc/get-import-template');
+}
+
+/** 瀵煎叆鏉ユ枡妫�楠屽崟 */
+export function importIqc(file: File) {
+  return requestClient.upload<MesQcIqcApi.ImportResult>('/mes/qc/iqc/import', {
+    file,
+  });
+}
diff --git a/src/api/mes/qc/oqc/index.ts b/src/api/mes/qc/oqc/index.ts
index 793f6db..98c341a 100644
--- a/src/api/mes/qc/oqc/index.ts
+++ b/src/api/mes/qc/oqc/index.ts
@@ -34,12 +34,19 @@
     majorQuantity?: number; // 涓ラ噸缂洪櫡鏁伴噺
     minorQuantity?: number; // 杞诲井缂洪櫡鏁伴噺
     checkResult?: number; // 妫�娴嬬粨鏋�
+    indicatorResultSummary?: string; // 妫�娴嬬粨鏋滐紙妫�楠岄」瀹炴祴鍊兼眹鎬伙紝濡傘�屾俯搴�:15锛岀數闃�:1銆嶏級
     outDate?: number; // 鍑鸿揣鏃ユ湡
     inspectDate?: number; // 妫�娴嬫棩鏈�
     inspectorUserId?: number; // 妫�娴嬩汉鍛樼敤鎴� ID
     inspectorNickname?: string; // 妫�娴嬩汉鍛樻樀绉帮紙鍏宠仈鏌ヨ锛�
     status?: number; // 鐘舵��
     remark?: string; // 澶囨敞
+  }
+
+  /** 鍑鸿揣妫�楠屽崟瀵煎叆缁撴灉 */
+  export interface ImportResult {
+    createList?: string[]; // 鏂板鎴愬姛鐨勬楠屽崟缂栧彿
+    failureList?: Record<string, string>; // 瀵煎叆澶辫触鏄庣粏锛歬ey 涓恒�屾楠屽崟缂栧彿 X銆嶆垨銆岀 N 琛屻��
   }
 }
 
@@ -79,3 +86,15 @@
 export function exportOqc(params: any) {
   return requestClient.download('/mes/qc/oqc/export-excel', { params });
 }
+
+/** 涓嬭浇鍑鸿揣妫�楠屽崟瀵煎叆妯℃澘 */
+export function importOqcTemplate() {
+  return requestClient.download('/mes/qc/oqc/get-import-template');
+}
+
+/** 瀵煎叆鍑鸿揣妫�楠屽崟 */
+export function importOqc(file: File) {
+  return requestClient.upload<MesQcOqcApi.ImportResult>('/mes/qc/oqc/import', {
+    file,
+  });
+}
diff --git a/src/api/mes/qc/rqc/index.ts b/src/api/mes/qc/rqc/index.ts
index 60b54da..2765f99 100644
--- a/src/api/mes/qc/rqc/index.ts
+++ b/src/api/mes/qc/rqc/index.ts
@@ -24,6 +24,7 @@
     qualifiedQuantity?: number; // 鍚堟牸鍝佹暟閲�
     unqualifiedQuantity?: number; // 涓嶅悎鏍兼暟閲�
     checkResult?: number; // 妫�娴嬬粨鏋�
+    indicatorResultSummary?: string; // 妫�娴嬬粨鏋滐紙妫�楠岄」瀹炴祴鍊兼眹鎬伙紝濡傘�屾俯搴�:15锛岀數闃�:1銆嶏級
     inspectDate?: number; // 妫�娴嬫棩鏈�
     inspectorUserId?: number; // 妫�娴嬩汉鍛樼敤鎴� ID
     inspectorNickname?: string; // 妫�娴嬩汉鍛樻樀绉帮紙鍏宠仈鏌ヨ锛�
diff --git a/src/views/crm/invoice/index.vue b/src/views/crm/invoice/index.vue
index 6adafea..bf65d18 100644
--- a/src/views/crm/invoice/index.vue
+++ b/src/views/crm/invoice/index.vue
@@ -21,6 +21,7 @@
 
 import { useGridColumns, useGridFormSchema } from './data';
 import Form from './modules/form.vue';
+import ImportForm from './modules/import-form.vue';
 
 const { push } = useRouter();
 const sceneType = ref('1');
@@ -29,6 +30,16 @@
   connectedComponent: Form,
   destroyOnClose: true,
 });
+
+const [ImportModal, importModalApi] = useVbenModal({
+  connectedComponent: ImportForm,
+  destroyOnClose: true,
+});
+
+/** 鎵撳紑瀵煎叆寮�绁ㄥ脊绐� */
+function handleImport() {
+  importModalApi.open();
+}
 
 /** 鍒锋柊琛ㄦ牸 */
 function handleRefresh() {
@@ -149,6 +160,7 @@
 <template>
   <Page auto-content-height>
     <FormModal @success="handleRefresh" />
+    <ImportModal @success="handleRefresh" />
     <Grid>
       <template #toolbar-actions>
         <Tabs class="w-full" @change="handleChangeSceneType">
@@ -168,6 +180,13 @@
               onClick: handleCreate,
             },
             {
+              label: '瀵煎叆',
+              type: 'primary',
+              icon: ACTION_ICON.UPLOAD,
+              auth: ['crm:invoice:import'],
+              onClick: handleImport,
+            },
+            {
               label: $t('ui.actionTitle.export'),
               type: 'primary',
               icon: ACTION_ICON.DOWNLOAD,
diff --git a/src/views/crm/invoice/modules/import-form.vue b/src/views/crm/invoice/modules/import-form.vue
new file mode 100644
index 0000000..8529726
--- /dev/null
+++ b/src/views/crm/invoice/modules/import-form.vue
@@ -0,0 +1,157 @@
+<script lang="ts" setup>
+import type { FileType } from 'ant-design-vue/es/upload/interface';
+import type { InvoiceImportResult } from '#/api/crm/invoice';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '#/packages/effects/common-ui/src';
+import { downloadFileFromBlobPart } from '#/packages/utils/src';
+
+import { Button, message, Progress, Upload } from 'ant-design-vue';
+
+import { importInvoice, importInvoiceTemplate } from '#/api/crm/invoice';
+
+const emit = defineEmits(['success']);
+
+const fileList = ref<FileType[]>([]);
+const importResult = ref<InvoiceImportResult>();
+const importing = ref(false);
+const progressPercent = ref(0);
+let progressTimer: ReturnType<typeof setInterval> | undefined;
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    let shouldClose = false;
+    if (fileList.value.length === 0) {
+      message.warning('璇烽�夋嫨瑕佸鍏ョ殑鏂囦欢');
+      return;
+    }
+    if (importing.value) return;
+    modalApi.lock();
+    importing.value = true;
+    startMockProgress();
+    try {
+      importResult.value = await importInvoice(fileList.value[0] as File);
+      // 瀹屾垚锛氳繘搴︽潯璧版弧
+      stopMockProgress();
+      progressPercent.value = 100;
+      await delay(300);
+      emit('success');
+      const failedCount = Object.keys(
+        importResult.value?.failureList ?? {},
+      ).length;
+      if (failedCount > 0) {
+        message.warning(
+          `瀵煎叆瀹屾垚锛氭柊澧� ${createCount.value} 鏉★紝澶辫触 ${failedCount} 鏉★紝璇锋煡鐪嬩笅鏂瑰け璐ユ槑缁哷,
+        );
+      } else {
+        message.success('瀵煎叆瀹屾垚');
+        shouldClose = true;
+      }
+    } finally {
+      stopMockProgress();
+      importing.value = false;
+      modalApi.unlock();
+      if (shouldClose) {
+        await modalApi.close();
+      }
+    }
+  },
+});
+
+/** 妯℃嫙杩涘害鏉★細璇锋眰鏈熼棿浠� 0 璧板埌 90% */
+function startMockProgress() {
+  progressPercent.value = 0;
+  progressTimer = setInterval(() => {
+    if (progressPercent.value < 90) {
+      progressPercent.value = Math.min(
+        90,
+        progressPercent.value + Math.random() * 12,
+      );
+    }
+  }, 300);
+}
+
+function stopMockProgress() {
+  if (progressTimer) {
+    clearInterval(progressTimer);
+    progressTimer = undefined;
+  }
+}
+
+function delay(ms: number) {
+  return new Promise((resolve) => setTimeout(resolve, ms));
+}
+
+function beforeUpload(file: FileType) {
+  fileList.value = [file];
+  importResult.value = undefined;
+  return false;
+}
+
+function handleRemove() {
+  fileList.value = [];
+  importResult.value = undefined;
+}
+
+async function handleDownloadTemplate() {
+  const data = await importInvoiceTemplate();
+  downloadFileFromBlobPart({ fileName: '寮�绁ㄥ鍏ユā鏉�.xls', source: data });
+}
+
+const createCount = computed(() => importResult.value?.createList?.length ?? 0);
+const failureCount = computed(
+  () => Object.keys(importResult.value?.failureList ?? {}).length,
+);
+</script>
+
+<template>
+  <Modal title="瀵煎叆寮�绁�" class="w-[720px]">
+    <div class="mx-4">
+      <Upload
+        :max-count="1"
+        accept=".xls,.xlsx"
+        :file-list="fileList"
+        :before-upload="beforeUpload"
+        @remove="handleRemove"
+      >
+        <Button type="primary">閫夋嫨 Excel 鏂囦欢</Button>
+      </Upload>
+      <div class="mt-2 rounded-md bg-gray-50 p-2 text-xs leading-5 text-gray-500">
+        <p>浠呭厑璁稿鍏� xls銆亁lsx 鏍煎紡鏂囦欢锛涘繀濉垪锛氬悎鍚岀紪鍙枫�佸彂绁ㄧ被鍨嬨�佸紑绁ㄩ噾棰�(鍏�)銆傗�滃悎鍚岀紪鍙封�濋渶涓庣郴缁熶腑宸插鏍搁�氳繃鐨勫悎鍚岀紪鍙蜂竴鑷达紙缂栧彿闇�瀹屽叏涓�鑷达級锛屽鎴风敱鍚堝悓鑷姩甯﹀叆锛屾棤闇�濉啓锛涒�滃彂绁ㄧ被鍨嬧�濇寜瀛楀吀鍚嶇О濉啓锛堝鍊肩◣涓撶敤鍙戠エ / 澧炲�肩◣鏅�氬彂绁� / 鐢靛瓙鍙戠エ锛夛紱鈥滃紑绁ㄩ噾棰�(鍏�)鈥濋渶澶т簬 0 涓斾笉瓒呰繃璇ュ悎鍚屽墿浣欏彲寮�绁ㄩ噾棰濄��</p>
+        <p>鈥滃紑绁ㄧ紪鍙封�濈暀绌哄垯鐢辩郴缁熻嚜鍔ㄧ敓鎴愶紝濉啓鏃朵笉鍙笌绯荤粺涓凡鏈夊紑绁ㄧ紪鍙烽噸澶嶏紱鈥滃紑绁ㄦ棩鏈熲�濇牸寮忎负 2026-01-01 10:00:00 鎴� 2026-01-01锛岀暀绌哄彇褰撳墠鏃堕棿銆�</p>
+        <p>閫愯瀵煎叆锛屽け璐ヨ涓嶅奖鍝嶆垚鍔熻锛涘鍏ョ殑寮�绁ㄨ礋璐d汉 = 褰撳墠鐧诲綍鐢ㄦ埛锛岀姸鎬佷负鈥滄湭鎻愪氦鈥濓紝鍙湪鍒楄〃涓户缁紪杈戙�佷笂浼犲彂绁ㄩ檮浠跺悗鎻愪氦瀹℃壒銆�</p>
+      </div>
+      <!-- 瀵煎叆杩涘害鏉� -->
+      <div v-if="importing" class="mt-4">
+        <div class="mb-2 text-sm text-gray-600">
+          姝e湪瀵煎叆寮�绁ㄦ暟鎹紝璇风◢鍊�...
+        </div>
+        <Progress :percent="progressPercent" status="active" />
+      </div>
+      <!-- 瀵煎叆缁撴灉 -->
+      <div
+        v-if="importResult"
+        class="mt-4 rounded-md border border-gray-200 bg-gray-50 p-3"
+      >
+        <div class="mb-2 font-medium">瀵煎叆缁撴灉</div>
+        <div class="mb-2 flex gap-4">
+          <span class="text-green-600">鏂板锛歿{ createCount }} 鏉�</span>
+          <span class="text-red-600">澶辫触锛歿{ failureCount }} 鏉�</span>
+        </div>
+        <div v-if="failureCount > 0" class="max-h-40 overflow-y-auto">
+          <div
+            v-for="(reason, name) in importResult.failureList"
+            :key="name"
+            class="text-sm leading-6 text-red-500"
+          >
+            {{ name }}锛歿{ reason }}
+          </div>
+        </div>
+      </div>
+    </div>
+    <template #prepend-footer>
+      <Button @click="handleDownloadTemplate">涓嬭浇瀵煎叆妯℃澘</Button>
+    </template>
+  </Modal>
+</template>
diff --git a/src/views/mes/qc/components/import-form-modal.vue b/src/views/mes/qc/components/import-form-modal.vue
new file mode 100644
index 0000000..93a97ca
--- /dev/null
+++ b/src/views/mes/qc/components/import-form-modal.vue
@@ -0,0 +1,204 @@
+<script lang="ts" setup>
+import type { FileType } from 'ant-design-vue/es/upload/interface';
+
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '@vben/common-ui';
+import { downloadFileFromBlobPart } from '@vben/utils';
+
+import { Button, message, Progress, Upload } from 'ant-design-vue';
+
+/** 瀵煎叆缁撴灉锛圛QC/IPQC/OQC 涓変釜瀵煎叆鎺ュ彛杩斿洖缁撴瀯涓�鑷达級 */
+interface ImportResult {
+  createList?: string[];
+  failureList?: Record<string, string>;
+}
+
+interface ImportModalData {
+  /** 寮圭獥鏍囬 */
+  title?: string;
+  /** 妯℃澘涓嬭浇鍑芥暟锛歳equestClient.download(...) 杩斿洖 Blob */
+  templateApi: () => Promise<Blob>;
+  /** 瀵煎叆鍑芥暟锛坢ultipart 涓婁紶 Excel锛� */
+  importApi: (file: File) => Promise<ImportResult>;
+  /** 涓嬭浇鐨勬ā鏉挎枃浠跺悕 */
+  fileName: string;
+  /** 鐏拌壊鎻愮ず鍖虹殑璇存槑娈佃惤 */
+  hints?: string[];
+}
+
+defineOptions({ name: 'QcImportFormModal' });
+
+const emit = defineEmits(['success']);
+
+const modalData = ref<ImportModalData>();
+const title = ref('鎵归噺瀵煎叆');
+const hints = ref<string[]>([]);
+const fileList = ref<FileType[]>([]);
+const importResult = ref<ImportResult>();
+const importing = ref(false);
+const progressPercent = ref(0);
+let progressTimer: ReturnType<typeof setInterval> | undefined;
+
+const createCount = computed(() => importResult.value?.createList?.length ?? 0);
+const failureCount = computed(
+  () => Object.keys(importResult.value?.failureList ?? {}).length,
+);
+
+/** 妯℃嫙杩涘害鏉★細璇锋眰鏈熼棿浠� 0 璧板埌 90% */
+function startMockProgress() {
+  progressPercent.value = 0;
+  progressTimer = setInterval(() => {
+    if (progressPercent.value < 90) {
+      progressPercent.value = Math.min(
+        90,
+        progressPercent.value + Math.random() * 12,
+      );
+    }
+  }, 300);
+}
+
+function stopMockProgress() {
+  if (progressTimer) {
+    clearInterval(progressTimer);
+    progressTimer = undefined;
+  }
+}
+
+function delay(ms: number) {
+  return new Promise((resolve) => setTimeout(resolve, ms));
+}
+
+const [Modal, modalApi] = useVbenModal({
+  async onConfirm() {
+    const data = modalData.value;
+    if (!data) {
+      return;
+    }
+    if (fileList.value.length === 0) {
+      message.warning('璇烽�夋嫨瑕佸鍏ョ殑鏂囦欢');
+      return;
+    }
+    if (importing.value) {
+      return;
+    }
+    modalApi.lock();
+    importing.value = true;
+    startMockProgress();
+    let shouldClose = false;
+    try {
+      importResult.value = await data.importApi(fileList.value[0] as File);
+      stopMockProgress();
+      progressPercent.value = 100;
+      await delay(300);
+      emit('success');
+      if (failureCount.value > 0) {
+        message.warning(
+          `瀵煎叆瀹屾垚锛氭柊澧� ${createCount.value} 鏉★紝澶辫触 ${failureCount.value} 鏉★紝璇锋煡鐪嬩笅鏂瑰け璐ユ槑缁哷,
+        );
+      } else {
+        message.success('瀵煎叆瀹屾垚');
+        shouldClose = true;
+      }
+    } finally {
+      stopMockProgress();
+      importing.value = false;
+      modalApi.unlock();
+      if (shouldClose) {
+        await modalApi.close();
+      }
+    }
+  },
+  onOpenChange(isOpen: boolean) {
+    if (!isOpen) {
+      modalData.value = undefined;
+      fileList.value = [];
+      importResult.value = undefined;
+      importing.value = false;
+      progressPercent.value = 0;
+      return;
+    }
+    const data = modalApi.getData<ImportModalData>();
+    modalData.value = data;
+    title.value = data?.title ?? '鎵归噺瀵煎叆';
+    hints.value = data?.hints ?? [];
+    modalApi.setState({ confirmText: '瀵煎叆' });
+  },
+});
+
+/** 涓婁紶鍓嶏細鍙繚鐣欎竴涓枃浠讹紝浜ょ敱 onConfirm 缁熶竴鎻愪氦 */
+function beforeUpload(file: FileType) {
+  fileList.value = [file];
+  importResult.value = undefined;
+  return false;
+}
+
+function handleRemove() {
+  fileList.value = [];
+  importResult.value = undefined;
+}
+
+async function handleDownloadTemplate() {
+  const data = modalData.value;
+  if (!data) {
+    return;
+  }
+  const blob = await data.templateApi();
+  downloadFileFromBlobPart({ fileName: data.fileName, source: blob });
+}
+</script>
+
+<template>
+  <Modal :title="title" class="w-[720px]">
+    <div class="mx-4">
+      <Upload
+        :max-count="1"
+        accept=".xls,.xlsx"
+        :file-list="fileList"
+        :before-upload="beforeUpload"
+        @remove="handleRemove"
+      >
+        <Button type="primary">閫夋嫨 Excel 鏂囦欢</Button>
+      </Upload>
+      <div
+        v-if="hints.length > 0"
+        class="mt-2 rounded-md bg-gray-50 p-2 text-xs leading-5 text-gray-500"
+      >
+        <p v-for="(hint, index) in hints" :key="index">{{ hint }}</p>
+      </div>
+      <!-- 瀵煎叆杩涘害鏉� -->
+      <div v-if="importing" class="mt-4">
+        <div class="mb-2 text-sm text-gray-600">
+          姝e湪瀵煎叆妫�楠屽崟鏁版嵁锛岃绋嶅��...
+        </div>
+        <Progress :percent="progressPercent" status="active" />
+      </div>
+      <!-- 瀵煎叆缁撴灉 -->
+      <div
+        v-if="importResult"
+        class="mt-4 rounded-md border border-gray-200 bg-gray-50 p-3"
+      >
+        <div class="mb-2 font-medium">瀵煎叆缁撴灉</div>
+        <div class="mb-2 flex gap-4">
+          <span class="text-green-600">鏂板锛歿{ createCount }} 鏉�</span>
+          <span class="text-red-600">澶辫触锛歿{ failureCount }} 鏉�</span>
+        </div>
+        <div v-if="createCount > 0" class="text-sm text-gray-600">
+          鏂板妫�楠屽崟涓恒�屽緟妫�楠屻�嶇姸鎬侊紝璇峰湪鍒楄〃涓紪杈戝苟褰曞叆妫�娴嬬粨鏋�
+        </div>
+        <div v-if="failureCount > 0" class="mt-2 max-h-40 overflow-y-auto">
+          <div
+            v-for="(reason, name) in importResult.failureList"
+            :key="name"
+            class="text-sm leading-6 text-red-500"
+          >
+            {{ name }}锛歿{ reason }}
+          </div>
+        </div>
+      </div>
+    </div>
+    <template #prepend-footer>
+      <Button @click="handleDownloadTemplate">涓嬭浇瀵煎叆妯℃澘</Button>
+    </template>
+  </Modal>
+</template>
diff --git a/src/views/mes/qc/ipqc/data.ts b/src/views/mes/qc/ipqc/data.ts
index afb351c..8a422f4 100644
--- a/src/views/mes/qc/ipqc/data.ts
+++ b/src/views/mes/qc/ipqc/data.ts
@@ -468,6 +468,12 @@
       },
     },
     {
+      field: 'indicatorResultSummary',
+      title: '妫�娴嬬粨鏋滐紙妫�楠岄」锛�',
+      minWidth: 200,
+      showOverflow: 'tooltip',
+    },
+    {
       field: 'inspectDate',
       title: '妫�娴嬫棩鏈�',
       width: 120,
diff --git a/src/views/mes/qc/ipqc/index.vue b/src/views/mes/qc/ipqc/index.vue
index 2d47360..786c402 100644
--- a/src/views/mes/qc/ipqc/index.vue
+++ b/src/views/mes/qc/ipqc/index.vue
@@ -8,9 +8,16 @@
 import { Button, message } from 'ant-design-vue';
 
 import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
-import { deleteIpqc, exportIpqc, getIpqcPage } from '#/api/mes/qc/ipqc';
+import {
+  deleteIpqc,
+  exportIpqc,
+  getIpqcPage,
+  importIpqc,
+  importIpqcTemplate,
+} from '#/api/mes/qc/ipqc';
 import { $t } from '#/locales';
 import QcExportRangeModal from '#/views/mes/qc/components/export-range-modal.vue';
+import QcImportFormModal from '#/views/mes/qc/components/import-form-modal.vue';
 import QcReportGenerateModal from '#/views/mes/qc/components/report-generate-modal.vue';
 
 import { useGridColumns, useGridFormSchema } from './data';
@@ -34,9 +41,34 @@
   destroyOnClose: true,
 });
 
+const [ImportModal, importModalApi] = useVbenModal({
+  connectedComponent: QcImportFormModal,
+  destroyOnClose: true,
+});
+
 /** 鍒锋柊琛ㄦ牸 */
 function handleRefresh() {
   gridApi.query();
+}
+
+/** 鎵归噺瀵煎叆杩囩▼妫�楠屽崟 */
+function handleImport() {
+  importModalApi
+    .setData({
+      title: '鎵归噺瀵煎叆杩囩▼妫�楠屽崟',
+      templateApi: importIpqcTemplate,
+      importApi: importIpqc,
+      fileName: '杩囩▼妫�楠屽崟瀵煎叆妯℃澘.xls',
+      hints: [
+        '浠呭厑璁稿鍏� xls銆亁lsx 鏍煎紡鏂囦欢锛涘繀濉垪锛氭楠屽崟鍚嶇О銆佷骇鍝佺墿鏂欑紪鐮併�佹壒鍙枫�佸悎鏍煎搧鏁伴噺銆佷笉鍚堟牸鍝佹暟閲忋��',
+        '銆屼骇鍝佺墿鏂欑紪鐮併�嶉渶涓庛�岀墿鏂欑鐞嗐�嶄腑鐨勭紪鐮佸畬鍏ㄤ竴鑷达紱銆屾壒鍙枫�嶄负蹇呭~锛屼笉鍏佽涓虹┖銆�',
+        '銆屾楠岀被鍨嬨�嶆寜瀛楀吀鍚嶇О濉啓锛堣嚜鍒跺伐搴忔楠� / 棣栨 / 宸℃ / 鑷 / 鎴愬搧妫�楠岋級锛涖�屾娴嬬粨鏋溿�嶆寜瀛楀吀鍚嶇О濉啓锛堟牎楠岄�氳繃 / 鏍¢獙涓嶉�氳繃锛夈��',
+        '鍚堟牸鍝佹暟閲� + 涓嶅悎鏍煎搧鏁伴噺 蹇呴』绛変簬銆屾娴嬫暟閲忋�嶏紙鐣欑┖鏃惰嚜鍔ㄥ彇涓よ�呬箣鍜岋級锛涘伐搴� + 鏂欏簾 + 鍏朵粬搴熷搧 涔嬪拰涓嶅緱瓒呰繃涓嶅悎鏍煎搧鏁伴噺銆�',
+        '銆岃川妫�鏂规銆嶅~鏂规鍚嶇О锛岀暀绌哄垯鎸夌墿鏂欒嚜鍔ㄥ尮閰� IPQC 鏂规锛涖�屾娴嬩汉鍛樸�嶇暀绌哄彇褰撳墠鐧诲綍鐢ㄦ埛锛涖�屾娴嬫棩鏈熴�嶅彲鐣欑┖銆�',
+        '閫愯瀵煎叆锛屽け璐ヨ涓嶅奖鍝嶆垚鍔熻锛涘鍏ュ悗鐨勬楠屽崟涓恒�屽緟妫�楠屻�嶇姸鎬侊紝鍙湪鍒楄〃涓户缁紪杈戙�佸綍鍏ユ娴嬬粨鏋溿��',
+      ],
+    })
+    .open();
 }
 
 /** 鍒涘缓杩囩▼妫�楠屽崟 */
@@ -123,6 +155,7 @@
     <FormModal @success="handleRefresh" />
     <ExportRangeModal />
     <ReportModal />
+    <ImportModal @success="handleRefresh" />
     <Grid table-title="杩囩▼妫�楠屽崟鍒楄〃">
       <template #toolbar-tools>
         <TableAction
@@ -135,6 +168,13 @@
               onClick: handleCreate,
             },
             {
+              label: '鎵归噺瀵煎叆',
+              type: 'primary',
+              icon: ACTION_ICON.UPLOAD,
+              auth: ['mes:qc-ipqc:import'],
+              onClick: handleImport,
+            },
+            {
               label: $t('ui.actionTitle.export'),
               type: 'primary',
               icon: ACTION_ICON.DOWNLOAD,
diff --git a/src/views/mes/qc/iqc/data.ts b/src/views/mes/qc/iqc/data.ts
index 63ff5f1..62bb1a1 100644
--- a/src/views/mes/qc/iqc/data.ts
+++ b/src/views/mes/qc/iqc/data.ts
@@ -427,6 +427,12 @@
       },
     },
     {
+      field: 'indicatorResultSummary',
+      title: '妫�娴嬬粨鏋滐紙妫�楠岄」锛�',
+      minWidth: 200,
+      showOverflow: 'tooltip',
+    },
+    {
       field: 'receiveDate',
       title: '鏉ユ枡鏃ユ湡',
       width: 120,
diff --git a/src/views/mes/qc/iqc/index.vue b/src/views/mes/qc/iqc/index.vue
index 5759607..4be3aed 100644
--- a/src/views/mes/qc/iqc/index.vue
+++ b/src/views/mes/qc/iqc/index.vue
@@ -11,9 +11,16 @@
 import { Button, message } from 'ant-design-vue';
 
 import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
-import { deleteIqc, exportIqc, getIqcPage } from '#/api/mes/qc/iqc';
+import {
+  deleteIqc,
+  exportIqc,
+  getIqcPage,
+  importIqc,
+  importIqcTemplate,
+} from '#/api/mes/qc/iqc';
 import { $t } from '#/locales';
 import QcExportRangeModal from '#/views/mes/qc/components/export-range-modal.vue';
+import QcImportFormModal from '#/views/mes/qc/components/import-form-modal.vue';
 import QcReportGenerateModal from '#/views/mes/qc/components/report-generate-modal.vue';
 
 import { useGridColumns, useGridFormSchema } from './data';
@@ -41,9 +48,33 @@
   destroyOnClose: true,
 });
 
+const [ImportModal, importModalApi] = useVbenModal({
+  connectedComponent: QcImportFormModal,
+  destroyOnClose: true,
+});
+
 /** 鍒锋柊琛ㄦ牸 */
 function handleRefresh() {
   gridApi.query();
+}
+
+/** 鎵归噺瀵煎叆鏉ユ枡妫�楠屽崟 */
+function handleImport() {
+  importModalApi
+    .setData({
+      title: '鎵归噺瀵煎叆鏉ユ枡妫�楠屽崟',
+      templateApi: importIqcTemplate,
+      importApi: importIqc,
+      fileName: '鏉ユ枡妫�楠屽崟瀵煎叆妯℃澘.xls',
+      hints: [
+        '浠呭厑璁稿鍏� xls銆亁lsx 鏍煎紡鏂囦欢锛涘繀濉垪锛氭楠屽崟鍚嶇О銆佷骇鍝佺墿鏂欑紪鐮併�佸悎鏍煎搧鏁伴噺銆佷笉鍚堟牸鍝佹暟閲忋�佹娴嬫棩鏈熴�佹潵鏂欐棩鏈熴��',
+        '銆屼骇鍝佺墿鏂欑紪鐮併�嶉渶涓庛�岀墿鏂欑鐞嗐�嶄腑鐨勭紪鐮佸畬鍏ㄤ竴鑷达紱銆屼緵搴斿晢鍚嶇О銆嶉渶涓庛�屼緵搴斿晢绠$悊銆嶄腑鐨勫悕绉颁竴鑷达紝鐣欑┖鍒欒鍗曟棤渚涘簲鍟嗐��',
+        '鍚堟牸鍝佹暟閲� + 涓嶅悎鏍煎搧鏁伴噺 蹇呴』绛変簬銆屾湰娆℃帴鏀舵暟閲忋�嶏紙鐣欑┖鏃惰嚜鍔ㄥ彇涓よ�呬箣鍜岋級锛涖�屾娴嬬粨鏋溿�嶆寜瀛楀吀鍚嶇О濉啓锛堟牎楠岄�氳繃 / 鏍¢獙涓嶉�氳繃锛夈��',
+        '銆岃川妫�鏂规銆嶅~鏂规鍚嶇О锛岀暀绌哄垯鎸夌墿鏂欒嚜鍔ㄥ尮閰� IQC 鏂规锛涖�屾娴嬩汉鍛樸�嶇暀绌哄彇褰撳墠鐧诲綍鐢ㄦ埛锛涙棩鏈熸敮鎸� 2026-01-01 10:00:00 鎴� 2026-01-01銆�',
+        '閫愯瀵煎叆锛屽け璐ヨ涓嶅奖鍝嶆垚鍔熻锛涘鍏ュ悗鐨勬楠屽崟涓恒�屽緟妫�楠屻�嶇姸鎬侊紝鍙湪鍒楄〃涓户缁紪杈戙�佸綍鍏ユ娴嬬粨鏋溿��',
+      ],
+    })
+    .open();
 }
 
 /** 鍒涘缓鏉ユ枡妫�楠屽崟 */
@@ -137,6 +168,7 @@
     <FormModal @success="handleRefresh" />
     <ExportRangeModal />
     <ReportModal />
+    <ImportModal @success="handleRefresh" />
     <Grid table-title="鏉ユ枡妫�楠屽崟鍒楄〃">
       <template #toolbar-tools>
         <TableAction
@@ -149,6 +181,13 @@
               onClick: handleCreate,
             },
             {
+              label: '鎵归噺瀵煎叆',
+              type: 'primary',
+              icon: ACTION_ICON.UPLOAD,
+              auth: ['mes:qc-iqc:import'],
+              onClick: handleImport,
+            },
+            {
               label: $t('ui.actionTitle.export'),
               type: 'primary',
               icon: ACTION_ICON.DOWNLOAD,
diff --git a/src/views/mes/qc/oqc/data.ts b/src/views/mes/qc/oqc/data.ts
index b457b34..23be659 100644
--- a/src/views/mes/qc/oqc/data.ts
+++ b/src/views/mes/qc/oqc/data.ts
@@ -425,6 +425,12 @@
       },
     },
     {
+      field: 'indicatorResultSummary',
+      title: '妫�娴嬬粨鏋滐紙妫�楠岄」锛�',
+      minWidth: 200,
+      showOverflow: 'tooltip',
+    },
+    {
       field: 'inspectDate',
       title: '妫�娴嬫棩鏈�',
       width: 120,
diff --git a/src/views/mes/qc/oqc/index.vue b/src/views/mes/qc/oqc/index.vue
index 9a62cdd..7491ac7 100644
--- a/src/views/mes/qc/oqc/index.vue
+++ b/src/views/mes/qc/oqc/index.vue
@@ -8,9 +8,16 @@
 import { Button, message } from 'ant-design-vue';
 
 import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
-import { deleteOqc, exportOqc, getOqcPage } from '#/api/mes/qc/oqc';
+import {
+  deleteOqc,
+  exportOqc,
+  getOqcPage,
+  importOqc,
+  importOqcTemplate,
+} from '#/api/mes/qc/oqc';
 import { $t } from '#/locales';
 import QcExportRangeModal from '#/views/mes/qc/components/export-range-modal.vue';
+import QcImportFormModal from '#/views/mes/qc/components/import-form-modal.vue';
 import QcReportGenerateModal from '#/views/mes/qc/components/report-generate-modal.vue';
 
 import { useGridColumns, useGridFormSchema } from './data';
@@ -34,9 +41,33 @@
   destroyOnClose: true,
 });
 
+const [ImportModal, importModalApi] = useVbenModal({
+  connectedComponent: QcImportFormModal,
+  destroyOnClose: true,
+});
+
 /** 鍒锋柊琛ㄦ牸 */
 function handleRefresh() {
   gridApi.query();
+}
+
+/** 鎵归噺瀵煎叆鍑鸿揣妫�楠屽崟 */
+function handleImport() {
+  importModalApi
+    .setData({
+      title: '鎵归噺瀵煎叆鍑鸿揣妫�楠屽崟',
+      templateApi: importOqcTemplate,
+      importApi: importOqc,
+      fileName: '鍑鸿揣妫�楠屽崟瀵煎叆妯℃澘.xls',
+      hints: [
+        '浠呭厑璁稿鍏� xls銆亁lsx 鏍煎紡鏂囦欢锛涘繀濉垪锛氭楠屽崟鍚嶇О銆佷骇鍝佺墿鏂欑紪鐮併�佸鎴峰悕绉般�佹湰娆″嚭璐ф暟閲忋�佸悎鏍煎搧鏁伴噺銆佷笉鍚堟牸鍝佹暟閲忋�佹娴嬫棩鏈熴�佸嚭璐ф棩鏈熴��',
+        '銆屼骇鍝佺墿鏂欑紪鐮併�嶉渶涓庛�岀墿鏂欑鐞嗐�嶄腑鐨勭紪鐮佸畬鍏ㄤ竴鑷达紱銆屽鎴峰悕绉般�嶉渶涓庛�孋RM 瀹㈡埛绠$悊銆嶄腑鐨勫悕绉颁竴鑷达紝涓斿繀椤诲湪褰撳墠鐧诲綍鐢ㄦ埛鐨勫鎴锋暟鎹潈闄愯寖鍥村唴銆�',
+        '鍚堟牸鍝佹暟閲� + 涓嶅悎鏍煎搧鏁伴噺 蹇呴』绛変簬銆屾娴嬫暟閲忋�嶏紙鐣欑┖鏃惰嚜鍔ㄥ彇涓よ�呬箣鍜岋級锛涖�屾娴嬬粨鏋溿�嶆寜瀛楀吀鍚嶇О濉啓锛堟牎楠岄�氳繃 / 鏍¢獙涓嶉�氳繃锛夈��',
+        '銆岃川妫�鏂规銆嶅~鏂规鍚嶇О锛岀暀绌哄垯鎸夌墿鏂欒嚜鍔ㄥ尮閰� OQC 鏂规锛涖�屾娴嬩汉鍛樸�嶇暀绌哄彇褰撳墠鐧诲綍鐢ㄦ埛锛涙棩鏈熸敮鎸� 2026-01-01 10:00:00 鎴� 2026-01-01銆�',
+        '閫愯瀵煎叆锛屽け璐ヨ涓嶅奖鍝嶆垚鍔熻锛涘鍏ュ悗鐨勬楠屽崟涓恒�屽緟妫�楠屻�嶇姸鎬侊紝鍙湪鍒楄〃涓户缁紪杈戙�佸綍鍏ユ娴嬬粨鏋溿��',
+      ],
+    })
+    .open();
 }
 
 /** 鍒涘缓鍑鸿揣妫�楠屽崟 */
@@ -123,6 +154,7 @@
     <FormModal @success="handleRefresh" />
     <ExportRangeModal />
     <ReportModal />
+    <ImportModal @success="handleRefresh" />
     <Grid table-title="鍑鸿揣妫�楠屽崟鍒楄〃">
       <template #toolbar-tools>
         <TableAction
@@ -135,6 +167,13 @@
               onClick: handleCreate,
             },
             {
+              label: '鎵归噺瀵煎叆',
+              type: 'primary',
+              icon: ACTION_ICON.UPLOAD,
+              auth: ['mes:qc-oqc:import'],
+              onClick: handleImport,
+            },
+            {
               label: $t('ui.actionTitle.export'),
               type: 'primary',
               icon: ACTION_ICON.DOWNLOAD,
diff --git a/src/views/mes/qc/rqc/data.ts b/src/views/mes/qc/rqc/data.ts
index 749a23d..17b5c54 100644
--- a/src/views/mes/qc/rqc/data.ts
+++ b/src/views/mes/qc/rqc/data.ts
@@ -414,6 +414,12 @@
       },
     },
     {
+      field: 'indicatorResultSummary',
+      title: '妫�娴嬬粨鏋滐紙妫�楠岄」锛�',
+      minWidth: 200,
+      showOverflow: 'tooltip',
+    },
+    {
       field: 'inspectDate',
       title: '妫�娴嬫棩鏈�',
       width: 160,

--
Gitblit v1.9.3