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