🐛 fix(query-editor): 修正 SQL 保存代码错位

- 恢复保存队列与组件挂载状态的声明位置
- 修正草稿同步、保存完成判断和结果关闭逻辑结构
- 恢复保存查询回归用例并补齐异步等待
This commit is contained in:
Syngnat
2026-07-23 16:31:41 +08:00
parent 3c24068190
commit ba28c626fd
2 changed files with 80 additions and 80 deletions

View File

@@ -7862,6 +7862,13 @@ describe('QueryEditor external SQL save', () => {
createdAt: 100,
},
];
storeState.saveQuery.mockImplementationOnce(async (savedQuery: SavedQuery) => {
storeState.savedQueries = storeState.savedQueries.map((item) => (
item.id === savedQuery.id ? savedQuery : item
));
storeSubscribers.forEach((subscriber) => subscriber());
return savedQuery;
});
let renderer!: ReactTestRenderer;
await act(async () => {
@@ -7875,7 +7882,7 @@ describe('QueryEditor external SQL save', () => {
expect(getQueryTabDraft('tab-1')).toBe('select 3;');
await act(async () => {
findButton(renderer!, '保存').props.onClick();
await findButton(renderer!, '保存').props.onClick();
});
expect(backendApp.WriteSQLFile).not.toHaveBeenCalled();
@@ -7887,75 +7894,6 @@ describe('QueryEditor external SQL save', () => {
dbName: 'main',
createdAt: 100,
}));
});
it('keeps untitled fallback when the new query tab title is localized', async () => {
setCurrentLanguage('en-US');
let renderer!: ReactTestRenderer;
await act(async () => {
renderer = create(<QueryEditor tab={createTab({ title: 'New Query', savedQueryId: 'saved-1' })} />);
});
editorState.value = 'select 8;';
await act(async () => {
findButton(renderer!, 'Save').props.onClick();
});
expect(storeState.saveQuery).toHaveBeenCalledWith(expect.objectContaining({
id: 'saved-1',
name: 'Untitled query',
sql: 'select 8;',
connectionId: 'conn-1',
dbName: 'main',
}));
});
it('keeps untitled fallback after a language switch when the tab title came from another locale', async () => {
setCurrentLanguage('ja-JP');
let renderer!: ReactTestRenderer;
await act(async () => {
renderer = create(<QueryEditor tab={createTab({ title: 'New Query', savedQueryId: 'saved-1' })} />);
});
editorState.value = 'select 10;';
await act(async () => {
findButton(renderer!, '保存').props.onClick();
});
expect(storeState.saveQuery).toHaveBeenCalledWith(expect.objectContaining({
id: 'saved-1',
storeState.saveQuery.mockImplementationOnce(async (savedQuery: SavedQuery) => {
storeState.savedQueries = storeState.savedQueries.map((item) => (
item.id === savedQuery.id ? savedQuery : item
));
storeSubscribers.forEach((subscriber) => subscriber());
return savedQuery;
});
name: '無題のクエリ',
sql: 'select 10;',
connectionId: 'conn-1',
dbName: 'main',
}));
});
it('keeps untitled fallback for database-scoped new query titles after a language switch', async () => {
setCurrentLanguage('ja-JP');
let renderer!: ReactTestRenderer;
await act(async () => {
renderer = create(<QueryEditor tab={createTab({ title: 'New query (main)', savedQueryId: 'saved-1' })} />);
});
editorState.value = 'select 11;';
await act(async () => {
await findButton(renderer!, '保存').props.onClick();
});
expect(getQueryTabDraft('tab-1')).toBe('');
});
@@ -8165,6 +8103,68 @@ describe('QueryEditor external SQL save', () => {
});
expect(getQueryTabDraft('tab-1')).toBe('select 3;');
});
it('keeps untitled fallback when the new query tab title is localized', async () => {
setCurrentLanguage('en-US');
let renderer!: ReactTestRenderer;
await act(async () => {
renderer = create(<QueryEditor tab={createTab({ title: 'New Query', savedQueryId: 'saved-1' })} />);
});
editorState.value = 'select 8;';
await act(async () => {
findButton(renderer!, 'Save').props.onClick();
});
expect(storeState.saveQuery).toHaveBeenCalledWith(expect.objectContaining({
id: 'saved-1',
name: 'Untitled query',
sql: 'select 8;',
connectionId: 'conn-1',
dbName: 'main',
}));
});
it('keeps untitled fallback after a language switch when the tab title came from another locale', async () => {
setCurrentLanguage('ja-JP');
let renderer!: ReactTestRenderer;
await act(async () => {
renderer = create(<QueryEditor tab={createTab({ title: 'New Query', savedQueryId: 'saved-1' })} />);
});
editorState.value = 'select 10;';
await act(async () => {
findButton(renderer!, '保存').props.onClick();
});
expect(storeState.saveQuery).toHaveBeenCalledWith(expect.objectContaining({
id: 'saved-1',
name: '無題のクエリ',
sql: 'select 10;',
connectionId: 'conn-1',
dbName: 'main',
}));
});
it('keeps untitled fallback for database-scoped new query titles after a language switch', async () => {
setCurrentLanguage('ja-JP');
let renderer!: ReactTestRenderer;
await act(async () => {
renderer = create(<QueryEditor tab={createTab({ title: 'New query (main)', savedQueryId: 'saved-1' })} />);
});
editorState.value = 'select 11;';
await act(async () => {
findButton(renderer!, '保存').props.onClick();
});
expect(storeState.saveQuery).toHaveBeenCalledWith(expect.objectContaining({
id: 'saved-1',
name: '無題のクエリ',

View File

@@ -1346,12 +1346,12 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
const toggleQueryResultsPanelActionRef = useRef<any>(null);
const lastExternalQueryRef = useRef<string>(getTabQueryValue(tab));
const lastLocalQueryRef = useRef<string>(query);
const saveOperationQueueRef = useRef<Promise<unknown>>(Promise.resolve());
const queryEditorMountedRef = useRef(true);
const imeCompositionFallbackRef = useRef<{
editor: any;
valueBefore: string;
selectionBefore: any;
const saveOperationQueueRef = useRef<Promise<unknown>>(Promise.resolve());
const queryEditorMountedRef = useRef(true);
positionBefore: { lineNumber: number; column: number } | null;
committedText: string;
} | null>(null);
@@ -1869,10 +1869,6 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
return savedQueries.find((item) => item.id === tabId) || null;
}, [savedQueries, tab.id, tab.savedQueryId]);
const syncQueryDraft = useCallback((nextQuery: string) => {
const next = String(nextQuery ?? '');
lastLocalQueryRef.current = next;
persistQueryTabDraftSnapshot(draftSnapshotTab, next, {
useEffect(() => {
queryEditorMountedRef.current = true;
return () => {
@@ -1889,6 +1885,10 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
return queued;
}, []);
const syncQueryDraft = useCallback((nextQuery: string) => {
const next = String(nextQuery ?? '');
lastLocalQueryRef.current = next;
persistQueryTabDraftSnapshot(draftSnapshotTab, next, {
connectionId: currentConnectionIdRef.current,
dbName: currentDbRef.current,
});
@@ -8238,6 +8238,7 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
const persistQuery = async (payload: { id: string; name: string; createdAt?: number }): Promise<boolean> => {
const sql = getCurrentQuery();
lastLocalQueryRef.current = sql;
const saved = {
id: payload.id,
name: payload.name,
@@ -8284,7 +8285,6 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
const openSaveQueryModal = (mode: 'save' | 'rename') => {
setSaveModalMode(mode);
lastLocalQueryRef.current = sql;
saveForm.setFieldsValue({ name: currentSavedQuery?.name || resolveDefaultQueryName() });
setIsSaveModalOpen(true);
};
@@ -8665,6 +8665,9 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
name: String(values.name || '').trim() || translate('query_editor.save_modal.unnamed'),
createdAt: existed?.createdAt,
});
if (!applied) {
return;
}
message.success(translate(
saveModalMode === 'rename' ? 'query_editor.message.renamed' : 'query_editor.message.saved'
));
@@ -8688,9 +8691,6 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
currentResultSets,
activeResultKeyRef.current,
isV2Ui,
if (!applied) {
return;
}
);
const nextResultSets = currentResultSets.filter(result => result.key !== key);
const nextActiveKey = currentActiveKey && currentActiveKey !== key