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 | 274 ++++++++++++++++++++++++++++++++++++++++--------------
1 files changed, 204 insertions(+), 70 deletions(-)
diff --git a/src/views/mes/pro/batch/modules/form.vue b/src/views/mes/pro/batch/modules/form.vue
index f405c55..1ef766d 100644
--- a/src/views/mes/pro/batch/modules/form.vue
+++ b/src/views/mes/pro/batch/modules/form.vue
@@ -21,12 +21,14 @@
getBatch,
updateBatch,
} from "#/api/mes/pro/batch";
- import { getUnboundPalletList } from "#/api/mes/pro/pallet";
- import { getUnboundTonBags } from "#/api/mes/pro/bagCode";
+ 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,
@@ -38,38 +40,177 @@
const formType = ref<FormType>("create");
const formData = ref<MesProBatchApi.Batch>();
- const BINDING_OPTION_PAGE_SIZE = 50;
+ /** 缁戝畾涓嬫媺姣忔鍔犺浇鐨勬潯鏁� */
+ const BINDING_PAGE_SIZE = 50;
+ /** 璺濈涓嬫媺搴曢儴澶氬皯鍍忕礌鏃惰Е鍙戝姞杞戒笅涓�椤� */
const BINDING_SCROLL_THRESHOLD = 32;
- let palletOptionSource: PalletOption[] = [];
- let tonBagOptionSource: TonBagOption[] = [];
+ /** 杈撳叆妗嗘悳绱㈤槻鎶栨椂闂达紙姣锛� */
+ 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[]>([]);
- let palletKeyword = "";
- let tonBagKeyword = "";
+ const palletSelected = new Map<number, PalletOption>();
+ const tonBagSelected = new Map<number, TonBagOption>();
+ const palletLoader = createBindingLoader<PalletOption>();
+ const tonBagLoader = createBindingLoader<TonBagOption>();
- function filterBindingOptions<T extends PalletOption>(source: T[], keyword: string): T[] {
- const normalized = keyword.trim().toLowerCase();
- if (!normalized) return source;
- return source.filter((option) => option.label.toLowerCase().includes(normalized));
+ /** 宸插姞杞藉垎椤电粨鏋� + 璺ㄩ〉閫変腑椤瑰悎骞朵负涓嬫媺娓叉煋鏁版嵁 */
+ 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 resetBindingOptions<T extends PalletOption>(
+ /** 鍚屾閫変腑椤癸紙鎹㈤〉鎼滅储鍚庝粛闇�灞曠ず宸查�夋爣绛撅級 */
+ function syncBindingSelection<T extends PalletOption>(
target: T[],
- source: T[],
- keyword = ""
+ loader: BindingLoader<T>,
+ selected: Map<number, T>,
+ ids: number[]
) {
- const filtered = filterBindingOptions(source, keyword);
- target.splice(0, target.length, ...filtered.slice(0, BINDING_OPTION_PAGE_SIZE));
+ 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);
}
- function appendBindingOptions<T extends PalletOption>(
+ /** 閫夐」绂佺敤鎬佷緷璧栧綋鍓嶈〃鍗曞�硷紝姣忔鍔犺浇鏂颁竴椤靛悗閲嶆柊璁$畻 */
+ 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[],
- source: T[],
- keyword: string
+ selected: Map<number, T>,
+ fetchPage: (params: {
+ keyword?: string;
+ pageNo: number;
+ pageSize: number;
+ }) => Promise<{ list: T[]; total: number }>,
+ reset: boolean
) {
- const filtered = filterBindingOptions(source, keyword);
- if (target.length >= filtered.length) return;
- target.push(...filtered.slice(target.length, target.length + BINDING_OPTION_PAGE_SIZE));
+ 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) {
@@ -77,26 +218,30 @@
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) {
- palletKeyword = keyword;
- resetBindingOptions(palletOptions, palletOptionSource, keyword);
+ scheduleBindingSearch(palletLoader, keyword, loadPalletPage);
}
function handleTonBagSearch(keyword: string) {
- tonBagKeyword = keyword;
- resetBindingOptions(tonBagOptions, tonBagOptionSource, keyword);
+ scheduleBindingSearch(tonBagLoader, keyword, loadTonBagPage);
}
function handlePalletPopupScroll(event: Event) {
- if (isPopupNearBottom(event)) {
- appendBindingOptions(palletOptions, palletOptionSource, palletKeyword);
- }
+ if (isPopupNearBottom(event)) void loadPalletPage(false);
}
function handleTonBagPopupScroll(event: Event) {
- if (isPopupNearBottom(event)) {
- appendBindingOptions(tonBagOptions, tonBagOptionSource, tonBagKeyword);
- }
+ if (isPopupNearBottom(event)) void loadTonBagPage(false);
}
const palletHandlers = {
@@ -109,19 +254,27 @@
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 = palletOptionSource.filter(option => ids.includes(option.value));
+ 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('鐢熶骇鎵规鍙兘缁戝畾鐩稿悓鍝佺鐨勬墭鐩�');
- await formApi.setFieldValue('palletIds', selected.filter(option => option.itemId === source?.itemId).map(option => option.value));
+ 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);
@@ -132,14 +285,21 @@
if (ids.length && (values.palletIds?.length || formData.value?.bindingMode === 'pallet')) {
message.warning('缁戝畾鎵樼洏鍜岀粦瀹氬惃琚嬪彧鑳介�夋嫨涓�绉�');
await formApi.setFieldValue('tonBagIds', []);
+ syncBindingSelection(tonBagOptions, tonBagLoader, tonBagSelected, []);
return;
}
- const selected = tonBagOptionSource.filter(option => ids.includes(option.value));
+ 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('鐢熶骇鎵规鍙兘缁戝畾鐩稿悓鍝佺鐨勫惃琚�');
- await formApi.setFieldValue('tonBagIds', selected.filter(option => option.itemId === source?.itemId).map(option => option.value));
+ 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);
@@ -319,14 +479,9 @@
}
const data = modalApi.getData<{ formType: FormType; id?: number }>();
formType.value = data.formType;
- // 鏂板/琛ュ綍/缂栬緫鎬佸姞杞芥湭缁戝畾鎵规鐨勬墭鐩橈紝渚涖�岀粦瀹氭墭鐩樸�嶅閫変笅鎷変娇鐢�
- palletOptionSource = [];
- tonBagOptionSource = [];
- palletOptions.splice(0);
- tonBagOptions.splice(0);
- palletKeyword = "";
- tonBagKeyword = "";
- // 鍏堟寕杞藉熀纭�琛ㄥ崟锛屽�欓�夋墭鐩�/鍚ㄨ鍦ㄥ悗鍙板姞杞斤紝閬垮厤鍙樉绀轰汉鍛橀厤缃殑绌虹獥鏈�
+ // 姣忔鎵撳紑閲嶇疆缁戝畾鍊欓�夛紝涓嬫媺鏀逛负婊氬姩鍒嗛〉鍔犺浇锛岄鎵瑰湪琛ㄥ崟鎸傝浇鍚庡紓姝ユ媺鍙�
+ resetBindingLoader(palletLoader, palletOptions, palletSelected);
+ resetBindingLoader(tonBagLoader, tonBagOptions, tonBagSelected);
formApi.setState({
schema: useFormSchema(
formType.value,
@@ -337,34 +492,11 @@
),
});
if (formType.value !== "detail") {
- const [pallets, tonBags] = await Promise.all([getUnboundPalletList(), getUnboundTonBags()]);
- palletOptionSource = pallets.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,
- }));
- tonBagOptionSource = tonBags.map((bag) => ({
- label: `${bag.code ?? `鍚ㄨ${bag.id}`} 路 ${bag.itemName || '寰呭畬鍠勫搧绉�'}`,
- value: bag.id as number,
- itemId: bag.itemId, itemName: bag.itemName, itemCode: bag.itemCode,
- }));
- resetBindingOptions(palletOptions, palletOptionSource);
- resetBindingOptions(tonBagOptions, tonBagOptionSource);
- }
- // 閲嶆柊鍚屾涓�娆� Schema锛屼娇渚濊禆瀛楁涓殑鍔ㄦ�� options 绔嬪嵆鎷垮埌棣栨壒鍊欓�夐」
- 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({
@@ -379,6 +511,8 @@
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 })
);
--
Gitblit v1.9.3