mirror of
				https://gitee.com/dromara/mayfly-go
				synced 2025-11-04 08:20:25 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			646 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			646 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
<template>
 | 
						||
    <div class="db-sql-exec">
 | 
						||
        <el-row>
 | 
						||
            <el-col :span="5">
 | 
						||
                <tag-tree ref="tagTreeRef" :loadTags="loadTags">
 | 
						||
                    <template #prefix="{ data }">
 | 
						||
                        <span v-if="data.type.value == SqlExecNodeType.DbInst">
 | 
						||
                            <el-popover :show-after="500" placement="right-start" title="数据库实例信息" trigger="hover" :width="250">
 | 
						||
                                <template #reference>
 | 
						||
                                    <SvgIcon :name="DbInst.getIconName(data.params.type)" :size="18" />
 | 
						||
                                </template>
 | 
						||
                                <template #default>
 | 
						||
                                    <el-descriptions :column="1" size="small">
 | 
						||
                                        <el-descriptions-item label="名称">
 | 
						||
                                            {{ data.params.name }}
 | 
						||
                                        </el-descriptions-item>
 | 
						||
                                        <el-descriptions-item label="host">
 | 
						||
                                            {{ `${data.params.host}:${data.params.port}` }}
 | 
						||
                                        </el-descriptions-item>
 | 
						||
                                        <el-descriptions-item label="user">
 | 
						||
                                            {{ data.params.username }}
 | 
						||
                                        </el-descriptions-item>
 | 
						||
                                        <el-descriptions-item label="备注">
 | 
						||
                                            {{ data.params.remark }}
 | 
						||
                                        </el-descriptions-item>
 | 
						||
                                    </el-descriptions>
 | 
						||
                                </template>
 | 
						||
                            </el-popover>
 | 
						||
                        </span>
 | 
						||
 | 
						||
                        <SvgIcon v-if="data.icon" :name="data.icon.name" :color="data.icon.color" />
 | 
						||
                    </template>
 | 
						||
 | 
						||
                    <template #label="{ data }">
 | 
						||
                        <el-tooltip placement="left" :show-after="1000" v-if="data.type.value == SqlExecNodeType.Table" :content="data.params.tableComment">
 | 
						||
                            {{ data.label }}
 | 
						||
                        </el-tooltip>
 | 
						||
                    </template>
 | 
						||
 | 
						||
                    <template #suffix="{ data }">
 | 
						||
                        <span class="db-table-size" v-if="data.type.value == SqlExecNodeType.Table && data.params.size">{{ ` ${data.params.size}` }}</span>
 | 
						||
                        <span class="db-table-size" v-if="data.type.value == SqlExecNodeType.TableMenu && data.params.dbTableSize">{{
 | 
						||
                            ` ${data.params.dbTableSize}`
 | 
						||
                        }}</span>
 | 
						||
                    </template>
 | 
						||
                </tag-tree>
 | 
						||
            </el-col>
 | 
						||
 | 
						||
            <el-col :span="19">
 | 
						||
                <el-row>
 | 
						||
                    <el-col :span="24" v-if="state.db">
 | 
						||
                        <el-descriptions :column="4" size="small" border class="ml5">
 | 
						||
                            <el-descriptions-item label-align="right" label="操作"
 | 
						||
                                ><el-button
 | 
						||
                                    :disabled="!state.db || !nowDbInst.id"
 | 
						||
                                    type="primary"
 | 
						||
                                    icon="Search"
 | 
						||
                                    @click="addQueryTab({ id: nowDbInst.id, dbs: nowDbInst.databases?.split(' ') }, state.db)"
 | 
						||
                                    size="small"
 | 
						||
                                    >新建查询</el-button
 | 
						||
                                ></el-descriptions-item
 | 
						||
                            >
 | 
						||
 | 
						||
                            <el-descriptions-item label-align="right" label="tag">{{ nowDbInst.tagPath }}</el-descriptions-item>
 | 
						||
 | 
						||
                            <el-descriptions-item label-align="right">
 | 
						||
                                <template #label>
 | 
						||
                                    <div>
 | 
						||
                                        <SvgIcon :name="DbInst.getIconName(nowDbInst.type)" :size="18" />
 | 
						||
                                        实例
 | 
						||
                                    </div>
 | 
						||
                                </template>
 | 
						||
                                {{ nowDbInst.id }}
 | 
						||
                                <el-divider direction="vertical" border-style="dashed" />
 | 
						||
                                {{ nowDbInst.name }}
 | 
						||
                                <el-divider direction="vertical" border-style="dashed" />
 | 
						||
                                {{ nowDbInst.host }}
 | 
						||
                            </el-descriptions-item>
 | 
						||
 | 
						||
                            <el-descriptions-item label="库名" label-align="right">{{ state.db }}</el-descriptions-item>
 | 
						||
                        </el-descriptions>
 | 
						||
                    </el-col>
 | 
						||
                </el-row>
 | 
						||
 | 
						||
                <div id="data-exec" class="mt5 ml5">
 | 
						||
                    <el-tabs
 | 
						||
                        v-if="state.tabs.size > 0"
 | 
						||
                        type="card"
 | 
						||
                        @tab-remove="onRemoveTab"
 | 
						||
                        @tab-change="onTabChange"
 | 
						||
                        style="width: 100%"
 | 
						||
                        v-model="state.activeName"
 | 
						||
                    >
 | 
						||
                        <el-tab-pane closable v-for="dt in state.tabs.values()" :label="dt.label" :name="dt.key" :key="dt.key">
 | 
						||
                            <template #label>
 | 
						||
                                <el-popover :show-after="1000" placement="bottom-start" trigger="hover" :width="250">
 | 
						||
                                    <template #reference> {{ dt.label }} </template>
 | 
						||
                                    <template #default>
 | 
						||
                                        <el-descriptions :column="1" size="small">
 | 
						||
                                            <el-descriptions-item label="tagPath">
 | 
						||
                                                {{ dt.params.tagPath }}
 | 
						||
                                            </el-descriptions-item>
 | 
						||
                                            <el-descriptions-item label="名称">
 | 
						||
                                                {{ dt.params.name }}
 | 
						||
                                            </el-descriptions-item>
 | 
						||
                                            <el-descriptions-item label="host">
 | 
						||
                                                <SvgIcon :name="DbInst.getIconName(dt.params.type)" :size="18" />
 | 
						||
                                                {{ dt.params.host }}
 | 
						||
                                            </el-descriptions-item>
 | 
						||
                                            <el-descriptions-item label="库名">
 | 
						||
                                                {{ dt.params.dbName }}
 | 
						||
                                            </el-descriptions-item>
 | 
						||
                                        </el-descriptions>
 | 
						||
                                    </template>
 | 
						||
                                </el-popover>
 | 
						||
                            </template>
 | 
						||
 | 
						||
                            <db-table-data-op
 | 
						||
                                v-if="dt.type === TabType.TableData"
 | 
						||
                                :db-id="dt.dbId"
 | 
						||
                                :db-name="dt.db"
 | 
						||
                                :table-name="dt.params.table"
 | 
						||
                                :table-height="state.dataTabsTableHeight"
 | 
						||
                            ></db-table-data-op>
 | 
						||
 | 
						||
                            <db-sql-editor
 | 
						||
                                v-if="dt.type === TabType.Query"
 | 
						||
                                :db-id="dt.dbId"
 | 
						||
                                :db-name="dt.db"
 | 
						||
                                :sql-name="dt.params.sqlName"
 | 
						||
                                @save-sql-success="reloadSqls"
 | 
						||
                                @delete-sql-success="deleteSqlScript(dt)"
 | 
						||
                                :editor-height="state.editorHeight"
 | 
						||
                            >
 | 
						||
                            </db-sql-editor>
 | 
						||
 | 
						||
                            <db-tables-op
 | 
						||
                                v-if="dt.type == TabType.TablesOp"
 | 
						||
                                :db-id="dt.params.id"
 | 
						||
                                :db="dt.params.db"
 | 
						||
                                :db-type="dt.params.type"
 | 
						||
                                :height="state.tablesOpHeight"
 | 
						||
                            />
 | 
						||
                        </el-tab-pane>
 | 
						||
                    </el-tabs>
 | 
						||
                </div>
 | 
						||
            </el-col>
 | 
						||
        </el-row>
 | 
						||
    </div>
 | 
						||
