From 5d321660a2f1a4b8756f816283e96481a8c934e4 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期三, 28 十月 2026 14:26:30 +0800
Subject: [PATCH] feat(app): 更新应用名称并优化批次绑定功能
---
src/views/mes/pro/batch/modules/form.vue | 322 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 314 insertions(+), 8 deletions(-)
diff --git a/src/views/mes/pro/batch/modules/form.vue b/src/views/mes/pro/batch/modules/form.vue
index 2e45243..1ef766d 100644
--- a/src/views/mes/pro/batch/modules/form.vue
+++ b/src/views/mes/pro/batch/modules/form.vue
@@ -1,11 +1,11 @@
<script lang="ts" setup>
- import type { FormType } from "../data";
+ import type { FormType, PalletOption, TonBagOption } from "../data";
import type { VxeTableGridOptions } from "#/adapter/vxe-table";
import type { MesProBatchApi } from "#/api/mes/pro/batch";
import type { SystemUserApi } from "#/api/system/user";
- import { computed, nextTick, ref } from "vue";
+ import { computed, nextTick, reactive, ref } from "vue";
import { useVbenModal } from "@vben/common-ui";
import { DICT_TYPE } from "@vben/constants";
@@ -15,11 +15,20 @@
import { useVbenForm } from "#/adapter/form";
import { TableAction, useVbenVxeGrid } from "#/adapter/vxe-table";
- import { createBatch, getBatch, updateBatch } from "#/api/mes/pro/batch";
+ import {
+ createBatch,
+ fillBatchInfo,
+ getBatch,
+ updateBatch,
+ } from "#/api/mes/pro/batch";
+ import { getUnboundPalletPage } from "#/api/mes/pro/pallet";
+ import { getUnboundTonBagPage } from "#/api/mes/pro/bagCode";
import { $t } from "#/locales";
import { UserSelect } from "#/views/system/user/components";
import {
+ applyPalletOptionState,
+ applyTonBagOptionState,
useFormSchema,
useWorkerEditGridColumns,
useWorkerReadGridColumns,
@@ -31,9 +40,273 @@
const formType = ref<FormType>("create");
const formData = ref<MesProBatchApi.Batch>();
+ /** 缁戝畾涓嬫媺姣忔鍔犺浇鐨勬潯鏁� */
+ const BINDING_PAGE_SIZE = 50;
+ /** 璺濈涓嬫媺搴曢儴澶氬皯鍍忕礌鏃惰Е鍙戝姞杞戒笅涓�椤� */
+ const BINDING_SCROLL_THRESHOLD = 32;
+ /** 杈撳叆妗嗘悳绱㈤槻鎶栨椂闂达紙姣锛� */
+ const BINDING_SEARCH_DEBOUNCE = 300;
+
+ /**
+ * 缁戝畾涓嬫媺鐨勫垎椤靛姞杞界姸鎬併��
+ *
+ * 涓嬫媺閫夐」鏈韩灏辨槸娓叉煋鏁版嵁婧愶紙淇濇寔鏁扮粍寮曠敤涓嶅彉锛岀洿鎺� splice 鏇存柊锛夛紝
+ * 宸插姞杞界殑鍒嗛〉缁撴灉鏀惧湪 loaded 涓紝璺ㄩ〉閫変腑鐨勯�夐」鍗曠嫭璁板綍锛岄伩鍏嶆崲椤垫悳绱㈠悗鏍囩涓㈠け銆�
+ */
+ interface BindingLoader<T extends PalletOption> {
+ hasMore: boolean;
+ keyword: string;
+ loaded: T[];
+ loading: boolean;
+ pageNo: number;
+ seq: number;
+ timer?: ReturnType<typeof setTimeout>;
+ }
+
+ function createBindingLoader<T extends PalletOption>(): BindingLoader<T> {
+ return {
+ hasMore: true,
+ keyword: "",
+ loaded: [],
+ loading: false,
+ pageNo: 0,
+ seq: 0,
+ };
+ }
+
+ const palletOptions = reactive<PalletOption[]>([]);
+ const tonBagOptions = reactive<TonBagOption[]>([]);
+ const palletSelected = new Map<number, PalletOption>();
+ const tonBagSelected = new Map<number, TonBagOption>();
+ const palletLoader = createBindingLoader<PalletOption>();
+ const tonBagLoader = createBindingLoader<TonBagOption>();
+
+ /** 宸插姞杞藉垎椤电粨鏋� + 璺ㄩ〉閫変腑椤瑰悎骞朵负涓嬫媺娓叉煋鏁版嵁 */
+ function renderBindingOptions<T extends PalletOption>(
+ target: T[],
+ loader: BindingLoader<T>,
+ selected: Map<number, T>
+ ) {
+ const merged = [...loader.loaded];
+ const loadedIds = new Set(loader.loaded.map((option) => option.value));
+ selected.forEach((option, id) => {
+ if (!loadedIds.has(id)) merged.push(option);
+ });
+ target.splice(0, target.length, ...merged);
+ }
+
+ /** 鍚屾閫変腑椤癸紙鎹㈤〉鎼滅储鍚庝粛闇�灞曠ず宸查�夋爣绛撅級 */
+ function syncBindingSelection<T extends PalletOption>(
+ target: T[],
+ loader: BindingLoader<T>,
+ selected: Map<number, T>,
+ ids: number[]
+ ) {
+ const previous = new Map(selected);
+ selected.clear();
+ ids.forEach((id) => {
+ const option = previous.get(id)
+ ?? loader.loaded.find((item) => item.value === id);
+ if (option) selected.set(id, option);
+ });
+ renderBindingOptions(target, loader, selected);
+ }
+
+ /** 閫夐」绂佺敤鎬佷緷璧栧綋鍓嶈〃鍗曞�硷紝姣忔鍔犺浇鏂颁竴椤靛悗閲嶆柊璁$畻 */
+ async function refreshBindingOptionState() {
+ const values = await formApi.getValues();
+ const existingBinding = formData.value?.bindingMode;
+ applyPalletOptionState(palletOptions, values, existingBinding);
+ applyTonBagOptionState(tonBagOptions, values, existingBinding);
+ }
+
+ /** 鎷夊彇涓�椤电粦瀹氬�欓�夐」锛況eset=true 琛ㄧず浠庣涓�椤甸噸鏂板紑濮嬶紙棣栨鎵撳紑銆佹悳绱級 */
+ async function loadBindingPage<T extends PalletOption>(
+ loader: BindingLoader<T>,
+ target: T[],
+ selected: Map<number, T>,
+ fetchPage: (params: {
+ keyword?: string;
+ pageNo: number;
+ pageSize: number;
+ }) => Promise<{ list: T[]; total: number }>,
+ reset: boolean
+ ) {
+ if (!reset && (loader.loading || !loader.hasMore)) return;
+ const seq = ++loader.seq;
+ loader.loading = true;
+ try {
+ const pageNo = reset ? 1 : loader.pageNo + 1;
+ const page = await fetchPage({
+ keyword: loader.keyword || undefined,
+ pageNo,
+ pageSize: BINDING_PAGE_SIZE,
+ });
+ // 宸茶鏇存柊鐨勬悳绱㈡垨骞跺彂璇锋眰瑕嗙洊锛屼涪寮冩湰娆$粨鏋�
+ if (seq !== loader.seq) return;
+ loader.loaded = reset ? page.list : [...loader.loaded, ...page.list];
+ loader.pageNo = pageNo;
+ loader.hasMore = loader.loaded.length < page.total;
+ renderBindingOptions(target, loader, selected);
+ await refreshBindingOptionState();
+ } finally {
+ if (seq === loader.seq) loader.loading = false;
+ }
+ }
+
+ function fetchPalletPage(params: {
+ keyword?: string;
+ pageNo: number;
+ pageSize: number;
+ }) {
+ return getUnboundPalletPage(params).then((page) => ({
+ list: page.list.map((pallet) => ({
+ label: `${pallet.tempCode ?? `鎵樼洏${pallet.id}`} 路 ${pallet.itemName || "寰呭畬鍠勫搧绉�"}锛堝凡瑁� ${pallet.bagCount ?? 0} 琚嬶級`,
+ value: pallet.id as number,
+ itemId: pallet.itemId,
+ itemName: pallet.itemName,
+ itemCode: pallet.itemCode,
+ })),
+ total: page.total,
+ }));
+ }
+
+ function fetchTonBagPage(params: {
+ keyword?: string;
+ pageNo: number;
+ pageSize: number;
+ }) {
+ return getUnboundTonBagPage(params).then((page) => ({
+ list: page.list.map((bag) => ({
+ label: `${bag.code ?? `鍚ㄨ${bag.id}`} 路 ${bag.itemName || "寰呭畬鍠勫搧绉�"}`,
+ value: bag.id as number,
+ itemId: bag.itemId,
+ itemName: bag.itemName,
+ itemCode: bag.itemCode,
+ })),
+ total: page.total,
+ }));
+ }
+
+ function loadPalletPage(reset: boolean) {
+ return loadBindingPage(palletLoader, palletOptions, palletSelected, fetchPalletPage, reset);
+ }
+
+ function loadTonBagPage(reset: boolean) {
+ return loadBindingPage(tonBagLoader, tonBagOptions, tonBagSelected, fetchTonBagPage, reset);
+ }
+
+ /** 閲嶇疆涓嬫媺鍒嗛〉鐘舵�侊紙姣忔鎵撳紑寮圭獥璋冪敤锛夛紝鍚屾椂璁╁湪閫旇姹傜殑缁撴灉澶辨晥 */
+ function resetBindingLoader<T extends PalletOption>(
+ loader: BindingLoader<T>,
+ target: T[],
+ selected: Map<number, T>
+ ) {
+ clearTimeout(loader.timer);
+ loader.seq += 1;
+ loader.hasMore = true;
+ loader.keyword = "";
+ loader.loaded = [];
+ loader.loading = false;
+ loader.pageNo = 0;
+ target.splice(0, target.length);
+ selected.clear();
+ }
+
+ function isPopupNearBottom(event: Event) {
+ const element = event.target as HTMLElement;
+ return element.scrollHeight - element.scrollTop - element.clientHeight <= BINDING_SCROLL_THRESHOLD;
+ }
+
+ function scheduleBindingSearch<T extends PalletOption>(
+ loader: BindingLoader<T>,
+ keyword: string,
+ reload: (reset: boolean) => Promise<void>
+ ) {
+ loader.keyword = keyword.trim();
+ clearTimeout(loader.timer);
+ loader.timer = setTimeout(() => void reload(true), BINDING_SEARCH_DEBOUNCE);
+ }
+
+ function handlePalletSearch(keyword: string) {
+ scheduleBindingSearch(palletLoader, keyword, loadPalletPage);
+ }
+
+ function handleTonBagSearch(keyword: string) {
+ scheduleBindingSearch(tonBagLoader, keyword, loadTonBagPage);
+ }
+
+ function handlePalletPopupScroll(event: Event) {
+ if (isPopupNearBottom(event)) void loadPalletPage(false);
+ }
+
+ function handleTonBagPopupScroll(event: Event) {
+ if (isPopupNearBottom(event)) void loadTonBagPage(false);
+ }
+
+ const palletHandlers = {
+ onChange: handlePalletChange,
+ onPopupScroll: handlePalletPopupScroll,
+ onSearch: handlePalletSearch,
+ };
+ const tonBagHandlers = {
+ onChange: handleTonBagChange,
+ onPopupScroll: handleTonBagPopupScroll,
+ onSearch: handleTonBagSearch,
+ };
+
+ async function handlePalletChange(ids: number[] = []) {
+ const values = await formApi.getValues();
+ if (ids.length && (values.tonBagIds?.length || formData.value?.bindingMode === 'tonBag')) {
+ message.warning('缁戝畾鎵樼洏鍜岀粦瀹氬惃琚嬪彧鑳介�夋嫨涓�绉�');
+ await formApi.setFieldValue('palletIds', []);
+ syncBindingSelection(palletOptions, palletLoader, palletSelected, []);
+ return;
+ }
+ const selected = ids
+ .map((id) => palletSelected.get(id) ?? palletLoader.loaded.find((option) => option.value === id))
+ .filter((option): option is PalletOption => !!option);
+ const source = formData.value?.itemId ? formData.value : selected[0];
+ let finalIds = ids;
+ if (selected.some(option => !option.itemId || (source?.itemId && option.itemId !== source.itemId))) {
+ message.warning('鐢熶骇鎵规鍙兘缁戝畾鐩稿悓鍝佺鐨勬墭鐩�');
+ finalIds = selected.filter(option => option.itemId === source?.itemId).map(option => option.value);
+ await formApi.setFieldValue('palletIds', finalIds);
+ }
+ syncBindingSelection(palletOptions, palletLoader, palletSelected, finalIds);
+ await refreshBindingOptionState();
+ await formApi.setFieldValue('itemId', source?.itemId);
+ await formApi.setFieldValue('itemName', source?.itemName);
+ await formApi.setFieldValue('itemCode', source?.itemCode);
+ }
+
+ async function handleTonBagChange(ids: number[] = []) {
+ const values = await formApi.getValues();
+ if (ids.length && (values.palletIds?.length || formData.value?.bindingMode === 'pallet')) {
+ message.warning('缁戝畾鎵樼洏鍜岀粦瀹氬惃琚嬪彧鑳介�夋嫨涓�绉�');
+ await formApi.setFieldValue('tonBagIds', []);
+ syncBindingSelection(tonBagOptions, tonBagLoader, tonBagSelected, []);
+ return;
+ }
+ const selected = ids
+ .map((id) => tonBagSelected.get(id) ?? tonBagLoader.loaded.find((option) => option.value === id))
+ .filter((option): option is TonBagOption => !!option);
+ const source = formData.value?.itemId ? formData.value : selected[0];
+ let finalIds = ids;
+ if (selected.some(option => option.itemId !== source?.itemId)) {
+ message.warning('鐢熶骇鎵规鍙兘缁戝畾鐩稿悓鍝佺鐨勫惃琚�');
+ finalIds = selected.filter(option => option.itemId === source?.itemId).map(option => option.value);
+ await formApi.setFieldValue('tonBagIds', finalIds);
+ }
+ syncBindingSelection(tonBagOptions, tonBagLoader, tonBagSelected, finalIds);
+ await refreshBindingOptionState();
+ await formApi.setFieldValue('itemId', source?.itemId);
+ await formApi.setFieldValue('itemName', source?.itemName);
+ await formApi.setFieldValue('itemCode', source?.itemCode);
+ }
const isEditable = computed(() =>
- ["create", "update"].includes(formType.value)
+ ["create", "update", "fill"].includes(formType.value)
);
const getTitle = computed(() => {
@@ -43,6 +316,9 @@
}
case "update": {
return $t("ui.actionTitle.edit", ["鐢熶骇鎵规"]);
+ }
+ case "fill": {
+ return "琛ュ綍鎵规淇℃伅";
}
default: {
return $t("ui.actionTitle.create", ["鐢熶骇鎵规"]);
@@ -82,8 +358,9 @@
let workerRowSeq = 0;
/** 娣诲姞浜哄憳琛� */
- function handleAddWorker() {
- workerGridApi.grid.insertAt({ _rowKey: -(++workerRowSeq) }, -1);
+ async function handleAddWorker() {
+ await waitWorkerGridReady();
+ await workerGridApi.grid.insertAt({ _rowKey: -(++workerRowSeq) }, -1);
}
/** 鍒犻櫎浜哄憳琛� */
@@ -177,7 +454,13 @@
modalApi.lock();
try {
const data = (await formApi.getValues()) as MesProBatchApi.Batch;
- if (formData.value?.id) {
+ if (data.palletIds?.length && data.tonBagIds?.length) {
+ message.warning('缁戝畾鎵樼洏鍜岀粦瀹氬惃琚嬪彧鑳介�夋嫨涓�绉�');
+ return;
+ }
+ if (formType.value === "fill") {
+ await fillBatchInfo({ ...data, id: formData.value!.id, workers });
+ } else if (formData.value?.id) {
await updateBatch({ ...data, id: formData.value.id, workers });
} else {
await createBatch({ ...data, workers });
@@ -196,7 +479,25 @@
}
const data = modalApi.getData<{ formType: FormType; id?: number }>();
formType.value = data.formType;
- formApi.setState({ schema: useFormSchema(formType.value) });
+ // 姣忔鎵撳紑閲嶇疆缁戝畾鍊欓�夛紝涓嬫媺鏀逛负婊氬姩鍒嗛〉鍔犺浇锛岄鎵瑰湪琛ㄥ崟鎸傝浇鍚庡紓姝ユ媺鍙�
+ resetBindingLoader(palletLoader, palletOptions, palletSelected);
+ resetBindingLoader(tonBagLoader, tonBagOptions, tonBagSelected);
+ formApi.setState({
+ schema: useFormSchema(
+ formType.value,
+ palletOptions,
+ palletHandlers,
+ tonBagOptions,
+ tonBagHandlers
+ ),
+ });
+ if (formType.value !== "detail") {
+ // 娓呯┖涓婃鎵撳紑鏃跺嬀閫夌殑鎵樼洏/鍚ㄨ锛岄伩鍏嶆畫鐣欏�艰瑙﹀彂銆屾壒娆¤繘鍏ョ敓浜т腑銆�
+ await formApi.setFieldValue("palletIds", undefined);
+ await formApi.setFieldValue("tonBagIds", undefined);
+ void loadPalletPage(true);
+ void loadTonBagPage(true);
+ }
// 鏍规嵁琛ㄥ崟鎬佸垏鎹汉鍛樺瓙琛ㄥ垪
workerGridApi.setGridOptions({
columns: isEditable.value
@@ -207,7 +508,11 @@
modalApi.lock();
try {
formData.value = await getBatch(data.id);
+ const existingBinding = formData.value.bindingMode;
+ formApi.setState({ schema: useFormSchema(formType.value, palletOptions, palletHandlers, tonBagOptions, tonBagHandlers, existingBinding) });
await formApi.setValues(formData.value);
+ // 宸叉湁缁戝畾浼氶攣瀹氬彟涓�渚т笅鎷夛紝閫夐」绂佺敤鎬侀渶瑕佹寜鏈�鏂拌〃鍗曞�奸噸绠�
+ await refreshBindingOptionState();
const workers = (formData.value.workers ?? []).map(
(worker, index) => ({ ...worker, _rowKey: worker.id ?? index })
);
@@ -219,6 +524,7 @@
} else {
await waitWorkerGridReady();
await workerGridApi.grid.loadData([]);
+ await handleAddWorker();
}
},
});
--
Gitblit v1.9.3