mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-05-31 13:39:48 +08:00
✨ feat(data-grid): 增加复制行和粘贴行操作
- 表数据工具栏新增复制行、粘贴行按钮 - 支持将选中行复制为新增行草稿,提交前可继续检查编辑 - 抽离行复制粘贴数据构造逻辑并补充回归测试 Refs #332
This commit is contained in:
41
frontend/src/components/dataGridRowClipboard.test.ts
Normal file
41
frontend/src/components/dataGridRowClipboard.test.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { buildCopiedRowsForPaste, buildPastedRowsFromCopiedRows } from './dataGridRowClipboard';
|
||||
|
||||
const rowKeyField = '__gonavi_row_key__';
|
||||
|
||||
describe('dataGridRowClipboard', () => {
|
||||
it('copies selected rows in selection order without the internal row key', () => {
|
||||
const copiedRows = buildCopiedRowsForPaste({
|
||||
rows: [
|
||||
{ [rowKeyField]: 'row-1', id: 1, name: 'alpha', hidden_note: 'A' },
|
||||
{ [rowKeyField]: 'row-2', id: 2, name: 'beta', hidden_note: 'B' },
|
||||
],
|
||||
selectedRowKeys: ['row-2', 'row-1'],
|
||||
columnNames: ['id', 'name', 'hidden_note'],
|
||||
rowKeyField,
|
||||
});
|
||||
|
||||
expect(copiedRows).toEqual([
|
||||
{ id: 2, name: 'beta', hidden_note: 'B' },
|
||||
{ id: 1, name: 'alpha', hidden_note: 'A' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('builds pasted rows as new rows with fresh internal keys', () => {
|
||||
const pastedRows = buildPastedRowsFromCopiedRows({
|
||||
rows: [
|
||||
{ id: 2, name: 'beta' },
|
||||
{ id: 1, name: 'alpha' },
|
||||
],
|
||||
columnNames: ['id', 'name'],
|
||||
rowKeyField,
|
||||
createRowKey: (index) => `paste-${index}`,
|
||||
});
|
||||
|
||||
expect(pastedRows).toEqual([
|
||||
{ [rowKeyField]: 'paste-0', id: 2, name: 'beta' },
|
||||
{ [rowKeyField]: 'paste-1', id: 1, name: 'alpha' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
66
frontend/src/components/dataGridRowClipboard.ts
Normal file
66
frontend/src/components/dataGridRowClipboard.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
export interface BuildCopiedRowsForPasteInput {
|
||||
rows: Array<Record<string, any>>;
|
||||
selectedRowKeys: any[];
|
||||
columnNames: string[];
|
||||
rowKeyField: string;
|
||||
rowKeyToString?: (key: any) => string;
|
||||
}
|
||||
|
||||
export interface BuildPastedRowsFromCopiedRowsInput {
|
||||
rows: Array<Record<string, any>>;
|
||||
columnNames: string[];
|
||||
rowKeyField: string;
|
||||
createRowKey: (index: number) => string;
|
||||
}
|
||||
|
||||
const defaultRowKeyToString = (key: any): string => String(key);
|
||||
|
||||
const getCopyableColumnNames = (columnNames: string[], rowKeyField: string): string[] =>
|
||||
columnNames.filter((columnName) => columnName !== rowKeyField);
|
||||
|
||||
const pickCopyableRowValues = (
|
||||
row: Record<string, any>,
|
||||
columnNames: string[],
|
||||
rowKeyField: string,
|
||||
): Record<string, any> => {
|
||||
const next: Record<string, any> = {};
|
||||
getCopyableColumnNames(columnNames, rowKeyField).forEach((columnName) => {
|
||||
next[columnName] = row?.[columnName];
|
||||
});
|
||||
return next;
|
||||
};
|
||||
|
||||
export const buildCopiedRowsForPaste = ({
|
||||
rows,
|
||||
selectedRowKeys,
|
||||
columnNames,
|
||||
rowKeyField,
|
||||
rowKeyToString = defaultRowKeyToString,
|
||||
}: BuildCopiedRowsForPasteInput): Array<Record<string, any>> => {
|
||||
if (!Array.isArray(rows) || !Array.isArray(selectedRowKeys) || selectedRowKeys.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const rowsByKey = new Map<string, Record<string, any>>();
|
||||
rows.forEach((row) => {
|
||||
const rowKey = row?.[rowKeyField];
|
||||
if (rowKey === undefined || rowKey === null) return;
|
||||
rowsByKey.set(rowKeyToString(rowKey), row);
|
||||
});
|
||||
|
||||
return selectedRowKeys
|
||||
.map((selectedKey) => rowsByKey.get(rowKeyToString(selectedKey)))
|
||||
.filter((row): row is Record<string, any> => Boolean(row))
|
||||
.map((row) => pickCopyableRowValues(row, columnNames, rowKeyField));
|
||||
};
|
||||
|
||||
export const buildPastedRowsFromCopiedRows = ({
|
||||
rows,
|
||||
columnNames,
|
||||
rowKeyField,
|
||||
createRowKey,
|
||||
}: BuildPastedRowsFromCopiedRowsInput): Array<Record<string, any>> =>
|
||||
rows.map((row, index) => ({
|
||||
[rowKeyField]: createRowKey(index),
|
||||
...pickCopyableRowValues(row, columnNames, rowKeyField),
|
||||
}));
|
||||
Reference in New Issue
Block a user