refactor(mes): 统一 antd 和 ele 选择组件代码风格

- 规范 select 空值判断和回显逻辑
- 统一物料、供应商、客户选择弹窗的单选/多选行为
- 清理 components 内 TODO 并修复相关 DICT_TYPE 导入
pull/351/MERGE
YunaiV 2026-05-30 19:03:57 +08:00
parent b3154ef87a
commit 22e9081a45
40 changed files with 484 additions and 419 deletions

View File

@ -9,6 +9,8 @@ import { Select } from 'ant-design-vue';
import { getCheckPlanPage } from '#/api/mes/dv/checkplan'; import { getCheckPlanPage } from '#/api/mes/dv/checkplan';
defineOptions({ name: 'DvCheckPlanSelect' });
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
allowClear?: boolean; allowClear?: boolean;
@ -48,15 +50,11 @@ async function getList() {
function handleChange(value: SelectValue) { function handleChange(value: SelectValue) {
const planId = typeof value === 'number' ? value : undefined; const planId = typeof value === 'number' ? value : undefined;
emit('update:modelValue', planId); 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 @AI2
watch(() => [props.status, props.type], getList); watch(() => [props.status, props.type], getList);
onMounted(getList); onMounted(getList);
</script> </script>

View File

@ -9,6 +9,8 @@ import { Select } from 'ant-design-vue';
import { getMachinerySimpleList } from '#/api/mes/dv/machinery'; import { getMachinerySimpleList } from '#/api/mes/dv/machinery';
defineOptions({ name: 'DvMachinerySelect' });
withDefaults( withDefaults(
defineProps<{ defineProps<{
allowClear?: boolean; allowClear?: boolean;
@ -16,7 +18,12 @@ withDefaults(
modelValue?: number; modelValue?: number;
placeholder?: string; placeholder?: string;
}>(), }>(),
{ allowClear: true, disabled: false, modelValue: undefined, placeholder: '请选择设备' }, {
allowClear: true,
disabled: false,
modelValue: undefined,
placeholder: '请选择设备',
},
); );
const emit = defineEmits<{ const emit = defineEmits<{
change: [row?: MesDvMachineryApi.Machinery]; change: [row?: MesDvMachineryApi.Machinery];
@ -33,13 +40,9 @@ async function getList() {
function handleChange(value: SelectValue) { function handleChange(value: SelectValue) {
const machineryId = typeof value === 'number' ? value : undefined; const machineryId = typeof value === 'number' ? value : undefined;
emit('update:modelValue', machineryId); emit('update:modelValue', machineryId);
emit( emit('change', list.value.find((item) => item.id === machineryId));
'change',
list.value.find((item) => item.id === machineryId),
);
} }
// TODO @AI /** */ style.vue
onMounted(getList); onMounted(getList);
</script> </script>

View File

@ -2,9 +2,9 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesDvMachineryTypeApi } from '#/api/mes/dv/machinery/type'; 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 { getDictOptions } from '@vben/hooks';
import { handleTree } from '@vben/utils'; import { handleTree } from '@vben/utils';

View File

@ -9,6 +9,8 @@ import { Select } from 'ant-design-vue';
import { getSubjectSimpleList } from '#/api/mes/dv/subject'; import { getSubjectSimpleList } from '#/api/mes/dv/subject';
defineOptions({ name: 'DvSubjectSelect' });
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
allowClear?: boolean; allowClear?: boolean;
@ -30,7 +32,7 @@ const emit = defineEmits<{
'update:modelValue': [value?: number]; 'update:modelValue': [value?: number];
}>(); }>();
const list = ref<MesDvSubjectApi.Subject[]>([]); // const list = ref<MesDvSubjectApi.Subject[]>([]); //
const filteredList = computed( // const filteredList = computed(
() => list.value.filter((item) => !props.type || item.type === props.type), () => list.value.filter((item) => !props.type || item.type === props.type),
); );
@ -43,10 +45,7 @@ async function getList() {
function handleChange(value: SelectValue) { function handleChange(value: SelectValue) {
const subjectId = typeof value === 'number' ? value : undefined; const subjectId = typeof value === 'number' ? value : undefined;
emit('update:modelValue', subjectId); emit('update:modelValue', subjectId);
emit( emit('change', list.value.find((item) => item.id === subjectId));
'change',
list.value.find((item) => item.id === subjectId),
);
} }
onMounted(getList); onMounted(getList);

View File

@ -2,9 +2,14 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesDvSubjectApi } from '#/api/mes/dv/subject'; 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 { getDictOptions } from '@vben/hooks';
import { Button } from 'ant-design-vue'; import { Button } from 'ant-design-vue';

View File

@ -16,6 +16,8 @@ import {
useClientSelectGridFormSchema, useClientSelectGridFormSchema,
} from '../data'; } from '../data';
defineOptions({ name: 'MdClientSelectDialog' });
const emit = defineEmits<{ const emit = defineEmits<{
selected: [rows: MesMdClientApi.Client[]]; selected: [rows: MesMdClientApi.Client[]];
}>(); }>();
@ -24,44 +26,12 @@ const open = ref(false); // 弹窗是否打开
const multiple = ref(true); // const multiple = ref(true); //
const selectedRows = ref<MesMdClientApi.Client[]>([]); // const selectedRows = ref<MesMdClientApi.Client[]>([]); //
const preSelectedIds = ref<number[]>([]); // const preSelectedIds = ref<number[]>([]); //
const latestQueryRows = ref<MesMdClientApi.Client[]>([]); //
const queryFinished = ref(false); //
// TODO @
// TODO @AI
const MAX_TABLE_READY_FRAMES = 60;
/** 等待下一帧 */
function waitNextFrame(): Promise<void> {
return new Promise((resolve) => {
requestAnimationFrame(() => resolve());
});
}
/** 获取当前表格数据 */ /** 获取当前表格数据 */
function getTableRows() { function getTableRows() {
return gridApi.grid.getTableData().fullData as MesMdClientApi.Client[]; return gridApi.grid.getTableData().fullData as MesMdClientApi.Client[];
} }
/** 等待 VXE 将当前查询结果写入表格数据后再回显选中 */
async function waitTableReady(): Promise<void> {
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 跨页记录 */ /** 获取多选记录,包含 VXE reserve 跨页记录 */
function getMultipleSelectedRows() { function getMultipleSelectedRows() {
const selectedMap = new Map<number, MesMdClientApi.Client>(); const selectedMap = new Map<number, MesMdClientApi.Client>();
@ -71,7 +41,7 @@ function getMultipleSelectedRows() {
] as MesMdClientApi.Client[]; ] as MesMdClientApi.Client[];
records.forEach((row) => { records.forEach((row) => {
const rowId = row.id; const rowId = row.id;
if (rowId !== null && rowId !== undefined) { if (rowId != null) {
selectedMap.set(rowId, row); selectedMap.set(rowId, row);
} }
}); });
@ -111,10 +81,9 @@ async function applyPreSelection() {
if (preSelectedIds.value.length === 0) { if (preSelectedIds.value.length === 0) {
return; return;
} }
// proxy fullData
const rows = getTableRows(); const rows = getTableRows();
for (const row of rows) { 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; continue;
} }
if (multiple.value) { if (multiple.value) {
@ -150,15 +119,12 @@ const [Grid, gridApi] = useVbenVxeGrid({
proxyConfig: { proxyConfig: {
ajax: { ajax: {
query: async ({ page }, formValues) => { query: async ({ page }, formValues) => {
const data = await getClientPage({ return await getClientPage({
pageNo: page.currentPage, pageNo: page.currentPage,
pageSize: page.pageSize, pageSize: page.pageSize,
...formValues, ...formValues,
status: CommonStatusEnum.ENABLE, status: CommonStatusEnum.ENABLE,
}); });
latestQueryRows.value = data.list || [];
queryFinished.value = true;
return data;
}, },
}, },
}, },
@ -198,8 +164,6 @@ async function openModal(
open.value = true; open.value = true;
multiple.value = options?.multiple ?? true; multiple.value = options?.multiple ?? true;
preSelectedIds.value = selectedIds || []; preSelectedIds.value = selectedIds || [];
latestQueryRows.value = [];
queryFinished.value = false;
await nextTick(); await nextTick();
gridApi.setGridOptions({ gridApi.setGridOptions({
columns: useClientSelectGridColumns(multiple.value), columns: useClientSelectGridColumns(multiple.value),
@ -207,13 +171,13 @@ async function openModal(
await resetQueryState(); await resetQueryState();
await gridApi.query(); await gridApi.query();
await nextTick(); await nextTick();
await waitTableReady();
await applyPreSelection(); await applyPreSelection();
} }
/** 关闭客户选择弹窗 */ /** 关闭客户选择弹窗 */
function closeModal() { async function closeModal() {
open.value = false; open.value = false;
await resetQueryState();
} }
/** 确认选择客户 */ /** 确认选择客户 */

View File

@ -38,18 +38,16 @@ const selectedItem = ref<MesMdClientApi.Client>(); // 当前选中客户
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // const displayLabel = computed(() => selectedItem.value?.name ?? ''); //
const showClear = computed( const showClear = computed(
// TODO @AI // computed(
//
() => () =>
props.allowClear && props.allowClear &&
!props.disabled && !props.disabled &&
hovering.value && hovering.value &&
props.modelValue !== null, props.modelValue != null,
); );
/** 根据客户编号回显选择器 */ /** 根据客户编号回显选择器 */
async function resolveItemById(id: number | undefined) { async function resolveItemById(id: number | undefined) {
if (id === null) { if (id == null) {
selectedItem.value = undefined; selectedItem.value = undefined;
return; return;
} }
@ -57,7 +55,7 @@ async function resolveItemById(id: number | undefined) {
return; return;
} }
try { try {
selectedItem.value = await getClient(id as number); selectedItem.value = await getClient(id);
} catch (error) { } catch (error) {
console.error('[MdClientSelect] resolveItemById failed:', error); console.error('[MdClientSelect] resolveItemById failed:', error);
} }
@ -89,8 +87,8 @@ function handleClick(event: MouseEvent) {
clearSelected(); clearSelected();
return; return;
} }
const selectedIds = props.modelValue === null ? [] : [props.modelValue]; const selectedIds = props.modelValue == null ? [] : [props.modelValue];
dialogRef.value?.open(selectedIds as number[], { multiple: false }); dialogRef.value?.open(selectedIds, { multiple: false });
} }
/** 回填选中的客户 */ /** 回填选中的客户 */

View File

@ -2,9 +2,9 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesMdClientApi } from '#/api/mes/md/client'; 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 { getDictOptions } from '@vben/hooks';
import { Button } from 'ant-design-vue'; import { Button } from 'ant-design-vue';

View File

@ -68,7 +68,7 @@ async function openModal(itemId: number, selectedBomItemId?: number) {
list.value = await getProductBomListByItemId(itemId); list.value = await getProductBomListByItemId(itemId);
gridApi.setGridOptions({ data: list.value }); gridApi.setGridOptions({ data: list.value });
await nextTick(); await nextTick();
if (selectedBomItemId !== null) { if (selectedBomItemId != null) {
const match = list.value.find( const match = list.value.find(
(row) => row.bomItemId === selectedBomItemId, (row) => row.bomItemId === selectedBomItemId,
); );

View File

@ -40,17 +40,16 @@ const selectedBom = ref<MesMdProductBomApi.ProductBom>(); // 当前选中的 BOM
const displayLabel = computed(() => selectedBom.value?.bomItemName ?? ''); // const displayLabel = computed(() => selectedBom.value?.bomItemName ?? ''); //
const showClear = computed( const showClear = computed(
//
() => () =>
props.allowClear && props.allowClear &&
!props.disabled && !props.disabled &&
hovering.value && hovering.value &&
props.modelValue !== null, props.modelValue != null,
); );
/** 根据 BOM 子物料编号回显选择器 */ /** 根据 BOM 子物料编号回显选择器 */
async function resolveBomById(bomItemId: number | undefined) { async function resolveBomById(bomItemId: number | undefined) {
if (bomItemId === null || props.itemId === null) { if (bomItemId == null || props.itemId == null) {
selectedBom.value = undefined; selectedBom.value = undefined;
return; return;
} }
@ -58,7 +57,7 @@ async function resolveBomById(bomItemId: number | undefined) {
return; return;
} }
try { try {
const list = await getProductBomListByItemId(props.itemId as number); const list = await getProductBomListByItemId(props.itemId);
selectedBom.value = list.find((item) => item.bomItemId === bomItemId); selectedBom.value = list.find((item) => item.bomItemId === bomItemId);
} catch (error) { } catch (error) {
console.error('[MdProductBomSelect] resolveBomById failed:', error); console.error('[MdProductBomSelect] resolveBomById failed:', error);
@ -91,7 +90,7 @@ function clearSelected() {
/** 打开 BOM 物料选择弹窗 */ /** 打开 BOM 物料选择弹窗 */
function handleClick(event: MouseEvent) { function handleClick(event: MouseEvent) {
if (props.disabled || props.itemId === null) { if (props.disabled || props.itemId == null) {
return; return;
} }
const target = event.target as HTMLElement; const target = event.target as HTMLElement;
@ -100,7 +99,7 @@ function handleClick(event: MouseEvent) {
clearSelected(); clearSelected();
return; return;
} }
dialogRef.value?.open(props.itemId as number, props.modelValue); dialogRef.value?.open(props.itemId, props.modelValue);
} }
/** 回填选中的 BOM 物料 */ /** 回填选中的 BOM 物料 */

View File

@ -23,52 +23,58 @@ const emit = defineEmits<{
const open = ref(false); // const open = ref(false); //
const multiple = ref(true); // const multiple = ref(true); //
const syncingSingleSelection = ref(false); //
const selectedRows = ref<MesMdItemApi.Item[]>([]); // const selectedRows = ref<MesMdItemApi.Item[]>([]); //
const selectedItemTypeId = ref<number>(); // const selectedItemTypeId = ref<number>(); //
const preSelectedIds = ref<number[]>([]); // const preSelectedIds = ref<number[]>([]); //
const typeTreeRef = ref<InstanceType<typeof MdItemTypeTree>>(); // const typeTreeRef = ref<InstanceType<typeof MdItemTypeTree>>(); //
/** 单选模式下同步 VXE 勾选状态,避免跨页残留多选 */ /** 获取当前表格数据 */
async function syncSingleSelection(row?: MesMdItemApi.Item) { function getTableRows() {
syncingSingleSelection.value = true; return gridApi.grid.getTableData().fullData as MesMdItemApi.Item[];
await nextTick();
await gridApi.grid.clearCheckboxRow();
if (row) {
await gridApi.grid.setCheckboxRow(row, true);
}
await nextTick();
syncingSingleSelection.value = false;
} }
/** 处理勾选变化,单选模式只保留最后一条 */ /** 获取多选记录,包含 VXE reserve 跨页记录 */
async function handleCheckboxChange({ function getMultipleSelectedRows() {
checked, const selectedMap = new Map<number, MesMdItemApi.Item>();
records, const records = [
row, ...(gridApi.grid.getCheckboxReserveRecords?.() ?? []),
}: { ...(gridApi.grid.getCheckboxRecords?.() ?? []),
checked: boolean; ] as MesMdItemApi.Item[];
records: MesMdItemApi.Item[]; records.forEach((row) => {
row?: MesMdItemApi.Item; const rowId = row.id;
}) { if (rowId != null) {
if (syncingSingleSelection.value) { selectedMap.set(rowId, row);
return; }
} });
if (!multiple.value) { return [...selectedMap.values()];
const selected = checked && row ? [row] : [];
selectedRows.value = selected;
await syncSingleSelection(selected[0]);
return;
}
selectedRows.value = records;
} }
/** 处理全选变化 */ /** 处理勾选变化 */
function handleCheckboxAll({ records }: { records: MesMdItemApi.Item[] }) { function handleCheckboxSelectChange() {
if (syncingSingleSelection.value) { 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; 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) { if (preSelectedIds.value.length === 0) {
return; return;
} }
const rows = gridApi.grid.getData() as MesMdItemApi.Item[]; const rows = getTableRows();
for (const row of rows) { for (const row of rows) {
if (row.id && preSelectedIds.value.includes(row.id)) { if (row.id == null || !preSelectedIds.value.includes(row.id)) {
gridApi.grid.setCheckboxRow(row, true); continue;
if (!multiple.value) {
selectedRows.value = [row];
}
} }
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(), schema: useItemSelectGridFormSchema(),
}, },
gridOptions: { gridOptions: {
columns: useItemSelectGridColumns(), columns: useItemSelectGridColumns(true),
height: 560, height: 560,
keepSource: true, keepSource: true,
checkboxConfig: { checkboxConfig: {
@ -106,6 +119,10 @@ const [Grid, gridApi] = useVbenVxeGrid({
range: true, range: true,
reserve: true, reserve: true,
}, },
radioConfig: {
highlight: true,
trigger: 'row',
},
proxyConfig: { proxyConfig: {
ajax: { ajax: {
query: async ({ page }, formValues) => { query: async ({ page }, formValues) => {
@ -129,8 +146,12 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
} as VxeTableGridOptions<MesMdItemApi.Item>, } as VxeTableGridOptions<MesMdItemApi.Item>,
gridEvents: { gridEvents: {
checkboxAll: handleCheckboxAll, cellDblclick: handleCellDblclick,
checkboxChange: handleCheckboxChange, checkboxAll: handleCheckboxSelectChange,
checkboxChange: handleCheckboxSelectChange,
radioChange: ({ row }: { row: MesMdItemApi.Item }) => {
handleRadioChange(row);
},
}, },
}); });
@ -140,6 +161,8 @@ async function resetQueryState() {
selectedRows.value = []; selectedRows.value = [];
typeTreeRef.value?.reset(); typeTreeRef.value?.reset();
await gridApi.grid.clearCheckboxRow(); await gridApi.grid.clearCheckboxRow();
await gridApi.grid.clearCheckboxReserve();
await gridApi.grid.clearRadioRow();
await gridApi.formApi.resetForm(); await gridApi.formApi.resetForm();
} }
@ -152,10 +175,13 @@ async function openModal(
multiple.value = options?.multiple ?? true; multiple.value = options?.multiple ?? true;
preSelectedIds.value = selectedIds || []; preSelectedIds.value = selectedIds || [];
await nextTick(); await nextTick();
gridApi.setGridOptions({
columns: useItemSelectGridColumns(multiple.value),
});
await resetQueryState(); await resetQueryState();
await gridApi.query(); await gridApi.query();
await nextTick(); await nextTick();
applyPreSelection(); await applyPreSelection();
} }
/** 关闭物料选择弹窗 */ /** 关闭物料选择弹窗 */
@ -166,14 +192,12 @@ async function closeModal() {
/** 确认选择物料 */ /** 确认选择物料 */
function handleConfirm() { function handleConfirm() {
if (selectedRows.value.length === 0) { const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value;
if (rows.length === 0) {
message.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据'); message.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据');
return; return;
} }
emit( emit('selected', multiple.value ? rows : [rows[0]!]);
'selected',
multiple.value ? selectedRows.value : [selectedRows.value[0]!],
);
open.value = false; open.value = false;
} }

View File

@ -38,17 +38,16 @@ const selectedItem = ref<MesMdItemApi.Item>(); // 当前选中物料
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // const displayLabel = computed(() => selectedItem.value?.name ?? ''); //
const showClear = computed( const showClear = computed(
//
() => () =>
props.allowClear && props.allowClear &&
!props.disabled && !props.disabled &&
hovering.value && hovering.value &&
props.modelValue !== null, props.modelValue != null,
); );
/** 根据物料编号回显选择器 */ /** 根据物料编号回显选择器 */
async function resolveItemById(id: number | undefined) { async function resolveItemById(id: number | undefined) {
if (id === null) { if (id == null) {
selectedItem.value = undefined; selectedItem.value = undefined;
return; return;
} }
@ -56,7 +55,7 @@ async function resolveItemById(id: number | undefined) {
return; return;
} }
try { try {
selectedItem.value = await getItem(id as number); selectedItem.value = await getItem(id);
} catch (error) { } catch (error) {
console.error('[MdItemSelect] resolveItemById failed:', error); console.error('[MdItemSelect] resolveItemById failed:', error);
} }
@ -88,8 +87,8 @@ function handleClick(event: MouseEvent) {
clearSelected(); clearSelected();
return; return;
} }
const selectedIds = props.modelValue === null ? [] : [props.modelValue]; const selectedIds = props.modelValue == null ? [] : [props.modelValue];
dialogRef.value?.open(selectedIds as number[], { multiple: false }); dialogRef.value?.open(selectedIds, { multiple: false });
} }
/** 回填选中的物料 */ /** 回填选中的物料 */

View File

@ -3,9 +3,9 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesMdItemApi } from '#/api/mes/md/item'; import type { MesMdItemApi } from '#/api/mes/md/item';
import type { MesMdProductBomApi } from '#/api/mes/md/item/productBom'; 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 { getDictOptions } from '@vben/hooks';
import { Button } from 'ant-design-vue'; import { Button } from 'ant-design-vue';
@ -356,9 +356,11 @@ export function useItemSelectGridFormSchema(): VbenFormSchema[] {
} }
/** 物料选择弹窗列表字段 */ /** 物料选择弹窗列表字段 */
export function useItemSelectGridColumns(): VxeTableGridOptions<MesMdItemApi.Item>['columns'] { export function useItemSelectGridColumns(
multiple = true,
): VxeTableGridOptions<MesMdItemApi.Item>['columns'] {
return [ return [
{ type: 'checkbox', width: 50 }, { type: multiple ? 'checkbox' : 'radio', width: 50 },
{ {
field: 'code', field: 'code',
title: '物料编码', title: '物料编码',

View File

@ -2,9 +2,14 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesMdItemTypeApi } from '#/api/mes/md/item/type'; 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 { getDictOptions } from '@vben/hooks';
import { handleTree } from '@vben/utils'; import { handleTree } from '@vben/utils';

View File

@ -16,71 +16,86 @@ import {
useVendorSelectGridFormSchema, useVendorSelectGridFormSchema,
} from '../data'; } from '../data';
defineOptions({ name: 'MdVendorSelectDialog' });
const emit = defineEmits<{ const emit = defineEmits<{
selected: [rows: MesMdVendorApi.Vendor[]]; selected: [rows: MesMdVendorApi.Vendor[]];
}>(); }>();
const open = ref(false); // const open = ref(false); //
const multiple = ref(true); // const multiple = ref(true); //
const syncingSingleSelection = ref(false); //
const selectedRows = ref<MesMdVendorApi.Vendor[]>([]); // const selectedRows = ref<MesMdVendorApi.Vendor[]>([]); //
const preSelectedIds = ref<number[]>([]); // const preSelectedIds = ref<number[]>([]); //
/** 单选模式下同步 VXE 勾选状态,避免跨页残留多选 */ /** 获取当前表格数据 */
async function syncSingleSelection(row?: MesMdVendorApi.Vendor) { function getTableRows() {
syncingSingleSelection.value = true; return gridApi.grid.getTableData().fullData as MesMdVendorApi.Vendor[];
await nextTick();
await gridApi.grid.clearCheckboxRow();
if (row) {
await gridApi.grid.setCheckboxRow(row, true);
}
await nextTick();
syncingSingleSelection.value = false;
} }
/** 处理勾选变化,单选模式只保留最后一条 */ /** 获取多选记录,包含 VXE reserve 跨页记录 */
async function handleCheckboxChange({ function getMultipleSelectedRows() {
checked, const selectedMap = new Map<number, MesMdVendorApi.Vendor>();
records, const records = [
row, ...(gridApi.grid.getCheckboxReserveRecords?.() ?? []),
}: { ...(gridApi.grid.getCheckboxRecords?.() ?? []),
checked: boolean; ] as MesMdVendorApi.Vendor[];
records: MesMdVendorApi.Vendor[]; records.forEach((row) => {
row?: MesMdVendorApi.Vendor; const rowId = row.id;
}) { if (rowId != null) {
if (syncingSingleSelection.value) { selectedMap.set(rowId, row);
return; }
} });
if (!multiple.value) { return [...selectedMap.values()];
const selected = checked && row ? [row] : [];
selectedRows.value = selected;
await syncSingleSelection(selected[0]);
return;
}
selectedRows.value = records;
} }
/** 处理全选变化 */ /** 处理勾选变化 */
function handleCheckboxAll({ records }: { records: MesMdVendorApi.Vendor[] }) { function handleCheckboxSelectChange() {
if (syncingSingleSelection.value) { 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; return;
} }
selectedRows.value = records; selectedRows.value = [row];
await gridApi.grid.setRadioRow(row);
handleConfirm();
} }
/** 回显预选供应商 */ /** 回显预选供应商 */
function applyPreSelection() { async function applyPreSelection() {
if (preSelectedIds.value.length === 0) { if (preSelectedIds.value.length === 0) {
return; return;
} }
const rows = gridApi.grid.getData() as MesMdVendorApi.Vendor[]; const rows = getTableRows();
for (const row of rows) { for (const row of rows) {
if (row.id && preSelectedIds.value.includes(row.id)) { if (row.id == null || !preSelectedIds.value.includes(row.id)) {
gridApi.grid.setCheckboxRow(row, true); continue;
if (!multiple.value) {
selectedRows.value = [row];
}
} }
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(), schema: useVendorSelectGridFormSchema(),
}, },
gridOptions: { gridOptions: {
columns: useVendorSelectGridColumns(), columns: useVendorSelectGridColumns(true),
height: 520, height: 520,
keepSource: true, keepSource: true,
checkboxConfig: { checkboxConfig: {
@ -97,6 +112,10 @@ const [Grid, gridApi] = useVbenVxeGrid({
range: true, range: true,
reserve: true, reserve: true,
}, },
radioConfig: {
highlight: true,
trigger: 'row',
},
proxyConfig: { proxyConfig: {
ajax: { ajax: {
query: async ({ page }, formValues) => { query: async ({ page }, formValues) => {
@ -119,8 +138,12 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
} as VxeTableGridOptions<MesMdVendorApi.Vendor>, } as VxeTableGridOptions<MesMdVendorApi.Vendor>,
gridEvents: { gridEvents: {
checkboxAll: handleCheckboxAll, cellDblclick: handleCellDblclick,
checkboxChange: handleCheckboxChange, checkboxAll: handleCheckboxSelectChange,
checkboxChange: handleCheckboxSelectChange,
radioChange: ({ row }: { row: MesMdVendorApi.Vendor }) => {
handleRadioChange(row);
},
}, },
}); });
@ -128,6 +151,8 @@ const [Grid, gridApi] = useVbenVxeGrid({
async function resetQueryState() { async function resetQueryState() {
selectedRows.value = []; selectedRows.value = [];
await gridApi.grid.clearCheckboxRow(); await gridApi.grid.clearCheckboxRow();
await gridApi.grid.clearCheckboxReserve();
await gridApi.grid.clearRadioRow();
await gridApi.formApi.resetForm(); await gridApi.formApi.resetForm();
} }
@ -140,10 +165,13 @@ async function openModal(
multiple.value = options?.multiple ?? true; multiple.value = options?.multiple ?? true;
preSelectedIds.value = selectedIds || []; preSelectedIds.value = selectedIds || [];
await nextTick(); await nextTick();
gridApi.setGridOptions({
columns: useVendorSelectGridColumns(multiple.value),
});
await resetQueryState(); await resetQueryState();
await gridApi.query(); await gridApi.query();
await nextTick(); await nextTick();
applyPreSelection(); await applyPreSelection();
} }
/** 关闭供应商选择弹窗 */ /** 关闭供应商选择弹窗 */
@ -154,14 +182,12 @@ async function closeModal() {
/** 确认选择供应商 */ /** 确认选择供应商 */
function handleConfirm() { function handleConfirm() {
if (selectedRows.value.length === 0) { const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value;
if (rows.length === 0) {
message.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据'); message.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据');
return; return;
} }
emit( emit('selected', multiple.value ? rows : [rows[0]!]);
'selected',
multiple.value ? selectedRows.value : [selectedRows.value[0]!],
);
open.value = false; open.value = false;
} }

View File

@ -38,17 +38,16 @@ const selectedItem = ref<MesMdVendorApi.Vendor>(); // 当前选中供应商
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // const displayLabel = computed(() => selectedItem.value?.name ?? ''); //
const showClear = computed( const showClear = computed(
//
() => () =>
props.allowClear && props.allowClear &&
!props.disabled && !props.disabled &&
hovering.value && hovering.value &&
props.modelValue !== null, props.modelValue != null,
); );
/** 根据供应商编号回显选择器 */ /** 根据供应商编号回显选择器 */
async function resolveItemById(id: number | undefined) { async function resolveItemById(id: number | undefined) {
if (id === null) { if (id == null) {
selectedItem.value = undefined; selectedItem.value = undefined;
return; return;
} }
@ -56,7 +55,7 @@ async function resolveItemById(id: number | undefined) {
return; return;
} }
try { try {
selectedItem.value = await getVendor(id as number); selectedItem.value = await getVendor(id);
} catch (error) { } catch (error) {
console.error('[MdVendorSelect] resolveItemById failed:', error); console.error('[MdVendorSelect] resolveItemById failed:', error);
} }
@ -88,8 +87,8 @@ function handleClick(event: MouseEvent) {
clearSelected(); clearSelected();
return; return;
} }
const selectedIds = props.modelValue === null ? [] : [props.modelValue]; const selectedIds = props.modelValue == null ? [] : [props.modelValue];
dialogRef.value?.open(selectedIds as number[], { multiple: false }); dialogRef.value?.open(selectedIds, { multiple: false });
} }
/** 回填选中的供应商 */ /** 回填选中的供应商 */

View File

@ -2,9 +2,9 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesMdVendorApi } from '#/api/mes/md/vendor'; 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 { getDictOptions } from '@vben/hooks';
import { Button } from 'ant-design-vue'; import { Button } from 'ant-design-vue';
@ -416,9 +416,11 @@ export function useVendorSelectGridFormSchema(): VbenFormSchema[] {
} }
/** 供应商选择弹窗的字段 */ /** 供应商选择弹窗的字段 */
export function useVendorSelectGridColumns(): VxeTableGridOptions<MesMdVendorApi.Vendor>['columns'] { export function useVendorSelectGridColumns(
multiple = true,
): VxeTableGridOptions<MesMdVendorApi.Vendor>['columns'] {
return [ return [
{ type: 'checkbox', width: 50 }, { type: multiple ? 'checkbox' : 'radio', width: 50 },
{ {
field: 'code', field: 'code',
title: '供应商编码', title: '供应商编码',

View File

@ -40,12 +40,11 @@ const selectedItem = ref<MesMdWorkstationApi.Workstation>(); // 选中的工作
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // const displayLabel = computed(() => selectedItem.value?.name ?? ''); //
const showClear = computed( const showClear = computed(
//
() => () =>
props.allowClear && props.allowClear &&
!props.disabled && !props.disabled &&
hovering.value && hovering.value &&
props.modelValue !== null, props.modelValue != null,
); );
/** 根据工作站编号回显选择器 */ /** 根据工作站编号回显选择器 */

View File

@ -2,9 +2,9 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesMdWorkstationApi } from '#/api/mes/md/workstation'; 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 { getDictOptions } from '@vben/hooks';
import { Button } from 'ant-design-vue'; import { Button } from 'ant-design-vue';

View File

@ -2,9 +2,9 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesMdWorkshopApi } from '#/api/mes/md/workstation/workshop'; 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 { getDictOptions } from '@vben/hooks';
import { Button } from 'ant-design-vue'; import { Button } from 'ant-design-vue';

View File

@ -7,6 +7,8 @@ import { ElOption, ElSelect } from 'element-plus';
import { getCheckPlanPage } from '#/api/mes/dv/checkplan'; import { getCheckPlanPage } from '#/api/mes/dv/checkplan';
defineOptions({ name: 'DvCheckPlanSelect' });
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
clearable?: boolean; clearable?: boolean;
@ -46,13 +48,11 @@ async function getList() {
function handleChange(value: number | string | undefined) { function handleChange(value: number | string | undefined) {
const planId = typeof value === 'number' ? value : undefined; const planId = typeof value === 'number' ? value : undefined;
emit('update:modelValue', planId); emit('update:modelValue', planId);
emit( emit('change', list.value.find((item) => item.id === planId));
'change',
list.value.find((item) => item.id === planId),
);
} }
watch(() => [props.status, props.type], getList); watch(() => [props.status, props.type], getList);
onMounted(getList); onMounted(getList);
</script> </script>
@ -66,6 +66,11 @@ onMounted(getList);
filterable filterable
@change="handleChange" @change="handleChange"
> >
<ElOption v-for="item in list" :key="item.id" :label="item.name" :value="item.id!" /> <ElOption
v-for="item in list"
:key="item.id"
:label="item.name"
:value="item.id!"
/>
</ElSelect> </ElSelect>
</template> </template>

View File

@ -7,6 +7,8 @@ import { ElOption, ElSelect } from 'element-plus';
import { getMachinerySimpleList } from '#/api/mes/dv/machinery'; import { getMachinerySimpleList } from '#/api/mes/dv/machinery';
defineOptions({ name: 'DvMachinerySelect' });
withDefaults( withDefaults(
defineProps<{ defineProps<{
clearable?: boolean; clearable?: boolean;
@ -14,7 +16,12 @@ withDefaults(
modelValue?: number; modelValue?: number;
placeholder?: string; placeholder?: string;
}>(), }>(),
{ clearable: true, disabled: false, modelValue: undefined, placeholder: '请选择设备' }, {
clearable: true,
disabled: false,
modelValue: undefined,
placeholder: '请选择设备',
},
); );
const emit = defineEmits<{ const emit = defineEmits<{
change: [row?: MesDvMachineryApi.Machinery]; change: [row?: MesDvMachineryApi.Machinery];
@ -31,10 +38,7 @@ async function getList() {
function handleChange(value: number | string | undefined) { function handleChange(value: number | string | undefined) {
const machineryId = typeof value === 'number' ? value : undefined; const machineryId = typeof value === 'number' ? value : undefined;
emit('update:modelValue', machineryId); emit('update:modelValue', machineryId);
emit( emit('change', list.value.find((item) => item.id === machineryId));
'change',
list.value.find((item) => item.id === machineryId),
);
} }
onMounted(getList); onMounted(getList);
@ -50,6 +54,11 @@ onMounted(getList);
filterable filterable
@change="handleChange" @change="handleChange"
> >
<ElOption v-for="item in list" :key="item.id" :label="item.name" :value="item.id!" /> <ElOption
v-for="item in list"
:key="item.id"
:label="item.name"
:value="item.id!"
/>
</ElSelect> </ElSelect>
</template> </template>

View File

@ -2,9 +2,9 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesDvMachineryTypeApi } from '#/api/mes/dv/machinery/type'; 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 { getDictOptions } from '@vben/hooks';
import { handleTree } from '@vben/utils'; import { handleTree } from '@vben/utils';

View File

@ -7,6 +7,8 @@ import { ElOption, ElSelect } from 'element-plus';
import { getSubjectSimpleList } from '#/api/mes/dv/subject'; import { getSubjectSimpleList } from '#/api/mes/dv/subject';
defineOptions({ name: 'DvSubjectSelect' });
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
clearable?: boolean; clearable?: boolean;
@ -28,7 +30,7 @@ const emit = defineEmits<{
'update:modelValue': [value?: number]; 'update:modelValue': [value?: number];
}>(); }>();
const list = ref<MesDvSubjectApi.Subject[]>([]); // const list = ref<MesDvSubjectApi.Subject[]>([]); //
const filteredList = computed( // const filteredList = computed(
() => list.value.filter((item) => !props.type || item.type === props.type), () => list.value.filter((item) => !props.type || item.type === props.type),
); );
@ -41,10 +43,7 @@ async function getList() {
function handleChange(value: number | string | undefined) { function handleChange(value: number | string | undefined) {
const subjectId = typeof value === 'number' ? value : undefined; const subjectId = typeof value === 'number' ? value : undefined;
emit('update:modelValue', subjectId); emit('update:modelValue', subjectId);
emit( emit('change', list.value.find((item) => item.id === subjectId));
'change',
list.value.find((item) => item.id === subjectId),
);
} }
onMounted(getList); onMounted(getList);
@ -60,6 +59,11 @@ onMounted(getList);
filterable filterable
@change="handleChange" @change="handleChange"
> >
<ElOption v-for="item in filteredList" :key="item.id" :label="item.name" :value="item.id!" /> <ElOption
v-for="item in filteredList"
:key="item.id"
:label="item.name"
:value="item.id!"
/>
</ElSelect> </ElSelect>
</template> </template>

View File

@ -2,9 +2,14 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesDvSubjectApi } from '#/api/mes/dv/subject'; 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 { getDictOptions } from '@vben/hooks';
import { ElButton } from 'element-plus'; import { ElButton } from 'element-plus';

View File

@ -16,6 +16,8 @@ import {
useClientSelectGridFormSchema, useClientSelectGridFormSchema,
} from '../data'; } from '../data';
defineOptions({ name: 'MdClientSelectDialog' });
const emit = defineEmits<{ const emit = defineEmits<{
selected: [rows: MesMdClientApi.Client[]]; selected: [rows: MesMdClientApi.Client[]];
}>(); }>();
@ -24,43 +26,12 @@ const open = ref(false); // 弹窗是否打开
const multiple = ref(true); // const multiple = ref(true); //
const selectedRows = ref<MesMdClientApi.Client[]>([]); // const selectedRows = ref<MesMdClientApi.Client[]>([]); //
const preSelectedIds = ref<number[]>([]); // const preSelectedIds = ref<number[]>([]); //
const latestQueryRows = ref<MesMdClientApi.Client[]>([]); //
const queryFinished = ref(false); //
// TODO @
const MAX_TABLE_READY_FRAMES = 60;
/** 等待下一帧 */
function waitNextFrame(): Promise<void> {
return new Promise((resolve) => {
requestAnimationFrame(() => resolve());
});
}
/** 获取当前表格数据 */ /** 获取当前表格数据 */
function getTableRows() { function getTableRows() {
return gridApi.grid.getTableData().fullData as MesMdClientApi.Client[]; return gridApi.grid.getTableData().fullData as MesMdClientApi.Client[];
} }
/** 等待 VXE 将当前查询结果写入表格数据后再回显选中 */
async function waitTableReady(): Promise<void> {
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 跨页记录 */ /** 获取多选记录,包含 VXE reserve 跨页记录 */
function getMultipleSelectedRows() { function getMultipleSelectedRows() {
const selectedMap = new Map<number, MesMdClientApi.Client>(); const selectedMap = new Map<number, MesMdClientApi.Client>();
@ -69,8 +40,9 @@ function getMultipleSelectedRows() {
...(gridApi.grid.getCheckboxRecords?.() ?? []), ...(gridApi.grid.getCheckboxRecords?.() ?? []),
] as MesMdClientApi.Client[]; ] as MesMdClientApi.Client[];
records.forEach((row) => { records.forEach((row) => {
if (row.id !== null) { const rowId = row.id;
selectedMap.set(row.id as number, row); if (rowId != null) {
selectedMap.set(rowId, row);
} }
}); });
return [...selectedMap.values()]; return [...selectedMap.values()];
@ -109,10 +81,9 @@ async function applyPreSelection() {
if (preSelectedIds.value.length === 0) { if (preSelectedIds.value.length === 0) {
return; return;
} }
// proxy fullData
const rows = getTableRows(); const rows = getTableRows();
for (const row of rows) { 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; continue;
} }
if (multiple.value) { if (multiple.value) {
@ -148,15 +119,12 @@ const [Grid, gridApi] = useVbenVxeGrid({
proxyConfig: { proxyConfig: {
ajax: { ajax: {
query: async ({ page }, formValues) => { query: async ({ page }, formValues) => {
const data = await getClientPage({ return await getClientPage({
pageNo: page.currentPage, pageNo: page.currentPage,
pageSize: page.pageSize, pageSize: page.pageSize,
...formValues, ...formValues,
status: CommonStatusEnum.ENABLE, status: CommonStatusEnum.ENABLE,
}); });
latestQueryRows.value = data.list || [];
queryFinished.value = true;
return data;
}, },
}, },
}, },
@ -196,8 +164,6 @@ async function openModal(
open.value = true; open.value = true;
multiple.value = options?.multiple ?? true; multiple.value = options?.multiple ?? true;
preSelectedIds.value = selectedIds || []; preSelectedIds.value = selectedIds || [];
latestQueryRows.value = [];
queryFinished.value = false;
await nextTick(); await nextTick();
gridApi.setGridOptions({ gridApi.setGridOptions({
columns: useClientSelectGridColumns(multiple.value), columns: useClientSelectGridColumns(multiple.value),
@ -205,13 +171,13 @@ async function openModal(
await resetQueryState(); await resetQueryState();
await gridApi.query(); await gridApi.query();
await nextTick(); await nextTick();
await waitTableReady();
await applyPreSelection(); await applyPreSelection();
} }
/** 关闭客户选择弹窗 */ /** 关闭客户选择弹窗 */
function closeModal() { async function closeModal() {
open.value = false; open.value = false;
await resetQueryState();
} }
/** 确认选择客户 */ /** 确认选择客户 */

View File

@ -38,17 +38,16 @@ const selectedItem = ref<MesMdClientApi.Client>(); // 当前选中客户
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // const displayLabel = computed(() => selectedItem.value?.name ?? ''); //
const showClear = computed( const showClear = computed(
//
() => () =>
props.clearable && props.clearable &&
!props.disabled && !props.disabled &&
hovering.value && hovering.value &&
props.modelValue !== null, props.modelValue != null,
); );
/** 根据客户编号回显选择器 */ /** 根据客户编号回显选择器 */
async function resolveItemById(id: number | undefined) { async function resolveItemById(id: number | undefined) {
if (id === null) { if (id == null) {
selectedItem.value = undefined; selectedItem.value = undefined;
return; return;
} }
@ -56,7 +55,7 @@ async function resolveItemById(id: number | undefined) {
return; return;
} }
try { try {
selectedItem.value = await getClient(id as number); selectedItem.value = await getClient(id);
} catch (error) { } catch (error) {
console.error('[MdClientSelect] resolveItemById failed:', error); console.error('[MdClientSelect] resolveItemById failed:', error);
} }
@ -88,8 +87,8 @@ function handleClick(event: MouseEvent) {
clearSelected(); clearSelected();
return; return;
} }
const selectedIds = props.modelValue === null ? [] : [props.modelValue]; const selectedIds = props.modelValue == null ? [] : [props.modelValue];
dialogRef.value?.open(selectedIds as number[], { multiple: false }); dialogRef.value?.open(selectedIds, { multiple: false });
} }
/** 回填选中的客户 */ /** 回填选中的客户 */

View File

@ -2,9 +2,9 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesMdClientApi } from '#/api/mes/md/client'; 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 { getDictOptions } from '@vben/hooks';
import { ElButton } from 'element-plus'; import { ElButton } from 'element-plus';

View File

@ -68,7 +68,7 @@ async function openModal(itemId: number, selectedBomItemId?: number) {
list.value = await getProductBomListByItemId(itemId); list.value = await getProductBomListByItemId(itemId);
gridApi.setGridOptions({ data: list.value }); gridApi.setGridOptions({ data: list.value });
await nextTick(); await nextTick();
if (selectedBomItemId !== null) { if (selectedBomItemId != null) {
const match = list.value.find( const match = list.value.find(
(row) => row.bomItemId === selectedBomItemId, (row) => row.bomItemId === selectedBomItemId,
); );

View File

@ -40,17 +40,16 @@ const selectedBom = ref<MesMdProductBomApi.ProductBom>(); // 当前选中的 BOM
const displayLabel = computed(() => selectedBom.value?.bomItemName ?? ''); // const displayLabel = computed(() => selectedBom.value?.bomItemName ?? ''); //
const showClear = computed( const showClear = computed(
//
() => () =>
props.clearable && props.clearable &&
!props.disabled && !props.disabled &&
hovering.value && hovering.value &&
props.modelValue !== null, props.modelValue != null,
); );
/** 根据 BOM 子物料编号回显选择器 */ /** 根据 BOM 子物料编号回显选择器 */
async function resolveBomById(bomItemId: number | undefined) { async function resolveBomById(bomItemId: number | undefined) {
if (bomItemId === null || props.itemId === null) { if (bomItemId == null || props.itemId == null) {
selectedBom.value = undefined; selectedBom.value = undefined;
return; return;
} }
@ -58,7 +57,7 @@ async function resolveBomById(bomItemId: number | undefined) {
return; return;
} }
try { try {
const list = await getProductBomListByItemId(props.itemId as number); const list = await getProductBomListByItemId(props.itemId);
selectedBom.value = list.find((item) => item.bomItemId === bomItemId); selectedBom.value = list.find((item) => item.bomItemId === bomItemId);
} catch (error) { } catch (error) {
console.error('[MdProductBomSelect] resolveBomById failed:', error); console.error('[MdProductBomSelect] resolveBomById failed:', error);
@ -91,7 +90,7 @@ function clearSelected() {
/** 打开 BOM 物料选择弹窗 */ /** 打开 BOM 物料选择弹窗 */
function handleClick(event: MouseEvent) { function handleClick(event: MouseEvent) {
if (props.disabled || props.itemId === null) { if (props.disabled || props.itemId == null) {
return; return;
} }
const target = event.target as HTMLElement; const target = event.target as HTMLElement;
@ -100,7 +99,7 @@ function handleClick(event: MouseEvent) {
clearSelected(); clearSelected();
return; return;
} }
dialogRef.value?.open(props.itemId as number, props.modelValue); dialogRef.value?.open(props.itemId, props.modelValue);
} }
/** 回填选中的 BOM 物料 */ /** 回填选中的 BOM 物料 */

View File

@ -23,52 +23,58 @@ const emit = defineEmits<{
const open = ref(false); // const open = ref(false); //
const multiple = ref(true); // const multiple = ref(true); //
const syncingSingleSelection = ref(false); //
const selectedRows = ref<MesMdItemApi.Item[]>([]); // const selectedRows = ref<MesMdItemApi.Item[]>([]); //
const selectedItemTypeId = ref<number>(); // const selectedItemTypeId = ref<number>(); //
const preSelectedIds = ref<number[]>([]); // const preSelectedIds = ref<number[]>([]); //
const typeTreeRef = ref<InstanceType<typeof MdItemTypeTree>>(); // const typeTreeRef = ref<InstanceType<typeof MdItemTypeTree>>(); //
/** 单选模式下同步 VXE 勾选状态,避免跨页残留多选 */ /** 获取当前表格数据 */
async function syncSingleSelection(row?: MesMdItemApi.Item) { function getTableRows() {
syncingSingleSelection.value = true; return gridApi.grid.getTableData().fullData as MesMdItemApi.Item[];
await nextTick();
await gridApi.grid.clearCheckboxRow();
if (row) {
await gridApi.grid.setCheckboxRow(row, true);
}
await nextTick();
syncingSingleSelection.value = false;
} }
/** 处理勾选变化,单选模式只保留最后一条 */ /** 获取多选记录,包含 VXE reserve 跨页记录 */
async function handleCheckboxChange({ function getMultipleSelectedRows() {
checked, const selectedMap = new Map<number, MesMdItemApi.Item>();
records, const records = [
row, ...(gridApi.grid.getCheckboxReserveRecords?.() ?? []),
}: { ...(gridApi.grid.getCheckboxRecords?.() ?? []),
checked: boolean; ] as MesMdItemApi.Item[];
records: MesMdItemApi.Item[]; records.forEach((row) => {
row?: MesMdItemApi.Item; const rowId = row.id;
}) { if (rowId != null) {
if (syncingSingleSelection.value) { selectedMap.set(rowId, row);
return; }
} });
if (!multiple.value) { return [...selectedMap.values()];
const selected = checked && row ? [row] : [];
selectedRows.value = selected;
await syncSingleSelection(selected[0]);
return;
}
selectedRows.value = records;
} }
/** 处理全选变化 */ /** 处理勾选变化 */
function handleCheckboxAll({ records }: { records: MesMdItemApi.Item[] }) { function handleCheckboxSelectChange() {
if (syncingSingleSelection.value) { 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; 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) { if (preSelectedIds.value.length === 0) {
return; return;
} }
const rows = gridApi.grid.getData() as MesMdItemApi.Item[]; const rows = getTableRows();
for (const row of rows) { for (const row of rows) {
if (row.id && preSelectedIds.value.includes(row.id)) { if (row.id == null || !preSelectedIds.value.includes(row.id)) {
gridApi.grid.setCheckboxRow(row, true); continue;
if (!multiple.value) {
selectedRows.value = [row];
}
} }
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(), schema: useItemSelectGridFormSchema(),
}, },
gridOptions: { gridOptions: {
columns: useItemSelectGridColumns(), columns: useItemSelectGridColumns(true),
height: 560, height: 560,
keepSource: true, keepSource: true,
checkboxConfig: { checkboxConfig: {
@ -106,6 +119,10 @@ const [Grid, gridApi] = useVbenVxeGrid({
range: true, range: true,
reserve: true, reserve: true,
}, },
radioConfig: {
highlight: true,
trigger: 'row',
},
proxyConfig: { proxyConfig: {
ajax: { ajax: {
query: async ({ page }, formValues) => { query: async ({ page }, formValues) => {
@ -129,8 +146,12 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
} as VxeTableGridOptions<MesMdItemApi.Item>, } as VxeTableGridOptions<MesMdItemApi.Item>,
gridEvents: { gridEvents: {
checkboxAll: handleCheckboxAll, cellDblclick: handleCellDblclick,
checkboxChange: handleCheckboxChange, checkboxAll: handleCheckboxSelectChange,
checkboxChange: handleCheckboxSelectChange,
radioChange: ({ row }: { row: MesMdItemApi.Item }) => {
handleRadioChange(row);
},
}, },
}); });
@ -140,6 +161,8 @@ async function resetQueryState() {
selectedRows.value = []; selectedRows.value = [];
typeTreeRef.value?.reset(); typeTreeRef.value?.reset();
await gridApi.grid.clearCheckboxRow(); await gridApi.grid.clearCheckboxRow();
await gridApi.grid.clearCheckboxReserve();
await gridApi.grid.clearRadioRow();
await gridApi.formApi.resetForm(); await gridApi.formApi.resetForm();
} }
@ -152,10 +175,13 @@ async function openModal(
multiple.value = options?.multiple ?? true; multiple.value = options?.multiple ?? true;
preSelectedIds.value = selectedIds || []; preSelectedIds.value = selectedIds || [];
await nextTick(); await nextTick();
gridApi.setGridOptions({
columns: useItemSelectGridColumns(multiple.value),
});
await resetQueryState(); await resetQueryState();
await gridApi.query(); await gridApi.query();
await nextTick(); await nextTick();
applyPreSelection(); await applyPreSelection();
} }
/** 关闭物料选择弹窗 */ /** 关闭物料选择弹窗 */
@ -166,14 +192,12 @@ async function closeModal() {
/** 确认选择物料 */ /** 确认选择物料 */
function handleConfirm() { function handleConfirm() {
if (selectedRows.value.length === 0) { const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value;
if (rows.length === 0) {
ElMessage.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据'); ElMessage.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据');
return; return;
} }
emit( emit('selected', multiple.value ? rows : [rows[0]!]);
'selected',
multiple.value ? selectedRows.value : [selectedRows.value[0]!],
);
open.value = false; open.value = false;
} }

View File

@ -38,17 +38,16 @@ const selectedItem = ref<MesMdItemApi.Item>(); // 当前选中物料
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // const displayLabel = computed(() => selectedItem.value?.name ?? ''); //
const showClear = computed( const showClear = computed(
//
() => () =>
props.clearable && props.clearable &&
!props.disabled && !props.disabled &&
hovering.value && hovering.value &&
props.modelValue !== null, props.modelValue != null,
); );
/** 根据物料编号回显选择器 */ /** 根据物料编号回显选择器 */
async function resolveItemById(id: number | undefined) { async function resolveItemById(id: number | undefined) {
if (id === null) { if (id == null) {
selectedItem.value = undefined; selectedItem.value = undefined;
return; return;
} }
@ -56,7 +55,7 @@ async function resolveItemById(id: number | undefined) {
return; return;
} }
try { try {
selectedItem.value = await getItem(id as number); selectedItem.value = await getItem(id);
} catch (error) { } catch (error) {
console.error('[MdItemSelect] resolveItemById failed:', error); console.error('[MdItemSelect] resolveItemById failed:', error);
} }
@ -88,8 +87,8 @@ function handleClick(event: MouseEvent) {
clearSelected(); clearSelected();
return; return;
} }
const selectedIds = props.modelValue === null ? [] : [props.modelValue]; const selectedIds = props.modelValue == null ? [] : [props.modelValue];
dialogRef.value?.open(selectedIds as number[], { multiple: false }); dialogRef.value?.open(selectedIds, { multiple: false });
} }
/** 回填选中的物料 */ /** 回填选中的物料 */

View File

@ -3,9 +3,9 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesMdItemApi } from '#/api/mes/md/item'; import type { MesMdItemApi } from '#/api/mes/md/item';
import type { MesMdProductBomApi } from '#/api/mes/md/item/productBom'; 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 { getDictOptions } from '@vben/hooks';
import { ElButton } from 'element-plus'; import { ElButton } from 'element-plus';
@ -360,9 +360,11 @@ export function useItemSelectGridFormSchema(): VbenFormSchema[] {
} }
/** 物料选择弹窗列表字段 */ /** 物料选择弹窗列表字段 */
export function useItemSelectGridColumns(): VxeTableGridOptions<MesMdItemApi.Item>['columns'] { export function useItemSelectGridColumns(
multiple = true,
): VxeTableGridOptions<MesMdItemApi.Item>['columns'] {
return [ return [
{ type: 'checkbox', width: 50 }, { type: multiple ? 'checkbox' : 'radio', width: 50 },
{ {
field: 'code', field: 'code',
title: '物料编码', title: '物料编码',

View File

@ -2,9 +2,14 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesMdItemTypeApi } from '#/api/mes/md/item/type'; 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 { getDictOptions } from '@vben/hooks';
import { handleTree } from '@vben/utils'; import { handleTree } from '@vben/utils';

View File

@ -16,71 +16,86 @@ import {
useVendorSelectGridFormSchema, useVendorSelectGridFormSchema,
} from '../data'; } from '../data';
defineOptions({ name: 'MdVendorSelectDialog' });
const emit = defineEmits<{ const emit = defineEmits<{
selected: [rows: MesMdVendorApi.Vendor[]]; selected: [rows: MesMdVendorApi.Vendor[]];
}>(); }>();
const open = ref(false); // const open = ref(false); //
const multiple = ref(true); // const multiple = ref(true); //
const syncingSingleSelection = ref(false); //
const selectedRows = ref<MesMdVendorApi.Vendor[]>([]); // const selectedRows = ref<MesMdVendorApi.Vendor[]>([]); //
const preSelectedIds = ref<number[]>([]); // const preSelectedIds = ref<number[]>([]); //
/** 单选模式下同步 VXE 勾选状态,避免跨页残留多选 */ /** 获取当前表格数据 */
async function syncSingleSelection(row?: MesMdVendorApi.Vendor) { function getTableRows() {
syncingSingleSelection.value = true; return gridApi.grid.getTableData().fullData as MesMdVendorApi.Vendor[];
await nextTick();
await gridApi.grid.clearCheckboxRow();
if (row) {
await gridApi.grid.setCheckboxRow(row, true);
}
await nextTick();
syncingSingleSelection.value = false;
} }
/** 处理勾选变化,单选模式只保留最后一条 */ /** 获取多选记录,包含 VXE reserve 跨页记录 */
async function handleCheckboxChange({ function getMultipleSelectedRows() {
checked, const selectedMap = new Map<number, MesMdVendorApi.Vendor>();
records, const records = [
row, ...(gridApi.grid.getCheckboxReserveRecords?.() ?? []),
}: { ...(gridApi.grid.getCheckboxRecords?.() ?? []),
checked: boolean; ] as MesMdVendorApi.Vendor[];
records: MesMdVendorApi.Vendor[]; records.forEach((row) => {
row?: MesMdVendorApi.Vendor; const rowId = row.id;
}) { if (rowId != null) {
if (syncingSingleSelection.value) { selectedMap.set(rowId, row);
return; }
} });
if (!multiple.value) { return [...selectedMap.values()];
const selected = checked && row ? [row] : [];
selectedRows.value = selected;
await syncSingleSelection(selected[0]);
return;
}
selectedRows.value = records;
} }
/** 处理全选变化 */ /** 处理勾选变化 */
function handleCheckboxAll({ records }: { records: MesMdVendorApi.Vendor[] }) { function handleCheckboxSelectChange() {
if (syncingSingleSelection.value) { 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; return;
} }
selectedRows.value = records; selectedRows.value = [row];
await gridApi.grid.setRadioRow(row);
handleConfirm();
} }
/** 回显预选供应商 */ /** 回显预选供应商 */
function applyPreSelection() { async function applyPreSelection() {
if (preSelectedIds.value.length === 0) { if (preSelectedIds.value.length === 0) {
return; return;
} }
const rows = gridApi.grid.getData() as MesMdVendorApi.Vendor[]; const rows = getTableRows();
for (const row of rows) { for (const row of rows) {
if (row.id && preSelectedIds.value.includes(row.id)) { if (row.id == null || !preSelectedIds.value.includes(row.id)) {
gridApi.grid.setCheckboxRow(row, true); continue;
if (!multiple.value) {
selectedRows.value = [row];
}
} }
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(), schema: useVendorSelectGridFormSchema(),
}, },
gridOptions: { gridOptions: {
columns: useVendorSelectGridColumns(), columns: useVendorSelectGridColumns(true),
height: 520, height: 520,
keepSource: true, keepSource: true,
checkboxConfig: { checkboxConfig: {
@ -97,6 +112,10 @@ const [Grid, gridApi] = useVbenVxeGrid({
range: true, range: true,
reserve: true, reserve: true,
}, },
radioConfig: {
highlight: true,
trigger: 'row',
},
proxyConfig: { proxyConfig: {
ajax: { ajax: {
query: async ({ page }, formValues) => { query: async ({ page }, formValues) => {
@ -119,8 +138,12 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
} as VxeTableGridOptions<MesMdVendorApi.Vendor>, } as VxeTableGridOptions<MesMdVendorApi.Vendor>,
gridEvents: { gridEvents: {
checkboxAll: handleCheckboxAll, cellDblclick: handleCellDblclick,
checkboxChange: handleCheckboxChange, checkboxAll: handleCheckboxSelectChange,
checkboxChange: handleCheckboxSelectChange,
radioChange: ({ row }: { row: MesMdVendorApi.Vendor }) => {
handleRadioChange(row);
},
}, },
}); });
@ -128,6 +151,8 @@ const [Grid, gridApi] = useVbenVxeGrid({
async function resetQueryState() { async function resetQueryState() {
selectedRows.value = []; selectedRows.value = [];
await gridApi.grid.clearCheckboxRow(); await gridApi.grid.clearCheckboxRow();
await gridApi.grid.clearCheckboxReserve();
await gridApi.grid.clearRadioRow();
await gridApi.formApi.resetForm(); await gridApi.formApi.resetForm();
} }
@ -140,10 +165,13 @@ async function openModal(
multiple.value = options?.multiple ?? true; multiple.value = options?.multiple ?? true;
preSelectedIds.value = selectedIds || []; preSelectedIds.value = selectedIds || [];
await nextTick(); await nextTick();
gridApi.setGridOptions({
columns: useVendorSelectGridColumns(multiple.value),
});
await resetQueryState(); await resetQueryState();
await gridApi.query(); await gridApi.query();
await nextTick(); await nextTick();
applyPreSelection(); await applyPreSelection();
} }
/** 关闭供应商选择弹窗 */ /** 关闭供应商选择弹窗 */
@ -154,14 +182,12 @@ async function closeModal() {
/** 确认选择供应商 */ /** 确认选择供应商 */
function handleConfirm() { function handleConfirm() {
if (selectedRows.value.length === 0) { const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value;
if (rows.length === 0) {
ElMessage.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据'); ElMessage.warning(multiple.value ? '请至少选择一条数据' : '请选择一条数据');
return; return;
} }
emit( emit('selected', multiple.value ? rows : [rows[0]!]);
'selected',
multiple.value ? selectedRows.value : [selectedRows.value[0]!],
);
open.value = false; open.value = false;
} }

View File

@ -38,17 +38,16 @@ const selectedItem = ref<MesMdVendorApi.Vendor>(); // 当前选中供应商
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // const displayLabel = computed(() => selectedItem.value?.name ?? ''); //
const showClear = computed( const showClear = computed(
//
() => () =>
props.clearable && props.clearable &&
!props.disabled && !props.disabled &&
hovering.value && hovering.value &&
props.modelValue !== null, props.modelValue != null,
); );
/** 根据供应商编号回显选择器 */ /** 根据供应商编号回显选择器 */
async function resolveItemById(id: number | undefined) { async function resolveItemById(id: number | undefined) {
if (id === null) { if (id == null) {
selectedItem.value = undefined; selectedItem.value = undefined;
return; return;
} }
@ -56,7 +55,7 @@ async function resolveItemById(id: number | undefined) {
return; return;
} }
try { try {
selectedItem.value = await getVendor(id as number); selectedItem.value = await getVendor(id);
} catch (error) { } catch (error) {
console.error('[MdVendorSelect] resolveItemById failed:', error); console.error('[MdVendorSelect] resolveItemById failed:', error);
} }
@ -88,8 +87,8 @@ function handleClick(event: MouseEvent) {
clearSelected(); clearSelected();
return; return;
} }
const selectedIds = props.modelValue === null ? [] : [props.modelValue]; const selectedIds = props.modelValue == null ? [] : [props.modelValue];
dialogRef.value?.open(selectedIds as number[], { multiple: false }); dialogRef.value?.open(selectedIds, { multiple: false });
} }
/** 回填选中的供应商 */ /** 回填选中的供应商 */

View File

@ -2,9 +2,9 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesMdVendorApi } from '#/api/mes/md/vendor'; 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 { getDictOptions } from '@vben/hooks';
import { ElButton } from 'element-plus'; import { ElButton } from 'element-plus';
@ -415,9 +415,11 @@ export function useVendorSelectGridFormSchema(): VbenFormSchema[] {
} }
/** 供应商选择弹窗的字段 */ /** 供应商选择弹窗的字段 */
export function useVendorSelectGridColumns(): VxeTableGridOptions<MesMdVendorApi.Vendor>['columns'] { export function useVendorSelectGridColumns(
multiple = true,
): VxeTableGridOptions<MesMdVendorApi.Vendor>['columns'] {
return [ return [
{ type: 'checkbox', width: 50 }, { type: multiple ? 'checkbox' : 'radio', width: 50 },
{ {
field: 'code', field: 'code',
title: '供应商编码', title: '供应商编码',

View File

@ -40,12 +40,11 @@ const selectedItem = ref<MesMdWorkstationApi.Workstation>(); // 选中的工作
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // const displayLabel = computed(() => selectedItem.value?.name ?? ''); //
const showClear = computed( const showClear = computed(
//
() => () =>
props.clearable && props.clearable &&
!props.disabled && !props.disabled &&
hovering.value && hovering.value &&
props.modelValue !== null, props.modelValue != null,
); );
/** 根据工作站编号回显选择器 */ /** 根据工作站编号回显选择器 */

View File

@ -2,9 +2,9 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesMdWorkstationApi } from '#/api/mes/md/workstation'; 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 { getDictOptions } from '@vben/hooks';
import { ElButton } from 'element-plus'; import { ElButton } from 'element-plus';

View File

@ -2,9 +2,9 @@ import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MesMdWorkshopApi } from '#/api/mes/md/workstation/workshop'; 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 { getDictOptions } from '@vben/hooks';
import { ElButton } from 'element-plus'; import { ElButton } from 'element-plus';