</template>
 | 
						||
 | 
						||
<script lang="ts" setup>
 | 
						||
import { defineAsyncComponent, onMounted, reactive, ref, toRefs, onBeforeUnmount } from 'vue';
 | 
						||
import { ElMessage } from 'element-plus';
 | 
						||
import { formatByteSize } from '@/common/utils/format';
 | 
						||
import { DbInst, TabInfo, TabType, registerDbCompletionItemProvider } from './db';
 | 
						||
import { TagTreeNode, NodeType } from '../component/tag';
 | 
						||
import TagTree from '../component/TagTree.vue';
 | 
						||
import { dbApi } from './api';
 | 
						||
import { dispposeCompletionItemProvider } from '@/components/monaco/completionItemProvider';
 | 
						||
import SvgIcon from '@/components/svgIcon/index.vue';
 | 
						||
import { ContextmenuItem } from '@/components/contextmenu/index';
 | 
						||
 | 
						||
const DbSqlEditor = defineAsyncComponent(() => import('./component/sqleditor/DbSqlEditor.vue'));
 | 
						||
const DbTableDataOp = defineAsyncComponent(() => import('./component/table/DbTableDataOp.vue'));
 | 
						||
const DbTablesOp = defineAsyncComponent(() => import('./component/table/DbTablesOp.vue'));
 | 
						||
 | 
						||
