From 5c0f6f8ff4e91dcd124993acc3191827ac238617 Mon Sep 17 00:00:00 2001 From: Syngnat Date: Wed, 1 Apr 2026 16:21:57 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(data-grid):=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E6=95=B0=E6=8D=AE=E9=A2=84=E8=A7=88=E9=9D=A2=E6=9D=BF?= =?UTF-8?q?=E6=97=A5=E6=9C=9F=E6=A0=BC=E5=BC=8F=E5=8C=96=E3=80=81JSON?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E5=A4=B1=E6=95=88=E5=8F=8A=E5=B9=BD=E7=81=B5?= =?UTF-8?q?=E5=8F=98=E6=9B=B4=E8=AE=A1=E6=95=B0=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 日期时间字段预览时通过 normalizeDateTimeString 格式化带时区的 ISO 格式 - 切换单元格时始终更新预览值,用 dataPanelOriginalRef 替代 suppress 机制判断 dirty - handleCellSave 增加根源级变更检测,与原始 data 逐字段比较后才写入 modifiedRows - 英文消息 "No changes to commit" 改为中文 "没有可提交的变更" - refs #301 --- frontend/src/components/DataGrid.tsx | 64 ++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/frontend/src/components/DataGrid.tsx b/frontend/src/components/DataGrid.tsx index 5357f4c..79644df 100644 --- a/frontend/src/components/DataGrid.tsx +++ b/frontend/src/components/DataGrid.tsx @@ -1173,6 +1173,7 @@ const DataGrid: React.FC = ({ const [dataPanelValue, setDataPanelValue] = useState(''); const [dataPanelIsJson, setDataPanelIsJson] = useState(false); const dataPanelDirtyRef = useRef(false); + const dataPanelOriginalRef = useRef(''); const [rowEditorOpen, setRowEditorOpen] = useState(false); const [rowEditorRowKey, setRowEditorRowKey] = useState(''); const rowEditorBaseRawRef = useRef>({}); @@ -1495,14 +1496,18 @@ const DataGrid: React.FC = ({ const updateFocusedCell = useCallback((record: Item, dataIndex: string) => { if (!record || !dataIndex) return; const raw = record?.[dataIndex]; - const text = toEditableText(raw); + let text = toEditableText(raw); + // 日期时间字段格式化(处理带时区的 ISO 格式如 2026-03-22T00:00:00+08:00) + if (typeof raw === 'string') { + text = normalizeDateTimeString(raw); + } const isJson = looksLikeJsonText(text); setFocusedCellInfo({ record, dataIndex, title: dataIndex }); - // 仅在面板未被用户手动编辑时自动同步值 - if (!dataPanelDirtyRef.current) { - setDataPanelValue(text); - setDataPanelIsJson(isJson); - } + // 切换到新单元格时总是更新预览值并重置 dirty 标记 + dataPanelOriginalRef.current = text; + setDataPanelValue(text); + setDataPanelIsJson(isJson); + dataPanelDirtyRef.current = false; }, []); const handleDataPanelFormatJson = useCallback(() => { @@ -2899,28 +2904,49 @@ const DataGrid: React.FC = ({ }, []); const handleCellSave = useCallback((row: any) => { - // Optimistic update for display - // In parent-controlled data, we might need parent to update 'data', - // but here we manage 'modifiedRows' locally and overlay it. - // Since 'displayData' is derived from 'data' + 'modifiedRows', we need to update the source if it's in 'data'. - // But 'data' prop is immutable. - // So we update 'modifiedRows'. - - // Check if it's an added row const rowKey = row?.[GONAVI_ROW_KEY]; if (rowKey === undefined) return; const isAdded = addedRows.some(r => r?.[GONAVI_ROW_KEY] === rowKey); if (isAdded) { setAddedRows(prev => prev.map(r => r?.[GONAVI_ROW_KEY] === rowKey ? { ...r, ...row } : r)); } else { + // 查找原始行数据,对比是否真正有值变更 + const originalRow = data.find(r => r?.[GONAVI_ROW_KEY] === rowKey); + if (originalRow) { + const changedFields: Record = {}; + for (const col of Object.keys(row)) { + if (col === GONAVI_ROW_KEY) continue; + if (!isCellValueEqualForDiff(originalRow[col], row[col])) { + changedFields[col] = row[col]; + } + } + if (Object.keys(changedFields).length === 0) { + // 没有实际变更,从 modifiedRows 中移除该行(如有) + setModifiedRows(prev => { + const keyStr = rowKeyStr(rowKey); + if (!(keyStr in prev)) return prev; + const next = { ...prev }; + delete next[keyStr]; + return next; + }); + return; + } + } setModifiedRows(prev => ({ ...prev, [rowKeyStr(rowKey)]: row })); } - }, [addedRows]); + }, [addedRows, data]); const handleDataPanelSave = useCallback(() => { if (!focusedCellInfo) return; + // 与 updateFocusedCell 设置的原始值比较,避免幽灵变更 + if (dataPanelValue === dataPanelOriginalRef.current) { + dataPanelDirtyRef.current = false; + void message.info('数据未变更'); + return; + } const nextRow: any = { ...focusedCellInfo.record, [focusedCellInfo.dataIndex]: dataPanelValue }; handleCellSave(nextRow); + dataPanelOriginalRef.current = dataPanelValue; dataPanelDirtyRef.current = false; void message.success('已保存'); }, [focusedCellInfo, dataPanelValue, handleCellSave]); @@ -3488,7 +3514,7 @@ const DataGrid: React.FC = ({ }); if (inserts.length === 0 && updates.length === 0 && deletes.length === 0) { - void message.info("No changes to commit"); + void message.info("没有可提交的变更"); return; } @@ -5341,8 +5367,10 @@ const DataGrid: React.FC = ({ theme={darkMode ? 'transparent-dark' : 'transparent-light'} value={dataPanelValue} onChange={(val) => { - setDataPanelValue(val || ''); - dataPanelDirtyRef.current = true; + const newVal = val || ''; + setDataPanelValue(newVal); + // 只有值真正与原始值不同时才标记 dirty + dataPanelDirtyRef.current = newVal !== dataPanelOriginalRef.current; }} options={{ minimap: { enabled: false },