fix(data-grid): 修复行号列整格双击切换文本视图

This commit is contained in:
Kunghim
2026-08-01 23:26:12 +08:00
parent b80843483a
commit f46dc08519
13 changed files with 111 additions and 14 deletions

View File

@@ -933,6 +933,7 @@ describe('DataGrid layout', () => {
'data_grid.record_view.empty': 'No rows label',
'data_grid.record_view.json_record_count': `${params?.count} JSON rows label`,
'data_grid.record_view.edit_json': 'Edit JSON label',
'data_grid.record_view.back_to_table': 'Back to table label',
'data_grid.record_view.previous': 'Previous label',
'data_grid.record_view.next': 'Next label',
'data_grid.record_view.record_position': `Record label ${params?.current} of ${params?.total}`,
@@ -1054,10 +1055,12 @@ describe('DataGrid layout', () => {
jsonViewText="[]"
translate={translate}
onOpenJsonEditor={() => {}}
onReturnToTable={() => {}}
/>,
);
expect(jsonRecordMarkup).toContain('5 JSON rows label');
expect(jsonRecordMarkup).toContain('Edit JSON label');
expect(jsonRecordMarkup).toContain('Back to table label');
expect(jsonRecordMarkup).not.toContain('data_grid.record_view');
const textRecordMarkup = renderToStaticMarkup(
@@ -1076,6 +1079,7 @@ describe('DataGrid layout', () => {
onPrev={() => {}}
onNext={() => {}}
onEditCurrent={() => {}}
onReturnToTable={() => {}}
formatTextViewValue={(value) => String(value)}
/>,
);
@@ -1083,6 +1087,7 @@ describe('DataGrid layout', () => {
expect(textRecordMarkup).toContain('Next label');
expect(textRecordMarkup).toContain('Record label 1 of 2');
expect(textRecordMarkup).toContain('Edit current label');
expect(textRecordMarkup).toContain('Back to table label');
expect(textRecordMarkup).toContain('raw_sql');
expect(textRecordMarkup).toContain('TYPE varchar(128)');
expect(textRecordMarkup).toContain('COMMENT SQL text payload');
@@ -1105,6 +1110,7 @@ describe('DataGrid layout', () => {
onPrev={() => {}}
onNext={() => {}}
onEditCurrent={() => {}}
onReturnToTable={() => {}}
formatTextViewValue={(value) => String(value)}
/>,
);
@@ -1415,9 +1421,13 @@ describe('DataGrid layout', () => {
expect(markup).toContain('align-items:center');
expect(markup).toContain('min-height:var(--gonavi-header-min-height, 40px)');
expect(markup).toContain('text-align:center');
expect(markup).toContain('padding-inline:2');
expect(markup).toContain('padding:0');
expect(markup).toContain('vertical-align:middle');
expect(markup).toContain('data-grid-row-number="true"');
expect(markup).toContain('data-grid-row-number-action="true"');
expect(markup).toContain('display:flex');
expect(markup).toContain('width:100%');
expect(markup).toContain('height:100%');
expect(markup).toContain('width:36');
// ant Table fixed 列会渲染 fix 相关 class
expect(markup.includes('ant-table-cell-fix') || markup.includes('fixed')).toBe(true);

View File

@@ -3322,6 +3322,10 @@ const DataGrid: React.FC<DataGridProps> = ({
return ROW_NUMBER_COLUMN_WIDTH;
}, [columnWidths]);
const handleRowNumberDoubleClick = useCallback((index: number) => {
handleViewModeChange('text', { textRecordIndex: index });
}, [handleViewModeChange]);
const rowNumberColumn = useMemo<ColumnType<any>>(() => ({
title: (
<div
@@ -3368,12 +3372,18 @@ const DataGrid: React.FC<DataGridProps> = ({
minWidth: 28,
},
}),
onCell: () => ({
onCell: (_record: Item, index?: number) => ({
'data-grid-row-number-action': 'true',
style: {
width: rowNumberColumnWidth,
minWidth: 28,
paddingInline: 2,
padding: 0,
textAlign: 'center' as const,
},
onDoubleClick: (event: React.MouseEvent<HTMLElement>) => {
event.preventDefault();
event.stopPropagation();
handleRowNumberDoubleClick(index ?? 0);
},
}),
render: (_value: unknown, _record: Item, index: number) => {
@@ -3381,12 +3391,31 @@ const DataGrid: React.FC<DataGridProps> = ({
const pageSize = Math.max(1, Number(pagination?.pageSize) || 0);
const offset = pageSize > 0 ? (currentPage - 1) * pageSize : 0;
return (
<span className="data-grid-row-number" data-grid-row-number="true">
{offset + index + 1}
</span>
<Tooltip title={translateDataGrid('data_grid.row_number.double_click_to_view')}>
<span
className="data-grid-row-number"
data-grid-row-number="true"
style={{
display: 'flex',
width: '100%',
height: '100%',
minHeight: 24,
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
}}
onDoubleClick={(event) => {
event.preventDefault();
event.stopPropagation();
handleRowNumberDoubleClick(index);
}}
>
{offset + index + 1}
</span>
</Tooltip>
);
},
}), [handleResizeAutoFit, handleResizeStart, pagination?.current, pagination?.pageSize, rowNumberColumnWidth]);
}), [handleResizeAutoFit, handleResizeStart, handleRowNumberDoubleClick, pagination?.current, pagination?.pageSize, rowNumberColumnWidth, translateDataGrid]);
const tableColumns = useMemo(() => {
const baseColumns = resolvedShowRowNumberColumn

View File

@@ -12,6 +12,7 @@ interface DataGridJsonViewProps {
jsonViewText: string;
translate?: DataGridRecordViewTranslate;
onOpenJsonEditor: () => void;
onReturnToTable: () => void;
}
export const DataGridJsonView: React.FC<DataGridJsonViewProps> = ({
@@ -21,6 +22,7 @@ export const DataGridJsonView: React.FC<DataGridJsonViewProps> = ({
jsonViewText,
translate = defaultTranslate,
onOpenJsonEditor,
onReturnToTable,
}) => (
<div style={{ height: '100%', minHeight: 0, display: 'flex', flexDirection: 'column' }}>
<div style={{ padding: '8px 10px', borderBottom: darkMode ? '1px solid rgba(255,255,255,0.08)' : '1px solid rgba(0,0,0,0.08)', display: 'flex', alignItems: 'center', gap: 8 }}>
@@ -34,6 +36,9 @@ export const DataGridJsonView: React.FC<DataGridJsonViewProps> = ({
{translate('data_grid.record_view.edit_json')}
</Button>
)}
<Button size="small" onClick={onReturnToTable}>
{translate('data_grid.record_view.back_to_table')}
</Button>
</div>
<div style={{ flex: 1, minHeight: 0, padding: '8px 10px 10px 10px' }}>
<Editor
@@ -72,6 +77,7 @@ interface DataGridTextViewProps {
onPrev: () => void;
onNext: () => void;
onEditCurrent: () => void;
onReturnToTable: () => void;
formatTextViewValue: (value: any, columnName?: string) => string;
}
@@ -90,6 +96,7 @@ export const DataGridTextView: React.FC<DataGridTextViewProps> = ({
onPrev,
onNext,
onEditCurrent,
onReturnToTable,
formatTextViewValue,
}) => {
const metaTextColor = darkMode ? 'rgba(255,255,255,0.52)' : 'rgba(0,0,0,0.48)';
@@ -113,6 +120,9 @@ export const DataGridTextView: React.FC<DataGridTextViewProps> = ({
{translate('data_grid.record_view.edit_current')}
</Button>
)}
<Button size="small" onClick={onReturnToTable}>
{translate('data_grid.record_view.back_to_table')}
</Button>
</div>
<div className="custom-scrollbar" style={{ flex: 1, minHeight: 0, overflow: 'auto', padding: '8px 12px' }}>
{currentTextRow ? displayOutputColumnNames.map((col) => {

View File

@@ -852,6 +852,7 @@ const renderDataTableView = () => (
canModifyData={canModifyData}
jsonViewText={jsonViewText}
translate={translateDataGrid}
onReturnToTable={() => handleViewModeChange('table')}
onOpenJsonEditor={handleOpenJsonEditor}
/>
) : (
@@ -867,6 +868,7 @@ const renderDataTableView = () => (
showColumnType={showColumnType}
showColumnComment={showColumnComment}
translate={translateDataGrid}
onReturnToTable={() => handleViewModeChange('table')}
onPrev={() => setTextRecordIndex((i: number) => Math.max(0, i - 1))}
onNext={() => setTextRecordIndex((i: number) => Math.min(textViewRows.length - 1, i + 1))}
onEditCurrent={openCurrentViewRowEditor}

View File

@@ -386,6 +386,14 @@ export const buildDataGridCssText = ({
.${gridId} .data-grid-row-number-cell {
text-align: center !important;
overflow: hidden !important;
padding: 0 !important;
}
.${gridId} .ant-table-tbody-virtual-holder .ant-table-row > .ant-table-cell.data-grid-row-number-cell,
.${gridId} .ant-table-tbody .ant-table-row > .ant-table-cell.data-grid-row-number-cell,
.${gridId} .ant-table-header th.data-grid-row-number-cell,
body[data-ui-version="v2"] .${gridId}.gn-v2-data-grid .ant-table-tbody-virtual-holder .ant-table-row > .ant-table-cell.data-grid-row-number-cell,
body[data-ui-version="v2"] .${gridId}.gn-v2-data-grid .ant-table-tbody .ant-table-row > .ant-table-cell.data-grid-row-number-cell {
padding: 0 !important;
}
.${gridId} .ant-table-thead > tr:first-child > th:first-child,

View File

@@ -99,4 +99,24 @@ describe('useDataGridDdlView i18n', () => {
expect(messageApi.error).toHaveBeenCalledWith('ORA-31603: object "USERS" not found');
});
it('honors an explicit text row when opening from the row number', () => {
const setTextRecordIndex = vi.fn();
renderHook({
selectedRowKeys: ['row-10'],
mergedDisplayDataRef: {
current: [
{ __gonavi_row_key__: 'row-8' },
{ __gonavi_row_key__: 'row-10' },
],
},
setTextRecordIndex,
});
act(() => {
controller?.handleViewModeChange('text', { textRecordIndex: 0 });
});
expect(setTextRecordIndex).toHaveBeenCalledWith(0);
});
});

View File

@@ -122,7 +122,7 @@ export interface UseDataGridDdlViewResult {
ddlRequestSeqRef: React.MutableRefObject<number>;
isTableSurfaceActive: boolean;
handleOpenTableDdl: (options?: { asView?: boolean }) => Promise<void>;
handleViewModeChange: (nextMode: GridViewMode) => void;
handleViewModeChange: (nextMode: GridViewMode, options?: { textRecordIndex?: number }) => void;
handleDdlSidebarResizeStart: (event: React.MouseEvent<HTMLDivElement>) => void;
resetDdlViewState: () => void;
closeDdlView: () => void;
@@ -291,7 +291,7 @@ export const useDataGridDdlView = ({
void handleOpenTableDdl({ asView: true });
}, [canRestoreSharedDdlView, ddlContextKey, handleOpenTableDdl, hasPendingLocalTableViewRequest, isActive]);
const handleViewModeChange = React.useCallback((nextMode: GridViewMode) => {
const handleViewModeChange = React.useCallback((nextMode: GridViewMode, options?: { textRecordIndex?: number }) => {
setSuppressSharedDdlView(false);
if ((nextMode === 'fields' || nextMode === 'ddl' || nextMode === 'er' || nextMode === 'sqlLog') && !isV2Ui) {
setSharedDdlViewOpen(false);
@@ -317,11 +317,17 @@ export const useDataGridDdlView = ({
}
if (nextMode === 'text') {
const selectedKey = selectedRowKeys[0];
if (selectedKey !== undefined) {
const idx = mergedDisplayDataRef.current.findIndex((row) => rowKeyStr(row?.__gonavi_row_key__) === rowKeyStr(selectedKey));
if (idx >= 0) {
setTextRecordIndex(idx);
const explicitIndex = options?.textRecordIndex;
if (explicitIndex !== undefined && Number.isFinite(explicitIndex)) {
const maxIndex = Math.max(0, mergedDisplayDataRef.current.length - 1);
setTextRecordIndex(Math.max(0, Math.min(Math.trunc(explicitIndex), maxIndex)));
} else {
const selectedKey = selectedRowKeys[0];
if (selectedKey !== undefined) {
const idx = mergedDisplayDataRef.current.findIndex((row) => rowKeyStr(row?.__gonavi_row_key__) === rowKeyStr(selectedKey));
if (idx >= 0) {
setTextRecordIndex(idx);
}
}
}
}