/**
 | 
						||
 * 树节点类型
 | 
						||
 */
 | 
						||
class SqlExecNodeType {
 | 
						||
    static DbInst = 1;
 | 
						||
    static Db = 2;
 | 
						||
    static TableMenu = 3;
 | 
						||
    static SqlMenu = 4;
 | 
						||
    static Table = 5;
 | 
						||
    static Sql = 6;
 | 
						||
    static PgSchemaMenu = 7;
 | 
						||
    static PgSchema = 8;
 | 
						||
}
 | 
						||
 | 
						||
const DbIcon = {
 | 
						||
    name: 'Coin',
 | 
						||
    color: '#67c23a',
 | 
						||
};
 | 
						||
 | 
						||
// pgsql schema icon
 | 
						||
const SchemaIcon = {
 | 
						||
    name: 'List',
 | 
						||
    color: '#67c23a',
 | 
						||
};
 | 
						||
 | 
						||
const TableIcon = {
 | 
						||
    name: 'Calendar',
 | 
						||
    color: '#409eff',
 | 
						||
};
 | 
						||
 | 
						||
const SqlIcon = {
 | 
						||
    name: 'Files',
 | 
						||
    color: '#f56c6c',
 | 
						||
};
 | 
						||
 | 
						||
// node节点点击时,触发改变db事件
 | 
						||
const nodeClickChangeDb = (nodeData: TagTreeNode) => {
 | 
						||
    const params = nodeData.params;
 | 
						||
    if (params.db) {
 | 
						||
        changeDb(
 | 
						||
            { id: params.id, host: `${params.host}`, name: params.name, type: params.type, tagPath: params.tagPath, databases: params.database },
 | 
						||
            params.db
 | 
						||
        );
 | 
						||
    }
 | 
						||
};
 | 
						||
 | 
						||
// tagpath 节点类型
 | 
						||
const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
 | 
						||
    const dbInfos = instMap.get(parentNode.key);
 | 
						||
    if (!dbInfos) {
 | 
						||
        return [];
 | 
						||
    }
 | 
						||
    return dbInfos?.map((x: any) => {
 | 
						||
        return new TagTreeNode(`${parentNode.key}.${x.id}`, x.name, NodeTypeDbInst).withParams(x);
 | 
						||
    });
 | 
						||
});
 | 
						||
 | 
						||
// 数据库实例节点类型
 | 
						||
