feat(mes):md client select 迁移

pull/345/head
YunaiV 2026-05-22 08:41:54 +08:00
parent 00779aacb3
commit f8c869f1ff
4 changed files with 208 additions and 102 deletions

View File

@ -22,33 +22,29 @@ 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<MesMdClientApi.Client[]>([]); // const selectedRows = ref<MesMdClientApi.Client[]>([]); //
const preSelectedIds = ref<number[]>([]); // const preSelectedIds = ref<number[]>([]); //
const latestQueryRows = ref<MesMdClientApi.Client[]>([]); //
const queryFinished = ref(false); //
/** 单选模式下同步 VXE 勾选状态,避免跨页残留多选 */ // TODO @
async function syncSingleSelection(row?: MesMdClientApi.Client) { const MAX_TABLE_READY_FRAMES = 60;
syncingSingleSelection.value = true;
await nextTick(); /** 等待下一帧 */
await gridApi.grid.clearCheckboxRow(); function waitNextFrame(): Promise<void> {
if (row) { return new Promise((resolve) => {
await gridApi.grid.setCheckboxRow(row, true); requestAnimationFrame(() => resolve());
} });
await nextTick();
syncingSingleSelection.value = false;
} }
/** 处理勾选变化,单选模式只保留最后一条 */ /** 获取当前表格数据 */
async function handleCheckboxChange({ function getTableRows() {
checked, return gridApi.grid.getTableData().fullData as MesMdClientApi.Client[];
records, }
row,
}: { /** 等待 VXE 将当前查询结果写入表格数据后再回显选中 */
checked: boolean; async function waitTableReady(): Promise<void> {
records: MesMdClientApi.Client[]; if (preSelectedIds.value.length === 0) {
row?: MesMdClientApi.Client;
}) {
if (syncingSingleSelection.value) {
return; return;
} }
if (!multiple.value) { if (!multiple.value) {
@ -60,27 +56,55 @@ async function handleCheckboxChange({
selectedRows.value = records; selectedRows.value = records;
} }
/** 处理全选变化 */ /** 处理勾选变化 */
function handleCheckboxAll({ records }: { records: MesMdClientApi.Client[] }) { function handleCheckboxSelectChange() {
if (syncingSingleSelection.value) { selectedRows.value = getMultipleSelectedRows();
}
/** 处理单选变化 */
function handleRadioChange(row: MesMdClientApi.Client) {
selectedRows.value = [row];
}
/** 多选模式下切换行勾选 */
async function toggleMultipleRow(row: MesMdClientApi.Client) {
const selected = gridApi.grid.isCheckedByCheckboxRow(row);
await gridApi.grid.setCheckboxRow(row, !selected);
selectedRows.value = getMultipleSelectedRows();
}
/** 处理行双击 */
async function handleCellDblclick({ row }: { row: MesMdClientApi.Client }) {
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 MesMdClientApi.Client[]; // proxy fullData
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 +113,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
schema: useClientSelectGridFormSchema(), schema: useClientSelectGridFormSchema(),
}, },
gridOptions: { gridOptions: {
columns: useClientSelectGridColumns(), columns: useClientSelectGridColumns(true),
height: 520, height: 520,
keepSource: true, keepSource: true,
checkboxConfig: { checkboxConfig: {
@ -97,15 +121,22 @@ 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) => {
return await getClientPage({ const data = 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;
}, },
}, },
}, },
@ -119,8 +150,12 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
} as VxeTableGridOptions<MesMdClientApi.Client>, } as VxeTableGridOptions<MesMdClientApi.Client>,
gridEvents: { gridEvents: {
checkboxAll: handleCheckboxAll, cellDblclick: handleCellDblclick,
checkboxChange: handleCheckboxChange, checkboxAll: handleCheckboxSelectChange,
checkboxChange: handleCheckboxSelectChange,
radioChange: ({ row }: { row: MesMdClientApi.Client }) => {
handleRadioChange(row);
},
}, },
}); });
@ -128,6 +163,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();
} }
@ -136,26 +173,30 @@ async function openModal(selectedIds?: number[], options?: { multiple?: boolean
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({ columns: useClientSelectGridColumns(multiple.value) });
await resetQueryState(); await resetQueryState();
await gridApi.query(); await gridApi.query();
await nextTick(); await nextTick();
applyPreSelection(); await waitTableReady();
await applyPreSelection();
} }
/** 关闭客户选择弹窗 */ /** 关闭客户选择弹窗 */
async function closeModal() { function closeModal() {
open.value = false; open.value = false;
await resetQueryState();
} }
/** 确认选择客户 */ /** 确认选择客户 */
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('selected', multiple.value ? selectedRows.value : [selectedRows.value[0]!]); emit('selected', multiple.value ? rows : [rows[0]!]);
open.value = false; open.value = false;
} }

