From c8575c315b238ddebd27a64fd89afa7e3ea2f093 Mon Sep 17 00:00:00 2001 From: Syngnat Date: Mon, 9 Feb 2026 15:43:25 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7fix(data-grid):=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E6=9F=A5=E8=AF=A2=E5=90=ABSQL=E8=AF=AD=E5=8F=A5?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E6=97=B6=E5=BA=94=E7=94=A8=E5=B4=A9=E6=BA=83?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20-=20formatCellValue=20=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20try-catch=20=E4=BF=9D=E6=8A=A4=20-=20JSON.?= =?UTF-8?q?stringify=20=E5=BC=82=E5=B8=B8=E6=97=B6=E9=99=8D=E7=BA=A7?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=20[Object]=20-=20=E6=96=B0=E5=A2=9E=20DataGr?= =?UTF-8?q?idErrorBoundary=20=E9=94=99=E8=AF=AF=E8=BE=B9=E7=95=8C=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=20-=20=E6=B8=B2=E6=9F=93=E9=94=99=E8=AF=AF=E6=97=B6?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E5=8F=8B=E5=A5=BD=E6=8F=90=E7=A4=BA=E5=B9=B6?= =?UTF-8?q?=E6=8F=90=E4=BE=9B=E9=87=8D=E8=AF=95=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/DataGrid.tsx | 77 +++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/DataGrid.tsx b/frontend/src/components/DataGrid.tsx index db0fda8..d12f926 100644 --- a/frontend/src/components/DataGrid.tsx +++ b/frontend/src/components/DataGrid.tsx @@ -11,6 +11,51 @@ import 'react-resizable/css/styles.css'; import { buildWhereSQL, escapeLiteral, quoteIdentPart, quoteQualifiedIdent } from '../utils/sql'; import { blurToFilter, normalizeBlurForPlatform, normalizeOpacityForPlatform } from '../utils/appearance'; +// --- Error Boundary --- +interface DataGridErrorBoundaryState { + hasError: boolean; + error: Error | null; +} + +class DataGridErrorBoundary extends React.Component< + { children: React.ReactNode }, + DataGridErrorBoundaryState +> { + constructor(props: { children: React.ReactNode }) { + super(props); + this.state = { hasError: false, error: null }; + } + + static getDerivedStateFromError(error: Error): DataGridErrorBoundaryState { + return { hasError: true, error }; + } + + componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { + console.error('DataGrid render error:', error, errorInfo); + } + + render() { + if (this.state.hasError) { + return ( +
+

渲染错误

+

数据表格渲染时发生错误,可能是数据格式问题。

+
+                        {this.state.error?.message}
+                    
+ +
+ ); + } + return this.props.children; + } +} + // 内部行标识字段:避免与真实业务字段(如 `key` 列)冲突。 export const GONAVI_ROW_KEY = '__gonavi_row_key__'; @@ -23,12 +68,23 @@ const normalizeDateTimeString = (val: string) => { // --- Helper: Format Value --- const formatCellValue = (val: any) => { - if (val === null) return NULL; - if (typeof val === 'object') return JSON.stringify(val); - if (typeof val === 'string') { - return normalizeDateTimeString(val); + try { + if (val === null) return NULL; + if (typeof val === 'object') { + try { + return JSON.stringify(val); + } catch { + return '[Object]'; + } + } + if (typeof val === 'string') { + return normalizeDateTimeString(val); + } + return String(val); + } catch (e) { + console.error('formatCellValue error:', e); + return '[Error]'; } - return String(val); }; const toEditableText = (val: any): string => { @@ -2161,4 +2217,13 @@ const DataGrid: React.FC = ({ ); }; -export default React.memo(DataGrid); +// 使用 ErrorBoundary 包裹 DataGrid,防止数据渲染错误导致应用崩溃 +const MemoizedDataGrid = React.memo(DataGrid); + +const DataGridWithErrorBoundary: React.FC = (props) => ( + + + +); + +export default DataGridWithErrorBoundary;