const NodeTypeDbInst = new NodeType(SqlExecNodeType.DbInst).withLoadNodesFunc((parentNode: TagTreeNode) => {
 | 
						||
    const params = parentNode.params;
 | 
						||
    const dbs = params.database.split(' ')?.sort();
 | 
						||
    return dbs.map((x: any) => {
 | 
						||
        return new TagTreeNode(`${parentNode.key}.${x}`, x, NodeTypeDb)
 | 
						||
            .withParams({
 | 
						||
                tagPath: params.tagPath,
 | 
						||
                id: params.id,
 | 
						||
                name: params.name,
 | 
						||
                type: params.type,
 | 
						||
                host: `${params.host}:${params.port}`,
 | 
						||
                dbs: dbs,
 | 
						||
                db: x,
 | 
						||
            })
 | 
						||
            .withIcon(DbIcon);
 | 
						||
    });
 | 
						||
});
 | 
						||
 | 
						||
// 数据库节点
 | 
						||
const NodeTypeDb = new NodeType(SqlExecNodeType.Db)
 | 
						||
    .withLoadNodesFunc(async (parentNode: TagTreeNode) => {
 | 
						||
        const params = parentNode.params;
 | 
						||
        if (params.type == 'postgres') {
 | 
						||
            return [new TagTreeNode(`${params.id}.${params.db}.schema-menu`, 'schema', NodeTypePostgresScheamMenu).withParams(params).withIcon(SchemaIcon)];
 | 
						||
        }
 | 
						||
 | 
						||
        return [
 | 
						||
            new TagTreeNode(`${params.id}.${params.db}.table-menu`, '表', NodeTypeTableMenu).withParams(params).withIcon(TableIcon),
 | 
						||
            new TagTreeNode(getSqlMenuNodeKey(params.id, params.db), 'SQL', NodeTypeSqlMenu).withParams(params).withIcon(SqlIcon),
 | 
						||
        ];
 | 
						||
    })
 | 
						||
    .withNodeClickFunc(nodeClickChangeDb);
 | 
						||
 | 
						||
// postgres schema模式菜单
 | 
						||
const NodeTypePostgresScheamMenu = new NodeType(SqlExecNodeType.PgSchemaMenu)
 | 
						||
    .withLoadNodesFunc(async (parentNode: TagTreeNode) => {
 | 
						||
        const params = parentNode.params;
 | 
						||
        const { id, db } = params;
 | 
						||
        const schemaNames = await dbApi.pgSchemas.request({ id, db });
 | 
						||
        return schemaNames.map((sn: any) => {
 | 
						||
            // 将db变更为  db/schema;
 | 
						||
            const nParams = { ...params };
 | 
						||
            nParams.schema = sn;
 | 
						||
            nParams.db = nParams.db + '/' + sn;
 | 
						||
            return new TagTreeNode(`${params.id}.${params.db}.schema.${sn}`, sn, NodeTypePostgresScheam).withParams(nParams).withIcon(SchemaIcon);
 | 
						||
        });
 | 
						||
    })
 | 
						||
    .withNodeClickFunc(nodeClickChangeDb);
 | 
						||
 | 
						||
// postgres schema模式
 | 
						||
const NodeTypePostgresScheam = new NodeType(SqlExecNodeType.PgSchema)
 | 
						||
    .withLoadNodesFunc(async (parentNode: TagTreeNode) => {
 | 
						||
        const params = parentNode.params;
 | 
						||
        return [
 | 
						||
            new TagTreeNode(`${params.id}.${params.db}.table-menu`, '表', NodeTypeTableMenu).withParams(params).withIcon(TableIcon),
 | 
						||
            new TagTreeNode(getSqlMenuNodeKey(params.id, params.db), 'SQL', NodeTypeSqlMenu).withParams(params).withIcon(SqlIcon),
 | 
						||
        ];
 | 
						||
    })
 | 
						||
    .withNodeClickFunc(nodeClickChangeDb);
 | 
						||
 | 
						||
// 数据库表菜单节点
 | 
						||
