From ee7ac8df8bce38add1f75baf876b49de94b9e00a Mon Sep 17 00:00:00 2001
From: liu <2021943741@qq.com>
Date: 星期五, 16 十月 2026 17:58:33 +0800
Subject: [PATCH] feat(mes): 赋码管理绑定托盘支持未录入批次的袋码
---
src/views/mes/pro/bagCode/index.vue | 256 +++++++++++++++++++++++++++++++++++++++++---------
1 files changed, 208 insertions(+), 48 deletions(-)
diff --git a/src/views/mes/pro/bagCode/index.vue b/src/views/mes/pro/bagCode/index.vue
index f4ffa22..6bd6b3a 100644
--- a/src/views/mes/pro/bagCode/index.vue
+++ b/src/views/mes/pro/bagCode/index.vue
@@ -6,22 +6,32 @@
import { MesProBagCodeStatusEnum } from "@vben/constants";
import { downloadFileFromBlobPart } from "@vben/utils";
- import { message } from "ant-design-vue";
+ import { Button, message, Modal } from "ant-design-vue";
import { ACTION_ICON, TableAction, useVbenVxeGrid } from "#/adapter/vxe-table";
import {
deleteBagCodesByUuid,
exportBagCodeExcel,
exportBagQr,
+ exportBagBusinessQr,
+ generateBagBusinessCodes,
getBagCodePage,
+ bindBagCodeBatch,
unbindBagCodePallet,
} from "#/api/mes/pro/bagCode";
+ import { IconifyIcon } from "@vben/icons";
import { $t } from "#/locales";
import { useGridColumns, useGridFormSchema } from "./data";
+ import BindBatchModal from "./modules/bind-batch.vue";
import BindPalletModal from "./modules/bind-pallet.vue";
import GenerateModal from "./modules/generate-form.vue";
+ import RawGenerateModal from "./modules/raw-generate-form.vue";
+ import ReplaceModal from "./modules/replace-form.vue";
import QrTraceModal from "../components/qr-trace-modal.vue";
+ import BusinessQrModal from "../components/business-qr.vue";
+ import QrExportProgress from "../qrexport/qr-export-progress.vue";
+ import { useQrExport } from "../qrexport/use-qr-export";
defineOptions({ name: "MesProBagCode" });
@@ -30,8 +40,23 @@
destroyOnClose: true,
});
+ const [RawGenerateFormModal, rawGenerateFormModalApi] = useVbenModal({
+ connectedComponent: RawGenerateModal,
+ destroyOnClose: true,
+ });
+
+ const [BindBatchFormModal, bindBatchFormModalApi] = useVbenModal({
+ connectedComponent: BindBatchModal,
+ destroyOnClose: true,
+ });
+
const [BindPalletFormModal, bindPalletFormModalApi] = useVbenModal({
connectedComponent: BindPalletModal,
+ destroyOnClose: true,
+ });
+
+ const [ReplaceFormModal, replaceFormModalApi] = useVbenModal({
+ connectedComponent: ReplaceModal,
destroyOnClose: true,
});
@@ -40,14 +65,50 @@
destroyOnClose: true,
});
+ const [BusinessQrViewModal, businessQrApi] = useVbenModal({
+ connectedComponent: BusinessQrModal, destroyOnClose: true,
+ });
+
+ function handleBusinessQr(row: MesProBagCodeApi.BagCode) {
+ if (!row.businessCode) { message.warning('璇峰厛琛ョ敓鎴愪笟鍔$爜'); return; }
+ businessQrApi.setData({ code: row.businessCode }).open();
+ }
+
+ async function handleGenerateBusinessCodes() {
+ const records = getSelectedRecords();
+ if (!records) return;
+ if (records.length > 200) { message.warning('鍗曟鏈�澶氳ˉ鐢熸垚 200 鏉�'); return; }
+ if (records.some(row => row.status === MesProBagCodeStatusEnum.CANCELED)) {
+ message.warning('宸蹭綔搴熻鐮佷笉鍙ˉ鐢熸垚涓氬姟鐮�'); return;
+ }
+ const ids = records.map(row => row.id).filter((id): id is number => id != null);
+ if (ids.length !== records.length) { message.warning('鎵�閫夎鐮佺己灏戠紪鍙�'); return; }
+ const count = await generateBagBusinessCodes(ids);
+ message.success(`宸茶ˉ鐢熸垚 ${count} 浠戒笟鍔$爜锛屽凡鏈夌爜鍊间繚鎸佷笉鍙榒);
+ handleRefresh();
+ }
+
/** 鍒锋柊琛ㄦ牸 */
function handleRefresh() {
gridApi.query();
}
+ const { start: startQrExport } = useQrExport();
+
/** 鐢熸垚鏁版嵁鍖� */
function handleGenerate() {
generateFormModalApi.open();
+ }
+
+ /** 鐢熸垚鍘熷琚嬬爜 */
+ function handleRawGenerate() {
+ rawGenerateFormModalApi.open();
+ }
+
+ /** 鏇挎崲鎴愬姛鍚庡睍绀虹粨鏋滃苟鍒锋柊鍒楄〃 */
+ function handleReplaceSuccess(result: MesProBagCodeApi.ReplaceResult) {
+ showReplaceResult(result);
+ handleRefresh();
}
/** 鎵爜杩芥函 */
@@ -61,7 +122,7 @@
}
/** 瀵煎嚭浜岀淮鐮� ZIP锛堟寜褰撳墠鏌ヨ鏉′欢鐨勭敓鎴愮粍/鎵规锛� */
- async function handleExportQr() {
+ async function handleExportQr(kind: 'trace' | 'business' = 'trace') {
const values = await gridApi.formApi.getValues();
const params: { uuid?: string; batchId?: number } = {};
if (values.uuid) {
@@ -74,8 +135,24 @@
message.warning("璇峰厛鎸夌敓鎴愮粍鎴栨壒娆$瓫閫夊悗鍐嶅鍑轰簩缁寸爜");
return;
}
- const data = await exportBagQr(params);
- downloadFileFromBlobPart({ fileName: "琚嬬爜浜岀淮鐮�.zip", source: data });
+ // 鏁伴噺灏戝垯鍚屾绉掑嚭锛涜秴杩囧垎鐣岀嚎鑷姩杞悗鍙颁换鍔★紝鐢辫繘搴︽诞灞傚睍绀虹湡瀹炶繘搴﹀苟鏀寔鍙栨秷/闅愯棌
+ const bizType = kind === "business" ? "BAG_BUSINESS" : "BAG_LABEL";
+ const fileName = kind === "business" ? "琚嬬爜涓氬姟浜岀淮鐮�" : "琚嬬爜杩芥函浜岀淮鐮�";
+ await startQrExport(
+ { bizType, ...params },
+ {
+ fileName,
+ syncDownload: async () => {
+ const data = await (kind === "business"
+ ? exportBagBusinessQr
+ : exportBagQr)(params);
+ downloadFileFromBlobPart({
+ fileName: `${fileName}.zip`,
+ source: data,
+ });
+ },
+ },
+ );
}
/** 瀵煎嚭琚嬬爜 Excel锛堜氦浠樺嵃鍒凤級锛屽鍑哄悗琚嬬爜鐘舵�佸彉涓哄凡鍗板埛锛岄渶鍒锋柊鍒楄〃 */
@@ -88,19 +165,107 @@
/** 鑾峰彇鍕鹃�夌殑琚嬬爜璁板綍 */
function getSelectedRecords(): MesProBagCodeApi.BagCode[] | undefined {
- const records = gridApi.grid.getCheckboxRecords() as MesProBagCodeApi.BagCode[];
- if (records.length === 0) {
+ const records = [
+ ...(gridApi.grid.getCheckboxReserveRecords?.() ?? []),
+ ...(gridApi.grid.getCheckboxRecords?.() ?? []),
+ ] as MesProBagCodeApi.BagCode[];
+ const uniqueRecords = [
+ ...new Map(records.map((record) => [record.id, record])).values(),
+ ];
+ if (uniqueRecords.length === 0) {
message.warning("璇疯嚦灏戝嬀閫変竴鏉¤鐮佽褰�");
return undefined;
}
- return records;
+ return uniqueRecords;
+ }
+
+ /** 灞曠ず鏇挎崲缁撴灉 */
+ function showReplaceResult(result: MesProBagCodeApi.ReplaceResult) {
+ const items = result.items ?? [];
+ const content = items.length
+ ? items
+ .map(
+ (item) =>
+ `${item.oldCode ?? "-"} 鈫� ${item.newCode ?? "-"}${
+ item.newPalletCode ? `锛堟墭鐩橈細${item.newPalletCode}锛塦 : ""
+ }`,
+ )
+ .join("\n")
+ : "鏇挎崲鏄庣粏鏆傛棤";
+ Modal.success({
+ title: "鐮存崯琚嬬爜鏇挎崲鎴愬姛",
+ content: `鎵规锛�${result.batchCode ?? "-"}\n鏇挎崲鏁伴噺锛�${
+ result.count ?? items.length
+ }\n${content}`,
+ centered: true,
+ });
+ }
+
+ /** 鎵归噺鏇挎崲鐮存崯琚嬬爜 */
+ async function handleReplace() {
+ const records = getSelectedRecords();
+ if (!records) return;
+ if (records.length > 200) {
+ message.warning("鍗曟鏈�澶氭浛鎹� 200 涓鐮�");
+ return;
+ }
+ const batchIds = new Set(records.map((record) => record.batchId));
+ if (batchIds.size > 1 || !records[0]?.batchId) {
+ message.warning("璇烽�夋嫨鍚屼竴鎵规涓嬬殑琚嬬爜杩涜鏇挎崲");
+ return;
+ }
+ if (
+ records.some(
+ (record) =>
+ record.status !== MesProBagCodeStatusEnum.GENERATED &&
+ record.status !== MesProBagCodeStatusEnum.PRINTED,
+ )
+ ) {
+ message.warning("浠呭凡鐢熸垚鎴栧凡瀵煎嚭鍗板埛鐨勮鐮佸厑璁告浛鎹�");
+ return;
+ }
+ if (records.some((record) => !record.id)) {
+ message.warning("鎵�閫夎鐮佺己灏戠紪鍙凤紝鏃犳硶鏇挎崲");
+ return;
+ }
+ try {
+ await confirm(`纭浣滃簾鎵�閫� ${records.length} 涓鐮佸苟鐢熸垚绛夐噺鏂扮爜鍚楋紵`);
+ } catch {
+ return;
+ }
+ const first = records[0]!;
+ replaceFormModalApi
+ .setData({
+ batchId: first.batchId!,
+ batchCode: first.batchCode,
+ ids: records.map((record) => record.id!),
+ })
+ .open();
+ }
+
+ /** 鎵归噺褰曞叆鐢熶骇鎵规 */
+ function handleBindBatch() {
+ const records = getSelectedRecords();
+ if (!records) return;
+ if (records.some((record) => record.status !== MesProBagCodeStatusEnum.GENERATED || record.batchId)) {
+ message.warning("璇烽�夋嫨鏈綍鍏ユ壒娆′笖鐘舵�佷负宸茬敓鎴愮殑琚嬬爜");
+ return;
+ }
+ bindBatchFormModalApi.setData({ ids: records.map((record) => record.id!) }).open();
}
/** 鎵归噺缁戝畾鎵樼洏 */
function handleBindPallet() {
const records = getSelectedRecords();
if (!records) return;
- const batchIds = new Set(records.map((record) => record.batchId));
+ // 宸插綍鍏ユ壒娆$殑琚嬬爜鍙兘缁戝埌鏈壒娆$殑鎵樼洏锛涙湭褰曞叆鎵规鐨勮鐮佸彲缁戜换鎰忔墭鐩橈紙閫夊埌姝e紡鎵樼洏鏃跺悗绔細鍏堣ˉ褰曟壒娆★級銆�
+ // 涓よ�呮贩閫夋椂鎵规褰掑睘涓嶆槑纭紝瑕佹眰鍒嗗紑鎿嶄綔銆�
+ const boundRecords = records.filter((record) => record.batchId);
+ if (boundRecords.length > 0 && boundRecords.length < records.length) {
+ message.warning("鎵�閫夎鐮佷腑鏃㈡湁宸插綍鍏ユ壒娆°�佷篃鏈夋湭褰曞叆鎵规鐨勮褰曪紝璇峰垎寮�缁戝畾");
+ return;
+ }
+ const batchIds = new Set(boundRecords.map((record) => record.batchId));
if (batchIds.size > 1) {
message.warning("璇烽�夋嫨鍚屼竴鎵规涓嬬殑琚嬬爜杩涜缁戝畾");
return;
@@ -122,6 +287,7 @@
.setData({
batchId: first.batchId,
batchCode: first.batchCode,
+ itemId: first.itemId,
ids: records.map((record) => record.id!),
})
.open();
@@ -199,57 +365,45 @@
<template>
<Page auto-content-height>
<GenerateFormModal @success="handleRefresh" />
+ <RawGenerateFormModal @success="handleRefresh" />
+ <BindBatchFormModal @success="handleRefresh" />
<BindPalletFormModal @success="handleRefresh" />
+ <ReplaceFormModal @success="handleReplaceSuccess" />
<QrTraceQueryModal />
+ <BusinessQrViewModal />
+ <QrExportProgress />
<Grid table-title="琚嬬爜鍒楄〃锛堜竴琚嬩竴鐮侊級">
+ <template #table-title><span class="text-[1rem] font-bold" style="white-space:nowrap">琚嬬爜鍒楄〃锛堜竴琚嬩竴鐮侊級</span></template>
<template #toolbar-tools>
- <TableAction :actions="[
- {
- label: '鐢熸垚鏁版嵁鍖�',
- type: 'primary',
- icon: ACTION_ICON.ADD,
- auth: ['mes:pro-bag-code:create'],
- onClick: handleGenerate,
- },
- {
- label: '缁戝畾鎵樼洏',
- type: 'primary',
- auth: ['mes:pro-bag-code:update'],
- onClick: handleBindPallet,
- },
- {
- label: '瑙g粦鎵樼洏',
- auth: ['mes:pro-bag-code:update'],
- onClick: handleUnbindPallet,
- },
- {
- label: '鎵爜杩芥函',
- icon: ACTION_ICON.SEARCH,
- auth: ['mes:pro-bag-code:query'],
- onClick: handleTrace,
- },
- {
- label: '瀵煎嚭浜岀淮鐮�',
- icon: ACTION_ICON.DOWNLOAD,
- auth: ['mes:pro-bag-code:export'],
- onClick: handleExportQr,
- },
- {
- label: $t('ui.actionTitle.export'),
- type: 'primary',
- icon: ACTION_ICON.DOWNLOAD,
- auth: ['mes:pro-bag-code:export'],
- onClick: handleExport,
- },
- ]" />
+ <div class="mes-code-toolbar">
+ <TableAction :actions="[{ label: '鐢熸垚鏁版嵁鍖�', type: 'primary', icon: ACTION_ICON.ADD, auth: ['mes:pro-bag-code:create'], onClick: handleGenerate },
+ { label: '褰曞叆鎵规', auth: ['mes:pro-bag-code:update'], onClick: handleBindBatch },
+ { label: '缁戝畾鎵樼洏', auth: ['mes:pro-bag-code:update'], onClick: handleBindPallet },
+ { label: '鎵爜杩芥函', icon: ACTION_ICON.SEARCH, auth: ['mes:pro-bag-code:query'], onClick: handleTrace }]" />
+ <TableAction :dropdown-trigger="['click']" :drop-down-actions="[{ label: '杩芥函浜岀淮鐮�', auth: ['mes:pro-bag-code:export'], onClick: () => handleExportQr('trace') },
+ { label: '涓氬姟浜岀淮鐮�', auth: ['mes:pro-bag-code:export'], onClick: () => handleExportQr('business') },
+ { label: 'Excel 鏁版嵁鍖�', auth: ['mes:pro-bag-code:export'], onClick: handleExport }]">
+ <template #more><Button>瀵煎嚭 <IconifyIcon icon="lucide:chevron-down" /></Button></template>
+ </TableAction>
+ <TableAction :dropdown-trigger="['click']" :drop-down-actions="[{ label: '鐢熸垚鍘熷琚嬬爜', auth: ['mes:pro-bag-code:create'], onClick: handleRawGenerate },
+ { label: '琛ョ敓鎴愪笟鍔$爜', auth: ['mes:pro-bag-code:create'], onClick: handleGenerateBusinessCodes },
+ { label: '鐮存崯鏇挎崲', auth: ['mes:pro-bag-code:replace'], onClick: handleReplace },
+ { label: '瑙g粦鎵樼洏', auth: ['mes:pro-bag-code:update'], onClick: handleUnbindPallet }]">
+ <template #more><Button>鏇村鎿嶄綔 <IconifyIcon icon="lucide:chevron-down" /></Button></template>
+ </TableAction>
+ </div>
</template>
<template #actions="{ row }">
<TableAction :actions="[
{
- label: '鏌ョ湅浜岀淮鐮�',
+ label: '杩芥函浜岀淮鐮�',
type: 'link',
auth: ['mes:pro-bag-code:query'],
onClick: handleViewQr.bind(null, row),
+ },
+ {
+ label: '涓氬姟浜岀淮鐮�', type: 'link', auth: ['mes:pro-bag-code:query'],
+ ifShow: !!row.businessCode, onClick: handleBusinessQr.bind(null, row),
},
{
label: $t('common.delete'),
@@ -271,3 +425,9 @@
</Grid>
</Page>
</template>
+
+<style scoped>
+.mes-code-toolbar { display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:8px; }
+.mes-code-toolbar :deep(.ant-btn) { white-space:nowrap; }
+.mes-code-toolbar :deep(.ant-btn > .iconify) { display:inline-block;width:14px;height:14px;vertical-align:middle; }
+</style>
--
Gitblit v1.9.3