From e6b822c967bed8550ad2df1325a44b8aa3b99098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E5=9B=BD=E9=94=8B?= Date: Tue, 17 Mar 2026 22:39:55 +0800 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(QueryEditor):=20?= =?UTF-8?q?=E6=B8=85=E7=90=86=E6=9C=AA=E4=BD=BF=E7=94=A8=E5=8F=98=E9=87=8F?= =?UTF-8?q?=E5=8F=8AIDE=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除未使用的 DBQuery 导入和 currentQueryId 解构 - 简化正则表达式 [\w] 为 \w(4处) - 移除变量初始值冗余和 nextKey 中间变量 - 为异步调用添加 void 前缀消除 Promise 忽略警告 - 为暂未使用的 getLeadingKeyword/applyAutoLimit 添加 DEBT 标记 --- frontend/src/components/QueryEditor.tsx | 31 ++++++++++++++----------- frontend/vite.config.d.ts | 2 ++ frontend/vite.config.js | 15 ++++++++++++ 3 files changed, 34 insertions(+), 14 deletions(-) create mode 100644 frontend/vite.config.d.ts create mode 100644 frontend/vite.config.js diff --git a/frontend/src/components/QueryEditor.tsx b/frontend/src/components/QueryEditor.tsx index 58c2acc..7c8c2a1 100644 --- a/frontend/src/components/QueryEditor.tsx +++ b/frontend/src/components/QueryEditor.tsx @@ -6,7 +6,7 @@ import { format } from 'sql-formatter'; import { v4 as uuidv4 } from 'uuid'; import { TabData, ColumnDefinition } from '../types'; import { useStore } from '../store'; -import { DBQuery, DBQueryWithCancel, DBQueryMulti, DBGetTables, DBGetAllColumns, DBGetDatabases, DBGetColumns, CancelQuery, GenerateQueryID } from '../../wailsjs/go/app/App'; +import { DBQueryWithCancel, DBQueryMulti, DBGetTables, DBGetAllColumns, DBGetDatabases, DBGetColumns, CancelQuery, GenerateQueryID } from '../../wailsjs/go/app/App'; import DataGrid, { GONAVI_ROW_KEY } from './DataGrid'; import { getDataSourceCapabilities } from '../utils/dataSourceCapabilities'; import { convertMongoShellToJsonCommand } from '../utils/mongodb'; @@ -41,7 +41,7 @@ const QueryEditor: React.FC<{ tab: TabData }> = ({ tab }) => { const [activeResultKey, setActiveResultKey] = useState(''); const [loading, setLoading] = useState(false); - const [currentQueryId, setCurrentQueryId] = useState(''); + const [, setCurrentQueryId] = useState(''); const runSeqRef = useRef(0); const currentQueryIdRef = useRef(''); const [isSaveModalOpen, setIsSaveModalOpen] = useState(false); @@ -183,7 +183,7 @@ const QueryEditor: React.FC<{ tab: TabData }> = ({ tab }) => { setDbList([]); } }; - fetchDbs(); + void fetchDbs(); }, [currentConnectionId, connections]); // Fetch Metadata for Autocomplete (Cross-database) @@ -235,7 +235,7 @@ const QueryEditor: React.FC<{ tab: TabData }> = ({ tab }) => { tablesRef.current = allTables; allColumnsRef.current = allColumns; }; - fetchMetadata(); + void fetchMetadata(); }, [currentConnectionId, connections, dbList]); // dbList 变化时触发重新加载 // Query ID management helpers @@ -370,7 +370,7 @@ const QueryEditor: React.FC<{ tab: TabData }> = ({ tab }) => { const linePrefix = model.getLineContent(position.lineNumber).slice(0, position.column - 1); // 0) 三段式 db.table.column 格式:当输入 db.table. 时提示列 - const threePartMatch = linePrefix.match(/([`"]?[\w]+[`"]?)\.([`"]?[\w]+[`"]?)\.(\w*)$/); + const threePartMatch = linePrefix.match(/([`"]?\w+[`"]?)\.([`"]?\w+[`"]?)\.(\w*)$/); if (threePartMatch) { const dbPart = stripQuotes(threePartMatch[1]); const tablePart = stripQuotes(threePartMatch[2]); @@ -398,7 +398,7 @@ const QueryEditor: React.FC<{ tab: TabData }> = ({ tab }) => { } // 1) 两段式 qualifier.xxx 格式 - const qualifierMatch = linePrefix.match(/([`"]?[A-Za-z_][\w]*[`"]?)\.(\w*)$/); + const qualifierMatch = linePrefix.match(/([`"]?[A-Za-z_]\w*[`"]?)\.(\w*)$/); if (qualifierMatch) { const qualifier = stripQuotes(qualifierMatch[1]); const prefix = (qualifierMatch[2] || '').toLowerCase(); @@ -463,7 +463,7 @@ const QueryEditor: React.FC<{ tab: TabData }> = ({ tab }) => { const aliasMap: Record = {}; // Capture table and optional alias, support db.table format - const aliasRegex = /\b(?:FROM|JOIN|UPDATE|INTO|DELETE\s+FROM)\s+([`"]?[\w]+[`"]?(?:\s*\.\s*[`"]?[\w]+[`"]?)?)(?:\s+(?:AS\s+)?([`"]?[\w]+[`"]?))?/gi; + const aliasRegex = /\b(?:FROM|JOIN|UPDATE|INTO|DELETE\s+FROM)\s+([`"]?\w+[`"]?(?:\s*\.\s*[`"]?\w+[`"]?)?)(?:\s+(?:AS\s+)?([`"]?\w+[`"]?))?/gi; let m; while ((m = aliasRegex.exec(fullText)) !== null) { const tableIdent = normalizeQualifiedName(m[1] || ''); @@ -492,7 +492,7 @@ const QueryEditor: React.FC<{ tab: TabData }> = ({ tab }) => { const tableInfo = aliasMap[qualifier.toLowerCase()]; if (tableInfo) { // Prefer preloaded MySQL all-columns cache - let cols: { name: string, type?: string, tableName?: string, dbName?: string }[] = []; + let cols: { name: string, type?: string, tableName?: string, dbName?: string }[]; if (allColumnsRef.current.length > 0) { cols = allColumnsRef.current .filter(c => @@ -522,7 +522,7 @@ const QueryEditor: React.FC<{ tab: TabData }> = ({ tab }) => { } // 2) global/table/column completion - const tableRegex = /\b(?:FROM|JOIN|UPDATE|INTO|DELETE\s+FROM)\s+([`"]?[\w]+[`"]?(?:\s*\.\s*[`"]?[\w]+[`"]?)?)/gi; + const tableRegex = /\b(?:FROM|JOIN|UPDATE|INTO|DELETE\s+FROM)\s+([`"]?\w+[`"]?(?:\s*\.\s*[`"]?\w+[`"]?)?)/gi; const foundTables = new Set(); let match; while ((match = tableRegex.exec(fullText)) !== null) { @@ -626,7 +626,7 @@ const QueryEditor: React.FC<{ tab: TabData }> = ({ tab }) => { const formatted = format(getCurrentQuery(), { language: 'mysql', keywordCase: sqlFormatOptions.keywordCase }); syncQueryToEditor(formatted); } catch (e) { - message.error("格式化失败: SQL 语法可能有误"); + void message.error("格式化失败: SQL 语法可能有误"); } }; @@ -776,6 +776,9 @@ const QueryEditor: React.FC<{ tab: TabData }> = ({ tab }) => { return statements; }; + // DEBT: 改用 DBQueryMulti 后前端不再逐条处理语句,此函数暂时未使用。 + // 当恢复前端自动行数限制功能时需要启用。 + // eslint-disable-next-line @typescript-eslint/no-unused-vars const getLeadingKeyword = (sql: string): string => { const text = (sql || '').replace(/\r\n/g, '\n'); const isWS = (ch: string) => ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r'; @@ -1068,6 +1071,9 @@ const QueryEditor: React.FC<{ tab: TabData }> = ({ tab }) => { return -1; }; + // DEBT: 改用 DBQueryMulti 后前端不再逐条处理语句,此函数暂时未使用。 + // 当恢复前端自动行数限制功能时需要启用。 + // eslint-disable-next-line @typescript-eslint/no-unused-vars const applyAutoLimit = (sql: string, dbType: string, maxRows: number): { sql: string; applied: boolean; maxRows: number } => { const normalizedType = (dbType || 'mysql').toLowerCase(); const supportsLimit = normalizedType === 'mysql' || normalizedType === 'mariadb' || normalizedType === 'diros' || normalizedType === 'sphinx' || normalizedType === 'postgres' || normalizedType === 'kingbase' || normalizedType === 'sqlite' || normalizedType === 'duckdb' || normalizedType === 'tdengine' || normalizedType === 'clickhouse' || normalizedType === ''; @@ -1176,9 +1182,7 @@ const QueryEditor: React.FC<{ tab: TabData }> = ({ tab }) => { const nextResultSets: ResultSet[] = []; const maxRows = Number(queryOptions?.maxRows) || 0; - const forceReadOnlyResult = connCaps.forceReadOnlyQueryResult; const wantsLimitProbe = Number.isFinite(maxRows) && maxRows > 0; - const probeLimit = wantsLimitProbe ? (maxRows + 1) : 0; let anyTruncated = false; for (let idx = 0; idx < statements.length; idx++) { @@ -1635,8 +1639,7 @@ const QueryEditor: React.FC<{ tab: TabData }> = ({ tab }) => { setActiveResultKey(prevActive => { if (prevActive && prevActive !== key) return prevActive; - const nextKey = next[idx]?.key || next[idx - 1]?.key || next[0]?.key || ''; - return nextKey; + return next[idx]?.key || next[idx - 1]?.key || next[0]?.key || ''; }); return next; diff --git a/frontend/vite.config.d.ts b/frontend/vite.config.d.ts new file mode 100644 index 0000000..340562a --- /dev/null +++ b/frontend/vite.config.d.ts @@ -0,0 +1,2 @@ +declare const _default: import("vite").UserConfig; +export default _default; diff --git a/frontend/vite.config.js b/frontend/vite.config.js new file mode 100644 index 0000000..2cec03f --- /dev/null +++ b/frontend/vite.config.js @@ -0,0 +1,15 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], + server: { + host: '127.0.0.1', + port: 5173, + strictPort: true, + }, + build: { + outDir: 'dist', // Standard Wails output directory + emptyOutDir: true, + } +});