const NodeTypeTableMenu = new NodeType(SqlExecNodeType.TableMenu)
 | 
						||
    .withContextMenuItems([
 | 
						||
        new ContextmenuItem('reloadTables', '刷新').withIcon('RefreshRight').withOnClick((data: any) => reloadTables(data.key)),
 | 
						||
 | 
						||
        new ContextmenuItem('tablesOp', '表操作').withIcon('Setting').withOnClick((data: any) => {
 | 
						||
            const params = data.params;
 | 
						||
            addTablesOpTab({ id: params.id, db: params.db, type: params.type, nodeKey: data.key });
 | 
						||
        }),
 | 
						||
    ])
 | 
						||
    .withLoadNodesFunc(async (parentNode: TagTreeNode) => {
 | 
						||
        const params = parentNode.params;
 | 
						||
        let { id, db } = params;
 | 
						||
        // 获取当前库的所有表信息
 | 
						||
        let tables = await DbInst.getInst(id).loadTables(db, state.reloadStatus);
 | 
						||
        state.reloadStatus = false;
 | 
						||
        let dbTableSize = 0;
 | 
						||
        const tablesNode = tables.map((x: any) => {
 | 
						||
            dbTableSize += x.dataLength + x.indexLength;
 | 
						||
            return new TagTreeNode(`${id}.${db}.${x.tableName}`, x.tableName, NodeTypeTable)
 | 
						||
                .withIsLeaf(true)
 | 
						||
                .withParams({
 | 
						||
                    id,
 | 
						||
                    db,
 | 
						||
                    tableName: x.tableName,
 | 
						||
                    tableComment: x.tableComment,
 | 
						||
                    size: formatByteSize(x.dataLength + x.indexLength, 1),
 | 
						||
                })
 | 
						||
                .withIcon(TableIcon);
 | 
						||
        });
 | 
						||
        // 设置父节点参数的表大小
 | 
						||
        parentNode.params.dbTableSize = formatByteSize(dbTableSize);
 | 
						||
        return tablesNode;
 | 
						||
    })
 | 
						||
    .withNodeClickFunc(nodeClickChangeDb);
 | 
						||
 | 
						||
// 数据库sql模板菜单节点
 | 
						||
const NodeTypeSqlMenu = new NodeType(SqlExecNodeType.SqlMenu)
 | 
						||
    .withLoadNodesFunc(async (parentNode: TagTreeNode) => {
 | 
						||
        const params = parentNode.params;
 | 
						||
        const id = params.id;
 | 
						||
        const db = params.db;
 | 
						||
        const dbs = params.dbs;
 | 
						||
        // 加载用户保存的sql脚本
 | 
						||
        const sqls = await dbApi.getSqlNames.request({ id: id, db: db });
 | 
						||
        return sqls.map((x: any) => {
 | 
						||
            return new TagTreeNode(`${id}.${db}.${x.name}`, x.name, NodeTypeSql)
 | 
						||
                .withIsLeaf(true)
 | 
						||
                .withParams({
 | 
						||
                    id,
 | 
						||
                    db,
 | 
						||
                    dbs,
 | 
						||
                    sqlName: x.name,
 | 
						||
                })
 | 
						||
                .withIcon(SqlIcon);
 | 
						||
        });
 | 
						||
    })
 | 
						||
    .withNodeClickFunc(nodeClickChangeDb);
 | 
						||
 | 
						||
// 表节点类型
 | 
						||
const NodeTypeTable = new NodeType(SqlExecNodeType.Table).withNodeClickFunc((nodeData: TagTreeNode) => {
 | 
						||
    const params = nodeData.params;
 | 
						||
    loadTableData({ id: params.id, nodeKey: nodeData.key }, params.db, params.tableName);
 | 
						||
});
 | 
						||
 | 
						||
// sql模板节点类型
 | 
						||
const NodeTypeSql = new NodeType(SqlExecNodeType.Sql).withNodeClickFunc((nodeData: TagTreeNode) => {
 | 
						||
    const params = nodeData.params;
 | 
						||
    addQueryTab({ id: params.id, nodeKey: nodeData.key, dbs: params.dbs }, params.db, params.sqlName);
 | 
						||
});
 | 
						||
 | 
						||
const tagTreeRef: any = ref(null);
 | 
						||
 | 
						||
const tabs: Map<string, TabInfo> = new Map();
 | 
						||
