mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-12 16:02:48 +08:00
🐛 fix(data-viewer): 延迟对象设计页的数据预览加载
This commit is contained in:
@@ -1924,6 +1924,46 @@ describe('DataGrid DDL interactions', () => {
|
||||
expect(content).toContain('name');
|
||||
});
|
||||
|
||||
it('notifies deferred data loading only after leaving the embedded object designer', 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: '' },
|
||||
],
|
||||
});
|
||||
const handleDataViewActivate = vi.fn();
|
||||
|
||||
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="users"
|
||||
dbName="main"
|
||||
connectionId="conn-1"
|
||||
initialViewMode="fields"
|
||||
initialViewModeRequestId="query-editor-jump-2"
|
||||
onDataViewActivate={handleDataViewActivate}
|
||||
/>,
|
||||
);
|
||||
});
|
||||
await waitForEffects();
|
||||
|
||||
expect(handleDataViewActivate).not.toHaveBeenCalled();
|
||||
|
||||
await act(async () => {
|
||||
findButton(renderer!, '数据预览').props.onClick();
|
||||
});
|
||||
await waitForEffects();
|
||||
|
||||
expect(handleDataViewActivate).toHaveBeenCalledTimes(1);
|
||||
renderer!.unmount();
|
||||
});
|
||||
|
||||
it('returns to the legacy table view when v2-only footer views are active during UI switch', async () => {
|
||||
storeState.appearance.uiVersion = 'v2';
|
||||
|
||||
|
||||
@@ -288,7 +288,8 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
onApplyQuickWhereCondition,
|
||||
scrollSnapshot, onScrollSnapshotChange, toolbarExtraActions, showRowNumberColumn = false, isActive = true, enableSqlLogEvent = false,
|
||||
initialViewMode,
|
||||
initialViewModeRequestId
|
||||
initialViewModeRequestId,
|
||||
onDataViewActivate,
|
||||
}) => {
|
||||
const connections = useStore(state => state.connections);
|
||||
const addTab = useStore(state => state.addTab);
|
||||
@@ -1450,6 +1451,23 @@ const DataGrid: React.FC<DataGridProps> = ({
|
||||
return () => window.removeEventListener('gonavi:show-sql-execution-log', handleOpenSqlExecutionLog as EventListener);
|
||||
}, [enableSqlLogEvent, handleViewModeChange, isActive, isV2Ui]);
|
||||
|
||||
const dataViewActiveRef = useRef(false);
|
||||
useEffect(() => {
|
||||
const requiresTableData = isActive && (
|
||||
viewMode === 'table'
|
||||
|| viewMode === 'json'
|
||||
|| viewMode === 'text'
|
||||
|| (viewMode === 'ddl' && isTableSurfaceActive)
|
||||
);
|
||||
if (!requiresTableData) {
|
||||
dataViewActiveRef.current = false;
|
||||
return;
|
||||
}
|
||||
if (dataViewActiveRef.current) return;
|
||||
dataViewActiveRef.current = true;
|
||||
onDataViewActivate?.();
|
||||
}, [isActive, isTableSurfaceActive, onDataViewActivate, viewMode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isTableSurfaceActive || !isV2Ui || !cellContextMenu.visible) return;
|
||||
const portal = cellContextMenuPortalRef.current;
|
||||
|
||||
@@ -1338,6 +1338,7 @@ interface DataGridProps {
|
||||
enableSqlLogEvent?: boolean;
|
||||
initialViewMode?: GridViewMode;
|
||||
initialViewModeRequestId?: string;
|
||||
onDataViewActivate?: () => void;
|
||||
}
|
||||
|
||||
type GridFilterCondition = FilterCondition & {
|
||||
|
||||
@@ -147,6 +147,29 @@ describe('DataViewer safe editing locator', () => {
|
||||
renderer!.unmount();
|
||||
});
|
||||
|
||||
it('defers the initial table query when the table opens in embedded object design', async () => {
|
||||
backendApp.DBGetColumns.mockResolvedValue({
|
||||
success: true,
|
||||
data: [{ name: 'ID', key: 'PRI' }, { name: 'NAME', key: '' }],
|
||||
});
|
||||
|
||||
let renderer: ReactTestRenderer;
|
||||
await act(async () => {
|
||||
renderer = create(<DataViewer tab={createTab({ initialViewMode: 'fields', initialViewModeRequestId: 'open-design-1' })} />);
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
expect(backendApp.DBQuery).not.toHaveBeenCalled();
|
||||
|
||||
await act(async () => {
|
||||
dataGridState.latestProps?.onDataViewActivate?.();
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
expect(backendApp.DBQuery).toHaveBeenCalled();
|
||||
renderer!.unmount();
|
||||
});
|
||||
|
||||
it('keeps DataViewer message wrappers and SQL log phase labels keyed', () => {
|
||||
const source = readFileSync(new URL('./DataViewer.tsx', import.meta.url), 'utf8');
|
||||
|
||||
|
||||
@@ -291,6 +291,9 @@ type ViewerScrollSnapshot = {
|
||||
const viewerFilterSnapshotsByTab = new Map<string, ViewerFilterSnapshot>();
|
||||
const MAX_VIEWER_FILTER_SNAPSHOTS = 64;
|
||||
const VIEWER_SCROLL_SNAPSHOT_PERSIST_DELAY_MS = 160;
|
||||
const shouldDeferInitialDataViewerFetch = (initialViewMode?: TabData['initialViewMode']): boolean => (
|
||||
initialViewMode === 'fields'
|
||||
);
|
||||
|
||||
const trimViewerFilterSnapshots = () => {
|
||||
while (viewerFilterSnapshotsByTab.size > MAX_VIEWER_FILTER_SNAPSHOTS) {
|
||||
@@ -387,6 +390,7 @@ const DataViewer: React.FC<{ tab: TabData; isActive?: boolean }> = React.memo(({
|
||||
const scrollSnapshotPersistTimerRef = useRef<number | null>(null);
|
||||
const initialLoadRef = useRef(false);
|
||||
const skipNextAutoFetchRef = useRef(false);
|
||||
const deferredInitialFetchRef = useRef(shouldDeferInitialDataViewerFetch(tab.initialViewMode));
|
||||
|
||||
const [pagination, setPagination] = useState<ViewerPaginationState>({
|
||||
current: initialViewerSnapshot.currentPage,
|
||||
@@ -474,6 +478,7 @@ const DataViewer: React.FC<{ tab: TabData; isActive?: boolean }> = React.memo(({
|
||||
latestCountKeyRef.current = '';
|
||||
scrollSnapshotRef.current = { top: snapshot.scrollTop, left: snapshot.scrollLeft };
|
||||
initialLoadRef.current = false;
|
||||
deferredInitialFetchRef.current = shouldDeferInitialDataViewerFetch(tab.initialViewMode);
|
||||
skipNextAutoFetchRef.current = true;
|
||||
setPagination(prev => ({
|
||||
...prev,
|
||||
@@ -1129,6 +1134,12 @@ const DataViewer: React.FC<{ tab: TabData; isActive?: boolean }> = React.memo(({
|
||||
// 定位信息只会在表上下文变化后重新加载,避免循环查询。
|
||||
|
||||
// Handlers memoized
|
||||
const handleDataViewActivate = useCallback(() => {
|
||||
if (!deferredInitialFetchRef.current || initialLoadRef.current) return;
|
||||
deferredInitialFetchRef.current = false;
|
||||
initialLoadRef.current = true;
|
||||
void fetchData(pagination.current, pagination.pageSize);
|
||||
}, [fetchData, pagination.current, pagination.pageSize]);
|
||||
const handleReload = useCallback(() => {
|
||||
fetchData(pagination.current, pagination.pageSize);
|
||||
}, [fetchData, pagination.current, pagination.pageSize]);
|
||||
@@ -1200,6 +1211,9 @@ const DataViewer: React.FC<{ tab: TabData; isActive?: boolean }> = React.memo(({
|
||||
}, [tab.tableName, currentConnConfig?.type, currentConnConfig?.driver, filterConditions, quickWhereCondition, sortInfo, editLocator, pkColumns]);
|
||||
|
||||
useEffect(() => {
|
||||
if (deferredInitialFetchRef.current && !initialLoadRef.current) {
|
||||
return;
|
||||
}
|
||||
const action = resolveDataViewerAutoFetchAction({
|
||||
skipNextAutoFetch: skipNextAutoFetchRef.current,
|
||||
hasInitialLoad: initialLoadRef.current,
|
||||
@@ -1251,6 +1265,7 @@ const DataViewer: React.FC<{ tab: TabData; isActive?: boolean }> = React.memo(({
|
||||
enableSqlLogEvent
|
||||
initialViewMode={tab.initialViewMode}
|
||||
initialViewModeRequestId={tab.initialViewModeRequestId}
|
||||
onDataViewActivate={handleDataViewActivate}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user