From ec6b4e9e0182a85b862627177c8d373137d1a90b Mon Sep 17 00:00:00 2001 From: mango <1711456624@qq.com> Date: Wed, 29 Jul 2026 18:30:17 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(data-grid):=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=8D=95=E5=80=BC=E7=B2=98=E8=B4=B4=E5=A1=AB=E5=85=85?= =?UTF-8?q?=E9=80=89=E5=8C=BA=20(#781)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/dataGridClipboardPaste.ts | 31 +++++++++++++++---- .../useDataGridBatchActions.test.tsx | 31 +++++++++++++++++++ .../src/components/useDataGridBatchActions.ts | 20 +++++++++++- 3 files changed, 75 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/dataGridClipboardPaste.ts b/frontend/src/components/dataGridClipboardPaste.ts index f9d1c8fd..7e441c7e 100644 --- a/frontend/src/components/dataGridClipboardPaste.ts +++ b/frontend/src/components/dataGridClipboardPaste.ts @@ -23,6 +23,7 @@ export const buildDataGridClipboardPasteRows = ({ columnNames, startRowIndex, startColumnIndex, + targetCells, rowKeyField, addedRowKeys, modifiedRows, @@ -35,6 +36,7 @@ export const buildDataGridClipboardPasteRows = ({ columnNames: string[]; startRowIndex: number; startColumnIndex: number; + targetCells?: Array<{ rowIndex: number; columnIndex: number }>; rowKeyField: string; addedRowKeys: Set; modifiedRows: Record; @@ -46,11 +48,28 @@ export const buildDataGridClipboardPasteRows = ({ return { rows: [], updatedCellCount: 0 }; } + const valuesByRowIndex = new Map>(); + const appendValue = (rowIndex: number, columnIndex: number, value: DataGridClipboardValue) => { + const rowValues = valuesByRowIndex.get(rowIndex) || []; + rowValues.push({ columnIndex, value }); + valuesByRowIndex.set(rowIndex, rowValues); + }; + + if (targetCells && matrix.length === 1 && matrix[0]?.length === 1) { + targetCells.forEach(({ rowIndex, columnIndex }) => appendValue(rowIndex, columnIndex, matrix[0][0])); + } else { + matrix.forEach((sourceValues, sourceRowIndex) => { + sourceValues.forEach((value, sourceColumnIndex) => { + appendValue(startRowIndex + sourceRowIndex, startColumnIndex + sourceColumnIndex, value); + }); + }); + } + const pasteRows: DataGridClipboardPasteRow[] = []; let updatedCellCount = 0; - matrix.forEach((sourceValues, sourceRowIndex) => { - const baseRow = rows[startRowIndex + sourceRowIndex]; + valuesByRowIndex.forEach((targetValues, targetRowIndex) => { + const baseRow = rows[targetRowIndex]; const rowKeyValue = baseRow?.[rowKeyField]; if (rowKeyValue === undefined || rowKeyValue === null) return; @@ -61,11 +80,11 @@ export const buildDataGridClipboardPasteRows = ({ const currentRow = addedRowKeys.has(rowKey) ? baseRow : { ...baseRow, ...existing }; const values: Record = {}; - sourceValues.forEach((nextValue, sourceColumnIndex) => { - const columnName = columnNames[startColumnIndex + sourceColumnIndex]; + targetValues.forEach(({ columnIndex, value }) => { + const columnName = columnNames[columnIndex]; if (!columnName || !isWritableColumn(columnName)) return; - if (isValueEqual(currentRow?.[columnName], nextValue)) return; - values[columnName] = nextValue; + if (isValueEqual(currentRow?.[columnName], value)) return; + values[columnName] = value; updatedCellCount += 1; }); diff --git a/frontend/src/components/useDataGridBatchActions.test.tsx b/frontend/src/components/useDataGridBatchActions.test.tsx index 1dbc9108..97fb7f0e 100644 --- a/frontend/src/components/useDataGridBatchActions.test.tsx +++ b/frontend/src/components/useDataGridBatchActions.test.tsx @@ -212,6 +212,37 @@ describe('useDataGridBatchActions clipboard paste', () => { expect(messageApi.success).toHaveBeenCalledWith('data_grid.message.pasted_columns_to_rows:{"rows":2,"cells":4}'); }); + it('fills the selected cells when pasting a single value', () => { + const hook = renderHook(); + const cell = selectCell(hook.container, 'row-1', 'id'); + hook.currentSelectionRef.current = new Set([ + makeCellKey('row-1', 'id'), + makeCellKey('row-1', 'name'), + makeCellKey('row-2', 'id'), + makeCellKey('row-2', 'name'), + ]); + + const preventDefault = vi.fn(); + act(() => { + (windowTarget.listeners.get('paste') as any)?.({ + target: cell, + clipboardData: { types: ['text/plain'], getData: vi.fn(() => 'filled') }, + preventDefault, + }); + }); + + expect(preventDefault).toHaveBeenCalledOnce(); + const nextRows = hook.setModifiedRows.mock.calls[0][0]({}); + expect(nextRows).toEqual({ + 'row-1': { id: 'filled', name: 'filled' }, + 'row-2': { id: 'filled', name: 'filled' }, + }); + const nextColumns = hook.setModifiedColumns.mock.calls[0][0]({}); + expect(nextColumns['row-1']).toEqual(new Set(['id', 'name'])); + expect(nextColumns['row-2']).toEqual(new Set(['id', 'name'])); + expect(messageApi.success).toHaveBeenCalledWith('data_grid.message.pasted_columns_to_rows:{"rows":2,"cells":4}'); + }); + it('resolves the selected row and column again before pasting', () => { const hook = renderHook(); const cell = selectCell(hook.container, 'row-2', 'name'); diff --git a/frontend/src/components/useDataGridBatchActions.ts b/frontend/src/components/useDataGridBatchActions.ts index b5a16b84..b136b76d 100644 --- a/frontend/src/components/useDataGridBatchActions.ts +++ b/frontend/src/components/useDataGridBatchActions.ts @@ -531,6 +531,23 @@ const handleBatchFillCells = useCallback(() => { const startColumnIndex = columnIndexMap.get(start.colName) ?? -1; if (startRowIndex === -1 || startColumnIndex === -1) return; + let targetCells: Array<{ rowIndex: number; columnIndex: number }> | undefined; + if (matrix.length === 1 && matrix[0]?.length === 1 && currentSelectionRef.current.size > 1) { + const rowIndexes = new Map(); + currentRows.forEach((row, rowIndex) => { + const key = row?.[GONAVI_ROW_KEY]; + if (key !== undefined && key !== null) rowIndexes.set(rowKeyStr(key), rowIndex); + }); + const selectedTargets = Array.from(currentSelectionRef.current).flatMap((cellKey) => { + const cell = splitCellKey(cellKey); + if (!cell) return []; + const rowIndex = rowIndexes.get(cell.rowKey); + const columnIndex = columnIndexMap.get(cell.colName); + return rowIndex === undefined || columnIndex === undefined ? [] : [{ rowIndex, columnIndex }]; + }); + if (selectedTargets.length > 1) targetCells = selectedTargets; + } + const addedRowKeys = new Set(); addedRows.forEach((row) => { const key = row?.[GONAVI_ROW_KEY]; @@ -542,6 +559,7 @@ const handleBatchFillCells = useCallback(() => { columnNames: displayColumnNames, startRowIndex, startColumnIndex, + targetCells, rowKeyField: GONAVI_ROW_KEY, addedRowKeys, modifiedRows, @@ -615,7 +633,7 @@ const handleBatchFillCells = useCallback(() => { cellSelectionPointerRef.current = null; isDraggingRef.current = false; }; - }, [addedRows, canModifyData, deletedRowKeys, isActive, isTableSurfaceActive, displayColumnNames, columnIndexMap, effectiveEditLocator, isCellValueEqualForDiff, isWritableResultColumn, markCellSelectionDeleteEligible, modifiedRows, rowKeyStr, setAddedRows, setModifiedColumns, setModifiedRows, setSelectedCells, translateDataGrid, updateCellSelection]); + }, [addedRows, canModifyData, deletedRowKeys, isActive, isTableSurfaceActive, displayColumnNames, columnIndexMap, effectiveEditLocator, isCellValueEqualForDiff, isWritableResultColumn, markCellSelectionDeleteEligible, modifiedRows, rowKeyStr, setAddedRows, setModifiedColumns, setModifiedRows, setSelectedCells, splitCellKey, translateDataGrid, updateCellSelection]); const handleCopySelectedColumnsFromRow = useCallback(() => { const activeSelection = currentSelectionRef.current.size > 0 ? currentSelectionRef.current : selectedCells;