const state = reactive({
 | 
						||
    /**
 | 
						||
     * 当前操作的数据库实例
 | 
						||
     */
 | 
						||
    nowDbInst: {} as DbInst,
 | 
						||
    db: '', // 当前操作的数据库
 | 
						||
    activeName: '',
 | 
						||
    reloadStatus: false,
 | 
						||
    tabs,
 | 
						||
    dataTabsTableHeight: 600,
 | 
						||
    editorHeight: '600',
 | 
						||
    tablesOpHeight: '600',
 | 
						||
});
 | 
						||
 | 
						||
const { nowDbInst } = toRefs(state);
 | 
						||
 | 
						||
onMounted(() => {
 | 
						||
    setHeight();
 | 
						||
    // 监听浏览器窗口大小变化,更新对应组件高度
 | 
						||
    window.onresize = () => setHeight();
 | 
						||
});
 | 
						||
 | 
						||
onBeforeUnmount(() => {
 | 
						||
    dispposeCompletionItemProvider('sql');
 | 
						||
});
 | 
						||
 | 
						||
/**
 | 
						||
 * 设置editor高度和数据表高度
 | 
						||
 */
 | 
						||
const setHeight = () => {
 | 
						||
    state.editorHeight = window.innerHeight - 500 + 'px';
 | 
						||
    state.dataTabsTableHeight = window.innerHeight - 255;
 | 
						||
    state.tablesOpHeight = window.innerHeight - 220 + 'px';
 | 
						||
};
 | 
						||
 | 
						||
/**
 | 
						||
 * instmap; tagPaht -> info[]
 | 
						||
 */
 | 
						||
const instMap: Map<string, any[]> = new Map();
 | 
						||
 | 
						||
const getInsts = async () => {
 | 
						||
    const res = await dbApi.dbs.request({ pageNum: 1, pageSize: 1000 });
 | 
						||
    if (!res.total) return;
 | 
						||
    for (const db of res.list) {
 | 
						||
        const tagPath = db.tagPath;
 | 
						||
        let dbInsts = instMap.get(tagPath) || [];
 | 
						||
        dbInsts.push(db);
 | 
						||
        instMap.set(tagPath, dbInsts?.sort());
 | 
						||
    }
 | 
						||
};
 | 
						||
 | 
						||
/**
 | 
						||
 * 加载标签树节点
 | 
						||
 */
 | 
						||
const loadTags = async () => {
 | 
						||
    await getInsts();
 | 
						||
    const tagPaths = instMap.keys();
 | 
						||
    const tagNodes = [];
 | 
						||
    for (let tagPath of tagPaths) {
 | 
						||
        tagNodes.push(new TagTreeNode(tagPath, tagPath, NodeTypeTagPath));
 | 
						||
    }
 | 
						||
    return tagNodes;
 | 
						||
};
 | 
						||
 | 
						||
// 选择数据库,改变当前正在操作的数据库信息
 | 
						||
const changeDb = (db: any, dbName: string) => {
 | 
						||
    state.nowDbInst = DbInst.getOrNewInst(db);
 | 
						||
    state.db = dbName;
 | 
						||
};
 | 
						||
 | 
						||
// 加载选中的表数据,即新增表数据操作tab
 | 
						||
const loadTableData = async (db: any, dbName: string, tableName: string) => {
 | 
						||
    if (tableName == '') {
 | 
						||
        return;
 | 
						||
    }
 | 
						||
    changeDb(db, dbName);
 | 
						||
 | 
						||
    const key = `${db.id}:\`${dbName}\`.${tableName}`;
 | 
						||
    let tab = state.tabs.get(key);
 | 
						||
    state.activeName = key;
 | 
						||
    // 如果存在该表tab,则直接返回
 | 
						||
    if (tab) {
 | 
						||
        return;
 | 
						||
    }
 | 
						||
    tab = new TabInfo();
 | 
						||
    tab.label = tableName;
 | 
						||
    tab.key = key;
 | 
						||
    tab.treeNodeKey = db.nodeKey;
 | 
						||
    tab.dbId = db.id;
 | 
						||
    tab.db = dbName;
 | 
						||
    tab.type = TabType.TableData;
 | 
						||
    tab.params = {
 | 
						||
        ...getNowDbInfo(),
 | 
						||
        table: tableName,
 | 
						||
    };
 | 
						||
    state.tabs.set(key, tab);
 | 
						||
};
 | 
						||
 | 
						||
