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

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 [
{ type: 'checkbox', width: 50 },
{ type: multiple ? 'checkbox' : 'radio', width: 50 },
{
field: 'code',
title: '客户编码',

View File

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

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 [
{ type: 'checkbox', width: 50 },
{ type: multiple ? 'checkbox' : 'radio', width: 50 },
{
field: 'code',
title: '客户编码',