From 7dfbaba5930676252d8339f7d04861c1c2351d5b Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期四, 13 八月 2026 16:27:56 +0800
Subject: [PATCH] feat(mes): 增加产品设计项目审批流程和批次追溯功能
---
src/api/mes/wm/batch/index.ts | 81 ++++++
src/views/mes/qc/batchtrace/data.ts | 175 +++++++++++++
src/views/mes/qc/batchtrace/modules/purchase-source-list.vue | 59 ++++
src/views/mes/pd/project/index.vue | 53 +++
src/packages/constants/src/biz-mes-enum.ts | 3
company.config.ts | 6
src/views/mes/pd/project/detail/index.vue | 58 +++
/dev/null | 0
src/api/mes/pd/project/index.ts | 17 +
src/views/mes/qc/batchtrace/modules/trace-detail.vue | 51 +++
src/views/mes/qc/batchtrace/index.vue | 26 +
src/views/mes/pd/project/modules/process-select-modal.vue | 75 +++++
src/views/mes/qc/batchtrace/modules/process-trace-list.vue | 66 +++++
.env.production | 4
src/views/mes/qc/batchtrace/modules/sales-target-list.vue | 59 ++++
src/views/mes/pd/project/modules/workflow-timeline.vue | 23 +
16 files changed, 726 insertions(+), 30 deletions(-)
diff --git a/.env.production b/.env.production
index ec0c2aa..bfe3423 100644
--- a/.env.production
+++ b/.env.production
@@ -3,9 +3,9 @@
VITE_BASE=/
# 璇锋眰璺緞
-VITE_BASE_URL=http://42.63.70.90:9003
+VITE_BASE_URL=http://42.63.70.231:9001
# 鎺ュ彛鍦板潃
-VITE_GLOB_API_URL=http://42.63.70.90:9003/admin-api
+VITE_GLOB_API_URL=http://42.63.70.231:9001/admin-api
# 鏂囦欢涓婁紶绫诲瀷锛歴erver - 鍚庣涓婁紶锛� client - 鍓嶇鐩磋繛涓婁紶锛屼粎鏀寔S3鏈嶅姟
VITE_UPLOAD_TYPE=server
diff --git a/company.config.ts b/company.config.ts
index f4436c3..8cde172 100644
--- a/company.config.ts
+++ b/company.config.ts
@@ -23,4 +23,10 @@
favicon: '/logos/TEST.ico',
apiUrl: 'http://42.63.70.90:9003/admin-api',
},
+ YZFX: {
+ name: '瀹佸鑻辨辰闃查攬鏂版潗鏂欐湁闄愬叕鍙�',
+ logo: '/logos/TEST.png',
+ favicon: '/logos/TEST.ico',
+ apiUrl: 'http://42.63.70.231:9001/admin-api',
+ },
};
diff --git a/dist.zip b/dist.zip
deleted file mode 100644
index b695091..0000000
--- a/dist.zip
+++ /dev/null
Binary files differ
diff --git a/src/api/mes/pd/project/index.ts b/src/api/mes/pd/project/index.ts
index 40e34ab..e6841ce 100644
--- a/src/api/mes/pd/project/index.ts
+++ b/src/api/mes/pd/project/index.ts
@@ -32,6 +32,8 @@
remark?: string;
/** 鍒涘缓鏃堕棿 */
createTime?: number;
+ /** BPM 娴佺▼瀹炰緥缂栧彿 */
+ processInstanceId?: string;
}
export interface PageParams extends PageParam {
@@ -87,13 +89,20 @@
});
}
-/** 瀹℃牳锛堝緟瀹℃牳鈫掑緟褰掓。锛� */
-export function auditPdProject(id: number) {
- return requestClient.put('/mes/pd/project/audit', undefined, {
- params: { id },
+/** 鎻愪氦瀹℃壒锛堝緟瀹℃牳鈫掑鎵逛腑锛� */
+export function submitPdProjectApproval(id: number, processDefinitionKey: string) {
+ return requestClient.put('/mes/pd/project/submit-approval', undefined, {
+ params: { id, processDefinitionKey },
});
}
+/** 鑾峰緱瀹℃壒娴佺▼鍒楄〃 */
+export function getPdProjectApproveProcessList() {
+ return requestClient.get<Array<{ id: string; name: string; key: string }>>(
+ '/mes/pd/project/approve-process-list',
+ );
+}
+
/** 褰掓。锛堝緟褰掓。鈫掑凡褰掓。锛夛紝鍚庣鑷姩鍒涘缓褰掓。鍙拌处璁板綍 */
export function archivePdProject(id: number) {
return requestClient.put('/mes/pd/project/archive', undefined, {
diff --git a/src/api/mes/wm/batch/index.ts b/src/api/mes/wm/batch/index.ts
index 7ed63bc..569efd7 100644
--- a/src/api/mes/wm/batch/index.ts
+++ b/src/api/mes/wm/batch/index.ts
@@ -56,6 +56,56 @@
expireDate?: string[]; // 杩囨湡鏃ユ湡
receiptDate?: string[]; // 鍏ュ簱鏃ユ湡
}
+
+ /** MES 鎵规閲囪喘鏉ユ簮锛堥噰璐叆搴撳崟 + IQC锛� */
+ export interface PurchaseSource {
+ receiptCode?: string; // 閲囪喘鍏ュ簱鍗曞彿
+ receiptDate?: Date; // 鍏ュ簱鏃ユ湡
+ purchaseOrderCode?: string; // 閲囪喘璁㈠崟缂栧彿
+ vendorId?: number; // 渚涘簲鍟� ID
+ vendorCode?: string; // 渚涘簲鍟嗙紪鐮�
+ vendorName?: string; // 渚涘簲鍟嗗悕绉�
+ iqcCode?: string; // IQC 妫�楠屽崟鍙�
+ checkResult?: number; // IQC 妫�楠岀粨璁猴紙瀛楀吀 mes_qc_check_result锛�
+ itemId?: number; // 鐗╂枡 ID
+ itemCode?: string; // 鐗╂枡缂栫爜
+ itemName?: string; // 鐗╂枡鍚嶇О
+ batchCode?: string; // 鎵规鍙�
+ receivedQuantity?: number; // 鍏ュ簱鏁伴噺
+ }
+
+ /** MES 鎵规閿�鍞幓鍚戯紙閿�鍞嚭搴撳崟 + OQC锛� */
+ export interface SalesTarget {
+ salesCode?: string; // 閿�鍞嚭搴撳崟鍙�
+ salesDate?: Date; // 鍑哄簱鏃ユ湡
+ salesOrderCode?: string; // 閿�鍞鍗曠紪鍙�
+ clientId?: number; // 瀹㈡埛 ID
+ clientCode?: string; // 瀹㈡埛缂栫爜
+ clientName?: string; // 瀹㈡埛鍚嶇О
+ oqcCode?: string; // OQC 妫�楠屽崟鍙�
+ checkResult?: number; // OQC 妫�楠岀粨璁猴紙瀛楀吀 mes_qc_check_result锛�
+ itemId?: number; // 鐗╂枡 ID
+ itemCode?: string; // 鐗╂枡缂栫爜
+ itemName?: string; // 鐗╂枡鍚嶇О
+ batchCode?: string; // 鎵规鍙�
+ quantity?: number; // 鍑哄簱鏁伴噺
+ qualityStatus?: number; // 璐ㄩ噺鐘舵�侊紙瀛楀吀 mes_wm_quality_status锛�
+ }
+
+ /** MES 鎵规宸ュ簭娴佽浆锛堜骇鍑�/鎶曞叆浜嬩欢锛� */
+ export interface ProcessTrace {
+ eventType?: 'OUT' | 'IN'; // 浜嬩欢绫诲瀷锛歄UT-浜у嚭 / IN-鎶曞叆
+ workOrderId?: number; // 鐢熶骇宸ュ崟 ID
+ workOrderCode?: string; // 鐢熶骇宸ュ崟缂栧彿
+ processId?: number; // 宸ュ簭 ID
+ processCode?: string; // 宸ュ簭缂栫爜
+ processName?: string; // 宸ュ簭鍚嶇О
+ batchCode?: string; // 鎵规鍙�
+ quantity?: number; // 鏁伴噺
+ qualityStatus?: number; // 璐ㄩ噺鐘舵�侊紙瀛楀吀 mes_wm_quality_status锛夛紝浠呬骇鍑轰晶鏈夊��
+ eventTime?: Date; // 浜嬩欢鏃堕棿
+ processSort?: number; // 宸ュ簭搴忓彿
+ }
}
/** 鏌ヨ鎵规璇︽儏 */
@@ -88,3 +138,34 @@
{ params: { code } },
);
}
+
+/** 鎵规閲囪喘鏉ユ簮锛堥噰璐叆搴撳崟 + IQC锛� */
+export function getPurchaseSourceList(code: string) {
+ return requestClient.get<MesWmBatchApi.PurchaseSource[]>(
+ '/mes/wm/batch/purchase-sources',
+ { params: { code } },
+ );
+}
+
+/** 鎵规閿�鍞幓鍚戯紙閿�鍞嚭搴撳崟 + OQC锛� */
+export function getSalesTargetList(code: string) {
+ return requestClient.get<MesWmBatchApi.SalesTarget[]>(
+ '/mes/wm/batch/sales-targets',
+ { params: { code } },
+ );
+}
+
+/** 鎵规宸ュ簭娴佽浆锛堜骇鍑�/鎶曞叆浜嬩欢锛� */
+export function getProcessTraceList(code: string) {
+ return requestClient.get<MesWmBatchApi.ProcessTrace[]>(
+ '/mes/wm/batch/process-trace',
+ { params: { code } },
+ );
+}
+
+/** 鏍规嵁 SN 鐮佸弽鏌ユ壒娆� */
+export function getBatchBySn(sn: string) {
+ return requestClient.get<MesWmBatchApi.Batch | null>('/mes/wm/batch/get-by-sn', {
+ params: { sn },
+ });
+}
diff --git a/src/packages/constants/src/biz-mes-enum.ts b/src/packages/constants/src/biz-mes-enum.ts
index 435b1db..85c58db 100644
--- a/src/packages/constants/src/biz-mes-enum.ts
+++ b/src/packages/constants/src/biz-mes-enum.ts
@@ -530,11 +530,12 @@
RIGHT: 2, // 鍙宠ˉ榻�
} as const;
-/** MES 浜у搧璁捐椤圭洰鐘舵�佹灇涓撅紙绾挎�ф祦杞細寰呭彂甯冣啋寰呬笂浼犫啋寰呭鏍糕啋寰呭綊妗b啋宸插綊妗o級 */
+/** MES 浜у搧璁捐椤圭洰鐘舵�佹灇涓撅紙绾挎�ф祦杞細寰呭彂甯冣啋寰呬笂浼犫啋寰呭鏍糕啋瀹℃壒涓啋寰呭綊妗b啋宸插綊妗o級 */
export const MesPdProjectStatusEnum = {
PENDING_PUBLISH: 0, // 寰呭彂甯�
PENDING_UPLOAD: 1, // 寰呬笂浼�
PENDING_AUDIT: 2, // 寰呭鏍�
+ APPROVING: 5, // 瀹℃壒涓�
PENDING_ARCHIVE: 3, // 寰呭綊妗�
ARCHIVED: 4, // 宸插綊妗�
} as const;
diff --git a/src/views/mes/pd/project/detail/index.vue b/src/views/mes/pd/project/detail/index.vue
index 36da5ce..97f7f8b 100644
--- a/src/views/mes/pd/project/detail/index.vue
+++ b/src/views/mes/pd/project/detail/index.vue
@@ -13,8 +13,8 @@
import {
archivePdProject,
- auditPdProject,
getPdProject,
+ getPdProjectApproveProcessList,
publishPdProject,
uploadPdProject,
} from '#/api/mes/pd/project';
@@ -25,6 +25,7 @@
import Form from '../modules/form.vue';
import DocumentAuditList from '../modules/document-audit-list.vue';
import DocumentList from '../modules/document-list.vue';
+import ProcessSelectModal from '../modules/process-select-modal.vue';
import WorkflowTimeline from '../modules/workflow-timeline.vue';
import TaskDraftBanner from '../modules/task-draft-banner.vue';
import { useDetailSchema } from './data';
@@ -50,6 +51,9 @@
const isPendingArchive = computed(
() => project.value.status === MesPdProjectStatusEnum.PENDING_ARCHIVE,
);
+const isApproving = computed(
+ () => project.value.status === MesPdProjectStatusEnum.APPROVING,
+);
const isArchived = computed(
() => project.value.status === MesPdProjectStatusEnum.ARCHIVED,
);
@@ -70,6 +74,11 @@
const [FormModal, formModalApi] = useVbenModal({
connectedComponent: Form,
+ destroyOnClose: true,
+});
+
+const [ProcessModal, processModalApi] = useVbenModal({
+ connectedComponent: ProcessSelectModal,
destroyOnClose: true,
});
@@ -119,16 +128,38 @@
}
}
-async function handleAudit() {
- const hideLoading = message.loading({ content: '瀹℃牳涓��', duration: 0 });
+/** 鎻愪氦瀹℃壒 - 鍏堣幏鍙栨祦绋嬪垪琛� */
+async function handleSubmit() {
+ const hideLoading = message.loading({ content: '鑾峰彇瀹℃壒娴佺▼...', duration: 0 });
try {
- await auditPdProject(projectId.value);
- message.success('瀹℃牳閫氳繃锛岃繘鍏ュ緟褰掓。鐘舵��');
- tabs.closeCurrentTab();
- router.back();
- } finally {
+ const processList = await getPdProjectApproveProcessList();
+ hideLoading();
+ if (!processList || processList.length === 0) {
+ message.warning('鏆傛棤鍙敤瀹℃壒娴佺▼锛岃鍏堥厤缃� BPM 娴佺▼');
+ return;
+ }
+ // 鎵撳紑娴佺▼閫夋嫨寮圭獥
+ processModalApi.setData({ id: projectId.value, processList }).open();
+ } catch {
hideLoading();
}
+}
+
+/** 鏌ョ湅瀹℃壒璇︽儏 */
+function handleProcessDetail() {
+ if (!project.value.processInstanceId) {
+ message.warning('璇ラ」鐩皻鏈彁浜ゅ鎵�');
+ return;
+ }
+ router.push({
+ name: 'BpmProcessInstanceDetail',
+ query: { id: project.value.processInstanceId },
+ });
+}
+
+/** 鎻愪氦瀹℃壒鎴愬姛鍚庡埛鏂拌鎯� */
+function handleSubmitSuccess() {
+ loadProjectDetail();
}
async function handleArchive() {
@@ -155,6 +186,7 @@
:title="project.taskName || '璁捐浠诲姟璇︽儏'"
>
<FormModal @success="loadProjectDetail" />
+ <ProcessModal @success="handleSubmitSuccess" />
<template #extra>
<TableAction
@@ -185,10 +217,16 @@
onClick: handleUpload,
},
{
- label: '瀹℃牳閫氳繃',
+ label: '鎻愪氦瀹℃壒',
type: 'primary',
ifShow: isPendingAudit,
- onClick: handleAudit,
+ onClick: handleSubmit,
+ },
+ {
+ label: '鏌ョ湅瀹℃壒',
+ type: 'primary',
+ ifShow: isApproving,
+ onClick: handleProcessDetail,
},
{
label: '褰掓。',
diff --git a/src/views/mes/pd/project/index.vue b/src/views/mes/pd/project/index.vue
index 24516e1..6727d6d 100644
--- a/src/views/mes/pd/project/index.vue
+++ b/src/views/mes/pd/project/index.vue
@@ -17,6 +17,7 @@
deletePdProject,
exportPdProject,
+ getPdProjectApproveProcessList,
getPdProjectPage,
publishPdProject,
@@ -26,6 +27,7 @@
import { useGridColumns, useGridFormSchema } from './data';
import CreateForm from './modules/create-form.vue';
import Form from './modules/form.vue';
+import ProcessSelectModal from './modules/process-select-modal.vue';
const router = useRouter();
@@ -36,6 +38,11 @@
const [FormModal, formModalApi] = useVbenModal({
connectedComponent: Form,
+ destroyOnClose: true,
+});
+
+const [ProcessModal, processModalApi] = useVbenModal({
+ connectedComponent: ProcessSelectModal,
destroyOnClose: true,
});
@@ -71,6 +78,38 @@
} finally {
hideLoading();
}
+}
+
+/** 鎻愪氦瀹℃壒 - 鍏堣幏鍙栨祦绋嬪垪琛� */
+async function handleSubmit(row: MesPdProjectApi.Project) {
+ const hideLoading = message.loading({
+ content: '鑾峰彇瀹℃壒娴佺▼...',
+ duration: 0,
+ });
+ try {
+ const processList = await getPdProjectApproveProcessList();
+ hideLoading();
+ if (!processList || processList.length === 0) {
+ message.warning('鏆傛棤鍙敤瀹℃壒娴佺▼锛岃鍏堥厤缃� BPM 娴佺▼');
+ return;
+ }
+ // 鎵撳紑娴佺▼閫夋嫨寮圭獥
+ processModalApi.setData({ id: row.id, processList }).open();
+ } catch {
+ hideLoading();
+ }
+}
+
+/** 鏌ョ湅瀹℃壒璇︽儏 */
+function handleProcessDetail(row: MesPdProjectApi.Project) {
+ if (!row.processInstanceId) {
+ message.warning('璇ラ」鐩皻鏈彁浜ゅ鎵�');
+ return;
+ }
+ router.push({
+ name: 'BpmProcessInstanceDetail',
+ query: { id: row.processInstanceId },
+ });
}
async function handleArchive(row: MesPdProjectApi.Project) {
@@ -137,6 +176,7 @@
<Page auto-content-height>
<CreateFormModal @success="handleCreateSuccess" />
<FormModal @success="handleRefresh" />
+ <ProcessModal @success="handleRefresh" />
<Grid table-title="璁捐浠诲姟">
<template #toolbar-tools>
@@ -183,11 +223,18 @@
onClick: () => handleDetail(row),
},
{
- label: '瀹℃牳',
+ label: '鎻愪氦瀹℃壒',
type: 'link',
- auth: ['mes:pd-project:audit'],
+ auth: ['mes:pd-project:update'],
ifShow: row.status === MesPdProjectStatusEnum.PENDING_AUDIT,
- onClick: () => handleDetail(row),
+ onClick: handleSubmit.bind(null, row),
+ },
+ {
+ label: '鏌ョ湅瀹℃壒',
+ type: 'link',
+ auth: ['mes:pd-project:update'],
+ ifShow: row.status === MesPdProjectStatusEnum.APPROVING,
+ onClick: handleProcessDetail.bind(null, row),
},
{
label: '褰掓。',
diff --git a/src/views/mes/pd/project/modules/process-select-modal.vue b/src/views/mes/pd/project/modules/process-select-modal.vue
new file mode 100644
index 0000000..c0d099a
--- /dev/null
+++ b/src/views/mes/pd/project/modules/process-select-modal.vue
@@ -0,0 +1,75 @@
+<script lang="ts" setup>
+import { computed, ref } from 'vue';
+
+import { useVbenModal } from '@vben/common-ui';
+
+import { message, Select } from 'ant-design-vue';
+
+import { submitPdProjectApproval } from '#/api/mes/pd/project';
+
+interface ProcessDefinition {
+ id: string;
+ name: string;
+ key: string;
+}
+
+const emit = defineEmits(['success']);
+
+const projectId = ref<number>();
+const processList = ref<ProcessDefinition[]>([]);
+const selectedProcessKey = ref<string>('');
+
+const processOptions = computed(() => {
+ return processList.value.map((p) => ({
+ label: p.name,
+ value: p.key,
+ }));
+});
+
+const [Modal, modalApi] = useVbenModal({
+ async onConfirm() {
+ if (!selectedProcessKey.value) {
+ message.warning('璇烽�夋嫨瀹℃壒娴佺▼');
+ return;
+ }
+ modalApi.lock();
+ try {
+ await submitPdProjectApproval(projectId.value!, selectedProcessKey.value);
+ await modalApi.close();
+ emit('success');
+ message.success('鎻愪氦瀹℃壒鎴愬姛');
+ } finally {
+ modalApi.unlock();
+ }
+ },
+ async onOpenChange(isOpen: boolean) {
+ if (!isOpen) {
+ projectId.value = undefined;
+ selectedProcessKey.value = '';
+ processList.value = [];
+ return;
+ }
+ const data = modalApi.getData<{ id: number; processList: ProcessDefinition[] }>();
+ projectId.value = data.id;
+ processList.value = data.processList || [];
+ // 榛樿閫夋嫨绗竴涓祦绋�
+ if (processList.value.length > 0) {
+ selectedProcessKey.value = processList.value[0].key;
+ }
+ },
+});
+</script>
+
+<template>
+ <Modal title="閫夋嫨瀹℃壒娴佺▼" class="w-1/3">
+ <div class="mb-4">
+ <span class="mr-2">瀹℃壒娴佺▼锛�</span>
+ <Select
+ v-model:value="selectedProcessKey"
+ :options="processOptions"
+ style="width: 200px"
+ placeholder="璇烽�夋嫨瀹℃壒娴佺▼"
+ />
+ </div>
+ </Modal>
+</template>
diff --git a/src/views/mes/pd/project/modules/workflow-timeline.vue b/src/views/mes/pd/project/modules/workflow-timeline.vue
index 6989422..db03cc8 100644
--- a/src/views/mes/pd/project/modules/workflow-timeline.vue
+++ b/src/views/mes/pd/project/modules/workflow-timeline.vue
@@ -3,22 +3,35 @@
import { Steps } from 'ant-design-vue';
-const STEPS = ['寰呭彂甯�', '寰呬笂浼�', '寰呭鏍�', '寰呭綊妗�', '宸插綊妗�'] as const;
+import { MesPdProjectStatusEnum } from '#/packages/constants/src';
+
+const STEPS = ['寰呭彂甯�', '寰呬笂浼�', '寰呭鏍�', '瀹℃壒涓�', '寰呭綊妗�', '宸插綊妗�'] as const;
+
+// 鐘舵�佸�� 鈫� 鏃堕棿绾挎楠ょ储寮曪紙瀹℃壒涓�=5 鎻掑湪寰呭鏍镐箣鍚庯級
+const STATUS_ORDER = [
+ MesPdProjectStatusEnum.PENDING_PUBLISH,
+ MesPdProjectStatusEnum.PENDING_UPLOAD,
+ MesPdProjectStatusEnum.PENDING_AUDIT,
+ MesPdProjectStatusEnum.APPROVING,
+ MesPdProjectStatusEnum.PENDING_ARCHIVE,
+ MesPdProjectStatusEnum.ARCHIVED,
+];
const props = defineProps<{
- /** 椤圭洰褰撳墠鐘舵�� (0-4) */
+ /** 椤圭洰褰撳墠鐘舵�� */
status: number;
}>();
const current = computed(() => {
- // 鐘舵�佸�煎嵆褰撳墠宸插畬鎴愬埌绗嚑姝ワ細0=绗竴姝ヨ繘琛屼腑锛�4=鍏ㄩ儴瀹屾垚
- return props.status;
+ // 鏍规嵁鐘舵�佸�兼槧灏勫埌鏃堕棿绾挎楠ょ储寮�
+ const index = STATUS_ORDER.indexOf(props.status);
+ return index >= 0 ? index : 0;
});
const nodes = computed(() =>
STEPS.map((name, index) => ({
title: name,
- status: index < props.status ? 'finish' : index === props.status ? 'process' : 'wait',
+ status: index < current.value ? 'finish' : index === current.value ? 'process' : 'wait',
})),
);
</script>
diff --git a/src/views/mes/qc/batchtrace/data.ts b/src/views/mes/qc/batchtrace/data.ts
index 52e9620..3013c2e 100644
--- a/src/views/mes/qc/batchtrace/data.ts
+++ b/src/views/mes/qc/batchtrace/data.ts
@@ -5,6 +5,8 @@
import { markRaw } from 'vue';
+import { DICT_TYPE } from '@vben/constants';
+
import { MdClientSelect } from '#/views/mes/md/client/components';
import { MdItemSelect } from '#/views/mes/md/item/components';
import { MdVendorSelect } from '#/views/mes/md/vendor/components';
@@ -227,3 +229,176 @@
},
];
}
+
+/** 閲囪喘鏉ユ簮鍒楄〃鐨勫瓧娈� */
+export function usePurchaseSourceColumns(): VxeTableGridOptions<MesWmBatchApi.PurchaseSource>['columns'] {
+ return [
+ {
+ field: 'receiptCode',
+ title: '閲囪喘鍏ュ簱鍗曞彿',
+ minWidth: 160,
+ },
+ {
+ field: 'receiptDate',
+ title: '鍏ュ簱鏃ユ湡',
+ minWidth: 160,
+ formatter: 'formatDateTime',
+ },
+ {
+ field: 'purchaseOrderCode',
+ title: '閲囪喘璁㈠崟缂栧彿',
+ minWidth: 160,
+ },
+ {
+ field: 'vendorCode',
+ title: '渚涘簲鍟嗙紪鐮�',
+ minWidth: 140,
+ },
+ {
+ field: 'vendorName',
+ title: '渚涘簲鍟嗗悕绉�',
+ minWidth: 140,
+ },
+ {
+ field: 'itemCode',
+ title: '鐗╂枡缂栫爜',
+ minWidth: 140,
+ },
+ {
+ field: 'itemName',
+ title: '鐗╂枡鍚嶇О',
+ minWidth: 160,
+ },
+ {
+ field: 'iqcCode',
+ title: 'IQC 妫�楠屽崟鍙�',
+ minWidth: 160,
+ },
+ {
+ field: 'checkResult',
+ title: 'IQC 妫�楠岀粨璁�',
+ width: 110,
+ cellRender: {
+ name: 'CellDict',
+ props: { type: DICT_TYPE.MES_QC_CHECK_RESULT },
+ },
+ },
+ {
+ field: 'receivedQuantity',
+ title: '鍏ュ簱鏁伴噺',
+ width: 100,
+ },
+ ];
+}
+
+/** 閿�鍞幓鍚戝垪琛ㄧ殑瀛楁 */
+export function useSalesTargetColumns(): VxeTableGridOptions<MesWmBatchApi.SalesTarget>['columns'] {
+ return [
+ {
+ field: 'salesCode',
+ title: '閿�鍞嚭搴撳崟鍙�',
+ minWidth: 160,
+ },
+ {
+ field: 'salesDate',
+ title: '鍑哄簱鏃ユ湡',
+ minWidth: 160,
+ formatter: 'formatDateTime',
+ },
+ {
+ field: 'salesOrderCode',
+ title: '閿�鍞鍗曠紪鍙�',
+ minWidth: 160,
+ },
+ {
+ field: 'clientName',
+ title: '瀹㈡埛鍚嶇О',
+ minWidth: 140,
+ },
+ {
+ field: 'itemCode',
+ title: '鐗╂枡缂栫爜',
+ minWidth: 140,
+ },
+ {
+ field: 'itemName',
+ title: '鐗╂枡鍚嶇О',
+ minWidth: 160,
+ },
+ {
+ field: 'oqcCode',
+ title: 'OQC 妫�楠屽崟鍙�',
+ minWidth: 160,
+ },
+ {
+ field: 'checkResult',
+ title: 'OQC 妫�楠岀粨璁�',
+ width: 110,
+ cellRender: {
+ name: 'CellDict',
+ props: { type: DICT_TYPE.MES_QC_CHECK_RESULT },
+ },
+ },
+ {
+ field: 'quantity',
+ title: '鍑哄簱鏁伴噺',
+ width: 100,
+ },
+ {
+ field: 'qualityStatus',
+ title: '璐ㄩ噺鐘舵��',
+ width: 100,
+ cellRender: {
+ name: 'CellDict',
+ props: { type: DICT_TYPE.MES_WM_QUALITY_STATUS },
+ },
+ },
+ ];
+}
+
+/** 宸ュ簭娴佽浆鍒楄〃鐨勫瓧娈� */
+export function useProcessTraceColumns(): VxeTableGridOptions<MesWmBatchApi.ProcessTrace>['columns'] {
+ return [
+ {
+ field: 'eventType',
+ title: '浜嬩欢绫诲瀷',
+ width: 90,
+ slots: { default: 'eventType' },
+ },
+ {
+ field: 'workOrderCode',
+ title: '鐢熶骇宸ュ崟鍙�',
+ minWidth: 160,
+ },
+ {
+ field: 'processCode',
+ title: '宸ュ簭缂栫爜',
+ minWidth: 120,
+ },
+ {
+ field: 'processName',
+ title: '宸ュ簭鍚嶇О',
+ minWidth: 140,
+ },
+ {
+ field: 'quantity',
+ title: '鏁伴噺',
+ width: 100,
+ },
+ {
+ field: 'qualityStatus',
+ title: '璐ㄩ噺鐘舵��',
+ width: 100,
+ cellRender: {
+ name: 'CellDict',
+ props: { type: DICT_TYPE.MES_WM_QUALITY_STATUS },
+ },
+ },
+ {
+ field: 'eventTime',
+ title: '浜嬩欢鏃堕棿',
+ minWidth: 160,
+ formatter: 'formatDateTime',
+ },
+ ];
+}
diff --git a/src/views/mes/qc/batchtrace/index.vue b/src/views/mes/qc/batchtrace/index.vue
index b403dec..1762b35 100644
--- a/src/views/mes/qc/batchtrace/index.vue
+++ b/src/views/mes/qc/batchtrace/index.vue
@@ -4,8 +4,10 @@
import { Page, useVbenModal } from '@vben/common-ui';
+import { Input, message } from 'ant-design-vue';
+
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
-import { getBatchPage } from '#/api/mes/wm/batch';
+import { getBatchBySn, getBatchPage } from '#/api/mes/wm/batch';
import { useGridColumns, useGridFormSchema } from './data';
import TraceDetail from './modules/trace-detail.vue';
@@ -18,6 +20,19 @@
/** 鎵撳紑鎵规杩芥函璇︽儏 */
function handleTrace(row: MesWmBatchApi.Batch) {
detailModalApi.setData(row).open();
+}
+
+/** 鏍规嵁 SN 鍙嶆煡鎵规骞舵墦寮�璇︽儏 */
+async function handleSearchBySn(sn: string) {
+ if (!sn) {
+ return;
+ }
+ const batch = await getBatchBySn(sn.trim());
+ if (batch) {
+ handleTrace(batch);
+ } else {
+ message.warning(`鏈壘鍒� SN銆�${sn}銆嶅搴旂殑鎵规`);
+ }
}
const [Grid] = useVbenVxeGrid({
@@ -54,6 +69,15 @@
<template>
<Page auto-content-height><DetailModal />
+ <div class="mb-3 w-80">
+ <Input.Search
+ placeholder="杈撳叆 SN 搴忓垪鍙峰弽鏌ユ壒娆�"
+ enter-button="鍙嶆煡"
+ allow-clear
+ @search="handleSearchBySn"
+ />
+ </div>
+
<Grid table-title="鎵规杩芥函鍒楄〃">
<template #actions="{ row }">
<TableAction
diff --git a/src/views/mes/qc/batchtrace/modules/process-trace-list.vue b/src/views/mes/qc/batchtrace/modules/process-trace-list.vue
new file mode 100644
index 0000000..2c3725e
--- /dev/null
+++ b/src/views/mes/qc/batchtrace/modules/process-trace-list.vue
@@ -0,0 +1,66 @@
+<script lang="ts" setup>
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MesWmBatchApi } from '#/api/mes/wm/batch';
+
+import { watch } from 'vue';
+
+import { Tag } from 'ant-design-vue';
+
+import { useVbenVxeGrid } from '#/adapter/vxe-table';
+import { getProcessTraceList } from '#/api/mes/wm/batch';
+
+import { useProcessTraceColumns } from '../data';
+
+const props = defineProps<{
+ batchCode?: string;
+}>();
+
+const [Grid, gridApi] = useVbenVxeGrid({
+ gridOptions: {
+ columns: useProcessTraceColumns(),
+ minHeight: 240,
+ pagerConfig: {
+ enabled: false,
+ },
+ rowConfig: {
+ isHover: true,
+ },
+ showOverflow: true,
+ toolbarConfig: {
+ enabled: false,
+ },
+ } as VxeTableGridOptions<MesWmBatchApi.ProcessTrace>,
+});
+
+/** 鍔犺浇宸ュ簭娴佽浆鍒楄〃 */
+async function getList() {
+ if (!props.batchCode) {
+ await gridApi.grid?.loadData([]);
+ return;
+ }
+ gridApi.setLoading(true);
+ try {
+ const list = await getProcessTraceList(props.batchCode);
+ await gridApi.grid?.loadData(list);
+ } finally {
+ gridApi.setLoading(false);
+ }
+}
+
+watch(
+ () => props.batchCode,
+ () => {
+ getList();
+ },
+ { immediate: true },
+);
+</script>
+
+<template>
+ <Grid class="w-full">
+ <template #eventType="{ row }">
+ <Tag v-if="row.eventType === 'OUT'" color="success">浜у嚭</Tag>
+ <Tag v-else color="processing">鎶曞叆</Tag>
+ </template>
+ </Grid>
+</template>
diff --git a/src/views/mes/qc/batchtrace/modules/purchase-source-list.vue b/src/views/mes/qc/batchtrace/modules/purchase-source-list.vue
new file mode 100644
index 0000000..472a6a4
--- /dev/null
+++ b/src/views/mes/qc/batchtrace/modules/purchase-source-list.vue
@@ -0,0 +1,59 @@
+<script lang="ts" setup>
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MesWmBatchApi } from '#/api/mes/wm/batch';
+
+import { watch } from 'vue';
+
+import { useVbenVxeGrid } from '#/adapter/vxe-table';
+import { getPurchaseSourceList } from '#/api/mes/wm/batch';
+
+import { usePurchaseSourceColumns } from '../data';
+
+const props = defineProps<{
+ batchCode?: string;
+}>();
+
+const [Grid, gridApi] = useVbenVxeGrid({
+ gridOptions: {
+ columns: usePurchaseSourceColumns(),
+ minHeight: 240,
+ pagerConfig: {
+ enabled: false,
+ },
+ rowConfig: {
+ isHover: true,
+ },
+ showOverflow: true,
+ toolbarConfig: {
+ enabled: false,
+ },
+ } as VxeTableGridOptions<MesWmBatchApi.PurchaseSource>,
+});
+
+/** 鍔犺浇閲囪喘鏉ユ簮鍒楄〃 */
+async function getList() {
+ if (!props.batchCode) {
+ await gridApi.grid?.loadData([]);
+ return;
+ }
+ gridApi.setLoading(true);
+ try {
+ const list = await getPurchaseSourceList(props.batchCode);
+ await gridApi.grid?.loadData(list);
+ } finally {
+ gridApi.setLoading(false);
+ }
+}
+
+watch(
+ () => props.batchCode,
+ () => {
+ getList();
+ },
+ { immediate: true },
+);
+</script>
+
+<template>
+ <Grid class="w-full" />
+</template>
diff --git a/src/views/mes/qc/batchtrace/modules/sales-target-list.vue b/src/views/mes/qc/batchtrace/modules/sales-target-list.vue
new file mode 100644
index 0000000..0963502
--- /dev/null
+++ b/src/views/mes/qc/batchtrace/modules/sales-target-list.vue
@@ -0,0 +1,59 @@
+<script lang="ts" setup>
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MesWmBatchApi } from '#/api/mes/wm/batch';
+
+import { watch } from 'vue';
+
+import { useVbenVxeGrid } from '#/adapter/vxe-table';
+import { getSalesTargetList } from '#/api/mes/wm/batch';
+
+import { useSalesTargetColumns } from '../data';
+
+const props = defineProps<{
+ batchCode?: string;
+}>();
+
+const [Grid, gridApi] = useVbenVxeGrid({
+ gridOptions: {
+ columns: useSalesTargetColumns(),
+ minHeight: 240,
+ pagerConfig: {
+ enabled: false,
+ },
+ rowConfig: {
+ isHover: true,
+ },
+ showOverflow: true,
+ toolbarConfig: {
+ enabled: false,
+ },
+ } as VxeTableGridOptions<MesWmBatchApi.SalesTarget>,
+});
+
+/** 鍔犺浇閿�鍞幓鍚戝垪琛� */
+async function getList() {
+ if (!props.batchCode) {
+ await gridApi.grid?.loadData([]);
+ return;
+ }
+ gridApi.setLoading(true);
+ try {
+ const list = await getSalesTargetList(props.batchCode);
+ await gridApi.grid?.loadData(list);
+ } finally {
+ gridApi.setLoading(false);
+ }
+}
+
+watch(
+ () => props.batchCode,
+ () => {
+ getList();
+ },
+ { immediate: true },
+);
+</script>
+
+<template>
+ <Grid class="w-full" />
+</template>
diff --git a/src/views/mes/qc/batchtrace/modules/trace-detail.vue b/src/views/mes/qc/batchtrace/modules/trace-detail.vue
index 7fcba7e..8d01d5b 100644
--- a/src/views/mes/qc/batchtrace/modules/trace-detail.vue
+++ b/src/views/mes/qc/batchtrace/modules/trace-detail.vue
@@ -5,15 +5,24 @@
import { useVbenModal } from '@vben/common-ui';
-import { Tabs } from 'ant-design-vue';
+import { Tabs, Tooltip } from 'ant-design-vue';
import { useDescription } from '#/components/description';
import { useDetailSchema } from '../data';
+import ProcessTraceList from './process-trace-list.vue';
+import PurchaseSourceList from './purchase-source-list.vue';
+import SalesTargetList from './sales-target-list.vue';
import TraceList from './trace-list.vue';
const detailData = ref<MesWmBatchApi.Batch>(); // 褰撳墠鎵规璇︽儏
-const subTabsName = ref<'backward' | 'forward'>('forward'); // 褰撳墠婵�娲荤殑杩芥函鏂瑰悜
+const subTabsName = ref<
+ | 'backward'
+ | 'forward'
+ | 'purchase'
+ | 'sales'
+ | 'process'
+>('forward'); // 褰撳墠婵�娲荤殑杩芥函椤电
const [Descriptions] = useDescription({
bordered: true,
@@ -47,12 +56,46 @@
class="mx-4 mt-4"
type="card"
>
- <Tabs.TabPane key="forward" tab="鍚戝墠杩芥函">
+ <Tabs.TabPane key="forward">
+ <template #tab>
+ <Tooltip title="椤虹潃鐢熶骇娴佸線涓嬫父鏌ワ細璇ユ壒娆¤鍝簺宸ュ崟娑堣�楀悗锛屼骇鍑烘垚浜嗗摢浜涙垚鍝佹壒娆�">
+ <span>鍚戝墠杩芥函</span>
+ </Tooltip>
+ </template>
<TraceList :batch-code="detailData.code" direction="forward" />
</Tabs.TabPane>
- <Tabs.TabPane key="backward" tab="鍚戝悗杩芥函">
+ <Tabs.TabPane key="backward">
+ <template #tab>
+ <Tooltip title="閫嗙潃鐢熶骇娴佸線涓婃父鏌ワ細鐢熶骇璇ユ壒娆℃椂锛屾秷鑰椾簡鍝簺鍘熸枡鎵规">
+ <span>鍚戝悗杩芥函</span>
+ </Tooltip>
+ </template>
<TraceList :batch-code="detailData.code" direction="backward" />
</Tabs.TabPane>
+ <Tabs.TabPane key="purchase">
+ <template #tab>
+ <Tooltip title="鍘熸枡渚ф潵婧愶細璇ユ壒娆″搴旂殑閲囪喘鍏ュ簱鍗曚笌 IQC 鏉ユ枡妫�楠岃褰�">
+ <span>閲囪喘鏉ユ簮</span>
+ </Tooltip>
+ </template>
+ <PurchaseSourceList :batch-code="detailData.code" />
+ </Tabs.TabPane>
+ <Tabs.TabPane key="sales">
+ <template #tab>
+ <Tooltip title="鎴愬搧渚у幓鍚戯細璇ユ壒娆″搴旂殑閿�鍞嚭搴撳崟涓� OQC 鍑哄巶妫�楠岃褰�">
+ <span>閿�鍞幓鍚�</span>
+ </Tooltip>
+ </template>
+ <SalesTargetList :batch-code="detailData.code" />
+ </Tabs.TabPane>
+ <Tabs.TabPane key="process">
+ <template #tab>
+ <Tooltip title="鍚勫伐搴忕殑鎶曞叆/浜у嚭浜嬩欢锛氳鎵规鍦ㄥ摢浜涘伐搴忚鎶曞叆鎴栦骇鍑猴紝鍙婃暟閲忓拰璐ㄩ噺鐘舵��">
+ <span>宸ュ簭娴佽浆</span>
+ </Tooltip>
+ </template>
+ <ProcessTraceList :batch-code="detailData.code" />
+ </Tabs.TabPane>
</Tabs>
</Modal>
</template>
--
Gitblit v1.9.3