// 新建查询tab
 | 
						||
const addQueryTab = async (db: any, dbName: string, sqlName: string = '') => {
 | 
						||
    if (!dbName || !db.id) {
 | 
						||
        ElMessage.warning('请选择数据库实例及对应的schema');
 | 
						||
        return;
 | 
						||
    }
 | 
						||
    changeDb(db, dbName);
 | 
						||
 | 
						||
    const dbId = db.id;
 | 
						||
    let label;
 | 
						||
    let key;
 | 
						||
    // 存在sql模板名,则该模板名只允许一个tab
 | 
						||
    if (sqlName) {
 | 
						||
        label = `查询-${sqlName}`;
 | 
						||
        key = `查询:${dbId}:${dbName}.${sqlName}`;
 | 
						||
    } else {
 | 
						||
        let count = 1;
 | 
						||
        state.tabs.forEach((v) => {
 | 
						||
            if (v.type == TabType.Query && !v.params.sqlName) {
 | 
						||
                count++;
 | 
						||
            }
 | 
						||
        });
 | 
						||
        label = `新查询-${count}`;
 | 
						||
        key = `新查询${count}:${dbId}:${dbName}`;
 | 
						||
    }
 | 
						||
    state.activeName = key;
 | 
						||
    let tab = state.tabs.get(key);
 | 
						||
    if (tab) {
 | 
						||
        return;
 | 
						||
    }
 | 
						||
    tab = new TabInfo();
 | 
						||
    tab.key = key;
 | 
						||
    tab.label = label;
 | 
						||
    tab.treeNodeKey = db.nodeKey;
 | 
						||
    tab.dbId = dbId;
 | 
						||
    tab.db = dbName;
 | 
						||
    tab.type = TabType.Query;
 | 
						||
    tab.params = {
 | 
						||
        ...getNowDbInfo(),
 | 
						||
        sqlName: sqlName,
 | 
						||
        dbs: db.dbs,
 | 
						||
    };
 | 
						||
    state.tabs.set(key, tab);
 | 
						||
 | 
						||
    // 注册当前sql编辑框提示词
 | 
						||
    registerDbCompletionItemProvider('sql', tab.dbId, tab.db, tab.params.dbs);
 | 
						||
};
 | 
						||
 | 
						||
/**
 | 
						||
 * 添加数据操作tab
 | 
						||
 * @param inst
 | 
						||
 */
 | 
						||
const addTablesOpTab = async (db: any) => {
 | 
						||
    const dbName = db.db;
 | 
						||
    if (!db || !db.id) {
 | 
						||
        ElMessage.warning('请选择数据库实例及对应的schema');
 | 
						||
        return;
 | 
						||
    }
 | 
						||
    changeDb(db, dbName);
 | 
						||
 | 
						||
    const dbId = db.id;
 | 
						||
    let key = `表操作:${dbId}:${dbName}.tablesOp`;
 | 
						||
    state.activeName = key;
 | 
						||
 | 
						||
    let tab = state.tabs.get(key);
 | 
						||
    if (tab) {
 | 
						||
        return;
 | 
						||
    }
 | 
						||
    tab = new TabInfo();
 | 
						||
    tab.key = key;
 | 
						||
    tab.label = `表操作-${dbName}`;
 | 
						||
    tab.treeNodeKey = db.nodeKey;
 | 
						||
    tab.dbId = dbId;
 | 
						||
    tab.db = dbName;
 | 
						||
    tab.type = TabType.TablesOp;
 | 
						||
    tab.params = {
 | 
						||
        ...getNowDbInfo(),
 | 
						||
        id: db.id,
 | 
						||
        db: dbName,
 | 
						||
        type: db.type,
 | 
						||
    };
 | 
						||
    state.tabs.set(key, tab);
 | 
						||
};
 | 
						||
 | 
						||
