mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-04 21:07:45 +08:00
✨ feat(query-editor): 支持自定义新建查询默认 SQL
- 在 appearance 持久化设置中新增 newQuerySqlTemplate,支持默认回退与空白模板 - 新建空白查询标签页按用户模板初始化,不影响外部 SQL、保存查询和只读标签 - 在工具中心补充模板编辑与恢复默认入口,并完善 store、编辑器和 i18n 回归测试
This commit is contained in:
@@ -49,7 +49,10 @@ const storeState = vi.hoisted(() => ({
|
||||
saveQuery: vi.fn(),
|
||||
theme: 'light',
|
||||
languagePreference: 'zh-CN' as 'zh-CN' | 'en-US',
|
||||
appearance: { uiVersion: 'legacy' as 'legacy' | 'v2' },
|
||||
appearance: {
|
||||
uiVersion: 'legacy' as 'legacy' | 'v2',
|
||||
newQuerySqlTemplate: null as string | null,
|
||||
},
|
||||
sqlFormatOptions: { keywordCase: 'upper' as const },
|
||||
setSqlFormatOptions: vi.fn(),
|
||||
queryOptions: {
|
||||
@@ -705,6 +708,7 @@ describe('QueryEditor external SQL save', () => {
|
||||
storeState.aiPanelVisible = false;
|
||||
storeState.setAIPanelVisible.mockReset();
|
||||
storeState.appearance.uiVersion = 'legacy';
|
||||
storeState.appearance.newQuerySqlTemplate = null;
|
||||
storeState.queryOptions = {
|
||||
maxRows: 5000,
|
||||
showColumnComment: true,
|
||||
@@ -833,6 +837,26 @@ describe('QueryEditor external SQL save', () => {
|
||||
expect(editorState.value).toBe('SELECT * FROM ');
|
||||
});
|
||||
|
||||
it('uses the customized new query template for a fresh blank query tab', async () => {
|
||||
storeState.appearance.newQuerySqlTemplate = 'SELECT id,\n name\nFROM users;';
|
||||
|
||||
await act(async () => {
|
||||
create(<QueryEditor tab={createTab({ query: '' })} />);
|
||||
});
|
||||
|
||||
expect(editorState.value).toBe('SELECT id,\n name\nFROM users;');
|
||||
});
|
||||
|
||||
it('allows a blank new query template when the default content is cleared', async () => {
|
||||
storeState.appearance.newQuerySqlTemplate = '';
|
||||
|
||||
await act(async () => {
|
||||
create(<QueryEditor tab={createTab({ query: '' })} />);
|
||||
});
|
||||
|
||||
expect(editorState.value).toBe('');
|
||||
});
|
||||
|
||||
it('keeps the query results panel hidden by default on first entry', async () => {
|
||||
storeState.appearance.uiVersion = 'v2';
|
||||
|
||||
|
||||
@@ -124,6 +124,7 @@ import {
|
||||
queryCompletionMetadataRowsBySpecs,
|
||||
readSidebarSqlDropText,
|
||||
matchLeadingSelectTableReference,
|
||||
resolveNewQueryDefaultTemplate,
|
||||
resolveEventTargetNode,
|
||||
resolveNextResultSetIndex,
|
||||
resolveOracleExactCaseTableReference,
|
||||
@@ -759,7 +760,11 @@ const resetSharedQueryEditorMetadata = () => {
|
||||
|
||||
|
||||
const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isActive = true }) => {
|
||||
const [query, setQuery] = useState(getInitialEditorQuery(tab));
|
||||
const appearance = useStore(state => state.appearance);
|
||||
const [query, setQuery] = useState(() => getInitialEditorQuery(
|
||||
tab,
|
||||
resolveNewQueryDefaultTemplate(appearance.newQuerySqlTemplate),
|
||||
));
|
||||
const isExternalSQLFileTab = Boolean(String(tab.filePath || '').trim());
|
||||
const isObjectEditQueryTab = tab.type === 'query' && tab.queryMode === 'object-edit';
|
||||
|
||||
@@ -860,7 +865,6 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
|
||||
const theme = useStore(state => state.theme);
|
||||
const languagePreference = useStore((state) => state.languagePreference);
|
||||
void languagePreference;
|
||||
const appearance = useStore(state => state.appearance);
|
||||
const darkMode = theme === 'dark';
|
||||
const isV2Ui = appearance.uiVersion === 'v2';
|
||||
const sqlFormatOptions = useStore(state => state.sqlFormatOptions);
|
||||
|
||||
@@ -682,11 +682,25 @@ export const splitCompletionSchemaAndTable = (qualified: string): { schema: stri
|
||||
|
||||
export const DEFAULT_QUERY_TEMPLATE = 'SELECT * FROM ';
|
||||
|
||||
export const resolveNewQueryDefaultTemplate = (
|
||||
template: string | null | undefined,
|
||||
): string => {
|
||||
if (template === null || template === undefined) {
|
||||
return DEFAULT_QUERY_TEMPLATE;
|
||||
}
|
||||
return String(template)
|
||||
.replace(/\r\n/g, '\n')
|
||||
.replace(/\r/g, '\n');
|
||||
};
|
||||
|
||||
export const getTabQueryValue = (tab: TabData): string => (
|
||||
typeof tab.query === 'string' ? tab.query : ''
|
||||
);
|
||||
|
||||
export const getInitialEditorQuery = (tab: TabData): string => {
|
||||
export const getInitialEditorQuery = (
|
||||
tab: TabData,
|
||||
defaultQueryTemplate: string | null | undefined = DEFAULT_QUERY_TEMPLATE,
|
||||
): string => {
|
||||
if (hasQueryTabDraft(tab.id)) {
|
||||
return getQueryTabDraft(tab.id);
|
||||
}
|
||||
@@ -694,7 +708,7 @@ export const getInitialEditorQuery = (tab: TabData): string => {
|
||||
if (tabQuery || tab.filePath || tab.savedQueryId || tab.readOnly) {
|
||||
return tabQuery;
|
||||
}
|
||||
return DEFAULT_QUERY_TEMPLATE;
|
||||
return resolveNewQueryDefaultTemplate(defaultQueryTemplate);
|
||||
};
|
||||
|
||||
export const resolveNextResultSetIndex = (sets: Array<{ key?: string }>): number => {
|
||||
|
||||
Reference in New Issue
Block a user