From 4dd827a00627d9c30990b4c14549260c42bd06d7 Mon Sep 17 00:00:00 2001
From: 吴羡 <16976746+Xian-TT3@user.noreply.gitee.com>
Date: 星期一, 28 九月 2026 14:13:37 +0800
Subject: [PATCH] feat: 发票导入、质检批量导入与检测台账、回款必填、报价单号提示、退货物料搜索

---
 docs/合同导入模板.xlsx                                  |    0 
 src/views/mes/qc/iqc/index.vue                    |   41 ++
 src/views/crm/receivable/data.ts                  |   12 
 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/wls/returnsales/data.ts                 |    1 
 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/views/mes/qc/ledger/index.vue                 |   73 ++++
 src/views/crm/saleQuotation/data.ts               |    5 
 src/views/mes/qc/ledger/data.ts                   |  200 ++++++++++++
 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/api/mes/qc/ledger/index.ts                    |   48 +++
 src/views/mes/qc/components/import-form-modal.vue |  204 ++++++++++++
 src/views/crm/receivable/modules/form.vue         |    2 
 src/views/mes/qc/rqc/data.ts                      |    6 
 23 files changed, 936 insertions(+), 11 deletions(-)

diff --git "a/docs/\345\220\210\345\220\214\345\257\274\345\205\245\346\250\241\346\235\277.xlsx" "b/docs/\345\220\210\345\220\214\345\257\274\345\205\245\346\250\241\346\235\277.xlsx"
new file mode 100644
index 0000000..2dcfafa
--- /dev/null
+++ "b/docs/\345\220\210\345\220\214\345\257\274\345\205\245\346\250\241\346\235\277.xlsx"
Binary files differ
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 72c0e49..48fe15c 100644
--- a/src/api/mes/qc/ipqc/index.ts
+++ b/src/api/mes/qc/ipqc/index.ts
@@ -40,11 +40,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 琛屻��
   }
 }
 
@@ -84,3 +91,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/ledger/index.ts b/src/api/mes/qc/ledger/index.ts
new file mode 100644
index 0000000..40f86a8
--- /dev/null
+++ b/src/api/mes/qc/ledger/index.ts
@@ -0,0 +1,48 @@
+import type { PageParam, PageResult } from '@vben/request';
+
+import { requestClient } from '#/api/request';
+
+export namespace MesQcInspectionLedgerApi {
+  /** MES 妫�娴嬬粨鏋滃彴璐︼紙涓�琛屼竴涓娴嬮」缁撴灉锛涙楠屽崟鏈綍鍏ユ娴嬮」鏃惰缁勫瓧娈典负绌猴級 */
+  export interface Ledger {
+    qcType?: number; // 鍒嗙被锛堟楠岀被鍨嬶級锛�1 鏉ユ枡妫�楠� / 2 杩囩▼妫�楠� / 3 鍑鸿揣妫�楠� / 4 閫�璐ф楠�
+    qcId?: number; // 妫�楠屽崟 ID
+    code?: string; // 妫�楠屽崟缂栧彿
+    name?: string; // 妫�楠屽崟鍚嶇О
+    status?: number; // 鍗曟嵁鐘舵��
+    itemId?: number; // 鐗╂枡缂栧彿
+    itemCode?: string; // 鐗╂枡缂栫爜
+    itemName?: string; // 鐗╂枡鍚嶇О
+    itemSpecification?: string; // 瑙勬牸鍨嬪彿
+    unitName?: string; // 璁¢噺鍗曚綅鍚嶇О
+    batchNo?: string; // 鎵规鍙�
+    checkQuantity?: number; // 妫�娴嬫暟閲�
+    qualifiedQuantity?: number; // 鍚堟牸鏁伴噺
+    unqualifiedQuantity?: number; // 涓嶅悎鏍兼暟閲�
+    checkResult?: number; // 妫�娴嬬粨鏋�
+    inspectDate?: string; // 妫�娴嬫棩鏈�
+    inspectorUserId?: number; // 妫�娴嬩汉鍛樼紪鍙�
+    inspectorName?: string; // 妫�娴嬩汉鍛樻樀绉�
+    indicatorId?: number; // 妫�娴嬮」缂栧彿
+    indicatorName?: string; // 妫�娴嬮」鍚嶇О
+    indicatorValue?: string; // 妫�娴嬪�硷紙瀹炴祴鍊硷級
+    indicatorRemark?: string; // 妫�娴嬮」澶囨敞
+    judgementBasis?: string; // 鍒ゅ畾渚濇嵁锛屽 10~20銆佲墹20銆佲墺10銆佹爣鍑嗗�� 15
+    remark?: string; // 妫�楠屽崟澶囨敞
+  }
+}
+
+/** 鏌ヨ妫�娴嬬粨鏋滃彴璐﹀垎椤碉紙鍥涚被妫�楠屽崟缁熶竴鏌ヨ锛� */
+export function getInspectionLedgerPage(params: PageParam) {
+  return requestClient.get<PageResult<MesQcInspectionLedgerApi.Ledger>>(
+    '/mes/qc/inspection-ledger/page',
+    { params },
+  );
+}
+
+/** 瀵煎嚭妫�娴嬬粨鏋滃彴璐� */
+export function exportInspectionLedger(params: Record<string, unknown>) {
+  return requestClient.download('/mes/qc/inspection-ledger/export-excel', {
+    params,
+  });
+}
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/crm/receivable/data.ts b/src/views/crm/receivable/data.ts
index e401e1f..dbb8d5f 100644
--- a/src/views/crm/receivable/data.ts
+++ b/src/views/crm/receivable/data.ts
@@ -119,7 +119,7 @@
       fieldName: 'invoiceId',
       label: '鍏宠仈寮�绁�',
       component: 'Select',
