From 8a9338f2f43bfe936124feaaa22420b4ca86eb84 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期一, 19 十月 2026 11:31:24 +0800
Subject: [PATCH] feat(qc): 完善质检流程功能
---
src/views/mes/qc/oqc/index.vue | 129 +++++++++++++++++++++++++++++++++++++++++-
1 files changed, 124 insertions(+), 5 deletions(-)
diff --git a/src/views/mes/qc/oqc/index.vue b/src/views/mes/qc/oqc/index.vue
index 965cfda..92d4cea 100644
--- a/src/views/mes/qc/oqc/index.vue
+++ b/src/views/mes/qc/oqc/index.vue
@@ -1,19 +1,95 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesQcOqcApi } from '#/api/mes/qc/oqc';
+import type { OqcAction } from '#/api/mes/qc/oqc';
+
+import { ref } from 'vue';
+import { useAccess } from '@vben/access';
import { Page, useVbenModal } from '@vben/common-ui';
-import { MesQcStatusEnum } from '@vben/constants';
+import { MesBizTypeEnum, MesQcStatusEnum } from '@vben/constants';
import { downloadFileFromBlobPart } from '@vben/utils';
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 { approveOqc, deleteOqc, exportOqc, getOqcActions, getOqcPage, retestOqc } from '#/api/mes/qc/oqc';
import { $t } from '#/locales';
import { useGridColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';
+
+const props = withDefaults(defineProps<{ salesMode?: boolean }>(), { salesMode: false });
+const { hasAccessByCodes } = useAccess();
+const workflowLabels: Record<number, string> = {
+ 0: '鑽夌', 10: '寰呭妫�', 20: '寰呴攢鍞儴瀹℃壒', 30: '寰呭叕鍙歌川妫�瀹℃壒',
+ 40: '宸叉斁琛�', 50: '寰呴攢鍞儴涓嶅悎鏍艰瘎瀹�', 60: '寰呭叕鍙镐笉鍚堟牸瀹℃壒', 70: '宸叉嫆缁濆嚭璐�',
+};
+const active = ref<MesQcOqcApi.Oqc>();
+const retestOpen = ref(false);
+const retestBusy = ref(false);
+const retestForm = ref({ checkQuantity: 0, qualifiedQuantity: 0, unqualifiedQuantity: 0, opinion: '' });
+function openRetest(row: MesQcOqcApi.Oqc) {
+ active.value = row;
+ retestForm.value = {
+ checkQuantity: Number(row.checkQuantity ?? 0), qualifiedQuantity: Number(row.qualifiedQuantity ?? 0),
+ unqualifiedQuantity: Number(row.unqualifiedQuantity ?? 0), opinion: '',
+ };
+ retestOpen.value = true;
+}
+async function saveRetest() {
+ if (!active.value?.id) return;
+ const form = retestForm.value;
+ if (form.checkQuantity < 0 || form.qualifiedQuantity < 0 || form.unqualifiedQuantity < 0
+ || form.checkQuantity !== form.qualifiedQuantity + form.unqualifiedQuantity) {
+ message.warning('妫�娴嬫暟閲忓繀椤荤瓑浜庡悎鏍间笌涓嶅悎鏍兼暟閲忎箣鍜�');
+ return;
+ }
+ if (form.unqualifiedQuantity > 0 && !form.opinion.trim()) {
+ message.warning('澶嶆涓嶅悎鏍艰濉啓鍘熷洜');
+ return;
+ }
+ retestBusy.value = true;
+ try {
+ await retestOqc({ id: active.value.id, ...form, checkResult: form.unqualifiedQuantity === 0 ? 1 : 2 });
+ message.success('澶嶆缁撴灉宸叉彁浜わ紝寰呴攢鍞儴璇勫');
+ retestOpen.value = false;
+ handleRefresh();
+ } catch (error) {
+ message.error((error as Error).message || '澶嶆澶辫触');
+ } finally { retestBusy.value = false; }
+}
+const reviewOpen = ref(false);
+const reviewBusy = ref(false);
+const reviewStage = ref<'sales' | 'company'>('sales');
+const reviewApproved = ref(true);
+const reviewOpinion = ref('');
+function openReview(row: MesQcOqcApi.Oqc, stage: 'sales' | 'company') {
+ active.value = row; reviewStage.value = stage; reviewApproved.value = true;
+ reviewOpinion.value = ''; reviewOpen.value = true;
+}
+async function saveReview() {
+ if (!active.value?.id) return;
+ if (!reviewOpinion.value.trim()) { message.warning('璇峰~鍐欏鎵规剰瑙�'); return; }
+ reviewBusy.value = true;
+ try {
+ await approveOqc(reviewStage.value, { id: active.value.id, approved: reviewApproved.value, opinion: reviewOpinion.value.trim() });
+ message.success('瀹℃壒瀹屾垚'); reviewOpen.value = false; handleRefresh();
+ } catch (error) { message.error((error as Error).message || '瀹℃壒澶辫触'); }
+ finally { reviewBusy.value = false; }
+}
+const historyOpen = ref(false);
+const historyActions = ref<OqcAction[]>([]);
+const actionLabels: Record<string, string> = {
+ INITIAL: '鍒濇', RETEST: '澶嶆', SALES_APPROVE: '閿�鍞儴閫氳繃', SALES_REJECT: '閿�鍞儴椹冲洖',
+ COMPANY_APPROVE: '鍏徃璐ㄦ閫氳繃', COMPANY_REJECT: '鍏徃璐ㄦ椹冲洖',
+};
+async function openHistory(row: MesQcOqcApi.Oqc) {
+ if (!row.id) return;
+ active.value = row;
+ try { historyActions.value = await getOqcActions(row.id); historyOpen.value = true; }
+ catch (error) { message.error((error as Error).message || '璇诲彇娴佺▼璁板綍澶辫触'); }
+}
const [FormModal, formModalApi] = useVbenModal({
connectedComponent: Form,
@@ -58,15 +134,23 @@
/** 瀵煎嚭琛ㄦ牸 */
async function handleExport() {
const data = await exportOqc(await gridApi.formApi.getValues());
- downloadFileFromBlobPart({ fileName: '鍑鸿揣妫�楠屽崟.xls', source: data });
+ downloadFileFromBlobPart({ fileName: props.salesMode ? '閿�鍞川妫�鍗�.xls' : '鍑鸿揣妫�楠屽崟.xls', source: data });
}
+const columns = useGridColumns();
+if (props.salesMode && columns) {
+ columns.splice(columns.length - 1, 0, {
+ field: 'workflowStatus', title: '閿�鍞川妫�娴佺▼', width: 170, slots: { default: 'workflow' },
+ });
+ const actions = columns.at(-1);
+ if (actions) actions.width = 320;
+}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
- columns: useGridColumns(),
+ columns,
height: 'auto',
keepSource: true,
proxyConfig: {
@@ -76,6 +160,7 @@
pageNo: page.currentPage,
pageSize: page.pageSize,
...formValues,
+ sourceDocType: props.salesMode ? MesBizTypeEnum.WM_PRODUCT_SALES : undefined,
});
},
},
@@ -95,7 +180,7 @@
<template>
<Page auto-content-height><FormModal @success="handleRefresh" />
- <Grid table-title="鍑鸿揣妫�楠屽崟鍒楄〃">
+ <Grid :table-title="props.salesMode ? '閿�鍞川妫�鍗曞垪琛�' : '鍑鸿揣妫�楠屽崟鍒楄〃'">
<template #toolbar-tools>
<TableAction
:actions="[
@@ -120,6 +205,9 @@
<Button type="link" @click="handleDetail(row)">
{{ row.code }}
</Button>
+ </template>
+ <template #workflow="{ row }">
+ <span>{{ workflowLabels[row.workflowStatus ?? 0] || '鏈煡鐘舵��' }}</span>
</template>
<template #actions="{ row }">
<TableAction
@@ -153,7 +241,38 @@
},
]"
/>
+ <Button v-if="props.salesMode && row.workflowStatus === 10 && hasAccessByCodes(['mes:qc-oqc:retest'])" type="link" @click="openRetest(row)">澶嶆</Button>
+ <Button v-if="props.salesMode && [20, 50].includes(row.workflowStatus ?? 0) && hasAccessByCodes(['mes:qc-oqc:sales-approve'])" type="link" @click="openReview(row, 'sales')">閿�鍞儴瀹℃壒</Button>
+ <Button v-if="props.salesMode && [30, 60].includes(row.workflowStatus ?? 0) && hasAccessByCodes(['mes:qc-oqc:company-approve'])" type="link" @click="openReview(row, 'company')">鍏徃璐ㄦ瀹℃壒</Button>
+ <Button v-if="props.salesMode && hasAccessByCodes(['mes:qc-oqc:query'])" type="link" @click="openHistory(row)">娴佺▼璁板綍</Button>
</template>
</Grid>
+ <a-modal v-if="props.salesMode" v-model:open="retestOpen" title="閿�鍞川妫�澶嶆" :confirm-loading="retestBusy" ok-text="鎻愪氦澶嶆" @ok="saveRetest">
+ <p>妫�楠屽崟锛歿{ active?.code }}</p>
+ <a-form layout="vertical">
+ <a-form-item label="鏈妫�娴嬫暟閲�"><a-input-number v-model:value="retestForm.checkQuantity" :min="0" style="width: 100%" /></a-form-item>
+ <a-form-item label="鍚堟牸鏁伴噺"><a-input-number v-model:value="retestForm.qualifiedQuantity" :min="0" style="width: 100%" /></a-form-item>
+ <a-form-item label="涓嶅悎鏍兼暟閲�"><a-input-number v-model:value="retestForm.unqualifiedQuantity" :min="0" style="width: 100%" /></a-form-item>
+ <a-form-item label="澶嶆鎰忚"><a-textarea v-model:value="retestForm.opinion" :rows="3" :maxlength="500" /></a-form-item>
+ </a-form>
+ </a-modal>
+ <a-modal v-if="props.salesMode" v-model:open="reviewOpen" :title="reviewStage === 'sales' ? '閿�鍞儴瀹℃壒' : '鍏徃璐ㄦ瀹℃壒'" :confirm-loading="reviewBusy" ok-text="鎻愪氦瀹℃壒" @ok="saveReview">
+ <p>{{ active?.code }} 路 {{ workflowLabels[active?.workflowStatus ?? 0] }}</p>
+ <a-radio-group v-model:value="reviewApproved" style="display: flex; gap: 16px; margin: 12px 0 18px">
+ <a-radio :value="true">鍚屾剰{{ reviewStage === 'company' ? active?.workflowStatus === 30 ? '鍑鸿揣鏀捐' : '涓嶅悎鏍煎缃�' : '涓婃姤鍏徃璐ㄦ' }}</a-radio>
+ <a-radio :value="false">椹冲洖閲嶆</a-radio>
+ </a-radio-group>
+ <a-textarea v-model:value="reviewOpinion" :rows="3" :maxlength="500" placeholder="璇峰~鍐欏鎵规剰瑙�" />
+ </a-modal>
+ <a-modal v-if="props.salesMode" v-model:open="historyOpen" :title="`娴佺▼璁板綍 路 ${active?.code || ''}`" :footer="null">
+ <a-timeline v-if="historyActions.length">
+ <a-timeline-item v-for="action in historyActions" :key="action.id">
+ <strong>{{ actionLabels[action.action] || action.action }}</strong>
+ <span> 路 {{ action.createTime || '鈥�' }} 路 鎿嶄綔浜� {{ action.operatorId || '鈥�' }}</span>
+ <div v-if="action.opinion">{{ action.opinion }}</div>
+ </a-timeline-item>
+ </a-timeline>
+ <a-empty v-else description="鏆傛棤娴佺▼璁板綍" />
+ </a-modal>
</Page>
</template>
--
Gitblit v1.9.3