From 22e9081a45839c421e159fffecb5b13c3dd30d44 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 30 May 2026 19:03:57 +0800 Subject: [PATCH] =?UTF-8?q?refactor(mes):=20=E7=BB=9F=E4=B8=80=20antd=20?= =?UTF-8?q?=E5=92=8C=20ele=20=E9=80=89=E6=8B=A9=E7=BB=84=E4=BB=B6=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E9=A3=8E=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 规范 select 空值判断和回显逻辑 - 统一物料、供应商、客户选择弹窗的单选/多选行为 - 清理 components 内 TODO 并修复相关 DICT_TYPE 导入 --- .../mes/dv/checkplan/components/select.vue | 10 +- .../mes/dv/machinery/components/select.vue | 15 +- .../src/views/mes/dv/machinery/type/data.ts | 4 +- .../mes/dv/subject/components/select.vue | 9 +- .../web-antd/src/views/mes/dv/subject/data.ts | 9 +- .../md/client/components/select-dialog.vue | 50 +------ .../views/mes/md/client/components/select.vue | 12 +- apps/web-antd/src/views/mes/md/client/data.ts | 4 +- .../components/product-bom-select-dialog.vue | 2 +- .../md/item/components/product-bom-select.vue | 11 +- .../mes/md/item/components/select-dialog.vue | 126 ++++++++++------- .../views/mes/md/item/components/select.vue | 11 +- apps/web-antd/src/views/mes/md/item/data.ts | 10 +- .../src/views/mes/md/item/type/data.ts | 9 +- .../md/vendor/components/select-dialog.vue | 128 +++++++++++------- .../views/mes/md/vendor/components/select.vue | 11 +- apps/web-antd/src/views/mes/md/vendor/data.ts | 10 +- .../mes/md/workstation/components/select.vue | 3 +- .../src/views/mes/md/workstation/data.ts | 4 +- .../views/mes/md/workstation/workshop/data.ts | 4 +- .../mes/dv/checkplan/components/select.vue | 15 +- .../mes/dv/machinery/components/select.vue | 21 ++- .../src/views/mes/dv/machinery/type/data.ts | 4 +- .../mes/dv/subject/components/select.vue | 16 ++- apps/web-ele/src/views/mes/dv/subject/data.ts | 9 +- .../md/client/components/select-dialog.vue | 52 ++----- .../views/mes/md/client/components/select.vue | 11 +- apps/web-ele/src/views/mes/md/client/data.ts | 4 +- .../components/product-bom-select-dialog.vue | 2 +- .../md/item/components/product-bom-select.vue | 11 +- .../mes/md/item/components/select-dialog.vue | 126 ++++++++++------- .../views/mes/md/item/components/select.vue | 11 +- apps/web-ele/src/views/mes/md/item/data.ts | 10 +- .../src/views/mes/md/item/type/data.ts | 9 +- .../md/vendor/components/select-dialog.vue | 128 +++++++++++------- .../views/mes/md/vendor/components/select.vue | 11 +- apps/web-ele/src/views/mes/md/vendor/data.ts | 10 +- .../mes/md/workstation/components/select.vue | 3 +- .../src/views/mes/md/workstation/data.ts | 4 +- .../views/mes/md/workstation/workshop/data.ts | 4 +- 40 files changed, 484 insertions(+), 419 deletions(-) diff --git a/apps/web-antd/src/views/mes/dv/checkplan/components/select.vue b/apps/web-antd/src/views/mes/dv/checkplan/components/select.vue index 2dddc5a78..4c9a2c3d1 100644 --- a/apps/web-antd/src/views/mes/dv/checkplan/components/select.vue +++ b/apps/web-antd/src/views/mes/dv/checkplan/components/select.vue @@ -9,6 +9,8 @@ import { Select } from 'ant-design-vue'; import { getCheckPlanPage } from '#/api/mes/dv/checkplan'; +defineOptions({ name: 'DvCheckPlanSelect' }); + const props = withDefaults( defineProps<{ allowClear?: boolean; @@ -48,15 +50,11 @@ async function getList() { function handleChange(value: SelectValue) { const planId = typeof value === 'number' ? value : undefined; emit('update:modelValue', planId); - // TODO @AI:可以简化,不换行么? - emit( - 'change', - list.value.find((item) => item.id === planId), - ); + emit('change', list.value.find((item) => item.id === planId)); } -// TODO @AI:下面,2 个,需要有空行么? watch(() => [props.status, props.type], getList); + onMounted(getList); diff --git a/apps/web-antd/src/views/mes/dv/machinery/components/select.vue b/apps/web-antd/src/views/mes/dv/machinery/components/select.vue index e37d8f430..f0b6a7481 100644 --- a/apps/web-antd/src/views/mes/dv/machinery/components/select.vue +++ b/apps/web-antd/src/views/mes/dv/machinery/components/select.vue @@ -9,6 +9,8 @@ import { Select } from 'ant-design-vue'; import { getMachinerySimpleList } from '#/api/mes/dv/machinery'; +defineOptions({ name: 'DvMachinerySelect' }); + withDefaults( defineProps<{ allowClear?: boolean; @@ -16,7 +18,12 @@ withDefaults( modelValue?: number; placeholder?: string; }>(), - { allowClear: true, disabled: false, modelValue: undefined, placeholder: '请选择设备' }, + { + allowClear: true, + disabled: false, + modelValue: undefined, + placeholder: '请选择设备', + }, ); const emit = defineEmits<{ change: [row?: MesDvMachineryApi.Machinery]; @@ -33,13 +40,9 @@ async function getList() { function handleChange(value: SelectValue) { const machineryId = typeof value === 'number' ? value : undefined; emit('update:modelValue', machineryId); - emit( - 'change', - list.value.find((item) => item.id === machineryId), - ); + emit('change', list.value.find((item) => item.id === machineryId)); } -// TODO @AI:按照目前项目的规则,会希望 /** 初始化么 */ ?如果喜欢,是不是加到 style.vue 里? onMounted(getList); diff --git a/apps/web-antd/src/views/mes/dv/machinery/type/data.ts b/apps/web-antd/src/views/mes/dv/machinery/type/data.ts index e817fcea1..604ea3054 100644 --- a/apps/web-antd/src/views/mes/dv/machinery/type/data.ts +++ b/apps/web-antd/src/views/mes/dv/machinery/type/data.ts @@ -2,9 +2,9 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { MesDvMachineryTypeApi } from '#/api/mes/dv/machinery/type'; -import { DICT_TYPE, h } from 'vue'; +import { h } from 'vue'; -import { CommonStatusEnum, MesAutoCodeRuleCode } from '@vben/constants'; +import { CommonStatusEnum, DICT_TYPE, MesAutoCodeRuleCode } from '@vben/constants'; import { getDictOptions } from '@vben/hooks'; import { handleTree } from '@vben/utils'; diff --git a/apps/web-antd/src/views/mes/dv/subject/components/select.vue b/apps/web-antd/src/views/mes/dv/subject/components/select.vue index f11e16a6b..8c54234d4 100644 --- a/apps/web-antd/src/views/mes/dv/subject/components/select.vue +++ b/apps/web-antd/src/views/mes/dv/subject/components/select.vue @@ -9,6 +9,8 @@ import { Select } from 'ant-design-vue'; import { getSubjectSimpleList } from '#/api/mes/dv/subject'; +defineOptions({ name: 'DvSubjectSelect' }); + const props = withDefaults( defineProps<{ allowClear?: boolean; @@ -30,7 +32,7 @@ const emit = defineEmits<{ 'update:modelValue': [value?: number]; }>(); const list = ref([]); // 项目列表 -const filteredList = computed( // 筛选后的项目列表 +const filteredList = computed( () => list.value.filter((item) => !props.type || item.type === props.type), ); @@ -43,10 +45,7 @@ async function getList() { function handleChange(value: SelectValue) { const subjectId = typeof value === 'number' ? value : undefined; emit('update:modelValue', subjectId); - emit( - 'change', - list.value.find((item) => item.id === subjectId), - ); + emit('change', list.value.find((item) => item.id === subjectId)); } onMounted(getList); diff --git a/apps/web-antd/src/views/mes/dv/subject/data.ts b/apps/web-antd/src/views/mes/dv/subject/data.ts index 3748e9c42..7ebc009be 100644 --- a/apps/web-antd/src/views/mes/dv/subject/data.ts +++ b/apps/web-antd/src/views/mes/dv/subject/data.ts @@ -2,9 +2,14 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { MesDvSubjectApi } from '#/api/mes/dv/subject'; -import { DICT_TYPE, h } from 'vue'; +import { h } from 'vue'; -import { CommonStatusEnum, MesAutoCodeRuleCode, MesDvSubjectTypeEnum } from '@vben/constants'; +import { + CommonStatusEnum, + DICT_TYPE, + MesAutoCodeRuleCode, + MesDvSubjectTypeEnum, +} from '@vben/constants'; import { getDictOptions } from '@vben/hooks'; import { Button } from 'ant-design-vue'; diff --git a/apps/web-antd/src/views/mes/md/client/components/select-dialog.vue b/apps/web-antd/src/views/mes/md/client/components/select-dialog.vue index 80efd7181..844552a4f 100644 --- a/apps/web-antd/src/views/mes/md/client/components/select-dialog.vue +++ b/apps/web-antd/src/views/mes/md/client/components/select-dialog.vue @@ -16,6 +16,8 @@ import { useClientSelectGridFormSchema, } from '../data'; +defineOptions({ name: 'MdClientSelectDialog' }); + const emit = defineEmits<{ selected: [rows: MesMdClientApi.Client[]]; }>(); @@ -24,44 +26,12 @@ const open = ref(false); // 弹窗是否打开 const multiple = ref(true); // 是否多选 const selectedRows = ref([]); // 已选客户列表 const preSelectedIds = ref([]); // 预选客户编号列表 -const latestQueryRows = ref([]); // 最近一次查询返回的客户列表 -const queryFinished = ref(false); // 最近一次查询是否完成 - -// TODO @芋艿:是否有必要搞的这么复杂???后续测试看看。 -// TODO @AI:需要简化下么?好像只有它这里有。。。 -const MAX_TABLE_READY_FRAMES = 60; - -/** 等待下一帧 */ -function waitNextFrame(): Promise { - return new Promise((resolve) => { - requestAnimationFrame(() => resolve()); - }); -} /** 获取当前表格数据 */ function getTableRows() { return gridApi.grid.getTableData().fullData as MesMdClientApi.Client[]; } -/** 等待 VXE 将当前查询结果写入表格数据后再回显选中 */ -async function waitTableReady(): Promise { - if (preSelectedIds.value.length === 0) { - return; - } - for (let index = 0; index < MAX_TABLE_READY_FRAMES; index += 1) { - if (queryFinished.value) { - const rows = getTableRows(); - if (latestQueryRows.value.length === 0 && rows.length === 0) { - return; - } - if (latestQueryRows.value.length > 0 && rows.length > 0) { - return; - } - } - await waitNextFrame(); - } -} - /** 获取多选记录,包含 VXE reserve 跨页记录 */ function getMultipleSelectedRows() { const selectedMap = new Map(); @@ -71,7 +41,7 @@ function getMultipleSelectedRows() { ] as MesMdClientApi.Client[]; records.forEach((row) => { const rowId = row.id; - if (rowId !== null && rowId !== undefined) { + if (rowId != null) { selectedMap.set(rowId, row); } }); @@ -111,10 +81,9 @@ async function applyPreSelection() { if (preSelectedIds.value.length === 0) { return; } - // proxy 表格回显选中时要读取 fullData,否则首次打开可能读不到刚查询出的数据。 const rows = getTableRows(); for (const row of rows) { - if (row.id === null || !preSelectedIds.value.includes(row.id as number)) { + if (row.id == null || !preSelectedIds.value.includes(row.id)) { continue; } if (multiple.value) { @@ -150,15 +119,12 @@ const [Grid, gridApi] = useVbenVxeGrid({ proxyConfig: { ajax: { query: async ({ page }, formValues) => { - const data = await getClientPage({ + return await getClientPage({ pageNo: page.currentPage, pageSize: page.pageSize, ...formValues, status: CommonStatusEnum.ENABLE, }); - latestQueryRows.value = data.list || []; - queryFinished.value = true; - return data; }, }, }, @@ -198,8 +164,6 @@ async function openModal( open.value = true; multiple.value = options?.multiple ?? true; preSelectedIds.value = selectedIds || []; - latestQueryRows.value = []; - queryFinished.value = false; await nextTick(); gridApi.setGridOptions({ columns: useClientSelectGridColumns(multiple.value), @@ -207,13 +171,13 @@ async function openModal( await resetQueryState(); await gridApi.query(); await nextTick(); - await waitTableReady(); await applyPreSelection(); } /** 关闭客户选择弹窗 */ -function closeModal() { +async function closeModal() { open.value = false; + await resetQueryState(); } /** 确认选择客户 */ diff --git a/apps/web-antd/src/views/mes/md/client/components/select.vue b/apps/web-antd/src/views/mes/md/client/components/select.vue index 8a85e1919..bbe599651 100644 --- a/apps/web-antd/src/views/mes/md/client/components/select.vue +++ b/apps/web-antd/src/views/mes/md/client/components/select.vue @@ -38,18 +38,16 @@ const selectedItem = ref(); // 当前选中客户 const displayLabel = computed(() => selectedItem.value?.name ?? ''); // 选择器展示名称 const showClear = computed( - // TODO @AI:这种,是不是应该放到 // computed( 后面? - // 是否显示清空图标 () => props.allowClear && !props.disabled && hovering.value && - props.modelValue !== null, + props.modelValue != null, ); /** 根据客户编号回显选择器 */ async function resolveItemById(id: number | undefined) { - if (id === null) { + if (id == null) { selectedItem.value = undefined; return; } @@ -57,7 +55,7 @@ async function resolveItemById(id: number | undefined) { return; } try { - selectedItem.value = await getClient(id as number); + selectedItem.value = await getClient(id); } catch (error) { console.error('[MdClientSelect] resolveItemById failed:', error); } @@ -89,8 +87,8 @@ function handleClick(event: MouseEvent) { clearSelected(); return; } - const selectedIds = props.modelValue === null ? [] : [props.modelValue]; - dialogRef.value?.open(selectedIds as number[], { multiple: false }); + const selectedIds = props.modelValue == null ? [] : [props.modelValue]; + dialogRef.value?.open(selectedIds, { multiple: false }); } /** 回填选中的客户 */ diff --git a/apps/web-antd/src/views/mes/md/client/data.ts b/apps/web-antd/src/views/mes/md/client/data.ts index 8b03d6e84..3c14dc147 100644 --- a/apps/web-antd/src/views/mes/md/client/data.ts +++ b/apps/web-antd/src/views/mes/md/client/data.ts @@ -2,9 +2,9 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { MesMdClientApi } from '#/api/mes/md/client'; -import { DICT_TYPE, h } from 'vue'; +import { h } from 'vue'; -import { CommonStatusEnum, MesAutoCodeRuleCode } from '@vben/constants'; +import { CommonStatusEnum, DICT_TYPE, MesAutoCodeRuleCode } from '@vben/constants'; import { getDictOptions } from '@vben/hooks'; import { Button } from 'ant-design-vue'; diff --git a/apps/web-antd/src/views/mes/md/item/components/product-bom-select-dialog.vue b/apps/web-antd/src/views/mes/md/item/components/product-bom-select-dialog.vue index 9718d3255..90ae85a32 100644 --- a/apps/web-antd/src/views/mes/md/item/components/product-bom-select-dialog.vue +++ b/apps/web-antd/src/views/mes/md/item/components/product-bom-select-dialog.vue @@ -68,7 +68,7 @@ async function openModal(itemId: number, selectedBomItemId?: number) { list.value = await getProductBomListByItemId(itemId); gridApi.setGridOptions({ data: list.value }); await nextTick(); - if (selectedBomItemId !== null) { + if (selectedBomItemId != null) { const match = list.value.find( (row) => row.bomItemId === selectedBomItemId, ); diff --git a/apps/web-antd/src/views/mes/md/item/components/product-bom-select.vue b/apps/web-antd/src/views/mes/md/item/components/product-bom-select.vue index ac4964f94..a5085f356 100644 --- a/apps/web-antd/src/views/mes/md/item/components/product-bom-select.vue +++ b/apps/web-antd/src/views/mes/md/item/components/product-bom-select.vue @@ -40,17 +40,16 @@ const selectedBom = ref(); // 当前选中的 BOM const displayLabel = computed(() => selectedBom.value?.bomItemName ?? ''); // 选择器展示名称 const showClear = computed( - // 是否显示清空图标 () => props.allowClear && !props.disabled && hovering.value && - props.modelValue !== null, + props.modelValue != null, ); /** 根据 BOM 子物料编号回显选择器 */ async function resolveBomById(bomItemId: number | undefined) { - if (bomItemId === null || props.itemId === null) { + if (bomItemId == null || props.itemId == null) { selectedBom.value = undefined; return; } @@ -58,7 +57,7 @@ async function resolveBomById(bomItemId: number | undefined) { return; } try { - const list = await getProductBomListByItemId(props.itemId as number); + const list = await getProductBomListByItemId(props.itemId); selectedBom.value = list.find((item) => item.bomItemId === bomItemId); } catch (error) { console.error('[MdProductBomSelect] resolveBomById failed:', error); @@ -91,7 +90,7 @@ function clearSelected() { /** 打开 BOM 物料选择弹窗 */ function handleClick(event: MouseEvent) { - if (props.disabled || props.itemId === null) { + if (props.disabled || props.itemId == null) { return; } const target = event.target as HTMLElement; @@ -100,7 +99,7 @@ function handleClick(event: MouseEvent) { clearSelected(); return; } - dialogRef.value?.open(props.itemId as number, props.modelValue); + dialogRef.value?.open(props.itemId, props.modelValue); } /** 回填选中的 BOM 物料 */ diff --git a/apps/web-antd/src/views/mes/md/item/components/select-dialog.vue b/apps/web-antd/src/views/mes/md/item/components/select-dialog.vue index 18e3ffa99..2af087579 100644 --- a/apps/web-antd/src/views/mes/md/item/components/select-dialog.vue +++ b/apps/web-antd/src/views/mes/md/item/components/select-dialog.vue @@ -23,52 +23,58 @@ const emit = defineEmits<{ const open = ref(false); // 弹窗是否打开 const multiple = ref(true); // 是否多选 -const syncingSingleSelection = ref(false); // 是否同步单选勾选状态 const selectedRows = ref([]); // 已选物料列表 const selectedItemTypeId = ref(); // 当前筛选分类编号 const preSelectedIds = ref([]); // 预选物料编号列表 const typeTreeRef = ref>(); // 物料分类树 -/** 单选模式下同步 VXE 勾选状态,避免跨页残留多选 */ -async function syncSingleSelection(row?: MesMdItemApi.Item) { - syncingSingleSelection.value = true; - await nextTick(); - await gridApi.grid.clearCheckboxRow(); - if (row) { - await gridApi.grid.setCheckboxRow(row, true); - } - await nextTick(); - syncingSingleSelection.value = false; +/** 获取当前表格数据 */ +function getTableRows() { + return gridApi.grid.getTableData().fullData as MesMdItemApi.Item[]; } -/** 处理勾选变化,单选模式只保留最后一条 */ -async function handleCheckboxChange({ - checked, - records, - row, -}: { - checked: boolean; - records: MesMdItemApi.Item[]; - row?: MesMdItemApi.Item; -}) { - if (syncingSingleSelection.value) { - return; - } - if (!multiple.value) { - const selected = checked && row ? [row] : []; - selectedRows.value = selected; - await syncSingleSelection(selected[0]); - return; - } - selectedRows.value = records; +/** 获取多选记录,包含 VXE reserve 跨页记录 */ +function getMultipleSelectedRows() { + const selectedMap = new Map(); + const records = [ + ...(gridApi.grid.getCheckboxReserveRecords?.() ?? []), + ...(gridApi.grid.getCheckboxRecords?.() ?? []), + ] as MesMdItemApi.Item[]; + records.forEach((row) => { + const rowId = row.id; + if (rowId != null) { + selectedMap.set(rowId, row); + } + }); + return [...selectedMap.values()]; } -/** 处理全选变化 */ -function handleCheckboxAll({ records }: { records: MesMdItemApi.Item[] }) { - if (syncingSingleSelection.value) { +/** 处理勾选变化 */ +function handleCheckboxSelectChange() { + selectedRows.value = getMultipleSelectedRows(); +} + +/** 处理单选变化 */ +function handleRadioChange(row: MesMdItemApi.Item) { + selectedRows.value = [row]; +} + +/** 多选模式下切换行勾选 */ +async function toggleMultipleRow(row: MesMdItemApi.Item) { + const selected = gridApi.grid.isCheckedByCheckboxRow(row); + await gridApi.grid.setCheckboxRow(row, !selected); + selectedRows.value = getMultipleSelectedRows(); +} + +/** 处理行双击 */ +async function handleCellDblclick({ row }: { row: MesMdItemApi.Item }) { + if (multiple.value) { + await toggleMultipleRow(row); return; } - selectedRows.value = records; + selectedRows.value = [row]; + await gridApi.grid.setRadioRow(row); + handleConfirm(); } /** 按分类筛选物料 */ @@ -78,18 +84,25 @@ function handleItemTypeNodeClick(row: MesMdItemTypeApi.ItemType | undefined) { } /** 回显预选物料 */ -function applyPreSelection() { +async function applyPreSelection() { if (preSelectedIds.value.length === 0) { return; } - const rows = gridApi.grid.getData() as MesMdItemApi.Item[]; + const rows = getTableRows(); for (const row of rows) { - if (row.id && preSelectedIds.value.includes(row.id)) { - gridApi.grid.setCheckboxRow(row, true); - if (!multiple.value) { - selectedRows.value = [row]; - } + if (row.id == null || !preSelectedIds.value.includes(row.id)) { + continue; } + if (multiple.value) { + await gridApi.grid.setCheckboxRow(row, true); + } else { + await gridApi.grid.setRadioRow(row); + selectedRows.value = [row]; + return; + } + } + if (multiple.value) { + selectedRows.value = getMultipleSelectedRows(); } } @@ -98,7 +111,7 @@ const [Grid, gridApi] = useVbenVxeGrid({ schema: useItemSelectGridFormSchema(), }, gridOptions: { - columns: useItemSelectGridColumns(), + columns: useItemSelectGridColumns(true), height: 560, keepSource: true, checkboxConfig: { @@ -106,6 +119,10 @@ const [Grid, gridApi] = useVbenVxeGrid({ range: true, reserve: true, }, + radioConfig: { + highlight: true, + trigger: 'row', + }, proxyConfig: { ajax: { query: async ({ page }, formValues) => { @@ -129,8 +146,12 @@ const [Grid, gridApi] = useVbenVxeGrid({ }, } as VxeTableGridOptions, gridEvents: { - checkboxAll: handleCheckboxAll, - checkboxChange: handleCheckboxChange, + cellDblclick: handleCellDblclick, + checkboxAll: handleCheckboxSelectChange, + checkboxChange: handleCheckboxSelectChange, + radioChange: ({ row }: { row: MesMdItemApi.Item }) => { + handleRadioChange(row); + }, }, }); @@ -140,6 +161,8 @@ async function resetQueryState() { selectedRows.value = []; typeTreeRef.value?.reset(); await gridApi.grid.clearCheckboxRow(); + await gridApi.grid.clearCheckboxReserve(); + await gridApi.grid.clearRadioRow(); await gridApi.formApi.resetForm(); } @@ -152,10 +175,13 @@ async function openModal( multiple.value = options?.multiple ?? true; preSelectedIds.value = selectedIds || []; await nextTick(); + gridApi.setGridOptions({ + columns: useItemSelectGridColumns(multiple.value), + }); await resetQueryState(); await gridApi.query(); await nextTick(); - applyPreSelection(); + await applyPreSelection(); } /** 关闭物料选择弹窗 */ @@ -166,14 +192,12 @@ async function closeModal() { /** 确认选择物料 */ function handleConfirm() { - if (selectedRows.value.length === 0) { + const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value; + if (rows.length === 0) { message.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据'); return; } - emit( - 'selected', - multiple.value ? selectedRows.value : [selectedRows.value[0]!], - ); + emit('selected', multiple.value ? rows : [rows[0]!]); open.value = false; } diff --git a/apps/web-antd/src/views/mes/md/item/components/select.vue b/apps/web-antd/src/views/mes/md/item/components/select.vue index 22bd15384..bb62ec9a4 100644 --- a/apps/web-antd/src/views/mes/md/item/components/select.vue +++ b/apps/web-antd/src/views/mes/md/item/components/select.vue @@ -38,17 +38,16 @@ const selectedItem = ref(); // 当前选中物料 const displayLabel = computed(() => selectedItem.value?.name ?? ''); // 选择器展示名称 const showClear = computed( - // 是否显示清空图标 () => props.allowClear && !props.disabled && hovering.value && - props.modelValue !== null, + props.modelValue != null, ); /** 根据物料编号回显选择器 */ async function resolveItemById(id: number | undefined) { - if (id === null) { + if (id == null) { selectedItem.value = undefined; return; } @@ -56,7 +55,7 @@ async function resolveItemById(id: number | undefined) { return; } try { - selectedItem.value = await getItem(id as number); + selectedItem.value = await getItem(id); } catch (error) { console.error('[MdItemSelect] resolveItemById failed:', error); } @@ -88,8 +87,8 @@ function handleClick(event: MouseEvent) { clearSelected(); return; } - const selectedIds = props.modelValue === null ? [] : [props.modelValue]; - dialogRef.value?.open(selectedIds as number[], { multiple: false }); + const selectedIds = props.modelValue == null ? [] : [props.modelValue]; + dialogRef.value?.open(selectedIds, { multiple: false }); } /** 回填选中的物料 */ diff --git a/apps/web-antd/src/views/mes/md/item/data.ts b/apps/web-antd/src/views/mes/md/item/data.ts index 21e55f602..d2915d6fd 100644 --- a/apps/web-antd/src/views/mes/md/item/data.ts +++ b/apps/web-antd/src/views/mes/md/item/data.ts @@ -3,9 +3,9 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { MesMdItemApi } from '#/api/mes/md/item'; import type { MesMdProductBomApi } from '#/api/mes/md/item/productBom'; -import { DICT_TYPE, h, markRaw } from 'vue'; +import { h, markRaw } from 'vue'; -import { CommonStatusEnum, MesAutoCodeRuleCode } from '@vben/constants'; +import { CommonStatusEnum, DICT_TYPE, MesAutoCodeRuleCode } from '@vben/constants'; import { getDictOptions } from '@vben/hooks'; import { Button } from 'ant-design-vue'; @@ -356,9 +356,11 @@ export function useItemSelectGridFormSchema(): VbenFormSchema[] { } /** 物料选择弹窗列表字段 */ -export function useItemSelectGridColumns(): VxeTableGridOptions['columns'] { +export function useItemSelectGridColumns( + multiple = true, +): VxeTableGridOptions['columns'] { return [ - { type: 'checkbox', width: 50 }, + { type: multiple ? 'checkbox' : 'radio', width: 50 }, { field: 'code', title: '物料编码', diff --git a/apps/web-antd/src/views/mes/md/item/type/data.ts b/apps/web-antd/src/views/mes/md/item/type/data.ts index a5a8c7549..ebab1d063 100644 --- a/apps/web-antd/src/views/mes/md/item/type/data.ts +++ b/apps/web-antd/src/views/mes/md/item/type/data.ts @@ -2,9 +2,14 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { MesMdItemTypeApi } from '#/api/mes/md/item/type'; -import { DICT_TYPE, h } from 'vue'; +import { h } from 'vue'; -import { CommonStatusEnum, MesAutoCodeRuleCode, MesItemOrProductEnum } from '@vben/constants'; +import { + CommonStatusEnum, + DICT_TYPE, + MesAutoCodeRuleCode, + MesItemOrProductEnum, +} from '@vben/constants'; import { getDictOptions } from '@vben/hooks'; import { handleTree } from '@vben/utils'; diff --git a/apps/web-antd/src/views/mes/md/vendor/components/select-dialog.vue b/apps/web-antd/src/views/mes/md/vendor/components/select-dialog.vue index 7a3acd77d..9837b5d7a 100644 --- a/apps/web-antd/src/views/mes/md/vendor/components/select-dialog.vue +++ b/apps/web-antd/src/views/mes/md/vendor/components/select-dialog.vue @@ -16,71 +16,86 @@ import { useVendorSelectGridFormSchema, } from '../data'; +defineOptions({ name: 'MdVendorSelectDialog' }); + const emit = defineEmits<{ selected: [rows: MesMdVendorApi.Vendor[]]; }>(); const open = ref(false); // 弹窗是否打开 const multiple = ref(true); // 是否多选 -const syncingSingleSelection = ref(false); // 是否同步单选勾选状态 const selectedRows = ref([]); // 已选供应商列表 const preSelectedIds = ref([]); // 预选供应商编号列表 -/** 单选模式下同步 VXE 勾选状态,避免跨页残留多选 */ -async function syncSingleSelection(row?: MesMdVendorApi.Vendor) { - syncingSingleSelection.value = true; - await nextTick(); - await gridApi.grid.clearCheckboxRow(); - if (row) { - await gridApi.grid.setCheckboxRow(row, true); - } - await nextTick(); - syncingSingleSelection.value = false; +/** 获取当前表格数据 */ +function getTableRows() { + return gridApi.grid.getTableData().fullData as MesMdVendorApi.Vendor[]; } -/** 处理勾选变化,单选模式只保留最后一条 */ -async function handleCheckboxChange({ - checked, - records, - row, -}: { - checked: boolean; - records: MesMdVendorApi.Vendor[]; - row?: MesMdVendorApi.Vendor; -}) { - if (syncingSingleSelection.value) { - return; - } - if (!multiple.value) { - const selected = checked && row ? [row] : []; - selectedRows.value = selected; - await syncSingleSelection(selected[0]); - return; - } - selectedRows.value = records; +/** 获取多选记录,包含 VXE reserve 跨页记录 */ +function getMultipleSelectedRows() { + const selectedMap = new Map(); + const records = [ + ...(gridApi.grid.getCheckboxReserveRecords?.() ?? []), + ...(gridApi.grid.getCheckboxRecords?.() ?? []), + ] as MesMdVendorApi.Vendor[]; + records.forEach((row) => { + const rowId = row.id; + if (rowId != null) { + selectedMap.set(rowId, row); + } + }); + return [...selectedMap.values()]; } -/** 处理全选变化 */ -function handleCheckboxAll({ records }: { records: MesMdVendorApi.Vendor[] }) { - if (syncingSingleSelection.value) { +/** 处理勾选变化 */ +function handleCheckboxSelectChange() { + selectedRows.value = getMultipleSelectedRows(); +} + +/** 处理单选变化 */ +function handleRadioChange(row: MesMdVendorApi.Vendor) { + selectedRows.value = [row]; +} + +/** 多选模式下切换行勾选 */ +async function toggleMultipleRow(row: MesMdVendorApi.Vendor) { + const selected = gridApi.grid.isCheckedByCheckboxRow(row); + await gridApi.grid.setCheckboxRow(row, !selected); + selectedRows.value = getMultipleSelectedRows(); +} + +/** 处理行双击 */ +async function handleCellDblclick({ row }: { row: MesMdVendorApi.Vendor }) { + if (multiple.value) { + await toggleMultipleRow(row); return; } - selectedRows.value = records; + selectedRows.value = [row]; + await gridApi.grid.setRadioRow(row); + handleConfirm(); } /** 回显预选供应商 */ -function applyPreSelection() { +async function applyPreSelection() { if (preSelectedIds.value.length === 0) { return; } - const rows = gridApi.grid.getData() as MesMdVendorApi.Vendor[]; + const rows = getTableRows(); for (const row of rows) { - if (row.id && preSelectedIds.value.includes(row.id)) { - gridApi.grid.setCheckboxRow(row, true); - if (!multiple.value) { - selectedRows.value = [row]; - } + if (row.id == null || !preSelectedIds.value.includes(row.id)) { + continue; } + if (multiple.value) { + await gridApi.grid.setCheckboxRow(row, true); + } else { + await gridApi.grid.setRadioRow(row); + selectedRows.value = [row]; + return; + } + } + if (multiple.value) { + selectedRows.value = getMultipleSelectedRows(); } } @@ -89,7 +104,7 @@ const [Grid, gridApi] = useVbenVxeGrid({ schema: useVendorSelectGridFormSchema(), }, gridOptions: { - columns: useVendorSelectGridColumns(), + columns: useVendorSelectGridColumns(true), height: 520, keepSource: true, checkboxConfig: { @@ -97,6 +112,10 @@ const [Grid, gridApi] = useVbenVxeGrid({ range: true, reserve: true, }, + radioConfig: { + highlight: true, + trigger: 'row', + }, proxyConfig: { ajax: { query: async ({ page }, formValues) => { @@ -119,8 +138,12 @@ const [Grid, gridApi] = useVbenVxeGrid({ }, } as VxeTableGridOptions, gridEvents: { - checkboxAll: handleCheckboxAll, - checkboxChange: handleCheckboxChange, + cellDblclick: handleCellDblclick, + checkboxAll: handleCheckboxSelectChange, + checkboxChange: handleCheckboxSelectChange, + radioChange: ({ row }: { row: MesMdVendorApi.Vendor }) => { + handleRadioChange(row); + }, }, }); @@ -128,6 +151,8 @@ const [Grid, gridApi] = useVbenVxeGrid({ async function resetQueryState() { selectedRows.value = []; await gridApi.grid.clearCheckboxRow(); + await gridApi.grid.clearCheckboxReserve(); + await gridApi.grid.clearRadioRow(); await gridApi.formApi.resetForm(); } @@ -140,10 +165,13 @@ async function openModal( multiple.value = options?.multiple ?? true; preSelectedIds.value = selectedIds || []; await nextTick(); + gridApi.setGridOptions({ + columns: useVendorSelectGridColumns(multiple.value), + }); await resetQueryState(); await gridApi.query(); await nextTick(); - applyPreSelection(); + await applyPreSelection(); } /** 关闭供应商选择弹窗 */ @@ -154,14 +182,12 @@ async function closeModal() { /** 确认选择供应商 */ function handleConfirm() { - if (selectedRows.value.length === 0) { + const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value; + if (rows.length === 0) { message.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据'); return; } - emit( - 'selected', - multiple.value ? selectedRows.value : [selectedRows.value[0]!], - ); + emit('selected', multiple.value ? rows : [rows[0]!]); open.value = false; } diff --git a/apps/web-antd/src/views/mes/md/vendor/components/select.vue b/apps/web-antd/src/views/mes/md/vendor/components/select.vue index a202650f6..cc6e756b9 100644 --- a/apps/web-antd/src/views/mes/md/vendor/components/select.vue +++ b/apps/web-antd/src/views/mes/md/vendor/components/select.vue @@ -38,17 +38,16 @@ const selectedItem = ref(); // 当前选中供应商 const displayLabel = computed(() => selectedItem.value?.name ?? ''); // 选择器展示名称 const showClear = computed( - // 是否显示清空图标 () => props.allowClear && !props.disabled && hovering.value && - props.modelValue !== null, + props.modelValue != null, ); /** 根据供应商编号回显选择器 */ async function resolveItemById(id: number | undefined) { - if (id === null) { + if (id == null) { selectedItem.value = undefined; return; } @@ -56,7 +55,7 @@ async function resolveItemById(id: number | undefined) { return; } try { - selectedItem.value = await getVendor(id as number); + selectedItem.value = await getVendor(id); } catch (error) { console.error('[MdVendorSelect] resolveItemById failed:', error); } @@ -88,8 +87,8 @@ function handleClick(event: MouseEvent) { clearSelected(); return; } - const selectedIds = props.modelValue === null ? [] : [props.modelValue]; - dialogRef.value?.open(selectedIds as number[], { multiple: false }); + const selectedIds = props.modelValue == null ? [] : [props.modelValue]; + dialogRef.value?.open(selectedIds, { multiple: false }); } /** 回填选中的供应商 */ diff --git a/apps/web-antd/src/views/mes/md/vendor/data.ts b/apps/web-antd/src/views/mes/md/vendor/data.ts index 1395328b8..c9accb2c8 100644 --- a/apps/web-antd/src/views/mes/md/vendor/data.ts +++ b/apps/web-antd/src/views/mes/md/vendor/data.ts @@ -2,9 +2,9 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { MesMdVendorApi } from '#/api/mes/md/vendor'; -import { DICT_TYPE, h } from 'vue'; +import { h } from 'vue'; -import { CommonStatusEnum, MesAutoCodeRuleCode } from '@vben/constants'; +import { CommonStatusEnum, DICT_TYPE, MesAutoCodeRuleCode } from '@vben/constants'; import { getDictOptions } from '@vben/hooks'; import { Button } from 'ant-design-vue'; @@ -416,9 +416,11 @@ export function useVendorSelectGridFormSchema(): VbenFormSchema[] { } /** 供应商选择弹窗的字段 */ -export function useVendorSelectGridColumns(): VxeTableGridOptions['columns'] { +export function useVendorSelectGridColumns( + multiple = true, +): VxeTableGridOptions['columns'] { return [ - { type: 'checkbox', width: 50 }, + { type: multiple ? 'checkbox' : 'radio', width: 50 }, { field: 'code', title: '供应商编码', diff --git a/apps/web-antd/src/views/mes/md/workstation/components/select.vue b/apps/web-antd/src/views/mes/md/workstation/components/select.vue index 4f65aedea..fd4d6ecfd 100644 --- a/apps/web-antd/src/views/mes/md/workstation/components/select.vue +++ b/apps/web-antd/src/views/mes/md/workstation/components/select.vue @@ -40,12 +40,11 @@ const selectedItem = ref(); // 选中的工作 const displayLabel = computed(() => selectedItem.value?.name ?? ''); // 选择器展示名称 const showClear = computed( - // 是否显示清空图标 () => props.allowClear && !props.disabled && hovering.value && - props.modelValue !== null, + props.modelValue != null, ); /** 根据工作站编号回显选择器 */ diff --git a/apps/web-antd/src/views/mes/md/workstation/data.ts b/apps/web-antd/src/views/mes/md/workstation/data.ts index 3807d5db7..f5201219c 100644 --- a/apps/web-antd/src/views/mes/md/workstation/data.ts +++ b/apps/web-antd/src/views/mes/md/workstation/data.ts @@ -2,9 +2,9 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { MesMdWorkstationApi } from '#/api/mes/md/workstation'; -import { DICT_TYPE, h, markRaw } from 'vue'; +import { h, markRaw } from 'vue'; -import { CommonStatusEnum, MesAutoCodeRuleCode } from '@vben/constants'; +import { CommonStatusEnum, DICT_TYPE, MesAutoCodeRuleCode } from '@vben/constants'; import { getDictOptions } from '@vben/hooks'; import { Button } from 'ant-design-vue'; diff --git a/apps/web-antd/src/views/mes/md/workstation/workshop/data.ts b/apps/web-antd/src/views/mes/md/workstation/workshop/data.ts index 1db3673b4..eb0937316 100644 --- a/apps/web-antd/src/views/mes/md/workstation/workshop/data.ts +++ b/apps/web-antd/src/views/mes/md/workstation/workshop/data.ts @@ -2,9 +2,9 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { MesMdWorkshopApi } from '#/api/mes/md/workstation/workshop'; -import { DICT_TYPE, h } from 'vue'; +import { h } from 'vue'; -import { CommonStatusEnum, MesAutoCodeRuleCode } from '@vben/constants'; +import { CommonStatusEnum, DICT_TYPE, MesAutoCodeRuleCode } from '@vben/constants'; import { getDictOptions } from '@vben/hooks'; import { Button } from 'ant-design-vue'; diff --git a/apps/web-ele/src/views/mes/dv/checkplan/components/select.vue b/apps/web-ele/src/views/mes/dv/checkplan/components/select.vue index 81d905dc3..12009bbc8 100644 --- a/apps/web-ele/src/views/mes/dv/checkplan/components/select.vue +++ b/apps/web-ele/src/views/mes/dv/checkplan/components/select.vue @@ -7,6 +7,8 @@ import { ElOption, ElSelect } from 'element-plus'; import { getCheckPlanPage } from '#/api/mes/dv/checkplan'; +defineOptions({ name: 'DvCheckPlanSelect' }); + const props = withDefaults( defineProps<{ clearable?: boolean; @@ -46,13 +48,11 @@ async function getList() { function handleChange(value: number | string | undefined) { const planId = typeof value === 'number' ? value : undefined; emit('update:modelValue', planId); - emit( - 'change', - list.value.find((item) => item.id === planId), - ); + emit('change', list.value.find((item) => item.id === planId)); } watch(() => [props.status, props.type], getList); + onMounted(getList); @@ -66,6 +66,11 @@ onMounted(getList); filterable @change="handleChange" > - + diff --git a/apps/web-ele/src/views/mes/dv/machinery/components/select.vue b/apps/web-ele/src/views/mes/dv/machinery/components/select.vue index 09f55a66b..f16f10e0e 100644 --- a/apps/web-ele/src/views/mes/dv/machinery/components/select.vue +++ b/apps/web-ele/src/views/mes/dv/machinery/components/select.vue @@ -7,6 +7,8 @@ import { ElOption, ElSelect } from 'element-plus'; import { getMachinerySimpleList } from '#/api/mes/dv/machinery'; +defineOptions({ name: 'DvMachinerySelect' }); + withDefaults( defineProps<{ clearable?: boolean; @@ -14,7 +16,12 @@ withDefaults( modelValue?: number; placeholder?: string; }>(), - { clearable: true, disabled: false, modelValue: undefined, placeholder: '请选择设备' }, + { + clearable: true, + disabled: false, + modelValue: undefined, + placeholder: '请选择设备', + }, ); const emit = defineEmits<{ change: [row?: MesDvMachineryApi.Machinery]; @@ -31,10 +38,7 @@ async function getList() { function handleChange(value: number | string | undefined) { const machineryId = typeof value === 'number' ? value : undefined; emit('update:modelValue', machineryId); - emit( - 'change', - list.value.find((item) => item.id === machineryId), - ); + emit('change', list.value.find((item) => item.id === machineryId)); } onMounted(getList); @@ -50,6 +54,11 @@ onMounted(getList); filterable @change="handleChange" > - + diff --git a/apps/web-ele/src/views/mes/dv/machinery/type/data.ts b/apps/web-ele/src/views/mes/dv/machinery/type/data.ts index e5a20b1c7..8ec5ebb5d 100644 --- a/apps/web-ele/src/views/mes/dv/machinery/type/data.ts +++ b/apps/web-ele/src/views/mes/dv/machinery/type/data.ts @@ -2,9 +2,9 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { MesDvMachineryTypeApi } from '#/api/mes/dv/machinery/type'; -import { DICT_TYPE, h } from 'vue'; +import { h } from 'vue'; -import { CommonStatusEnum, MesAutoCodeRuleCode } from '@vben/constants'; +import { CommonStatusEnum, DICT_TYPE, MesAutoCodeRuleCode } from '@vben/constants'; import { getDictOptions } from '@vben/hooks'; import { handleTree } from '@vben/utils'; diff --git a/apps/web-ele/src/views/mes/dv/subject/components/select.vue b/apps/web-ele/src/views/mes/dv/subject/components/select.vue index 36e75fb68..918efe5ee 100644 --- a/apps/web-ele/src/views/mes/dv/subject/components/select.vue +++ b/apps/web-ele/src/views/mes/dv/subject/components/select.vue @@ -7,6 +7,8 @@ import { ElOption, ElSelect } from 'element-plus'; import { getSubjectSimpleList } from '#/api/mes/dv/subject'; +defineOptions({ name: 'DvSubjectSelect' }); + const props = withDefaults( defineProps<{ clearable?: boolean; @@ -28,7 +30,7 @@ const emit = defineEmits<{ 'update:modelValue': [value?: number]; }>(); const list = ref([]); // 项目列表 -const filteredList = computed( // 筛选后的项目列表 +const filteredList = computed( () => list.value.filter((item) => !props.type || item.type === props.type), ); @@ -41,10 +43,7 @@ async function getList() { function handleChange(value: number | string | undefined) { const subjectId = typeof value === 'number' ? value : undefined; emit('update:modelValue', subjectId); - emit( - 'change', - list.value.find((item) => item.id === subjectId), - ); + emit('change', list.value.find((item) => item.id === subjectId)); } onMounted(getList); @@ -60,6 +59,11 @@ onMounted(getList); filterable @change="handleChange" > - + diff --git a/apps/web-ele/src/views/mes/dv/subject/data.ts b/apps/web-ele/src/views/mes/dv/subject/data.ts index 5a778f118..f35bffd20 100644 --- a/apps/web-ele/src/views/mes/dv/subject/data.ts +++ b/apps/web-ele/src/views/mes/dv/subject/data.ts @@ -2,9 +2,14 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { MesDvSubjectApi } from '#/api/mes/dv/subject'; -import { DICT_TYPE, h } from 'vue'; +import { h } from 'vue'; -import { CommonStatusEnum, MesAutoCodeRuleCode, MesDvSubjectTypeEnum } from '@vben/constants'; +import { + CommonStatusEnum, + DICT_TYPE, + MesAutoCodeRuleCode, + MesDvSubjectTypeEnum, +} from '@vben/constants'; import { getDictOptions } from '@vben/hooks'; import { ElButton } from 'element-plus'; diff --git a/apps/web-ele/src/views/mes/md/client/components/select-dialog.vue b/apps/web-ele/src/views/mes/md/client/components/select-dialog.vue index 570b5bdff..8076bdee5 100644 --- a/apps/web-ele/src/views/mes/md/client/components/select-dialog.vue +++ b/apps/web-ele/src/views/mes/md/client/components/select-dialog.vue @@ -16,6 +16,8 @@ import { useClientSelectGridFormSchema, } from '../data'; +defineOptions({ name: 'MdClientSelectDialog' }); + const emit = defineEmits<{ selected: [rows: MesMdClientApi.Client[]]; }>(); @@ -24,43 +26,12 @@ const open = ref(false); // 弹窗是否打开 const multiple = ref(true); // 是否多选 const selectedRows = ref([]); // 已选客户列表 const preSelectedIds = ref([]); // 预选客户编号列表 -const latestQueryRows = ref([]); // 最近一次查询返回的客户列表 -const queryFinished = ref(false); // 最近一次查询是否完成 - -// TODO @芋艿:是否有必要搞的这么复杂???后续测试看看。 -const MAX_TABLE_READY_FRAMES = 60; - -/** 等待下一帧 */ -function waitNextFrame(): Promise { - return new Promise((resolve) => { - requestAnimationFrame(() => resolve()); - }); -} /** 获取当前表格数据 */ function getTableRows() { return gridApi.grid.getTableData().fullData as MesMdClientApi.Client[]; } -/** 等待 VXE 将当前查询结果写入表格数据后再回显选中 */ -async function waitTableReady(): Promise { - if (preSelectedIds.value.length === 0) { - return; - } - for (let index = 0; index < MAX_TABLE_READY_FRAMES; index += 1) { - if (queryFinished.value) { - const rows = getTableRows(); - if (latestQueryRows.value.length === 0 && rows.length === 0) { - return; - } - if (latestQueryRows.value.length > 0 && rows.length > 0) { - return; - } - } - await waitNextFrame(); - } -} - /** 获取多选记录,包含 VXE reserve 跨页记录 */ function getMultipleSelectedRows() { const selectedMap = new Map(); @@ -69,8 +40,9 @@ function getMultipleSelectedRows() { ...(gridApi.grid.getCheckboxRecords?.() ?? []), ] as MesMdClientApi.Client[]; records.forEach((row) => { - if (row.id !== null) { - selectedMap.set(row.id as number, row); + const rowId = row.id; + if (rowId != null) { + selectedMap.set(rowId, row); } }); return [...selectedMap.values()]; @@ -109,10 +81,9 @@ async function applyPreSelection() { if (preSelectedIds.value.length === 0) { return; } - // proxy 表格回显选中时要读取 fullData,否则首次打开可能读不到刚查询出的数据。 const rows = getTableRows(); for (const row of rows) { - if (row.id === null || !preSelectedIds.value.includes(row.id as number)) { + if (row.id == null || !preSelectedIds.value.includes(row.id)) { continue; } if (multiple.value) { @@ -148,15 +119,12 @@ const [Grid, gridApi] = useVbenVxeGrid({ proxyConfig: { ajax: { query: async ({ page }, formValues) => { - const data = await getClientPage({ + return await getClientPage({ pageNo: page.currentPage, pageSize: page.pageSize, ...formValues, status: CommonStatusEnum.ENABLE, }); - latestQueryRows.value = data.list || []; - queryFinished.value = true; - return data; }, }, }, @@ -196,8 +164,6 @@ async function openModal( open.value = true; multiple.value = options?.multiple ?? true; preSelectedIds.value = selectedIds || []; - latestQueryRows.value = []; - queryFinished.value = false; await nextTick(); gridApi.setGridOptions({ columns: useClientSelectGridColumns(multiple.value), @@ -205,13 +171,13 @@ async function openModal( await resetQueryState(); await gridApi.query(); await nextTick(); - await waitTableReady(); await applyPreSelection(); } /** 关闭客户选择弹窗 */ -function closeModal() { +async function closeModal() { open.value = false; + await resetQueryState(); } /** 确认选择客户 */ diff --git a/apps/web-ele/src/views/mes/md/client/components/select.vue b/apps/web-ele/src/views/mes/md/client/components/select.vue index eda669a31..6fe4417f0 100644 --- a/apps/web-ele/src/views/mes/md/client/components/select.vue +++ b/apps/web-ele/src/views/mes/md/client/components/select.vue @@ -38,17 +38,16 @@ const selectedItem = ref(); // 当前选中客户 const displayLabel = computed(() => selectedItem.value?.name ?? ''); // 选择器展示名称 const showClear = computed( - // 是否显示清空图标 () => props.clearable && !props.disabled && hovering.value && - props.modelValue !== null, + props.modelValue != null, ); /** 根据客户编号回显选择器 */ async function resolveItemById(id: number | undefined) { - if (id === null) { + if (id == null) { selectedItem.value = undefined; return; } @@ -56,7 +55,7 @@ async function resolveItemById(id: number | undefined) { return; } try { - selectedItem.value = await getClient(id as number); + selectedItem.value = await getClient(id); } catch (error) { console.error('[MdClientSelect] resolveItemById failed:', error); } @@ -88,8 +87,8 @@ function handleClick(event: MouseEvent) { clearSelected(); return; } - const selectedIds = props.modelValue === null ? [] : [props.modelValue]; - dialogRef.value?.open(selectedIds as number[], { multiple: false }); + const selectedIds = props.modelValue == null ? [] : [props.modelValue]; + dialogRef.value?.open(selectedIds, { multiple: false }); } /** 回填选中的客户 */ diff --git a/apps/web-ele/src/views/mes/md/client/data.ts b/apps/web-ele/src/views/mes/md/client/data.ts index e254e0576..97363e232 100644 --- a/apps/web-ele/src/views/mes/md/client/data.ts +++ b/apps/web-ele/src/views/mes/md/client/data.ts @@ -2,9 +2,9 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { MesMdClientApi } from '#/api/mes/md/client'; -import { DICT_TYPE, h } from 'vue'; +import { h } from 'vue'; -import { CommonStatusEnum, MesAutoCodeRuleCode } from '@vben/constants'; +import { CommonStatusEnum, DICT_TYPE, MesAutoCodeRuleCode } from '@vben/constants'; import { getDictOptions } from '@vben/hooks'; import { ElButton } from 'element-plus'; diff --git a/apps/web-ele/src/views/mes/md/item/components/product-bom-select-dialog.vue b/apps/web-ele/src/views/mes/md/item/components/product-bom-select-dialog.vue index ebde34dcd..0938dc898 100644 --- a/apps/web-ele/src/views/mes/md/item/components/product-bom-select-dialog.vue +++ b/apps/web-ele/src/views/mes/md/item/components/product-bom-select-dialog.vue @@ -68,7 +68,7 @@ async function openModal(itemId: number, selectedBomItemId?: number) { list.value = await getProductBomListByItemId(itemId); gridApi.setGridOptions({ data: list.value }); await nextTick(); - if (selectedBomItemId !== null) { + if (selectedBomItemId != null) { const match = list.value.find( (row) => row.bomItemId === selectedBomItemId, ); diff --git a/apps/web-ele/src/views/mes/md/item/components/product-bom-select.vue b/apps/web-ele/src/views/mes/md/item/components/product-bom-select.vue index 2a8ce7e1e..9661df112 100644 --- a/apps/web-ele/src/views/mes/md/item/components/product-bom-select.vue +++ b/apps/web-ele/src/views/mes/md/item/components/product-bom-select.vue @@ -40,17 +40,16 @@ const selectedBom = ref(); // 当前选中的 BOM const displayLabel = computed(() => selectedBom.value?.bomItemName ?? ''); // 选择器展示名称 const showClear = computed( - // 是否显示清空图标 () => props.clearable && !props.disabled && hovering.value && - props.modelValue !== null, + props.modelValue != null, ); /** 根据 BOM 子物料编号回显选择器 */ async function resolveBomById(bomItemId: number | undefined) { - if (bomItemId === null || props.itemId === null) { + if (bomItemId == null || props.itemId == null) { selectedBom.value = undefined; return; } @@ -58,7 +57,7 @@ async function resolveBomById(bomItemId: number | undefined) { return; } try { - const list = await getProductBomListByItemId(props.itemId as number); + const list = await getProductBomListByItemId(props.itemId); selectedBom.value = list.find((item) => item.bomItemId === bomItemId); } catch (error) { console.error('[MdProductBomSelect] resolveBomById failed:', error); @@ -91,7 +90,7 @@ function clearSelected() { /** 打开 BOM 物料选择弹窗 */ function handleClick(event: MouseEvent) { - if (props.disabled || props.itemId === null) { + if (props.disabled || props.itemId == null) { return; } const target = event.target as HTMLElement; @@ -100,7 +99,7 @@ function handleClick(event: MouseEvent) { clearSelected(); return; } - dialogRef.value?.open(props.itemId as number, props.modelValue); + dialogRef.value?.open(props.itemId, props.modelValue); } /** 回填选中的 BOM 物料 */ diff --git a/apps/web-ele/src/views/mes/md/item/components/select-dialog.vue b/apps/web-ele/src/views/mes/md/item/components/select-dialog.vue index 51f1cc0a5..e0985673c 100644 --- a/apps/web-ele/src/views/mes/md/item/components/select-dialog.vue +++ b/apps/web-ele/src/views/mes/md/item/components/select-dialog.vue @@ -23,52 +23,58 @@ const emit = defineEmits<{ const open = ref(false); // 弹窗是否打开 const multiple = ref(true); // 是否多选 -const syncingSingleSelection = ref(false); // 是否同步单选勾选状态 const selectedRows = ref([]); // 已选物料列表 const selectedItemTypeId = ref(); // 当前筛选分类编号 const preSelectedIds = ref([]); // 预选物料编号列表 const typeTreeRef = ref>(); // 物料分类树 -/** 单选模式下同步 VXE 勾选状态,避免跨页残留多选 */ -async function syncSingleSelection(row?: MesMdItemApi.Item) { - syncingSingleSelection.value = true; - await nextTick(); - await gridApi.grid.clearCheckboxRow(); - if (row) { - await gridApi.grid.setCheckboxRow(row, true); - } - await nextTick(); - syncingSingleSelection.value = false; +/** 获取当前表格数据 */ +function getTableRows() { + return gridApi.grid.getTableData().fullData as MesMdItemApi.Item[]; } -/** 处理勾选变化,单选模式只保留最后一条 */ -async function handleCheckboxChange({ - checked, - records, - row, -}: { - checked: boolean; - records: MesMdItemApi.Item[]; - row?: MesMdItemApi.Item; -}) { - if (syncingSingleSelection.value) { - return; - } - if (!multiple.value) { - const selected = checked && row ? [row] : []; - selectedRows.value = selected; - await syncSingleSelection(selected[0]); - return; - } - selectedRows.value = records; +/** 获取多选记录,包含 VXE reserve 跨页记录 */ +function getMultipleSelectedRows() { + const selectedMap = new Map(); + const records = [ + ...(gridApi.grid.getCheckboxReserveRecords?.() ?? []), + ...(gridApi.grid.getCheckboxRecords?.() ?? []), + ] as MesMdItemApi.Item[]; + records.forEach((row) => { + const rowId = row.id; + if (rowId != null) { + selectedMap.set(rowId, row); + } + }); + return [...selectedMap.values()]; } -/** 处理全选变化 */ -function handleCheckboxAll({ records }: { records: MesMdItemApi.Item[] }) { - if (syncingSingleSelection.value) { +/** 处理勾选变化 */ +function handleCheckboxSelectChange() { + selectedRows.value = getMultipleSelectedRows(); +} + +/** 处理单选变化 */ +function handleRadioChange(row: MesMdItemApi.Item) { + selectedRows.value = [row]; +} + +/** 多选模式下切换行勾选 */ +async function toggleMultipleRow(row: MesMdItemApi.Item) { + const selected = gridApi.grid.isCheckedByCheckboxRow(row); + await gridApi.grid.setCheckboxRow(row, !selected); + selectedRows.value = getMultipleSelectedRows(); +} + +/** 处理行双击 */ +async function handleCellDblclick({ row }: { row: MesMdItemApi.Item }) { + if (multiple.value) { + await toggleMultipleRow(row); return; } - selectedRows.value = records; + selectedRows.value = [row]; + await gridApi.grid.setRadioRow(row); + handleConfirm(); } /** 按分类筛选物料 */ @@ -78,18 +84,25 @@ function handleItemTypeNodeClick(row: MesMdItemTypeApi.ItemType | undefined) { } /** 回显预选物料 */ -function applyPreSelection() { +async function applyPreSelection() { if (preSelectedIds.value.length === 0) { return; } - const rows = gridApi.grid.getData() as MesMdItemApi.Item[]; + const rows = getTableRows(); for (const row of rows) { - if (row.id && preSelectedIds.value.includes(row.id)) { - gridApi.grid.setCheckboxRow(row, true); - if (!multiple.value) { - selectedRows.value = [row]; - } + if (row.id == null || !preSelectedIds.value.includes(row.id)) { + continue; } + if (multiple.value) { + await gridApi.grid.setCheckboxRow(row, true); + } else { + await gridApi.grid.setRadioRow(row); + selectedRows.value = [row]; + return; + } + } + if (multiple.value) { + selectedRows.value = getMultipleSelectedRows(); } } @@ -98,7 +111,7 @@ const [Grid, gridApi] = useVbenVxeGrid({ schema: useItemSelectGridFormSchema(), }, gridOptions: { - columns: useItemSelectGridColumns(), + columns: useItemSelectGridColumns(true), height: 560, keepSource: true, checkboxConfig: { @@ -106,6 +119,10 @@ const [Grid, gridApi] = useVbenVxeGrid({ range: true, reserve: true, }, + radioConfig: { + highlight: true, + trigger: 'row', + }, proxyConfig: { ajax: { query: async ({ page }, formValues) => { @@ -129,8 +146,12 @@ const [Grid, gridApi] = useVbenVxeGrid({ }, } as VxeTableGridOptions, gridEvents: { - checkboxAll: handleCheckboxAll, - checkboxChange: handleCheckboxChange, + cellDblclick: handleCellDblclick, + checkboxAll: handleCheckboxSelectChange, + checkboxChange: handleCheckboxSelectChange, + radioChange: ({ row }: { row: MesMdItemApi.Item }) => { + handleRadioChange(row); + }, }, }); @@ -140,6 +161,8 @@ async function resetQueryState() { selectedRows.value = []; typeTreeRef.value?.reset(); await gridApi.grid.clearCheckboxRow(); + await gridApi.grid.clearCheckboxReserve(); + await gridApi.grid.clearRadioRow(); await gridApi.formApi.resetForm(); } @@ -152,10 +175,13 @@ async function openModal( multiple.value = options?.multiple ?? true; preSelectedIds.value = selectedIds || []; await nextTick(); + gridApi.setGridOptions({ + columns: useItemSelectGridColumns(multiple.value), + }); await resetQueryState(); await gridApi.query(); await nextTick(); - applyPreSelection(); + await applyPreSelection(); } /** 关闭物料选择弹窗 */ @@ -166,14 +192,12 @@ async function closeModal() { /** 确认选择物料 */ function handleConfirm() { - if (selectedRows.value.length === 0) { + const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value; + if (rows.length === 0) { ElMessage.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据'); return; } - emit( - 'selected', - multiple.value ? selectedRows.value : [selectedRows.value[0]!], - ); + emit('selected', multiple.value ? rows : [rows[0]!]); open.value = false; } diff --git a/apps/web-ele/src/views/mes/md/item/components/select.vue b/apps/web-ele/src/views/mes/md/item/components/select.vue index 577c7a669..71de21f95 100644 --- a/apps/web-ele/src/views/mes/md/item/components/select.vue +++ b/apps/web-ele/src/views/mes/md/item/components/select.vue @@ -38,17 +38,16 @@ const selectedItem = ref(); // 当前选中物料 const displayLabel = computed(() => selectedItem.value?.name ?? ''); // 选择器展示名称 const showClear = computed( - // 是否显示清空图标 () => props.clearable && !props.disabled && hovering.value && - props.modelValue !== null, + props.modelValue != null, ); /** 根据物料编号回显选择器 */ async function resolveItemById(id: number | undefined) { - if (id === null) { + if (id == null) { selectedItem.value = undefined; return; } @@ -56,7 +55,7 @@ async function resolveItemById(id: number | undefined) { return; } try { - selectedItem.value = await getItem(id as number); + selectedItem.value = await getItem(id); } catch (error) { console.error('[MdItemSelect] resolveItemById failed:', error); } @@ -88,8 +87,8 @@ function handleClick(event: MouseEvent) { clearSelected(); return; } - const selectedIds = props.modelValue === null ? [] : [props.modelValue]; - dialogRef.value?.open(selectedIds as number[], { multiple: false }); + const selectedIds = props.modelValue == null ? [] : [props.modelValue]; + dialogRef.value?.open(selectedIds, { multiple: false }); } /** 回填选中的物料 */ diff --git a/apps/web-ele/src/views/mes/md/item/data.ts b/apps/web-ele/src/views/mes/md/item/data.ts index 5a6de2a82..970423c67 100644 --- a/apps/web-ele/src/views/mes/md/item/data.ts +++ b/apps/web-ele/src/views/mes/md/item/data.ts @@ -3,9 +3,9 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { MesMdItemApi } from '#/api/mes/md/item'; import type { MesMdProductBomApi } from '#/api/mes/md/item/productBom'; -import { DICT_TYPE, h, markRaw } from 'vue'; +import { h, markRaw } from 'vue'; -import { CommonStatusEnum, MesAutoCodeRuleCode } from '@vben/constants'; +import { CommonStatusEnum, DICT_TYPE, MesAutoCodeRuleCode } from '@vben/constants'; import { getDictOptions } from '@vben/hooks'; import { ElButton } from 'element-plus'; @@ -360,9 +360,11 @@ export function useItemSelectGridFormSchema(): VbenFormSchema[] { } /** 物料选择弹窗列表字段 */ -export function useItemSelectGridColumns(): VxeTableGridOptions['columns'] { +export function useItemSelectGridColumns( + multiple = true, +): VxeTableGridOptions['columns'] { return [ - { type: 'checkbox', width: 50 }, + { type: multiple ? 'checkbox' : 'radio', width: 50 }, { field: 'code', title: '物料编码', diff --git a/apps/web-ele/src/views/mes/md/item/type/data.ts b/apps/web-ele/src/views/mes/md/item/type/data.ts index bc6152f8e..fd3b623e9 100644 --- a/apps/web-ele/src/views/mes/md/item/type/data.ts +++ b/apps/web-ele/src/views/mes/md/item/type/data.ts @@ -2,9 +2,14 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { MesMdItemTypeApi } from '#/api/mes/md/item/type'; -import { DICT_TYPE, h } from 'vue'; +import { h } from 'vue'; -import { CommonStatusEnum, MesAutoCodeRuleCode, MesItemOrProductEnum } from '@vben/constants'; +import { + CommonStatusEnum, + DICT_TYPE, + MesAutoCodeRuleCode, + MesItemOrProductEnum, +} from '@vben/constants'; import { getDictOptions } from '@vben/hooks'; import { handleTree } from '@vben/utils'; diff --git a/apps/web-ele/src/views/mes/md/vendor/components/select-dialog.vue b/apps/web-ele/src/views/mes/md/vendor/components/select-dialog.vue index 3d42266bc..b37d2e68c 100644 --- a/apps/web-ele/src/views/mes/md/vendor/components/select-dialog.vue +++ b/apps/web-ele/src/views/mes/md/vendor/components/select-dialog.vue @@ -16,71 +16,86 @@ import { useVendorSelectGridFormSchema, } from '../data'; +defineOptions({ name: 'MdVendorSelectDialog' }); + const emit = defineEmits<{ selected: [rows: MesMdVendorApi.Vendor[]]; }>(); const open = ref(false); // 弹窗是否打开 const multiple = ref(true); // 是否多选 -const syncingSingleSelection = ref(false); // 是否同步单选勾选状态 const selectedRows = ref([]); // 已选供应商列表 const preSelectedIds = ref([]); // 预选供应商编号列表 -/** 单选模式下同步 VXE 勾选状态,避免跨页残留多选 */ -async function syncSingleSelection(row?: MesMdVendorApi.Vendor) { - syncingSingleSelection.value = true; - await nextTick(); - await gridApi.grid.clearCheckboxRow(); - if (row) { - await gridApi.grid.setCheckboxRow(row, true); - } - await nextTick(); - syncingSingleSelection.value = false; +/** 获取当前表格数据 */ +function getTableRows() { + return gridApi.grid.getTableData().fullData as MesMdVendorApi.Vendor[]; } -/** 处理勾选变化,单选模式只保留最后一条 */ -async function handleCheckboxChange({ - checked, - records, - row, -}: { - checked: boolean; - records: MesMdVendorApi.Vendor[]; - row?: MesMdVendorApi.Vendor; -}) { - if (syncingSingleSelection.value) { - return; - } - if (!multiple.value) { - const selected = checked && row ? [row] : []; - selectedRows.value = selected; - await syncSingleSelection(selected[0]); - return; - } - selectedRows.value = records; +/** 获取多选记录,包含 VXE reserve 跨页记录 */ +function getMultipleSelectedRows() { + const selectedMap = new Map(); + const records = [ + ...(gridApi.grid.getCheckboxReserveRecords?.() ?? []), + ...(gridApi.grid.getCheckboxRecords?.() ?? []), + ] as MesMdVendorApi.Vendor[]; + records.forEach((row) => { + const rowId = row.id; + if (rowId != null) { + selectedMap.set(rowId, row); + } + }); + return [...selectedMap.values()]; } -/** 处理全选变化 */ -function handleCheckboxAll({ records }: { records: MesMdVendorApi.Vendor[] }) { - if (syncingSingleSelection.value) { +/** 处理勾选变化 */ +function handleCheckboxSelectChange() { + selectedRows.value = getMultipleSelectedRows(); +} + +/** 处理单选变化 */ +function handleRadioChange(row: MesMdVendorApi.Vendor) { + selectedRows.value = [row]; +} + +/** 多选模式下切换行勾选 */ +async function toggleMultipleRow(row: MesMdVendorApi.Vendor) { + const selected = gridApi.grid.isCheckedByCheckboxRow(row); + await gridApi.grid.setCheckboxRow(row, !selected); + selectedRows.value = getMultipleSelectedRows(); +} + +/** 处理行双击 */ +async function handleCellDblclick({ row }: { row: MesMdVendorApi.Vendor }) { + if (multiple.value) { + await toggleMultipleRow(row); return; } - selectedRows.value = records; + selectedRows.value = [row]; + await gridApi.grid.setRadioRow(row); + handleConfirm(); } /** 回显预选供应商 */ -function applyPreSelection() { +async function applyPreSelection() { if (preSelectedIds.value.length === 0) { return; } - const rows = gridApi.grid.getData() as MesMdVendorApi.Vendor[]; + const rows = getTableRows(); for (const row of rows) { - if (row.id && preSelectedIds.value.includes(row.id)) { - gridApi.grid.setCheckboxRow(row, true); - if (!multiple.value) { - selectedRows.value = [row]; - } + if (row.id == null || !preSelectedIds.value.includes(row.id)) { + continue; } + if (multiple.value) { + await gridApi.grid.setCheckboxRow(row, true); + } else { + await gridApi.grid.setRadioRow(row); + selectedRows.value = [row]; + return; + } + } + if (multiple.value) { + selectedRows.value = getMultipleSelectedRows(); } } @@ -89,7 +104,7 @@ const [Grid, gridApi] = useVbenVxeGrid({ schema: useVendorSelectGridFormSchema(), }, gridOptions: { - columns: useVendorSelectGridColumns(), + columns: useVendorSelectGridColumns(true), height: 520, keepSource: true, checkboxConfig: { @@ -97,6 +112,10 @@ const [Grid, gridApi] = useVbenVxeGrid({ range: true, reserve: true, }, + radioConfig: { + highlight: true, + trigger: 'row', + }, proxyConfig: { ajax: { query: async ({ page }, formValues) => { @@ -119,8 +138,12 @@ const [Grid, gridApi] = useVbenVxeGrid({ }, } as VxeTableGridOptions, gridEvents: { - checkboxAll: handleCheckboxAll, - checkboxChange: handleCheckboxChange, + cellDblclick: handleCellDblclick, + checkboxAll: handleCheckboxSelectChange, + checkboxChange: handleCheckboxSelectChange, + radioChange: ({ row }: { row: MesMdVendorApi.Vendor }) => { + handleRadioChange(row); + }, }, }); @@ -128,6 +151,8 @@ const [Grid, gridApi] = useVbenVxeGrid({ async function resetQueryState() { selectedRows.value = []; await gridApi.grid.clearCheckboxRow(); + await gridApi.grid.clearCheckboxReserve(); + await gridApi.grid.clearRadioRow(); await gridApi.formApi.resetForm(); } @@ -140,10 +165,13 @@ async function openModal( multiple.value = options?.multiple ?? true; preSelectedIds.value = selectedIds || []; await nextTick(); + gridApi.setGridOptions({ + columns: useVendorSelectGridColumns(multiple.value), + }); await resetQueryState(); await gridApi.query(); await nextTick(); - applyPreSelection(); + await applyPreSelection(); } /** 关闭供应商选择弹窗 */ @@ -154,14 +182,12 @@ async function closeModal() { /** 确认选择供应商 */ function handleConfirm() { - if (selectedRows.value.length === 0) { + const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value; + if (rows.length === 0) { ElMessage.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据'); return; } - emit( - 'selected', - multiple.value ? selectedRows.value : [selectedRows.value[0]!], - ); + emit('selected', multiple.value ? rows : [rows[0]!]); open.value = false; } diff --git a/apps/web-ele/src/views/mes/md/vendor/components/select.vue b/apps/web-ele/src/views/mes/md/vendor/components/select.vue index 3a378b992..2d1973464 100644 --- a/apps/web-ele/src/views/mes/md/vendor/components/select.vue +++ b/apps/web-ele/src/views/mes/md/vendor/components/select.vue @@ -38,17 +38,16 @@ const selectedItem = ref(); // 当前选中供应商 const displayLabel = computed(() => selectedItem.value?.name ?? ''); // 选择器展示名称 const showClear = computed( - // 是否显示清空图标 () => props.clearable && !props.disabled && hovering.value && - props.modelValue !== null, + props.modelValue != null, ); /** 根据供应商编号回显选择器 */ async function resolveItemById(id: number | undefined) { - if (id === null) { + if (id == null) { selectedItem.value = undefined; return; } @@ -56,7 +55,7 @@ async function resolveItemById(id: number | undefined) { return; } try { - selectedItem.value = await getVendor(id as number); + selectedItem.value = await getVendor(id); } catch (error) { console.error('[MdVendorSelect] resolveItemById failed:', error); } @@ -88,8 +87,8 @@ function handleClick(event: MouseEvent) { clearSelected(); return; } - const selectedIds = props.modelValue === null ? [] : [props.modelValue]; - dialogRef.value?.open(selectedIds as number[], { multiple: false }); + const selectedIds = props.modelValue == null ? [] : [props.modelValue]; + dialogRef.value?.open(selectedIds, { multiple: false }); } /** 回填选中的供应商 */ diff --git a/apps/web-ele/src/views/mes/md/vendor/data.ts b/apps/web-ele/src/views/mes/md/vendor/data.ts index 7f10ba16e..3aed1d87c 100644 --- a/apps/web-ele/src/views/mes/md/vendor/data.ts +++ b/apps/web-ele/src/views/mes/md/vendor/data.ts @@ -2,9 +2,9 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { MesMdVendorApi } from '#/api/mes/md/vendor'; -import { DICT_TYPE, h } from 'vue'; +import { h } from 'vue'; -import { CommonStatusEnum, MesAutoCodeRuleCode } from '@vben/constants'; +import { CommonStatusEnum, DICT_TYPE, MesAutoCodeRuleCode } from '@vben/constants'; import { getDictOptions } from '@vben/hooks'; import { ElButton } from 'element-plus'; @@ -415,9 +415,11 @@ export function useVendorSelectGridFormSchema(): VbenFormSchema[] { } /** 供应商选择弹窗的字段 */ -export function useVendorSelectGridColumns(): VxeTableGridOptions['columns'] { +export function useVendorSelectGridColumns( + multiple = true, +): VxeTableGridOptions['columns'] { return [ - { type: 'checkbox', width: 50 }, + { type: multiple ? 'checkbox' : 'radio', width: 50 }, { field: 'code', title: '供应商编码', diff --git a/apps/web-ele/src/views/mes/md/workstation/components/select.vue b/apps/web-ele/src/views/mes/md/workstation/components/select.vue index 1f34206a8..c8b677e3e 100644 --- a/apps/web-ele/src/views/mes/md/workstation/components/select.vue +++ b/apps/web-ele/src/views/mes/md/workstation/components/select.vue @@ -40,12 +40,11 @@ const selectedItem = ref(); // 选中的工作 const displayLabel = computed(() => selectedItem.value?.name ?? ''); // 选择器展示名称 const showClear = computed( - // 是否显示清空图标 () => props.clearable && !props.disabled && hovering.value && - props.modelValue !== null, + props.modelValue != null, ); /** 根据工作站编号回显选择器 */ diff --git a/apps/web-ele/src/views/mes/md/workstation/data.ts b/apps/web-ele/src/views/mes/md/workstation/data.ts index f074a6170..6825b3868 100644 --- a/apps/web-ele/src/views/mes/md/workstation/data.ts +++ b/apps/web-ele/src/views/mes/md/workstation/data.ts @@ -2,9 +2,9 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { MesMdWorkstationApi } from '#/api/mes/md/workstation'; -import { DICT_TYPE, h, markRaw } from 'vue'; +import { h, markRaw } from 'vue'; -import { CommonStatusEnum, MesAutoCodeRuleCode } from '@vben/constants'; +import { CommonStatusEnum, DICT_TYPE, MesAutoCodeRuleCode } from '@vben/constants'; import { getDictOptions } from '@vben/hooks'; import { ElButton } from 'element-plus'; diff --git a/apps/web-ele/src/views/mes/md/workstation/workshop/data.ts b/apps/web-ele/src/views/mes/md/workstation/workshop/data.ts index b7ba9f169..a3eae1bc1 100644 --- a/apps/web-ele/src/views/mes/md/workstation/workshop/data.ts +++ b/apps/web-ele/src/views/mes/md/workstation/workshop/data.ts @@ -2,9 +2,9 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { MesMdWorkshopApi } from '#/api/mes/md/workstation/workshop'; -import { DICT_TYPE, h } from 'vue'; +import { h } from 'vue'; -import { CommonStatusEnum, MesAutoCodeRuleCode } from '@vben/constants'; +import { CommonStatusEnum, DICT_TYPE, MesAutoCodeRuleCode } from '@vben/constants'; import { getDictOptions } from '@vben/hooks'; import { ElButton } from 'element-plus';