-      rules: 'required',
+      help: '鍙笉閫夛細涓嶉�夎〃绀鸿绗斿洖娆炬殏涓嶅叧鑱斿紑绁�',
       dependencies: {
         triggerFields: ['contractId', 'invoiceId'],
         disabled: (values) => !values.contractId || !!values.id,
@@ -132,6 +132,7 @@
           }
           const invoices = await getInvoiceSimpleList(values.contractId);
           return {
+            allowClear: true,
             options: invoices.map((item) => {
               const remain = item.remainingInvoicePrice ?? item.price ?? 0;
               return {
@@ -139,11 +140,12 @@
                 value: item.id,
               };
             }),
-            placeholder: '璇烽�夋嫨鍏宠仈寮�绁�',
+            placeholder: '璇烽�夋嫨鍏宠仈寮�绁紙鍙笉閫夛級',
             onChange: (value: number) => {
               form.setFieldValue('planId', undefined);
               form.setFieldValue('returnTime', undefined);
               form.setFieldValue('returnType', undefined);
+              form.setFieldValue('price', undefined);
               const invoice = invoices.find((item) => item.id === value);
               if (invoice) {
                 form.setFieldValue(
@@ -161,8 +163,7 @@
       fieldName: 'planId',
       label: '鍥炴鏈熸暟',
       component: 'Select',
-      rules: 'required',
-      help: '姝ゅ弬鏁板彲鍦ㄥ洖娆捐鍒掗厤缃�',
+      help: '姝ゅ弬鏁板彲鍦ㄥ洖娆捐鍒掗厤缃紱鍙笉閫夛紝涓嶉�夊垯涓嶅崰鐢ㄥ洖娆捐鍒掓湡鏁�',
       dependencies: {
         triggerFields: ['contractId'],
         disabled: (values) => !values.contractId || values.id,
@@ -178,12 +179,13 @@
             values.contractId,
           );
           return {
+            allowClear: true,
             options: plans.map((item) => ({
               disabled: !!item.receivableId,
               label: `绗� ${item.period} 鏈焋,
               value: item.id,
             })),
-            placeholder: '璇烽�夋嫨鍥炴鏈熸暟',
+            placeholder: '璇烽�夋嫨鍥炴鏈熸暟锛堝彲涓嶉�夛級',
             onChange: async (value: any) => {
               if (!value) {
                 form.setFieldValue('returnTime', undefined);
diff --git a/src/views/crm/receivable/modules/form.vue b/src/views/crm/receivable/modules/form.vue
index d8a3672..3a81b1c 100644
--- a/src/views/crm/receivable/modules/form.vue
+++ b/src/views/crm/receivable/modules/form.vue
@@ -147,7 +147,7 @@
     <Alert
       v-if="showInvoiceHint"
       type="warning"
-      message="璇峰厛瀹屾垚寮�绁ㄥ苟涓婁紶鍙戠エ闄勪欢锛屽啀杩涜鍥炴鎿嶄綔"
+      message="濡傞渶鍏宠仈寮�绁紝璇峰厛瀹屾垚寮�绁ㄥ苟涓婁紶鍙戠エ闄勪欢锛屽啀杩涜鍥炴鎿嶄綔锛涖�愬叧鑱斿紑绁ㄣ�戙�愬洖娆炬湡鏁般�戝潎涓洪�夊~锛屼笉閫夊彲鐩存帴濉啓鍥炴淇℃伅"
       show-icon
       class="mb-4"
     />
diff --git a/src/views/crm/saleQuotation/data.ts b/src/views/crm/saleQuotation/data.ts
index f9b8a8c..71aee78 100644
--- a/src/views/crm/saleQuotation/data.ts
+++ b/src/views/crm/saleQuotation/data.ts
@@ -34,9 +34,10 @@
       fieldName: 'no',
       label: '鎶ヤ环鍗曞彿',
       component: 'Input',
+      help: '鐣欑┖鐢辩郴缁熻嚜鍔ㄧ敓鎴愶紱濉啓鍒欎娇鐢ㄨ缂栧彿锛岄渶鍏ㄥ簱鍞竴',
       componentProps: {
-        placeholder: '淇濆瓨鏃惰嚜鍔ㄧ敓鎴�',
-        disabled: true,
+        placeholder: '鎶ヤ环鍗曞彿鑷畾涔�',
+        maxLength: 32,
       },
     },
     {
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 86522b9..6f92628 100644
--- a/src/views/mes/qc/ipqc/data.ts
+++ b/src/views/mes/qc/ipqc/data.ts
@@ -513,6 +513,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..b879b96 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 a59f777..73c0f84 100644
--- a/src/views/mes/qc/iqc/data.ts
+++ b/src/views/mes/qc/iqc/data.ts
@@ -437,6 +437,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/ledger/data.ts b/src/views/mes/qc/ledger/data.ts
new file mode 100644
index 0000000..8bf9f20
--- /dev/null
+++ b/src/views/mes/qc/ledger/data.ts
@@ -0,0 +1,200 @@
+import type { VbenFormSchema } from '#/adapter/form';
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MesQcInspectionLedgerApi } from '#/api/mes/qc/ledger';
+
+import { markRaw } from 'vue';
+
+import { DICT_TYPE } from '@vben/constants';
+import { getDictOptions } from '@vben/hooks';
+
+import { getSimpleUserList } from '#/api/system/user';
+import { getRangePickerDefaultProps } from '#/utils';
+import { MdmItemSelect } from '#/views/basicData/mdm/components';
+
+/** 鍒楄〃鐨勬悳绱㈣〃鍗� */
+export function useGridFormSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'qcType',
+      label: '鍒嗙被',
+      component: 'Select',
+      componentProps: {
+        allowClear: true,
+        options: getDictOptions(DICT_TYPE.MES_QC_TYPE, 'number'),
+        placeholder: '璇烽�夋嫨鍒嗙被',
+      },
+    },
+    {
+      fieldName: 'inspectDate',
+      label: '妫�娴嬫棩鏈�',
+      component: 'RangePicker',
+      componentProps: {
+        ...getRangePickerDefaultProps(),
+      },
+    },
+    {
+      fieldName: 'code',
+      label: '妫�楠屽崟缂栧彿',
+      component: 'Input',
+      componentProps: {
+        allowClear: true,
+        placeholder: '璇疯緭鍏ユ楠屽崟缂栧彿',
+      },
+    },
+    {
+      fieldName: 'itemId',
+      label: '浜у搧鐗╂枡',
+      component: markRaw(MdmItemSelect),
+      componentProps: {
+        placeholder: '璇烽�夋嫨浜у搧鐗╂枡',
+      },
+    },
+    {
+      fieldName: 'batchNo',
+      label: '鎵规鍙�',
+      component: 'Input',
+      componentProps: {
+        allowClear: true,
+        placeholder: '璇疯緭鍏ユ壒娆″彿',
+      },
+    },
+    {
+      fieldName: 'checkResult',
+      label: '妫�娴嬬粨鏋�',
+      component: 'Select',
+      componentProps: {
+        allowClear: true,
+        options: getDictOptions(DICT_TYPE.MES_QC_CHECK_RESULT, 'number'),
+        placeholder: '璇烽�夋嫨妫�娴嬬粨鏋�',
+      },
+    },
+    {
+      fieldName: 'inspectorUserId',
+      label: '妫�娴嬩汉鍛�',
+      component: 'ApiSelect',
+      componentProps: {
+        allowClear: true,
+        api: getSimpleUserList,
+        labelField: 'nickname',
+        placeholder: '璇烽�夋嫨妫�娴嬩汉鍛�',
+        valueField: 'id',
+      },
+    },
+  ];
+}
+
+/** 鍒楄〃鐨勫瓧娈碉紙涓�琛屼竴涓娴嬮」缁撴灉锛� */
+export function useGridColumns(): VxeTableGridOptions<MesQcInspectionLedgerApi.Ledger>['columns'] {
+  return [
+    {
+      field: 'qcType',
+      title: '鍒嗙被',
+      width: 130,
+      cellRender: {
+        name: 'CellDict',
+        props: { type: DICT_TYPE.MES_QC_TYPE },
+      },
+    },
+    {
+      field: 'code',
+      title: '妫�楠屽崟缂栧彿',
+      width: 170,
+    },
+    {
+      field: 'name',
+      title: '妫�楠屽崟鍚嶇О',
+      minWidth: 180,
+      showOverflow: 'tooltip',
+    },
+    {
+      field: 'itemCode',
+      title: '鐗╂枡缂栫爜',
+      width: 130,
+    },
+    {
+      field: 'itemName',
+      title: '鐗╂枡鍚嶇О',
+      minWidth: 150,
+      showOverflow: 'tooltip',
+    },
+    {
+      field: 'itemSpecification',
+      title: '瑙勬牸鍨嬪彿',
+      minWidth: 140,
+      showOverflow: 'tooltip',
+    },
+    {
+      field: 'batchNo',
+      title: '鎵规鍙�',
+      width: 130,
+    },
+    {
+      field: 'indicatorName',
+      title: '妫�娴嬮」',
+      minWidth: 140,
+      showOverflow: 'tooltip',
+    },
+    {
+      field: 'indicatorValue',
+      title: '妫�娴嬪��',
+      minWidth: 120,
+      showOverflow: 'tooltip',
+    },
+    {
+      field: 'judgementBasis',
+      title: '鍒ゅ畾渚濇嵁',
+      minWidth: 120,
+      showOverflow: 'tooltip',
+    },
+    {
+      field: 'checkResult',
+      title: '妫�娴嬬粨鏋�',
+      width: 110,
+      cellRender: {
+        name: 'CellDict',
+        props: { type: DICT_TYPE.MES_QC_CHECK_RESULT },
+      },
+    },
+    {
+      field: 'checkQuantity',
+      title: '妫�娴嬫暟閲�',
+      width: 100,
+    },
+    {
+      field: 'qualifiedQuantity',
+      title: '鍚堟牸鏁伴噺',
+      width: 100,
+    },
+    {
+      field: 'unqualifiedQuantity',
+      title: '涓嶅悎鏍兼暟閲�',
+      width: 110,
+    },
+    {
+      field: 'inspectorName',
+      title: '妫�娴嬩汉鍛�',
+      width: 110,
+    },
+    {
+      field: 'inspectDate',
+      title: '妫�娴嬫棩鏈�',
+      width: 170,
+      formatter: 'formatDateTime',
+    },
+    {
+      field: 'status',
+      title: '鍗曟嵁鐘舵��',
+      width: 110,
+      cellRender: {
+        name: 'CellDict',
+        props: { type: DICT_TYPE.MES_ORDER_STATUS },
+      },
+    },
+    {
+      field: 'remark',
+      title: '澶囨敞',
+      minWidth: 160,
+      showOverflow: 'tooltip',
+    },
+  ];
+}
diff --git a/src/views/mes/qc/ledger/index.vue b/src/views/mes/qc/ledger/index.vue
new file mode 100644
index 0000000..8430917
--- /dev/null
+++ b/src/views/mes/qc/ledger/index.vue
@@ -0,0 +1,73 @@
+<script lang="ts" setup>
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MesQcInspectionLedgerApi } from '#/api/mes/qc/ledger';
+
+import { Page } from '@vben/common-ui';
+import { downloadFileFromBlobPart } from '@vben/utils';
+
+import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
+import {
+  exportInspectionLedger,
+  getInspectionLedgerPage,
+} from '#/api/mes/qc/ledger';
+import { $t } from '#/locales';
+
+import { useGridColumns, useGridFormSchema } from './data';
+
+/** 瀵煎嚭妫�娴嬬粨鏋滃彴璐︼紙涓庡垪琛ㄥ叡鐢ㄥ悓涓�濂楃瓫閫夋潯浠讹級 */
+async function handleExport() {
+  const formValues = await gridApi.formApi.getValues();
+  const data = await exportInspectionLedger(formValues);
+  downloadFileFromBlobPart({ fileName: '妫�娴嬬粨鏋滃彴璐�.xls', source: data });
+}
+
+const [Grid, gridApi] = useVbenVxeGrid({
+  formOptions: {
+    schema: useGridFormSchema(),
+  },
+  gridOptions: {
+    columns: useGridColumns(),
+    height: 'auto',
+    keepSource: true,
+    proxyConfig: {
+      ajax: {
+        query: async ({ page }, formValues) => {
+          return await getInspectionLedgerPage({
+            pageNo: page.currentPage,
+            pageSize: page.pageSize,
+            ...formValues,
+          });
+        },
+      },
+    },
+    // 鍙拌处琛屾棤鍞竴 id锛堝悓涓�妫�楠屽崟澶氳 qcId 閲嶅锛夛紝涓嶈 keyField锛岀敱 vxe 鍐呴儴鐢熸垚琛屾爣璇�
+    rowConfig: {
+      isHover: true,
+    },
+    toolbarConfig: {
+      refresh: true,
+      search: true,
+    },
+  } as VxeTableGridOptions<MesQcInspectionLedgerApi.Ledger>,
+});
+</script>
+
+<template>
+  <Page auto-content-height>
+    <Grid table-title="妫�娴嬬粨鏋滃彴璐�">
+      <template #toolbar-tools>
+        <TableAction
+          :actions="[
+            {
+              label: $t('ui.actionTitle.export'),
+              type: 'primary',
+              icon: ACTION_ICON.DOWNLOAD,
+              auth: ['mes:qc-inspection-ledger:export'],
+              onClick: handleExport,
+            },
+          ]"
+        />
+      </template>
+    </Grid>
+  </Page>
+</template>
diff --git a/src/views/mes/qc/oqc/data.ts b/src/views/mes/qc/oqc/data.ts
index 54b6766..4aeca09 100644
--- a/src/views/mes/qc/oqc/data.ts
+++ b/src/views/mes/qc/oqc/data.ts
@@ -449,6 +449,12 @@
       },
     },
     {
+      field: 'indicatorResultSummary',
+      title: '妫�娴嬬粨鏋滐紙妫�楠岄」锛�',
+      minWidth: 200,
+      showOverflow: 'tooltip',
+    },
+    {
       field: 'outDate',
       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 ab9af37..945d573 100644
--- a/src/views/mes/qc/rqc/data.ts
+++ b/src/views/mes/qc/rqc/data.ts
@@ -433,6 +433,12 @@
       },
     },
     {
+      field: 'indicatorResultSummary',
+      title: '妫�娴嬬粨鏋滐紙妫�楠岄」锛�',
+      minWidth: 200,
+      showOverflow: 'tooltip',
+    },
+    {
       field: 'inspectDate',
       title: '妫�娴嬫棩鏈�',
       width: 160,
diff --git a/src/views/wls/returnsales/data.ts b/src/views/wls/returnsales/data.ts
index 9b61d3c..7a3214b 100644
--- a/src/views/wls/returnsales/data.ts
+++ b/src/views/wls/returnsales/data.ts
@@ -362,6 +362,7 @@
               label: `${item.productName || ''}锛�${item.productBarCode || ''}锛塦,
               value: item.productId,
             })),
+            showSearch: true,
             onChange: (value: number | undefined) => {
               const selected = items.find((i) => i.productId === value);
               onItemChange?.(selected);

--
Gitblit v1.9.3