View File

@ -424,9 +424,11 @@ export function useClientSelectGridFormSchema(): VbenFormSchema[] {
} }
/** 客户选择弹窗的字段 */ /** 客户选择弹窗的字段 */
export function useClientSelectGridColumns(): VxeTableGridOptions<MesMdClientApi.Client>['columns'] { export function useClientSelectGridColumns(
multiple = true,
): VxeTableGridOptions<MesMdClientApi.Client>['columns'] {
return [ return [
{ type: 'checkbox', width: 50 }, { type: multiple ? 'checkbox' : 'radio', width: 50 },
{ {
field: 'code', field: 'code',
title: '客户编码', title: '客户编码',

View File

@ -22,65 +22,109 @@ 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<MesMdClientApi.Client[]>([]); // const selectedRows = ref<MesMdClientApi.Client[]>([]); //
const preSelectedIds = ref<number[]>([]); // const preSelectedIds = ref<number[]>([]); //
const latestQueryRows = ref<MesMdClientApi.Client[]>([]); //
const queryFinished = ref(false); //
/** 单选模式下同步 VXE 勾选状态,避免跨页残留多选 */ // TODO @
async function syncSingleSelection(row?: MesMdClientApi.Client) { const MAX_TABLE_READY_FRAMES = 60;
syncingSingleSelection.value = true;
await nextTick(); /** 等待下一帧 */
await gridApi.grid.clearCheckboxRow(); function waitNextFrame(): Promise<void> {
if (row) { return new Promise((resolve) => {
await gridApi.grid.setCheckboxRow(row, true); requestAnimationFrame(() => resolve());
} });
await nextTick();
syncingSingleSelection.value = false;
} }
/** 处理勾选变化,单选模式只保留最后一条 */ /** 获取当前表格数据 */
async function handleCheckboxChange({ function getTableRows() {
checked, return gridApi.grid.getTableData().fullData as MesMdClientApi.Client[];
records,
row,
}: {
checked: boolean;
records: MesMdClientApi.Client[];
row?: MesMdClientApi.Client;
}) {
if (syncingSingleSelection.value) {
return;
}
if (!multiple.value) {
const selected = checked && row ? [row] : [];
selectedRows.value = selected;
await syncSingleSelection(selected[0]);
return;
}
selectedRows.value = records;
} }
/** 处理全选变化 */ /** 等待 VXE 将当前查询结果写入表格数据后再回显选中 */
function handleCheckboxAll({ records }: { records: MesMdClientApi.Client[] }) { async function waitTableReady(): Promise<void> {
if (syncingSingleSelection.value) {
return;
}
selectedRows.value = records;
}
/** 回显预选客户 */
function applyPreSelection() {
if (preSelectedIds.value.length === 0) { if (preSelectedIds.value.length === 0) {
return; return;
} }
const rows = gridApi.grid.getData() as MesMdClientApi.Client[]; for (let index = 0; index < MAX_TABLE_READY_FRAMES; index += 1) {
for (const row of rows) { if (queryFinished.value) {
if (row.id && preSelectedIds.value.includes(row.id)) { const rows = getTableRows();
gridApi.grid.setCheckboxRow(row, true); if (latestQueryRows.value.length === 0 && rows.length === 0) {
if (!multiple.value) { return;
selectedRows.value = [row]; }
if (latestQueryRows.value.length > 0 && rows.length > 0) {
return;
} }
} }
await waitNextFrame();
}
}
/** 获取多选记录,包含 VXE reserve 跨页记录 */
function getMultipleSelectedRows() {
const selectedMap = new Map<number, MesMdClientApi.Client>();
const records = [
...(gridApi.grid.getCheckboxReserveRecords?.() ?? []),
...(gridApi.grid.getCheckboxRecords?.() ?? []),
] as MesMdClientApi.Client[];
records.forEach((row) => {
if (row.id != null) {
selectedMap.set(row.id, row);
}
});
return [...selectedMap.values()];
}
/** 处理勾选变化 */
function handleCheckboxSelectChange() {
selectedRows.value = getMultipleSelectedRows();
}
/** 处理单选变化 */
function handleRadioChange(row: MesMdClientApi.Client) {
selectedRows.value = [row];
}
/** 多选模式下切换行勾选 */
async function toggleMultipleRow(row: MesMdClientApi.Client) {
const selected = gridApi.grid.isCheckedByCheckboxRow(row);
await gridApi.grid.setCheckboxRow(row, !selected);
selectedRows.value = getMultipleSelectedRows();
}
/** 处理行双击 */
async function handleCellDblclick({ row }: { row: MesMdClientApi.Client }) {
if (multiple.value) {
await toggleMultipleRow(row);
return;
}
selectedRows.value = [row];
await gridApi.grid.setRadioRow(row);
handleConfirm();
}
/** 回显预选客户 */
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)) {
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 +133,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
schema: useClientSelectGridFormSchema(), schema: useClientSelectGridFormSchema(),
}, },
gridOptions: { gridOptions: {
columns: useClientSelectGridColumns(), columns: useClientSelectGridColumns(true),
height: 520, height: 520,
keepSource: true, keepSource: true,
checkboxConfig: { checkboxConfig: {
@ -97,15 +141,22 @@ 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) => {
return await getClientPage({ const data = 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;
}, },
}, },
}, },
@ -119,8 +170,12 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
} as VxeTableGridOptions<MesMdClientApi.Client>, } as VxeTableGridOptions<MesMdClientApi.Client>,
gridEvents: { gridEvents: {
checkboxAll: handleCheckboxAll, cellDblclick: handleCellDblclick,
checkboxChange: handleCheckboxChange, checkboxAll: handleCheckboxSelectChange,
checkboxChange: handleCheckboxSelectChange,
radioChange: ({ row }: { row: MesMdClientApi.Client }) => {
handleRadioChange(row);
},
}, },
}); });
@ -128,6 +183,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();
} }
@ -136,26 +193,30 @@ async function openModal(selectedIds?: number[], options?: { multiple?: boolean
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({ columns: useClientSelectGridColumns(multiple.value) });
await resetQueryState(); await resetQueryState();
await gridApi.query(); await gridApi.query();
await nextTick(); await nextTick();
applyPreSelection(); await waitTableReady();
await applyPreSelection();
} }
/** 关闭客户选择弹窗 */ /** 关闭客户选择弹窗 */
async function closeModal() { function closeModal() {
open.value = false; open.value = false;
await resetQueryState();
} }
/** 确认选择客户 */ /** 确认选择客户 */
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('selected', multiple.value ? selectedRows.value : [selectedRows.value[0]!]); emit('selected', multiple.value ? rows : [rows[0]!]);
open.value = false; open.value = false;
} }

View File

@ -422,9 +422,11 @@ export function useClientSelectGridFormSchema(): VbenFormSchema[] {
} }
/** 客户选择弹窗的字段 */ /** 客户选择弹窗的字段 */
export function useClientSelectGridColumns(): VxeTableGridOptions<MesMdClientApi.Client>['columns'] { export function useClientSelectGridColumns(
multiple = true,
): VxeTableGridOptions<MesMdClientApi.Client>['columns'] {
return [ return [
{ type: 'checkbox', width: 50 }, { type: multiple ? 'checkbox' : 'radio', width: 50 },
{ {
field: 'code', field: 'code',
title: '客户编码', title: '客户编码',