5151
5252 <div v-if =" loading" class =" flex-1 flex items-center justify-center text-sm text-gray-400" >{{ t('qb.loading') }}</div >
5353 <div v-else-if =" error" class =" flex-1 flex items-center justify-center text-sm text-red-500 px-6 text-center" >{{ error }}</div >
54+ <!-- MySQL 连接未指定库:先选库 -->
55+ <div v-else-if =" srcNoDb && !pickedDb" class =" flex-1 flex flex-col items-center justify-center gap-3 text-sm text-gray-400 px-6 text-center" >
56+ <span >{{ t('qb.pickDbFirst') }}</span >
57+ <select class =" text-sm rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 px-3 py-1.5 focus:outline-none cursor-pointer min-w-[200px]"
58+ :value =" ''" @change =" onPickDb($event)" >
59+ <option value =" " disabled >{{ t('qb.selectDb') }}</option >
60+ <option v-for =" d in databases" :key =" d" :value =" d" >{{ d }}</option >
61+ </select >
62+ </div >
5463 <div v-else-if =" tables.length === 0" class =" flex-1 flex items-center justify-center text-sm text-gray-400 px-6 text-center" >{{ t('qb.noTables') }}</div >
5564
5665 <div v-else class =" flex-1 flex min-h-0" >
8291 <div class =" rounded border border-gray-200 dark:border-gray-700" >
8392 <div class =" px-2.5 py-1 text-[11px] font-medium text-gray-500" >FROM / JOIN</div >
8493 <div class =" px-2.5 pb-2 flex flex-col gap-1.5" >
94+ <div v-if =" srcNoDb" class =" flex items-center gap-1.5" >
95+ <span class =" text-[11px] text-gray-400 w-10 flex-shrink-0" >{{ t('qb.db') }}</span >
96+ <select :value =" pickedDb" class =" text-xs rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 px-2 py-1 focus:outline-none min-w-[160px]" @change =" onPickDb($event)" >
97+ <option v-for =" d in databases" :key =" d" :value =" d" >{{ d }}</option >
98+ </select >
99+ </div >
85100 <div class =" flex items-center gap-1.5" >
86101 <span class =" text-[11px] text-gray-400 w-10 flex-shrink-0" >FROM</span >
87102 <select v-model =" table" class =" text-xs rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 px-2 py-1 focus:outline-none min-w-[160px]" >
@@ -332,6 +347,11 @@ const loading = ref(false)
332347const error = ref (' ' )
333348const tables = ref <Tbl []>([])
334349
350+ // MySQL 连接未指定库时:需先在构建器内选库
351+ const srcNoDb = ref (false )
352+ const databases = ref <string []>([])
353+ const pickedDb = ref (' ' )
354+
335355const table = ref (' ' )
336356const joins = ref <Join []>([])
337357const selectItems = ref <SelItem []>([])
@@ -475,6 +495,8 @@ watch(table, () => {
475495const kind = () => resolveActiveSource ().kind
476496const q = (name : string ) => quoteIdent (kind (), name )
477497const qRef = (r : ColRef ) => (multiTable .value ? ` ${q (r .t )}.${q (r .c )} ` : q (r .c ))
498+ // FROM/JOIN 表名:连接未指定库时用 库.表 限定
499+ const qTable = (name : string ) => (srcNoDb .value && pickedDb .value ? ` ${q (pickedDb .value )}.${q (name )} ` : q (name ))
478500
479501// 列引用 + 可选函数与额外参数:fn(col, args)
480502const exprSql = (r : ColRef & { fn? : string ; args? : string }) => wrapFunc (qRef (r ), r .fn , r .args )
@@ -540,9 +562,9 @@ const sql = computed(() => {
540562 return ' '
541563 }
542564 const cols = selectItems .value .length === 0 ? ' *' : selectItems .value .map (itemSql ).join (' , ' )
543- let out = ` SELECT ${distinct .value ? ' DISTINCT ' : ' ' }${cols } FROM ${q (table .value )} `
565+ let out = ` SELECT ${distinct .value ? ' DISTINCT ' : ' ' }${cols } FROM ${qTable (table .value )} `
544566 for (const j of joins .value ) {
545- out += ` ${j .type } JOIN ${q (j .table )} ON ${qRef ({t: j .leftT , c: j .leftC })} = ${q (j .table )}.${q (j .rightC )} `
567+ out += ` ${j .type } JOIN ${qTable (j .table )} ON ${qRef ({t: j .leftT , c: j .leftC })} = ${q (j .table )}.${q (j .rightC )} `
546568 }
547569 const conds = wheres .value .map (condSql ).filter (Boolean )
548570 if (conds .length ) {
@@ -567,6 +589,7 @@ const sql = computed(() => {
567589// ---- 状态记忆:按数据源持久化 ----
568590interface QbState {
569591 table: string
592+ pickedDb? : string
570593 joins? : Join []
571594 selectItems? : any []
572595 distinct? : boolean
@@ -584,6 +607,7 @@ const persist = () => {
584607 }
585608 kvSetJSON (stateKey (), {
586609 table: table .value ,
610+ pickedDb: pickedDb .value ,
587611 joins: joins .value ,
588612 selectItems: selectItems .value ,
589613 distinct: distinct .value ,
@@ -633,6 +657,7 @@ const saveName = ref('')
633657const showSave = ref (false )
634658const currentState = (): QbState => ({
635659 table: table .value ,
660+ pickedDb: pickedDb .value ,
636661 joins: joins .value ,
637662 selectItems: selectItems .value ,
638663 distinct: distinct .value ,
@@ -690,19 +715,37 @@ const reset = () => {
690715 })
691716}
692717
718+ const runRows = async (source : any , sqlStr : string ): Promise <any [][]> => {
719+ const res = await invoke <any >(' run_sql' , {sql: sqlStr , source })
720+ if (res .error ) {
721+ throw new Error (res .error )
722+ }
723+ return (res .result_sets || [])[0 ]?.rows || []
724+ }
725+ const fetchTables = async (source : any , db ? : string ) => groupTables (await runRows (source , columnsSql (source .kind , db )))
726+
693727const load = async () => {
694728 loading .value = true
695729 error .value = ' '
696730 try {
697731 const source = resolveActiveSource ()
698- const db = source .kind === ' mysql' ? source .database || undefined : undefined
699- const res = await invoke <any >(' run_sql' , {sql: columnsSql (source .kind , db ), source })
700- if (res .error ) {
701- throw new Error (res .error )
702- }
703- tables .value = groupTables ((res .result_sets || [])[0 ]?.rows || [])
704732 savedList .value = kvGetJSON <SavedQuery []>(savedKey (), [])
705- const restored = await restoreState ()
733+ srcNoDb .value = source .kind === ' mysql' && ! source .database
734+ if (srcNoDb .value ) {
735+ // 连接未指定库:先列出可选库,并尝试恢复上次所选库
736+ databases .value = (await runRows (source ,
737+ ' SELECT schema_name FROM information_schema.schemata '
738+ + " WHERE schema_name NOT IN ('information_schema','mysql','performance_schema','sys') "
739+ + ' ORDER BY schema_name' )).map (r => String (r [0 ]))
740+ const savedDb = kvGetJSON <QbState | null >(stateKey (), null )?.pickedDb
741+ pickedDb .value = savedDb && databases .value .includes (savedDb ) ? savedDb : ' '
742+ tables .value = pickedDb .value ? await fetchTables (source , pickedDb .value ) : []
743+ }
744+ else {
745+ pickedDb .value = ' '
746+ tables .value = await fetchTables (source , undefined )
747+ }
748+ const restored = tables .value .length ? await restoreState () : false
706749 if (! restored && tables .value .length && ! tables .value .find (tb => tb .name === table .value )) {
707750 table .value = tables .value [0 ].name
708751 }
@@ -716,6 +759,39 @@ const load = async () => {
716759 }
717760}
718761
762+ // 选择/切换数据库:加载该库表并重置构建器
763+ const onPickDb = async (e : Event ) => {
764+ const db = (e .target as HTMLSelectElement ).value
765+ if (! db || db === pickedDb .value ) {
766+ return
767+ }
768+ loading .value = true
769+ try {
770+ const source = resolveActiveSource ()
771+ const list = await fetchTables (source , db )
772+ restoring = true
773+ pickedDb .value = db
774+ tables .value = list
775+ joins .value = []
776+ selectItems .value = []
777+ distinct .value = false
778+ groupBy .value = []
779+ wheres .value = []
780+ havings .value = []
781+ orders .value = []
782+ table .value = list [0 ]?.name || ' '
783+ await nextTick ()
784+ restoring = false
785+ persist ()
786+ }
787+ catch (e : any ) {
788+ error .value = e ?.message || String (e )
789+ }
790+ finally {
791+ loading .value = false
792+ }
793+ }
794+
719795const openBuilder = () => {
720796 visible .value = true
721797 load ()
0 commit comments