From 2c3f4a103274df6f8ffb872813506f337ec90670 Mon Sep 17 00:00:00 2001 From: Syngnat Date: Thu, 19 Mar 2026 11:33:30 +0800 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(TableDesigner):?= =?UTF-8?q?=20=E9=87=8D=E6=9E=84=E9=80=89=E6=8B=A9=E4=BA=A4=E4=BA=92?= =?UTF-8?q?=E4=B8=BA=E6=89=8B=E5=8A=A8Checkbox=E5=B9=B6=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=B8=B2=E6=9F=93=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 交互重构:移除rowSelection依赖,改用手动Checkbox列避免Ant Design内部对齐差异 - 列隔离:Checkbox和Sort列脱离resizableColumns,不经过ResizableTitle处理 - 对齐修复:.ant-input padding-left归零,消除borderless Input导致的th/td文字偏移 - 性能优化:resizableColumns/sortColumn等用useMemo稳定引用,Tab切换startTransition降级 - 动画加速:ink-bar添加will-change:transform独立合成层,过渡缩短至0.15s --- frontend/src/components/TableDesigner.tsx | 88 ++++++++++++++++++----- 1 file changed, 71 insertions(+), 17 deletions(-) diff --git a/frontend/src/components/TableDesigner.tsx b/frontend/src/components/TableDesigner.tsx index 0df2840..2697949 100644 --- a/frontend/src/components/TableDesigner.tsx +++ b/frontend/src/components/TableDesigner.tsx @@ -121,7 +121,7 @@ const ResizableTitle = (props: any) => { nextStyle.width = width; } - if (!width) { + if (!onResizeStart) { return ; } @@ -415,11 +415,6 @@ const TableDesigner: React.FC<{ tab: TabData }> = ({ tab }) => { // Initial Columns Definition useEffect(() => { const initialCols = [ - ...(readOnly ? [] : [{ - key: 'sort', - width: 40, - render: () => , - }]), { title: '名', dataIndex: 'name', @@ -2020,13 +2015,60 @@ END;`; }; // Merge columns with resize handler - const resizableColumns = tableColumns.map((col, index) => ({ + const resizableColumns = useMemo(() => tableColumns.map((col, index) => ({ ...col, onHeaderCell: (column: any) => ({ width: column.width, onResizeStart: handleResizeStart(index), }), - })); + })), [tableColumns]); + + // 字段表 Checkbox 选择列(不参与 resize,支持全选) + const allColumnKeys = useMemo(() => columns.map(c => c._key), [columns]); + const isAllColumnsSelected = allColumnKeys.length > 0 && selectedColumnRowKeys.length === allColumnKeys.length; + const isColumnsIndeterminate = selectedColumnRowKeys.length > 0 && selectedColumnRowKeys.length < allColumnKeys.length; + + const columnSelectCol = useMemo(() => ({ + title: () => ( + setSelectedColumnRowKeys(e.target.checked ? allColumnKeys : [])} + style={{ margin: 0 }} + /> + ), + dataIndex: '_select', + key: '_select', + width: 48, + render: (_: any, record: any) => ( + { + e.stopPropagation(); + setSelectedColumnRowKeys((prev: string[]) => + e.target.checked + ? [...prev, record._key] + : prev.filter((k: string) => k !== record._key) + ); + }} + style={{ margin: 0 }} + /> + ), + }), [selectedColumnRowKeys, allColumnKeys, isAllColumnsSelected, isColumnsIndeterminate]); + + // sort 拖拽列(不参与 resize) + const sortColumn = useMemo(() => ({ + key: 'sort', + width: 40, + render: () => , + }), []); + + const columnsWithSelect = useMemo(() => + readOnly + ? resizableColumns + : [columnSelectCol, sortColumn, ...resizableColumns], + [readOnly, columnSelectCol, sortColumn, resizableColumns] + ); // --- Index Columns Init --- useEffect(() => { @@ -2153,7 +2195,7 @@ END;`; {readOnly ? ( record._key === focusColumnKey ? 'table-designer-focus-row' : ''} size="small" @@ -2172,11 +2214,7 @@ END;`; c._key)} strategy={verticalListSortingStrategy}>
setSelectedColumnRowKeys(nextSelectedRowKeys as string[]), - }} + columns={columnsWithSelect} rowKey="_key" rowClassName={(record: EditableColumn) => record._key === focusColumnKey ? 'table-designer-focus-row' : ''} size="small" @@ -2203,11 +2241,13 @@ END;`; .table-designer-shell .ant-table-container { background: transparent !important; } - .table-designer-shell .ant-table-wrapper, - .table-designer-shell .ant-table-container { + .table-designer-shell .ant-table-wrapper { border: none !important; overflow: hidden !important; } + .table-designer-shell .ant-table-container { + border: none !important; + } .table-designer-shell .ant-table-thead > tr > th { background: transparent !important; border-bottom: 1px solid ${darkMode ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.06)'} !important; @@ -2219,6 +2259,13 @@ END;`; border-bottom: 1px solid ${darkMode ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)'} !important; border-inline-end: 1px solid transparent !important; } + .table-designer-shell .ant-table-tbody td .ant-input { + padding-left: 0 !important; + padding-right: 0 !important; + } + .table-designer-shell .ant-table-tbody td .ant-select .ant-select-selector { + padding-left: 0 !important; + } .table-designer-shell .ant-table-thead > tr > th::before { display: none !important; } @@ -2237,6 +2284,13 @@ END;`; .table-designer-shell .ant-tabs-nav::before { border-bottom-color: ${darkMode ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.08)'} !important; } + .table-designer-shell .ant-tabs-ink-bar { + will-change: transform; + transition: width 0.15s ease, left 0.15s ease, transform 0.15s ease !important; + } + .table-designer-shell .ant-tabs-tab { + transition: color 0.15s ease !important; + } .table-designer-shell .ant-tabs-content-holder, .table-designer-shell .ant-tabs-content, .table-designer-shell .ant-tabs-tabpane { @@ -2343,7 +2397,7 @@ END;`; React.startTransition(() => setActiveKey(key))} style={{ flex: 1, minHeight: 0,