feat(data-grid): 增强数据表编辑与展示体验

- 新增变更预览能力,支持在提交前查看删除、更新和新增对应的 SQL 语句
- 增加表格密度配置,统一控制默认列宽、行高、字号与单元格内边距
- 优化 DataGrid 编辑状态展示,区分新增、修改和删除行列的视觉反馈
- 调整导出入口与 Wails 前端绑定,补齐变更预览相关调用与测试覆盖
This commit is contained in:
TonyJiangWJ
2026-05-10 19:00:47 +08:00
parent c0ae40c638
commit 1965564386
15 changed files with 780 additions and 108 deletions

View File

@@ -11,17 +11,18 @@ import {
describe('dataGridDisplay helpers', () => {
it('sanitizes missing display settings to safe defaults', () => {
expect(sanitizeDataGridDisplaySettings(undefined)).toEqual(DEFAULT_DATA_GRID_DISPLAY_SETTINGS);
expect(sanitizeDataGridDisplaySettings({ dataTableColumnWidthMode: 'invalid' as never })).toEqual(DEFAULT_DATA_GRID_DISPLAY_SETTINGS);
expect(sanitizeDataGridDisplaySettings({ dataTableDensity: 'invalid' as never })).toEqual(DEFAULT_DATA_GRID_DISPLAY_SETTINGS);
});
it('resolves standard and compact default column widths', () => {
expect(resolveDataTableDefaultColumnWidth('standard')).toBe(200);
expect(resolveDataTableDefaultColumnWidth('compact')).toBe(140);
it('resolves density-based default column widths', () => {
expect(resolveDataTableDefaultColumnWidth('comfortable')).toBe(180);
expect(resolveDataTableDefaultColumnWidth('standard')).toBe(140);
expect(resolveDataTableDefaultColumnWidth('compact')).toBe(100);
});
it('keeps manual column widths ahead of mode defaults', () => {
expect(resolveDataTableColumnWidth({ manualWidth: 320, widthMode: 'compact' })).toBe(320);
expect(resolveDataTableColumnWidth({ manualWidth: undefined, widthMode: 'compact' })).toBe(140);
it('keeps manual column widths ahead of density defaults', () => {
expect(resolveDataTableColumnWidth({ manualWidth: 320, density: 'compact' })).toBe(320);
expect(resolveDataTableColumnWidth({ manualWidth: undefined, density: 'compact' })).toBe(100);
});
it('uses subtle themed vertical border colors and transparent when disabled', () => {

View File

@@ -1,25 +1,64 @@
export type DataTableColumnWidthMode = 'standard' | 'compact';
export type DataTableDensity = 'comfortable' | 'standard' | 'compact';
export interface DataGridDisplaySettings {
showDataTableVerticalBorders: boolean;
dataTableColumnWidthMode: DataTableColumnWidthMode;
dataTableDensity: DataTableDensity;
}
export const DEFAULT_DATA_GRID_DISPLAY_SETTINGS: DataGridDisplaySettings = {
showDataTableVerticalBorders: false,
dataTableColumnWidthMode: 'standard',
dataTableDensity: 'comfortable',
};
export const DATA_GRID_COLUMN_WIDTH_MODE_OPTIONS = [
{ label: '标准 200px', value: 'standard' as const },
{ label: '紧凑 140px', value: 'compact' as const },
interface DensityParams {
defaultColumnWidth: number;
cellPadding: string;
inputCellPadding: string;
headerMinHeight: number;
dataFontSize: number;
metaFontSize: number;
}
const DENSITY_PARAMS: Record<DataTableDensity, DensityParams> = {
comfortable: {
defaultColumnWidth: 180,
cellPadding: '8px',
inputCellPadding: '0px 4px',
headerMinHeight: 40,
dataFontSize: 13,
metaFontSize: 11,
},
standard: {
defaultColumnWidth: 140,
cellPadding: '5px 8px',
inputCellPadding: '0px 3px',
headerMinHeight: 34,
dataFontSize: 13,
metaFontSize: 10,
},
compact: {
defaultColumnWidth: 100,
cellPadding: '2px 6px',
inputCellPadding: '0px 2px',
headerMinHeight: 28,
dataFontSize: 12,
metaFontSize: 10,
},
};
export const DENSITY_OPTIONS = [
{ label: '舒适', value: 'comfortable' as const },
{ label: '标准', value: 'standard' as const },
{ label: '紧凑', value: 'compact' as const },
];
const STANDARD_DATA_TABLE_COLUMN_WIDTH = 200;
const COMPACT_DATA_TABLE_COLUMN_WIDTH = 140;
export const sanitizeDataTableDensity = (value: unknown): DataTableDensity => {
if (value === 'standard' || value === 'compact') return value;
return 'comfortable';
};
export const sanitizeDataTableColumnWidthMode = (value: unknown): DataTableColumnWidthMode => {
return value === 'compact' ? 'compact' : 'standard';
export const getDensityParams = (density: DataTableDensity): DensityParams => {
return DENSITY_PARAMS[density] || DENSITY_PARAMS.comfortable;
};
export const sanitizeDataGridDisplaySettings = (
@@ -31,30 +70,28 @@ export const sanitizeDataGridDisplaySettings = (
return {
showDataTableVerticalBorders: value.showDataTableVerticalBorders === true,
dataTableColumnWidthMode: sanitizeDataTableColumnWidthMode(value.dataTableColumnWidthMode),
dataTableDensity: sanitizeDataTableDensity(value.dataTableDensity),
};
};
export const resolveDataTableDefaultColumnWidth = (
widthMode: DataTableColumnWidthMode | null | undefined
density: DataTableDensity | null | undefined
): number => {
return sanitizeDataTableColumnWidthMode(widthMode) === 'compact'
? COMPACT_DATA_TABLE_COLUMN_WIDTH
: STANDARD_DATA_TABLE_COLUMN_WIDTH;
return getDensityParams(sanitizeDataTableDensity(density)).defaultColumnWidth;
};
export const resolveDataTableColumnWidth = ({
manualWidth,
widthMode,
density,
}: {
manualWidth: number | null | undefined;
widthMode: DataTableColumnWidthMode | null | undefined;
density: DataTableDensity | null | undefined;
}): number => {
if (typeof manualWidth === 'number' && Number.isFinite(manualWidth) && manualWidth > 0) {
return manualWidth;
}
return resolveDataTableDefaultColumnWidth(widthMode);
return resolveDataTableDefaultColumnWidth(density);
};
export const resolveDataTableVerticalBorderColor = ({