feat(designer): 将对象设计整合进数据视图并统一设计表交互样式

This commit is contained in:
Syngnat
2026-06-03 15:27:54 +08:00
parent eeaf3c658b
commit 4b23c013d9
11 changed files with 766 additions and 109 deletions

View File

@@ -65,6 +65,8 @@ const backendApp = vi.hoisted(() => ({
DBGetColumns: vi.fn(),
DBGetIndexes: vi.fn(),
DBGetForeignKeys: vi.fn(),
DBGetTriggers: vi.fn(),
DBQuery: vi.fn(),
DBShowCreateTable: vi.fn(),
}));
@@ -112,6 +114,16 @@ vi.mock('./ImportPreviewModal', () => ({
default: () => null,
}));
vi.mock('./TableDesigner', () => ({
default: ({ tab, embedded }: { tab: { tableName?: string; initialTab?: string }; embedded?: boolean }) => (
<div data-table-designer={embedded ? 'embedded' : 'standalone'}>
<span>SCHEMA DESIGNER</span>
<span>{tab.tableName || 'unknown-table'}</span>
<span>{tab.initialTab || 'columns'}</span>
</div>
),
}));
vi.mock('@ant-design/icons', () => {
const Icon = () => <span />;
@@ -152,6 +164,7 @@ vi.mock('@ant-design/icons', () => {
vi.mock('@dnd-kit/core', () => ({
DndContext: ({ children }: any) => <>{children}</>,
PointerSensor: vi.fn(),
KeyboardSensor: vi.fn(),
MouseSensor: vi.fn(),
TouchSensor: vi.fn(),
useSensor: vi.fn(() => ({})),
@@ -170,6 +183,7 @@ vi.mock('@dnd-kit/sortable', () => ({
isDragging: false,
})),
horizontalListSortingStrategy: vi.fn(),
sortableKeyboardCoordinates: vi.fn(),
arrayMove: (items: any[], from: number, to: number) => {
const next = [...items];
const [item] = next.splice(from, 1);
@@ -223,6 +237,29 @@ vi.mock('antd', () => {
Modal.useModal = () => [{ info: vi.fn(() => ({ destroy: vi.fn() })) }, null];
const passthrough = ({ children }: any) => <>{children}</>;
const Space = ({ children }: any) => <div>{children}</div>;
const Tabs = ({ items = [], activeKey, onChange }: any) => {
const resolvedActiveKey = activeKey ?? items[0]?.key;
const activeItem = items.find((item: any) => item.key === resolvedActiveKey) || items[0];
return (
<div data-tabs-active-key={resolvedActiveKey}>
<div>
{items.map((item: any) => (
<button key={item.key} type="button" data-tab-key={item.key} onClick={() => onChange?.(item.key)}>
{item.label}
</button>
))}
</div>
<div>{activeItem?.children ?? null}</div>
</div>
);
};
const Empty: any = ({ description }: any) => <div>{description || 'empty'}</div>;
Empty.PRESENTED_IMAGE_SIMPLE = 'presented-image-simple';
const Tag = ({ children }: any) => <span>{children}</span>;
const Radio: any = ({ children }: any) => <span>{children}</span>;
Radio.Group = ({ children }: any) => <div>{children}</div>;
Radio.Button = ({ children }: any) => <button type="button">{children}</button>;
const Segmented = ({ value, options, onChange }: any) => (
<div data-segmented-value={value}>
{(options || []).map((option: any) => (
@@ -255,7 +292,7 @@ vi.mock('antd', () => {
Dropdown: passthrough,
Form,
Pagination: () => null,
Select: () => null,
Select: ({ children }: any) => <div>{children}</div>,
Modal,
Checkbox: ({ checked, onChange }: any) => <input type="checkbox" checked={checked} onChange={onChange} />,
Segmented,
@@ -264,6 +301,11 @@ vi.mock('antd', () => {
DatePicker: () => null,
TimePicker: () => null,
AutoComplete: ({ children }: any) => <>{children}</>,
Tabs,
Empty,
Space,
Tag,
Radio,
};
});
@@ -509,6 +551,8 @@ describe('DataGrid DDL interactions', () => {
backendApp.DBGetColumns.mockResolvedValue({ success: true, data: [] });
backendApp.DBGetIndexes.mockResolvedValue({ success: true, data: [] });
backendApp.DBGetForeignKeys.mockResolvedValue({ success: true, data: [] });
backendApp.DBGetTriggers.mockResolvedValue({ success: true, data: [] });
backendApp.DBQuery.mockResolvedValue({ success: true, data: [] });
backendApp.DBShowCreateTable.mockResolvedValue({ success: true, data: 'CREATE TABLE users' });
storeState.appearance.uiVersion = 'legacy';
storeState.addTab.mockReset();
@@ -557,6 +601,8 @@ describe('DataGrid DDL interactions', () => {
backendApp.DBGetColumns.mockReset();
backendApp.DBGetIndexes.mockReset();
backendApp.DBGetForeignKeys.mockReset();
backendApp.DBGetTriggers.mockReset();
backendApp.DBQuery.mockReset();
backendApp.DBShowCreateTable.mockReset();
vi.unstubAllGlobals();
});
@@ -654,6 +700,7 @@ describe('DataGrid DDL interactions', () => {
connectionId: 'conn-1',
dbName: 'main',
tableName: 'customers',
objectType: 'table',
});
},
);
@@ -949,8 +996,15 @@ describe('DataGrid DDL interactions', () => {
renderer!.unmount();
});
it('switches the v2 footer field tab into the main fields view', async () => {
it('switches the v2 footer object tab into the embedded designer view', async () => {
storeState.appearance.uiVersion = 'v2';
backendApp.DBGetColumns.mockResolvedValueOnce({
success: true,
data: [
{ name: 'id', type: 'bigint', key: 'PRI', nullable: 'NO', default: '', comment: '' },
{ name: 'name', type: 'varchar(255)', key: '', nullable: 'YES', default: '', comment: '' },
],
});
let renderer: ReactTestRenderer;
await act(async () => {
@@ -968,12 +1022,12 @@ describe('DataGrid DDL interactions', () => {
await waitForEffects();
await act(async () => {
findButton(renderer!, '字段信息').props.onClick();
findButton(renderer!, '对象设计').props.onClick();
});
const content = textContent(renderer!.root);
expect(content).toContain('FIELDS');
expect(content).toContain('2 个字段');
expect(content).toContain('SCHEMA DESIGNER');
expect(content).toContain('字段');
expect(content).toContain('id');
expect(content).toContain('name');
});
@@ -997,9 +1051,9 @@ describe('DataGrid DDL interactions', () => {
await waitForEffects();
await act(async () => {
findButton(renderer!, '字段信息').props.onClick();
findButton(renderer!, '对象设计').props.onClick();
});
expect(textContent(renderer!.root)).toContain('FIELDS');
expect(textContent(renderer!.root)).toContain('SCHEMA DESIGNER');
storeState.appearance.uiVersion = 'legacy';
await act(async () => {
@@ -1017,13 +1071,54 @@ describe('DataGrid DDL interactions', () => {
await waitForEffects();
const content = textContent(renderer!.root);
expect(content).not.toContain('FIELDS');
expect(content).not.toContain('SCHEMA DESIGNER');
expect(content).not.toContain('gn-v2-data-grid-fields-view');
expect(content).toContain('数据预览');
expect(content).toContain('结果视图');
expect(content).toContain('字段信息');
});
it('keeps the v2 fields tab as read-only field info for views', async () => {
storeState.appearance.uiVersion = 'v2';
backendApp.DBGetColumns.mockResolvedValueOnce({
success: true,
data: [
{ name: 'id', type: 'bigint', key: '', nullable: 'NO', default: '', comment: '' },
{ name: 'name', type: 'varchar(255)', key: '', nullable: 'YES', default: '', comment: '' },
],
});
let renderer: ReactTestRenderer;
await act(async () => {
renderer = create(
<DataGrid
data={[{ __gonavi_row_key__: 'row-1', id: 1, name: 'alpha' }]}
columnNames={['id', 'name']}
loading={false}
tableName="user_view"
dbName="main"
connectionId="conn-1"
objectType="view"
/>,
);
});
await waitForEffects();
expect(textContent(renderer!.root)).toContain('字段信息');
expect(textContent(renderer!.root)).not.toContain('对象设计');
await act(async () => {
findButton(renderer!, '字段信息').props.onClick();
});
const content = textContent(renderer!.root);
expect(content).toContain('FIELDS');
expect(content).toContain('2 个字段');
expect(content).toContain('id');
expect(content).toContain('name');
expect(content).not.toContain('SCHEMA DESIGNER');
});
it('renders the v2 footer DDL view with the Monaco SQL editor', async () => {
storeState.appearance.uiVersion = 'v2';
backendApp.DBShowCreateTable.mockResolvedValueOnce({

View File

@@ -79,6 +79,8 @@ describe('DataGrid layout', () => {
columnNames={['id', 'name']}
loading={false}
tableName="users"
dbName="main"
connectionId="conn-1"
readOnly
pagination={{
current: 1,
@@ -95,6 +97,7 @@ describe('DataGrid layout', () => {
expect(markup).toContain('data-grid-column-quick-find-action="true"');
expect(markup).toContain('字段显示');
expect(markup).toContain('跳列');
expect(markup).toContain('对象设计');
expect(markup).toContain('data-grid-page-find="true"');
expect(markup).toContain('data-grid-page-find-prev="true"');
expect(markup).toContain('data-grid-page-find-next="true"');
@@ -112,6 +115,34 @@ describe('DataGrid layout', () => {
expect(markup).toContain('当前页查找...');
});
it('keeps the v2 footer fields action labeled as field info for views', () => {
const markup = renderToStaticMarkup(
<DataGrid
data={[
{
__gonavi_row_key__: 'row-1',
id: 1,
name: 'alpha',
},
]}
columnNames={['id', 'name']}
loading={false}
tableName="user_view"
objectType="view"
readOnly
pagination={{
current: 1,
pageSize: 100,
total: 1,
}}
onPageChange={() => {}}
/>,
);
expect(markup).toContain('字段信息');
expect(markup).not.toContain('对象设计');
});
it('hides current-page find in JSON and text record views', () => {
const source = readFileSync(new URL('./DataGrid.tsx', import.meta.url), 'utf8');
@@ -321,6 +352,7 @@ describe('DataGrid layout', () => {
expect(tableMarkup).toContain('data-grid-ddl-action="true"');
expect(tableMarkup).toContain('查看 DDL');
expect(tableMarkup).toContain('对象设计');
expect(tableMarkup).not.toContain('data-grid-locate-sidebar-action="true"');
const schemaTableMarkup = renderToStaticMarkup(
@@ -342,6 +374,7 @@ describe('DataGrid layout', () => {
expect(schemaTableMarkup).toContain('data-grid-ddl-action="true"');
expect(schemaTableMarkup).toContain('查看 DDL');
expect(schemaTableMarkup).toContain('对象设计');
expect(schemaTableMarkup).toContain('data-grid-page-find="true"');
const queryMarkup = renderToStaticMarkup(
@@ -363,6 +396,8 @@ describe('DataGrid layout', () => {
);
expect(queryMarkup).not.toContain('data-grid-ddl-action="true"');
expect(queryMarkup).toContain('字段信息');
expect(queryMarkup).not.toContain('对象设计');
});
it('keeps row copy and paste as context menu actions instead of toolbar buttons', () => {

View File

@@ -129,6 +129,7 @@ import DataGridPreviewPanel from './DataGridPreviewPanel';
import { DataGridJsonView, DataGridTextView } from './DataGridRecordViews';
import { DataGridV2DdlSideWorkspace, DataGridV2DdlView } from './DataGridV2DdlWorkspace';
import { DataGridV2ErView, DataGridV2FieldsView } from './DataGridV2MetadataViews';
import TableDesigner from './TableDesigner';
import { useDataGridFilters } from './useDataGridFilters';
import { useDataGridDdlView } from './useDataGridDdlView';
import { useDataGridModalEditors } from './useDataGridModalEditors';
@@ -1191,6 +1192,7 @@ interface DataGridProps {
columnNames: string[];
loading: boolean;
tableName?: string;
objectType?: 'table' | 'view' | 'materialized-view';
exportScope?: 'table' | 'queryResult';
resultSql?: string;
dbName?: string;
@@ -1482,7 +1484,7 @@ const VIRTUAL_EDITING_CELL_STYLE: React.CSSProperties = {
};
const DataGrid: React.FC<DataGridProps> = ({
data, columnNames, loading, tableName, exportScope = 'table', dbName, connectionId, pkColumns = [], editLocator, readOnly = false,
data, columnNames, loading, tableName, objectType = 'table', exportScope = 'table', dbName, connectionId, pkColumns = [], editLocator, readOnly = false,
onReload, onSort, onPageChange, pagination, onRequestTotalCount, onCancelTotalCount, sortInfoExternal, showFilter, onToggleFilter, exportSqlWithFilter, onApplyFilter, appliedFilterConditions, quickWhereCondition,
onApplyQuickWhereCondition,
scrollSnapshot, onScrollSnapshotChange
@@ -1720,6 +1722,7 @@ const DataGrid: React.FC<DataGridProps> = ({
const canImport = exportScope === 'table' && !!tableName;
const canExport = !!connectionId && (isQueryResultExport || !!tableName);
const canViewDdl = exportScope === 'table' && !!connectionId && !!tableName;
const canOpenObjectDesigner = exportScope === 'table' && objectType === 'table' && !!connectionId && !!tableName;
const filteredExportSql = useMemo(() => String(exportSqlWithFilter || '').trim(), [exportSqlWithFilter]);
const hasFilteredExportSql = exportScope === 'table' && filteredExportSql.length > 0;
@@ -2357,6 +2360,7 @@ const DataGrid: React.FC<DataGridProps> = ({
connectionId,
dbName: targetDbName,
tableName: refTableName,
objectType: 'table',
});
}, [addTab, connectionId, dbName, setActiveContext]);
@@ -3225,6 +3229,22 @@ const DataGrid: React.FC<DataGridProps> = ({
},
});
useEffect(() => {
const handleExternalViewModeChange = (event: Event) => {
const detail = (event as CustomEvent<any>)?.detail || {};
if (String(detail.connectionId || '') !== String(connectionId || '')) return;
if (String(detail.dbName || '') !== String(dbName || '')) return;
if (String(detail.tableName || '') !== String(tableName || '')) return;
const nextMode = String(detail.viewMode || '').trim();
if (!nextMode) return;
if (!['table', 'json', 'text', 'fields', 'ddl', 'er'].includes(nextMode)) return;
handleViewModeChange(nextMode as GridViewMode);
};
window.addEventListener('gonavi:data-grid:set-view-mode', handleExternalViewModeChange as EventListener);
return () => window.removeEventListener('gonavi:data-grid:set-view-mode', handleExternalViewModeChange as EventListener);
}, [canOpenObjectDesigner, connectionId, dbName, handleViewModeChange, tableName]);
useEffect(() => {
if (!isTableSurfaceActive || !isV2Ui || !cellContextMenu.visible) return;
const portal = cellContextMenuPortalRef.current;
@@ -7542,14 +7562,31 @@ const DataGrid: React.FC<DataGridProps> = ({
{viewMode === 'table' ? (
renderDataTableView()
) : isV2Ui && viewMode === 'fields' ? (
<DataGridV2FieldsView
tableName={tableName}
displayOutputColumnNames={displayOutputColumnNames}
pkColumns={pkColumns}
locatorColumns={effectiveEditLocator?.columns}
columnMetaMap={columnMetaMap}
columnMetaMapByLowerName={columnMetaMapByLowerName}
/>
canOpenObjectDesigner ? (
<TableDesigner
embedded
tab={{
id: `embedded-design-${connectionId || ''}-${dbName || ''}-${tableName || ''}`,
title: `设计表 (${tableName || ''})`,
type: 'design',
connectionId: String(connectionId || ''),
dbName,
tableName,
initialTab: 'columns',
readOnly,
objectType: 'table',
}}
/>
) : (
<DataGridV2FieldsView
tableName={tableName}
displayOutputColumnNames={displayOutputColumnNames}
pkColumns={pkColumns}
locatorColumns={effectiveEditLocator?.columns}
columnMetaMap={columnMetaMap}
columnMetaMapByLowerName={columnMetaMapByLowerName}
/>
)
) : isV2Ui && viewMode === 'ddl' && ddlViewLayout === 'side' ? (
<DataGridV2DdlSideWorkspace
tableContent={renderDataTableView()}
@@ -7759,6 +7796,7 @@ const DataGrid: React.FC<DataGridProps> = ({
<DataGridSecondaryActions
isV2Ui={isV2Ui}
canViewDdl={canViewDdl}
canOpenObjectDesigner={canOpenObjectDesigner}
viewMode={viewMode}
ddlLoading={ddlLoading}
showColumnComment={showColumnComment}

View File

@@ -14,6 +14,7 @@ type GridViewMode = 'table' | 'json' | 'text' | 'fields' | 'ddl' | 'er';
export interface DataGridSecondaryActionsProps {
isV2Ui: boolean;
canViewDdl: boolean;
canOpenObjectDesigner: boolean;
viewMode: GridViewMode;
ddlLoading: boolean;
showColumnComment: boolean;
@@ -35,6 +36,7 @@ export interface DataGridSecondaryActionsProps {
const DataGridSecondaryActions: React.FC<DataGridSecondaryActionsProps> = ({
isV2Ui,
canViewDdl,
canOpenObjectDesigner,
viewMode,
ddlLoading,
showColumnComment,
@@ -53,9 +55,11 @@ const DataGridSecondaryActions: React.FC<DataGridSecondaryActionsProps> = ({
onOpenTableDdl,
}) => {
if (isV2Ui) {
const fieldsActionLabel = canOpenObjectDesigner ? '对象设计' : '字段信息';
const fieldsActionIcon = canOpenObjectDesigner ? <EditOutlined /> : <FileTextOutlined />;
const viewTabItems: Array<{ key: GridViewMode; label: string; icon: React.ReactNode; disabled?: boolean }> = [
{ key: 'table', label: '数据预览', icon: <TableOutlined /> },
{ key: 'fields', label: '字段信息', icon: <FileTextOutlined /> },
{ key: 'fields', label: fieldsActionLabel, icon: fieldsActionIcon },
{ key: 'ddl', label: '查看 DDL', icon: <ConsoleSqlOutlined />, disabled: !canViewDdl },
{ key: 'er', label: 'ER 图', icon: <LinkOutlined /> },
];

View File

@@ -1095,6 +1095,7 @@ const DataViewer: React.FC<{ tab: TabData; isActive?: boolean }> = React.memo(({
columnNames={columnNames}
loading={loading}
tableName={tab.tableName}
objectType={tab.objectType || 'table'}
exportScope="table"
dbName={tab.dbName}
connectionId={tab.connectionId}

View File

@@ -2726,6 +2726,7 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
connectionId,
dbName: targetDbName,
tableName: targetTableName,
objectType: 'table',
});
dispatchQueryEditorSidebarLocate({
connectionId,

View File

@@ -3415,6 +3415,7 @@ const Sidebar: React.FC<{
connectionId: id,
dbName,
tableName,
objectType: 'table',
});
return;
} else if (node.type === 'view' || node.type === 'materialized-view') {
@@ -3426,6 +3427,7 @@ const Sidebar: React.FC<{
connectionId: id,
dbName,
tableName: viewName,
objectType: node.type === 'materialized-view' ? 'materialized-view' : 'view',
});
return;
} else if (node.type === 'saved-query') {

View File

@@ -360,7 +360,23 @@ const SortableRow = ({ children, ...props }: RowProps) => {
);
};
const TableDesigner: React.FC<{ tab: TabData }> = ({ tab }) => {
const renderDesignerCellField = (content: React.ReactNode, className?: string) => (
<div className={`table-designer-cell-field${className ? ` ${className}` : ''}`}>
{content}
</div>
);
const renderDesignerCellCheck = (content: React.ReactNode, className?: string) => (
<div className={`table-designer-cell-check${className ? ` ${className}` : ''}`}>
{content}
</div>
);
const renderDesignerHeaderTitle = (title: string) => (
<span className="table-designer-header-title">{title}</span>
);
const TableDesigner: React.FC<{ tab: TabData; embedded?: boolean }> = ({ tab, embedded = false }) => {
const isNewTable = !tab.tableName;
const [columns, setColumns] = useState<EditableColumn[]>([]);
@@ -431,6 +447,7 @@ const TableDesigner: React.FC<{ tab: TabData }> = ({ tab }) => {
const [commentEditorColumnKey, setCommentEditorColumnKey] = useState('');
const [commentEditorColumnName, setCommentEditorColumnName] = useState('');
const [commentEditorValue, setCommentEditorValue] = useState('');
const [inlineCommentEditingKey, setInlineCommentEditingKey] = useState('');
const connections = useStore(state => state.connections);
const theme = useStore(state => state.theme);
@@ -439,9 +456,6 @@ const TableDesigner: React.FC<{ tab: TabData }> = ({ tab }) => {
const isV2Ui = appearance.uiVersion === 'v2';
const resizeGuideColor = darkMode ? '#f6c453' : '#1890ff';
const readOnly = !!tab.readOnly;
const designerTableTitle = tab.tableName || newTableName || '未命名表';
const designerDbTitle = tab.dbName || '默认库';
const designerColumnSummary = `${columns.length} 字段`;
const panelRadius = 10;
const panelFrameColor = darkMode ? 'rgba(0, 0, 0, 0.18)' : 'rgba(0, 0, 0, 0.12)';
const panelToolbarBorder = darkMode ? 'rgba(255, 255, 255, 0.12)' : 'rgba(0, 0, 0, 0.10)';
@@ -458,6 +472,7 @@ const TableDesigner: React.FC<{ tab: TabData }> = ({ tab }) => {
const openCommentEditor = useCallback((record: EditableColumn) => {
if (!record?._key) return;
setInlineCommentEditingKey('');
setCommentEditorColumnKey(record._key);
setCommentEditorColumnName(record.name || '');
setCommentEditorValue(record.comment || '');
@@ -518,6 +533,10 @@ const TableDesigner: React.FC<{ tab: TabData }> = ({ tab }) => {
setSelectedColumnRowKeys(prev => prev.filter(key => columns.some(c => c._key === key)));
}, [columns]);
useEffect(() => {
setInlineCommentEditingKey(prev => (prev && columns.some(c => c._key === prev) ? prev : ''));
}, [columns]);
useEffect(() => {
return () => {
if (focusHighlightTimerRef.current !== null) {
@@ -552,6 +571,15 @@ const TableDesigner: React.FC<{ tab: TabData }> = ({ tab }) => {
return true;
}, [activeKey, readOnly]);
const startInlineCommentEdit = useCallback((record: EditableColumn) => {
if (readOnly || !record?._key) return;
setInlineCommentEditingKey(record._key);
}, [readOnly]);
const finishInlineCommentEdit = useCallback(() => {
setInlineCommentEditingKey('');
}, []);
useEffect(() => {
const pendingKey = pendingFocusColumnKeyRef.current;
if (!pendingKey || activeKey !== 'columns') return;
@@ -578,64 +606,80 @@ const TableDesigner: React.FC<{ tab: TabData }> = ({ tab }) => {
const columnTypeOptions = resolveColumnTypeOptions(getDbType());
const initialCols = [
{
title: '名',
title: renderDesignerHeaderTitle('名'),
dataIndex: 'name',
key: 'name',
width: 180,
render: (text: string, record: EditableColumn) => readOnly ? text : (
<Input {...noAutoCapInputProps} value={text} onChange={e => handleColumnChange(record._key, 'name', e.target.value)} variant="borderless" />
renderDesignerCellField(
<Input {...noAutoCapInputProps} value={text} onChange={e => handleColumnChange(record._key, 'name', e.target.value)} variant="borderless" />
)
)
},
{
title: '类型',
title: renderDesignerHeaderTitle('类型'),
dataIndex: 'type',
key: 'type',
width: 150,
render: (text: string, record: EditableColumn) => readOnly ? text : (
<AutoComplete options={columnTypeOptions} value={text} onChange={val => handleColumnChange(record._key, 'type', val)} style={{ width: '100%' }} variant="borderless" />
renderDesignerCellField(
<AutoComplete options={columnTypeOptions} value={text} onChange={val => handleColumnChange(record._key, 'type', val)} style={{ width: '100%' }} variant="borderless" />,
'is-compact'
)
)
},
{
title: '主键',
title: renderDesignerHeaderTitle('主键'),
dataIndex: 'key',
key: 'key',
width: 60,
align: 'center',
render: (text: string, record: EditableColumn) => (
<Checkbox checked={text === 'PRI'} disabled={readOnly} onChange={e => handleColumnChange(record._key, 'key', e.target.checked ? 'PRI' : '')} />
renderDesignerCellCheck(
<Checkbox checked={text === 'PRI'} disabled={readOnly} onChange={e => handleColumnChange(record._key, 'key', e.target.checked ? 'PRI' : '')} />,
'is-left-aligned'
)
)
},
{
title: '自增',
title: renderDesignerHeaderTitle('自增'),
dataIndex: 'isAutoIncrement',
key: 'isAutoIncrement',
width: 60,
align: 'center',
render: (val: boolean, record: EditableColumn) => (
<Checkbox checked={val} disabled={readOnly} onChange={e => handleColumnChange(record._key, 'isAutoIncrement', e.target.checked)} />
renderDesignerCellCheck(
<Checkbox checked={val} disabled={readOnly} onChange={e => handleColumnChange(record._key, 'isAutoIncrement', e.target.checked)} />,
'is-left-aligned'
)
)
},
{
title: '不是 Null',
title: renderDesignerHeaderTitle('不是 Null'),
dataIndex: 'nullable',
key: 'nullable',
width: 80,
align: 'center',
render: (text: string, record: EditableColumn) => (
<Checkbox checked={text === 'NO'} disabled={readOnly || record.key === 'PRI'} onChange={e => handleColumnChange(record._key, 'nullable', e.target.checked ? 'NO' : 'YES')} />
renderDesignerCellCheck(
<Checkbox checked={text === 'NO'} disabled={readOnly || record.key === 'PRI'} onChange={e => handleColumnChange(record._key, 'nullable', e.target.checked ? 'NO' : 'YES')} />,
'is-left-aligned'
)
)
},
{
title: '默认',
title: renderDesignerHeaderTitle('默认'),
dataIndex: 'default',
key: 'default',
width: 180, // Increased default width
render: (text: string, record: EditableColumn) => readOnly ? text : (
<AutoComplete options={COMMON_DEFAULTS} value={text} onChange={val => handleColumnChange(record._key, 'default', val)} style={{ width: '100%' }} variant="borderless" placeholder="NULL" />
renderDesignerCellField(
<AutoComplete options={COMMON_DEFAULTS} value={text} onChange={val => handleColumnChange(record._key, 'default', val)} style={{ width: '100%' }} variant="borderless" placeholder="NULL" />
)
)
},
{
title: '注释',
title: renderDesignerHeaderTitle('注释'),
dataIndex: 'comment',
key: 'comment',
width: 200,
@@ -644,13 +688,26 @@ const TableDesigner: React.FC<{ tab: TabData }> = ({ tab }) => {
<div style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{text || ''}</div>
</Tooltip>
) : (
<div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
<Input
value={text}
onChange={e => handleColumnChange(record._key, 'comment', e.target.value)}
onDoubleClick={() => openCommentEditor(record)}
variant="borderless"
/>
<div className="table-designer-cell-field table-designer-comment-field">
{inlineCommentEditingKey !== record._key ? (
<Tooltip title={text || ''}>
<div
className={`table-designer-comment-display${text ? '' : ' is-empty'}`}
onDoubleClick={() => startInlineCommentEdit(record)}
>
{text || '\u00A0'}
</div>
</Tooltip>
) : (
<Input
value={text}
onChange={e => handleColumnChange(record._key, 'comment', e.target.value)}
onBlur={finishInlineCommentEdit}
onPressEnter={finishInlineCommentEdit}
autoFocus={inlineCommentEditingKey === record._key}
variant="borderless"
/>
)}
<Tooltip title="弹框编辑注释">
<Button
type="text"
@@ -663,16 +720,25 @@ const TableDesigner: React.FC<{ tab: TabData }> = ({ tab }) => {
)
},
...(readOnly ? [] : [{
title: '操作',
title: renderDesignerHeaderTitle('操作'),
key: 'action',
width: 60,
width: 92,
className: 'table-designer-action-column',
onHeaderCell: () => ({ className: 'table-designer-action-column' }),
render: (_: any, record: EditableColumn) => (
<Button type="text" danger icon={<DeleteOutlined />} onClick={() => handleDeleteColumn(record._key)} />
<div className="table-designer-action-cell">
<Tooltip title="编辑注释">
<Button type="text" size="small" icon={<EditOutlined />} onClick={() => openCommentEditor(record)} />
</Tooltip>
<Tooltip title="删除字段">
<Button type="text" size="small" danger icon={<DeleteOutlined />} onClick={() => handleDeleteColumn(record._key)} />
</Tooltip>
</div>
)
}])
];
setTableColumns(initialCols);
}, [connections, openCommentEditor, readOnly, tab.connectionId]); // Re-create when datasource dialect or readonly state changes
}, [connections, embedded, finishInlineCommentEdit, inlineCommentEditingKey, openCommentEditor, readOnly, startInlineCommentEdit, tab.connectionId]); // Re-create when datasource dialect or readonly state changes
const flushResizeGhost = useCallback(() => {
resizeRafRef.current = null;
@@ -2211,29 +2277,36 @@ END;`;
const columnSelectCol = useMemo(() => ({
title: () => (
<Checkbox
checked={isAllColumnsSelected}
indeterminate={isColumnsIndeterminate}
onChange={(e: any) => setSelectedColumnRowKeys(e.target.checked ? allColumnKeys : [])}
style={{ margin: 0 }}
/>
<div className="table-designer-select-check">
<Checkbox
checked={isAllColumnsSelected}
indeterminate={isColumnsIndeterminate}
onChange={(e: any) => setSelectedColumnRowKeys(e.target.checked ? allColumnKeys : [])}
style={{ margin: 0 }}
/>
</div>
),
dataIndex: '_select',
key: '_select',
width: 48,
width: 44,
className: 'table-designer-select-column',
onHeaderCell: () => ({ className: 'table-designer-select-column' }),
onCell: () => ({ className: 'table-designer-select-column' }),
render: (_: any, record: any) => (
<Checkbox
checked={selectedColumnRowKeys.includes(record._key)}
onChange={(e: any) => {
e.stopPropagation();
setSelectedColumnRowKeys((prev: string[]) =>
e.target.checked
? [...prev, record._key]
: prev.filter((k: string) => k !== record._key)
);
}}
style={{ margin: 0 }}
/>
<div className="table-designer-select-check">
<Checkbox
checked={selectedColumnRowKeys.includes(record._key)}
onChange={(e: any) => {
e.stopPropagation();
setSelectedColumnRowKeys((prev: string[]) =>
e.target.checked
? [...prev, record._key]
: prev.filter((k: string) => k !== record._key)
);
}}
style={{ margin: 0 }}
/>
</div>
),
}), [selectedColumnRowKeys, allColumnKeys, isAllColumnsSelected, isColumnsIndeterminate]);
@@ -2331,6 +2404,9 @@ END;`;
dataIndex: '_select',
key: '_select',
width: 48,
className: 'table-designer-select-column',
onHeaderCell: () => ({ className: 'table-designer-select-column' }),
onCell: () => ({ className: 'table-designer-select-column' }),
render: (_: any, record: any) => (
<span
onClick={(e) => {
@@ -2548,7 +2624,11 @@ END;`;
);
return (
<div ref={shellRef} className={`table-designer-shell${isV2Ui ? ' gn-v2-table-designer' : ''}`} style={{ display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0, padding: '6px 0', position: 'relative' }}>
<div
ref={shellRef}
className={`table-designer-shell${isV2Ui ? ' gn-v2-table-designer' : ''}${embedded ? ' is-embedded' : ''}`}
style={{ display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0, padding: embedded ? 0 : '6px 0', position: 'relative' }}
>
<style>{`
.table-designer-shell .ant-table,
.table-designer-shell .ant-table-wrapper,
@@ -2580,6 +2660,116 @@ END;`;
.table-designer-shell .ant-table-tbody td .ant-select .ant-select-selector {
padding-left: 0 !important;
}
.table-designer-shell .table-designer-cell-field {
display: flex;
align-items: center;
min-height: 34px;
padding: 0 10px;
border: 1px solid ${darkMode ? 'rgba(255,255,255,0.10)' : 'rgba(0,0,0,0.10)'};
border-radius: 10px;
background: ${darkMode ? 'rgba(255,255,255,0.02)' : 'rgba(255,255,255,0.72)'};
box-sizing: border-box;
}
.table-designer-shell .table-designer-cell-field .ant-input,
.table-designer-shell .table-designer-cell-field .ant-select,
.table-designer-shell .table-designer-cell-field .ant-select-selector,
.table-designer-shell .table-designer-cell-field .ant-select-selection-search,
.table-designer-shell .table-designer-cell-field .ant-select-selection-item {
background: transparent !important;
}
.table-designer-shell .table-designer-cell-field .ant-input,
.table-designer-shell .table-designer-cell-field .ant-select-selection-item,
.table-designer-shell .table-designer-cell-field input {
font-size: 13px;
line-height: 1.4;
}
.table-designer-shell .table-designer-cell-field .ant-select {
width: 100%;
}
.table-designer-shell .table-designer-cell-field .ant-select-selector,
.table-designer-shell .table-designer-cell-field .ant-input {
padding: 0 !important;
box-shadow: none !important;
}
.table-designer-shell .table-designer-cell-field.is-compact {
padding-right: 6px;
}
.table-designer-shell .table-designer-cell-check {
display: inline-flex;
align-items: center;
justify-content: center;
width: 100%;
min-height: 34px;
}
.table-designer-shell .table-designer-cell-check .ant-checkbox-wrapper {
margin-inline-end: 0 !important;
}
.table-designer-shell .table-designer-cell-check.is-left-aligned {
justify-content: flex-start;
}
.table-designer-shell .table-designer-header-title {
display: inline-flex;
align-items: center;
justify-content: flex-start;
width: 100%;
line-height: 1.1;
white-space: nowrap;
}
.table-designer-shell .table-designer-select-check {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
min-height: 28px;
}
.table-designer-shell .table-designer-select-check .ant-checkbox-wrapper {
margin-inline-end: 0 !important;
}
.table-designer-shell .table-designer-select-column {
text-align: center !important;
vertical-align: middle !important;
}
.table-designer-shell .table-designer-action-column {
text-align: left !important;
}
.table-designer-shell .table-designer-comment-field {
gap: 4px;
padding-right: 4px;
}
.table-designer-shell .table-designer-comment-field .ant-input {
flex: 1;
min-width: 0;
}
.table-designer-shell .table-designer-comment-display {
flex: 1;
min-width: 0;
min-height: 28px;
display: flex;
align-items: center;
font: inherit;
line-height: 1.4;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: text;
}
.table-designer-shell .table-designer-comment-display.is-empty {
color: ${darkMode ? 'rgba(255,255,255,0.28)' : 'rgba(0,0,0,0.28)'};
}
.table-designer-shell .table-designer-action-cell {
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
width: 100%;
}
.table-designer-shell .table-designer-action-cell .ant-btn {
width: 28px;
height: 28px;
padding: 0;
border-radius: 8px;
}
.table-designer-shell .ant-table-thead > tr > th::before {
display: none !important;
}
@@ -2595,6 +2785,12 @@ END;`;
.table-designer-shell .ant-tabs-nav {
margin-bottom: 8px !important;
}
.table-designer-shell.gn-v2-table-designer .ant-tabs-nav {
margin-bottom: 0 !important;
}
.table-designer-shell.is-embedded .ant-tabs-nav {
margin-bottom: 0 !important;
}
.table-designer-shell .ant-tabs-nav::before {
border-bottom-color: ${darkMode ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.08)'} !important;
}
@@ -2605,6 +2801,100 @@ END;`;
.table-designer-shell .ant-tabs-tab {
transition: color 0.15s ease !important;
}
.table-designer-shell.gn-v2-table-designer .ant-tabs-nav-wrap,
.table-designer-shell.gn-v2-table-designer .ant-tabs-nav-list {
width: auto !important;
min-height: 34px !important;
align-items: center !important;
}
.table-designer-shell.gn-v2-table-designer .ant-tabs-tab {
width: auto !important;
min-width: 0 !important;
max-width: none !important;
min-height: 34px !important;
margin: 0 !important;
padding: 0 12px !important;
border-right: 0 !important;
border-bottom: 0 !important;
white-space: nowrap !important;
}
.table-designer-shell.gn-v2-table-designer .ant-tabs-tab-btn {
width: auto !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
}
.table-designer-shell.is-embedded .ant-tabs-nav-wrap,
.table-designer-shell.is-embedded .ant-tabs-nav-list {
width: auto !important;
min-height: 34px !important;
align-items: center !important;
}
.table-designer-shell.is-embedded .ant-tabs-tab {
width: auto !important;
min-width: 0 !important;
max-width: none !important;
min-height: 34px !important;
margin: 0 !important;
padding: 0 12px !important;
border-right: 0 !important;
border-bottom: 0 !important;
white-space: nowrap !important;
}
.table-designer-shell.is-embedded .ant-tabs-tab-btn {
width: auto !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
}
.table-designer-shell.gn-v2-table-designer .table-designer-cell-field {
min-height: 28px;
padding-inline: 0;
border: none !important;
border-radius: 0;
background: transparent !important;
box-shadow: none !important;
}
.table-designer-shell.gn-v2-table-designer .table-designer-cell-field .ant-input,
.table-designer-shell.gn-v2-table-designer .table-designer-cell-field .ant-input:focus,
.table-designer-shell.gn-v2-table-designer .table-designer-cell-field .ant-input-focused,
.table-designer-shell.gn-v2-table-designer .table-designer-cell-field .ant-select-selector,
.table-designer-shell.gn-v2-table-designer .table-designer-cell-field .ant-select-focused .ant-select-selector {
border: none !important;
box-shadow: none !important;
background: transparent !important;
}
.table-designer-shell.gn-v2-table-designer .table-designer-comment-display,
.table-designer-shell.gn-v2-table-designer .table-designer-comment-field .ant-input,
.table-designer-shell.gn-v2-table-designer .table-designer-comment-field .ant-input input {
font-size: 12px !important;
line-height: 1.4 !important;
font-family: inherit !important;
}
.table-designer-shell.gn-v2-table-designer .table-designer-cell-field.is-compact {
padding-right: 0;
}
.table-designer-shell.gn-v2-table-designer .table-designer-comment-field {
padding-right: 0;
}
.table-designer-shell.gn-v2-table-designer .table-designer-cell-check {
min-height: 30px;
}
.table-designer-shell.gn-v2-table-designer .table-designer-select-check {
min-height: 22px;
}
.table-designer-shell.is-embedded .table-designer-select-check {
min-height: 14px !important;
}
.table-designer-shell.gn-v2-table-designer .table-designer-action-cell {
justify-content: flex-start;
gap: 4px;
}
.table-designer-shell.gn-v2-table-designer .table-designer-action-cell .ant-btn {
width: 26px;
height: 26px;
border-radius: 7px;
}
.table-designer-shell .ant-tabs-content-holder,
.table-designer-shell .ant-tabs-content,
.table-designer-shell .ant-tabs-tabpane {
@@ -2639,29 +2929,16 @@ END;`;
willChange: 'transform',
}}
/>
{isV2Ui && (
<div className="gn-v2-designer-header">
<div className="gn-v2-designer-title">
<span>SCHEMA DESIGNER</span>
<strong>{designerTableTitle}</strong>
</div>
<div className="gn-v2-designer-meta">
<span><TableOutlined /> {designerDbTitle}</span>
<span>{designerColumnSummary}</span>
{readOnly && <span></span>}
</div>
</div>
)}
<div
className={isV2Ui ? 'gn-v2-designer-toolbar' : undefined}
style={{
padding: '10px 12px 8px 12px',
borderBottom: `1px solid ${panelToolbarBorder}`,
borderTopLeftRadius: panelRadius,
borderTopRightRadius: panelRadius,
borderTopLeftRadius: embedded ? 0 : panelRadius,
borderTopRightRadius: embedded ? 0 : panelRadius,
borderLeft: `1px solid ${panelFrameColor}`,
borderRight: `1px solid ${panelFrameColor}`,
borderTop: `1px solid ${panelFrameColor}`,
borderTop: embedded ? 'none' : `1px solid ${panelFrameColor}`,
background: panelToolbarBg,
display: 'flex',
gap: '8px',
@@ -2731,9 +3008,9 @@ END;`;
style={{
flex: 1,
minHeight: 0,
padding: '8px 10px 10px 10px',
borderBottomLeftRadius: panelRadius,
borderBottomRightRadius: panelRadius,
padding: embedded ? 0 : '0 10px 10px 10px',
borderBottomLeftRadius: embedded ? 0 : panelRadius,
borderBottomRightRadius: embedded ? 0 : panelRadius,
borderLeft: `1px solid ${panelFrameColor}`,
borderRight: `1px solid ${panelFrameColor}`,
borderBottom: `1px solid ${panelFrameColor}`,
@@ -2757,7 +3034,7 @@ END;`;
key: 'indexes',
label: '索引',
children: (
<div className={`index-table-wrap${isV2Ui ? ' gn-v2-designer-tab-content' : ''}`} style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
<div className={`index-table-wrap${isV2Ui ? ' gn-v2-designer-tab-content gn-v2-designer-index-table' : ''}`} style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
{!readOnly && (
<div className={isV2Ui ? 'gn-v2-designer-actionbar' : undefined} style={{ display: 'flex', gap: 8 }}>
<Button size="small" icon={<PlusOutlined />} disabled={!supportsIndexSchemaOps()} onClick={openCreateIndexModal}></Button>
@@ -2935,11 +3212,11 @@ END;`;
}
] : []),
...(!isNewTable ? [{
key: 'ddl',
label: 'DDL',
icon: <FileTextOutlined />,
children: (
<div className={isV2Ui ? 'gn-v2-designer-ddl-shell' : undefined} style={{ height: 'calc(100vh - 200px)', border: `1px solid ${panelFrameColor}`, borderRadius: panelRadius, background: panelBodyBg }}>
key: 'ddl',
label: 'DDL',
icon: <FileTextOutlined />,
children: (
<div className={isV2Ui ? 'gn-v2-designer-ddl-shell' : undefined} style={{ height: '100%', minHeight: 320, border: `1px solid ${panelFrameColor}`, borderRadius: panelRadius, background: panelBodyBg }}>
<Editor
height="100%"
language="sql"

View File

@@ -406,6 +406,7 @@ const TableOverview: React.FC<TableOverviewProps> = ({ tab }) => {
connectionId: connection.id,
dbName: tab.dbName,
tableName,
objectType: 'table',
});
}, [connection, tab.dbName, addTab, setActiveContext]);

View File

@@ -429,6 +429,7 @@ export interface TabData {
schemaName?: string; // Schema / owner name for schema-grouped objects
sidebarLocateKey?: string; // Precise sidebar tree key for locating an object node
savedQueryId?: string; // Saved query identity for quick-save behavior
objectType?: 'table' | 'view' | 'materialized-view'; // Table-like object type for shared viewers
}
export interface JVMAIPlanContext {

View File

@@ -3339,6 +3339,30 @@ body[data-ui-version="v2"] .gn-v2-data-grid .ant-table-thead > tr > th {
vertical-align: top !important;
}
body[data-ui-version="v2"] .gn-v2-data-grid .gn-v2-table-designer.is-embedded .ant-table-thead > tr > th {
height: var(--gn-v2-designer-header-height, 40px) !important;
min-height: var(--gn-v2-designer-header-height, 40px) !important;
padding: var(--gn-v2-designer-header-padding, 0 8px) !important;
color: var(--gn-fg-3) !important;
font-family: inherit !important;
font-size: var(--gn-v2-designer-content-font-size, 12px) !important;
font-weight: 600 !important;
vertical-align: middle !important;
}
body[data-ui-version="v2"] .gn-v2-data-grid .gn-v2-table-designer.is-embedded .ant-table-thead .ant-table-column-title {
min-height: var(--gn-v2-designer-header-height, 40px) !important;
color: inherit !important;
font-family: inherit !important;
font-size: inherit !important;
font-weight: 600 !important;
}
body[data-ui-version="v2"] .gn-v2-data-grid .gn-v2-table-designer.is-embedded .ant-table-thead .ant-table-column-sorters {
min-height: var(--gn-v2-designer-header-height, 40px) !important;
align-items: center !important;
}
body[data-ui-version="v2"] .gn-v2-data-grid .ant-table-thead .ant-table-column-title {
font-size: var(--gn-data-table-font-size, var(--gn-font-size-mono, 12px));
font-weight: 400 !important;
@@ -3385,6 +3409,14 @@ body[data-ui-version="v2"] .gn-v2-data-grid .ant-table-selection-column {
border-right: 0.5px solid var(--gn-br-1) !important;
}
body[data-ui-version="v2"] .gn-v2-data-grid .gn-v2-table-designer.is-embedded .ant-table-thead > tr > th:first-child,
body[data-ui-version="v2"] .gn-v2-data-grid .gn-v2-table-designer.is-embedded .ant-table-thead > tr > th.ant-table-selection-column,
body[data-ui-version="v2"] .gn-v2-data-grid .gn-v2-table-designer.is-embedded .ant-table-selection-column {
width: var(--gn-v2-designer-selection-column-width, 44px) !important;
min-width: var(--gn-v2-designer-selection-column-width, 44px) !important;
max-width: var(--gn-v2-designer-selection-column-width, 44px) !important;
}
body[data-ui-version="v2"] .gn-v2-data-grid .ant-table-thead > tr > th:first-child,
body[data-ui-version="v2"] .gn-v2-data-grid .ant-table-thead > tr > th.ant-table-selection-column {
vertical-align: middle !important;
@@ -3401,6 +3433,11 @@ body[data-ui-version="v2"] .gn-v2-data-grid .ant-table-thead > tr > th.ant-table
justify-content: center;
}
body[data-ui-version="v2"] .gn-v2-data-grid .gn-v2-table-designer.is-embedded .ant-table-thead > tr > th:first-child .ant-table-selection,
body[data-ui-version="v2"] .gn-v2-data-grid .gn-v2-table-designer.is-embedded .ant-table-thead > tr > th.ant-table-selection-column .ant-table-selection {
min-height: var(--gn-v2-designer-header-height, 40px) !important;
}
body[data-ui-version="v2"] .gn-v2-column-title {
width: 100%;
min-width: 0;
@@ -4988,24 +5025,12 @@ body[data-ui-version="v2"] .gn-v2-query-error .custom-scrollbar {
body[data-ui-version="v2"] .gn-v2-table-designer {
padding: 0 !important;
background: var(--gn-bg-app);
}
body[data-ui-version="v2"] .gn-v2-designer-header {
min-height: 52px;
padding: 10px 14px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
border-bottom: 0.5px solid var(--gn-br-1);
background: var(--gn-bg-panel);
}
body[data-ui-version="v2"] .gn-v2-designer-title {
min-width: 0;
display: flex;
flex-direction: column;
gap: 2px;
--gn-v2-designer-header-height: 40px;
--gn-v2-designer-header-padding: 0 8px;
--gn-v2-designer-cell-padding: 3px 8px;
--gn-v2-designer-selection-column-width: 44px;
--gn-v2-designer-row-selection-min-height: 14px;
--gn-v2-designer-content-font-size: 12px;
}
body[data-ui-version="v2"] .gn-v2-designer-tabs {
@@ -5020,10 +5045,180 @@ body[data-ui-version="v2"] .gn-v2-designer-tabs > .ant-tabs-nav {
background: var(--gn-bg-panel-2);
}
body[data-ui-version="v2"] .gn-v2-table-designer .gn-v2-designer-tabs {
background: var(--gn-bg-panel) !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer .gn-v2-designer-tabs > .ant-tabs-nav {
padding: 0 12px !important;
min-height: 34px;
background: var(--gn-bg-panel) !important;
border-bottom: 0 !important;
box-shadow: none !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer .gn-v2-designer-tabs > .ant-tabs-nav .ant-tabs-nav-list {
width: auto !important;
min-height: 34px;
gap: 0;
}
body[data-ui-version="v2"] .gn-v2-table-designer .gn-v2-designer-tabs > .ant-tabs-nav .ant-tabs-tab {
height: 34px;
min-height: 34px;
padding-inline: 12px !important;
border-right: 0 !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer .gn-v2-designer-tabs > .ant-tabs-nav::before {
display: none !important;
}
body[data-ui-version="v2"] .gn-v2-designer-tabs .ant-tabs-content-holder {
padding: 10px;
}
body[data-ui-version="v2"] .gn-v2-table-designer .gn-v2-designer-tabs .ant-tabs-content-holder {
padding: 0 12px 12px !important;
border-top: 0 !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer.is-embedded {
background: var(--gn-bg-panel) !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer.is-embedded .gn-v2-designer-toolbar {
padding: 8px 12px 6px !important;
border-left: 0 !important;
border-right: 0 !important;
background: var(--gn-bg-panel) !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer.is-embedded .gn-v2-designer-tabs {
background: var(--gn-bg-panel) !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer.is-embedded .gn-v2-designer-tabs > .ant-tabs-nav {
padding: 0 12px !important;
min-height: 30px;
background: var(--gn-bg-panel) !important;
border-bottom: 0 !important;
box-shadow: none !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer.is-embedded .gn-v2-designer-tabs > .ant-tabs-nav .ant-tabs-nav-list {
width: auto !important;
min-height: 30px;
gap: 0;
}
body[data-ui-version="v2"] .gn-v2-table-designer.is-embedded .gn-v2-designer-tabs > .ant-tabs-nav .ant-tabs-tab {
height: 30px;
min-height: 30px;
padding-inline: 10px !important;
border-right: 0 !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer.is-embedded .gn-v2-designer-tabs > .ant-tabs-nav::before {
display: none !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer.is-embedded .gn-v2-designer-tabs .ant-tabs-content-holder {
padding: 0 12px 12px !important;
border-top: 0 !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer.is-embedded .gn-v2-designer-table-shell,
body[data-ui-version="v2"] .gn-v2-table-designer.is-embedded .gn-v2-designer-tab-content,
body[data-ui-version="v2"] .gn-v2-table-designer.is-embedded .gn-v2-designer-ddl-shell {
border-top: 0 !important;
border-left: 0 !important;
border-right: 0 !important;
border-bottom: 0 !important;
border-radius: 0 !important;
background: transparent !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer.is-embedded .gn-v2-designer-actionbar {
padding-inline: 0 !important;
background: transparent !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer .ant-table-thead > tr > th,
body[data-ui-version="v2"] .gn-v2-table-designer .ant-table-tbody > tr > td {
padding: var(--gn-v2-designer-cell-padding) !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer .ant-table-thead > tr > th {
text-align: left !important;
font-size: var(--gn-v2-designer-content-font-size);
line-height: 1.1;
height: var(--gn-v2-designer-header-height);
min-height: var(--gn-v2-designer-header-height);
padding-top: 0 !important;
padding-bottom: 0 !important;
padding-left: 8px !important;
padding-right: 8px !important;
vertical-align: middle !important;
white-space: nowrap;
}
body[data-ui-version="v2"] .gn-v2-table-designer .ant-table-thead > tr > th .ant-table-column-title {
display: flex;
align-items: center;
justify-content: flex-start;
width: 100%;
min-height: var(--gn-v2-designer-header-height);
text-align: left !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer .ant-table-thead > tr > th .ant-table-column-sorters {
min-height: var(--gn-v2-designer-header-height) !important;
align-items: center !important;
justify-content: flex-start !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer .ant-table-thead > tr > th.ant-table-selection-column,
body[data-ui-version="v2"] .gn-v2-table-designer .ant-table-tbody > tr > td.ant-table-selection-column,
body[data-ui-version="v2"] .gn-v2-table-designer .table-designer-select-column {
width: var(--gn-v2-designer-selection-column-width) !important;
min-width: var(--gn-v2-designer-selection-column-width) !important;
max-width: var(--gn-v2-designer-selection-column-width) !important;
padding-inline: 0 !important;
padding-left: 0 !important;
padding-right: 0 !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer .ant-table-thead > tr > th.ant-table-selection-column .ant-table-selection,
body[data-ui-version="v2"] .gn-v2-table-designer .ant-table-tbody > tr > td.ant-table-selection-column .ant-table-selection,
body[data-ui-version="v2"] .gn-v2-table-designer .table-designer-select-column .table-designer-select-check {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
min-height: var(--gn-v2-designer-row-selection-min-height);
}
body[data-ui-version="v2"] .gn-v2-table-designer .ant-table-thead > tr > th:first-child,
body[data-ui-version="v2"] .gn-v2-table-designer .ant-table-tbody > tr > td:first-child {
padding-inline: 0 !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer .ant-table-tbody > tr > td {
vertical-align: middle !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer .table-designer-cell-field .ant-input,
body[data-ui-version="v2"] .gn-v2-table-designer .table-designer-cell-field .ant-select-selection-item,
body[data-ui-version="v2"] .gn-v2-table-designer .table-designer-cell-field input {
font-size: var(--gn-v2-designer-content-font-size) !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer .table-designer-cell-check .ant-checkbox {
transform: scale(0.92);
}
body[data-ui-version="v2"] .gn-v2-designer-table-shell,
body[data-ui-version="v2"] .gn-v2-designer-tab-content,
body[data-ui-version="v2"] .gn-v2-designer-ddl-shell {
@@ -5048,6 +5243,13 @@ body[data-ui-version="v2"] .gn-v2-designer-section-note {
font-size: 11.5px !important;
}
body[data-ui-version="v2"] .gn-v2-designer-index-table .ant-tag {
font-size: 12px !important;
font-weight: 700 !important;
line-height: 18px !important;
padding: 1px 7px !important;
}
body[data-ui-version="v2"] .gn-v2-table-designer .ant-table-wrapper,
body[data-ui-version="v2"] .gn-v2-table-designer .ant-table-container {
border-radius: 0 !important;