const onRemoveTab = (targetName: string) => {
 | 
						||
    let activeName = state.activeName;
 | 
						||
    const tabNames = [...state.tabs.keys()];
 | 
						||
    for (let i = 0; i < tabNames.length; i++) {
 | 
						||
        const tabName = tabNames[i];
 | 
						||
        if (tabName !== targetName) {
 | 
						||
            continue;
 | 
						||
        }
 | 
						||
        const nextTab = tabNames[i + 1] || tabNames[i - 1];
 | 
						||
        if (nextTab) {
 | 
						||
            activeName = nextTab;
 | 
						||
        } else {
 | 
						||
            activeName = '';
 | 
						||
        }
 | 
						||
        state.tabs.delete(targetName);
 | 
						||
        state.activeName = activeName;
 | 
						||
        onTabChange();
 | 
						||
    }
 | 
						||
};
 | 
						||
 | 
						||
const onTabChange = () => {
 | 
						||
    if (!state.activeName) {
 | 
						||
        state.nowDbInst = {} as DbInst;
 | 
						||
        state.db = '';
 | 
						||
        return;
 | 
						||
    }
 | 
						||
 | 
						||
    const nowTab = state.tabs.get(state.activeName);
 | 
						||
    state.nowDbInst = DbInst.getInst(nowTab?.dbId);
 | 
						||
    state.db = nowTab?.db as string;
 | 
						||
 | 
						||
    if (nowTab?.type == TabType.Query) {
 | 
						||
        // 注册sql提示
 | 
						||
        registerDbCompletionItemProvider('sql', nowTab.dbId, nowTab.db, nowTab.params.dbs);
 | 
						||
    }
 | 
						||
};
 | 
						||
 | 
						||
const reloadSqls = (dbId: number, db: string) => {
 | 
						||
    tagTreeRef.value.reloadNode(getSqlMenuNodeKey(dbId, db));
 | 
						||
};
 | 
						||
 | 
						||
const deleteSqlScript = (ti: TabInfo) => {
 | 
						||
    reloadSqls(ti.dbId, ti.db);
 | 
						||
    onRemoveTab(ti.key);
 | 
						||
};
 | 
						||
 | 
						||
const getSqlMenuNodeKey = (dbId: number, db: string) => {
 | 
						||
    return `${dbId}.${db}.sql-menu`;
 | 
						||
};
 | 
						||
 | 
						||
const reloadTables = (nodeKey: string) => {
 | 
						||
    state.reloadStatus = true;
 | 
						||
    tagTreeRef.value.reloadNode(nodeKey);
 | 
						||
};
 | 
						||
 | 
						||
/**
 | 
						||
 * 获取当前操作的数据库信息
 | 
						||
 */
 | 
						||
const getNowDbInfo = () => {
 | 
						||
    const di = state.nowDbInst;
 | 
						||
    return {
 | 
						||
        tagPath: di.tagPath,
 | 
						||
        id: di.id,
 | 
						||
        name: di.name,
 | 
						||
        type: di.type,
 | 
						||
        host: di.host,
 | 
						||
        dbName: state.db,
 | 
						||
    };
 | 
						||
};
 | 
						||
</script>
 | 
						||
 | 
						||
<style lang="scss">
 | 
						||
.db-sql-exec {
 | 
						||
    .db-table-size {
 | 
						||
        color: #c4c9c4;
 | 
						||
        font-size: 9px;
 | 
						||
    }
 | 
						||
 | 
						||
    #data-exec {
 | 
						||
        min-height: calc(100vh - 155px);
 | 
						||
 | 
						||
        .el-tabs {
 | 
						||
            --el-tabs-header-height: 30px;
 | 
						||
        }
 | 
						||
 | 
						||
        .el-tabs__header {
 | 
						||
            margin: 0 0 5px;
 | 
						||
 | 
						||
            .el-tabs__item {
 | 
						||
                padding: 0 10px;
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        .el-tabs__nav-next,
 | 
						||
        .el-tabs__nav-prev {
 | 
						||
            line-height: 30px;
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    .update_field_active {
 | 
						||
        background-color: var(--el-color-success);
 | 
						||
    }
 | 
						||
}
 | 
						||
</style>
 |