mirror of
				https://gitee.com/dromara/mayfly-go
				synced 2025-11-04 08:20:25 +08:00 
			
		
		
		
	refactor: 标签树展示调整
This commit is contained in:
		@@ -45,7 +45,7 @@ const props = defineProps({
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    items: {
 | 
					    items: {
 | 
				
			||||||
        type: Array,
 | 
					        type: Array,
 | 
				
			||||||
        default: [],
 | 
					        default: () => [],
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@
 | 
				
			|||||||
                >
 | 
					                >
 | 
				
			||||||
                    <template #default="{ node, data }">
 | 
					                    <template #default="{ node, data }">
 | 
				
			||||||
                        <span>
 | 
					                        <span>
 | 
				
			||||||
                            <span v-if="data.type == TagTreeNode.TagPath">
 | 
					                            <span v-if="data.type.value == TagTreeNode.TagPath">
 | 
				
			||||||
                                <tag-info :tag-path="data.label" />
 | 
					                                <tag-info :tag-path="data.label" />
 | 
				
			||||||
                            </span>
 | 
					                            </span>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -54,7 +54,11 @@ const props = defineProps({
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    load: {
 | 
					    load: {
 | 
				
			||||||
        type: Function,
 | 
					        type: Function,
 | 
				
			||||||
        required: true,
 | 
					        required: false,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    loadTags: {
 | 
				
			||||||
 | 
					        type: Function,
 | 
				
			||||||
 | 
					        required: false,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    loadContextmenuItems: {
 | 
					    loadContextmenuItems: {
 | 
				
			||||||
        type: Function,
 | 
					        type: Function,
 | 
				
			||||||
@@ -117,7 +121,13 @@ const loadNode = async (node: any, resolve: any) => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    let nodes = [];
 | 
					    let nodes = [];
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
        nodes = await props.load(node);
 | 
					        if (node.level == 0 && props.loadTags) {
 | 
				
			||||||
 | 
					            nodes = await props.loadTags(node);
 | 
				
			||||||
 | 
					        } else if (props.load) {
 | 
				
			||||||
 | 
					            nodes = await props.load(node);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            nodes = await node.data.loadChildren();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    } catch (e: any) {
 | 
					    } catch (e: any) {
 | 
				
			||||||
        console.error(e);
 | 
					        console.error(e);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -126,18 +136,23 @@ const loadNode = async (node: any, resolve: any) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const treeNodeClick = (data: any) => {
 | 
					const treeNodeClick = (data: any) => {
 | 
				
			||||||
    emit('nodeClick', data);
 | 
					    emit('nodeClick', data);
 | 
				
			||||||
 | 
					    if (data.type.nodeClickFunc) {
 | 
				
			||||||
 | 
					        data.type.nodeClickFunc(data);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    // 关闭可能存在的右击菜单
 | 
					    // 关闭可能存在的右击菜单
 | 
				
			||||||
    contextmenuRef.value.closeContextmenu();
 | 
					    contextmenuRef.value.closeContextmenu();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 树节点右击事件
 | 
					// 树节点右击事件
 | 
				
			||||||
const nodeContextmenu = (event: any, data: any) => {
 | 
					const nodeContextmenu = (event: any, data: any) => {
 | 
				
			||||||
    if (!props.loadContextmenuItems) {
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    // 加载当前节点是否需要显示右击菜单
 | 
					    // 加载当前节点是否需要显示右击菜单
 | 
				
			||||||
    const items = props.loadContextmenuItems(data);
 | 
					    let items = data.type.contextMenuItems;
 | 
				
			||||||
    if (!items || items.length == 0) {
 | 
					    if (!items || items.length == 0) {
 | 
				
			||||||
 | 
					        if (props.loadContextmenuItems) {
 | 
				
			||||||
 | 
					            items = props.loadContextmenuItems(data);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!items) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    state.contextmenuItems = items;
 | 
					    state.contextmenuItems = items;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,18 +12,24 @@ export class TagTreeNode {
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 树节点类型
 | 
					     * 树节点类型
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    type: any;
 | 
					    type: NodeType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 是否为叶子节点
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    isLeaf: boolean = false;
 | 
					    isLeaf: boolean = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 额外需要传递的参数
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    params: any;
 | 
					    params: any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static TagPath = -1;
 | 
					    static TagPath = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(key: any, label: string, type?: any) {
 | 
					    constructor(key: any, label: string, type?: NodeType) {
 | 
				
			||||||
        this.key = key;
 | 
					        this.key = key;
 | 
				
			||||||
        this.label = label;
 | 
					        this.label = label;
 | 
				
			||||||
        this.type = type || TagTreeNode.TagPath;
 | 
					        this.type = type || new NodeType(TagTreeNode.TagPath);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    withIsLeaf(isLeaf: boolean) {
 | 
					    withIsLeaf(isLeaf: boolean) {
 | 
				
			||||||
@@ -35,4 +41,68 @@ export class TagTreeNode {
 | 
				
			|||||||
        this.params = params;
 | 
					        this.params = params;
 | 
				
			||||||
        return this;
 | 
					        return this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 加载子节点,使用节点类型的loadNodesFunc去加载子节点
 | 
				
			||||||
 | 
					     * @returns 子节点信息
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    async loadChildren() {
 | 
				
			||||||
 | 
					        if (this.isLeaf) {
 | 
				
			||||||
 | 
					            return [];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (this.type && this.type.loadNodesFunc) {
 | 
				
			||||||
 | 
					            return await this.type.loadNodesFunc(this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 节点类型,用于加载子节点及点击事件等
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export class NodeType {
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 节点类型值
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    value: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    contextMenuItems: [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    loadNodesFunc: (parentNode: TagTreeNode) => Promise<TagTreeNode[]>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    nodeClickFunc: (node: TagTreeNode) => void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(value: number) {
 | 
				
			||||||
 | 
					        this.value = value;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 赋值加载子节点回调函数
 | 
				
			||||||
 | 
					     * @param func 加载子节点回调函数
 | 
				
			||||||
 | 
					     * @returns this
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    withLoadNodesFunc(func: (parentNode: TagTreeNode) => Promise<TagTreeNode[]>) {
 | 
				
			||||||
 | 
					        this.loadNodesFunc = func;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 赋值节点点击事件回调函数
 | 
				
			||||||
 | 
					     * @param func 节点点击事件回调函数
 | 
				
			||||||
 | 
					     * @returns this
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    withNodeClickFunc(func: (node: TagTreeNode) => void) {
 | 
				
			||||||
 | 
					        this.nodeClickFunc = func;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 赋值右击菜单按钮选项
 | 
				
			||||||
 | 
					     * @param contextMenuItems 右击菜单按钮选项
 | 
				
			||||||
 | 
					     * @returns this
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    withContextMenuItems(contextMenuItems: []) {
 | 
				
			||||||
 | 
					        this.contextMenuItems = contextMenuItems;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,16 +31,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        <el-row type="flex">
 | 
					        <el-row type="flex">
 | 
				
			||||||
            <el-col :span="4">
 | 
					            <el-col :span="4">
 | 
				
			||||||
                <tag-tree
 | 
					                <tag-tree ref="tagTreeRef" :loadTags="loadTags" @current-contextmenu-click="onCurrentContextmenuClick" :height="state.tagTreeHeight">
 | 
				
			||||||
                    ref="tagTreeRef"
 | 
					 | 
				
			||||||
                    @node-click="nodeClick"
 | 
					 | 
				
			||||||
                    :load="loadNode"
 | 
					 | 
				
			||||||
                    :load-contextmenu-items="getContextmenuItems"
 | 
					 | 
				
			||||||
                    @current-contextmenu-click="onCurrentContextmenuClick"
 | 
					 | 
				
			||||||
                    :height="state.tagTreeHeight"
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                    <template #prefix="{ data }">
 | 
					                    <template #prefix="{ data }">
 | 
				
			||||||
                        <span v-if="data.type == NodeType.DbInst">
 | 
					                        <span v-if="data.type.value == SqlExecNodeType.DbInst">
 | 
				
			||||||
                            <el-popover :show-after="500" placement="right-start" title="数据库实例信息" trigger="hover" :width="210">
 | 
					                            <el-popover :show-after="500" placement="right-start" title="数据库实例信息" trigger="hover" :width="210">
 | 
				
			||||||
                                <template #reference>
 | 
					                                <template #reference>
 | 
				
			||||||
                                    <SvgIcon v-if="data.params.type === 'mysql'" name="iconfont icon-op-mysql" :size="18" />
 | 
					                                    <SvgIcon v-if="data.params.type === 'mysql'" name="iconfont icon-op-mysql" :size="18" />
 | 
				
			||||||
@@ -60,13 +53,13 @@
 | 
				
			|||||||
                            </el-popover>
 | 
					                            </el-popover>
 | 
				
			||||||
                        </span>
 | 
					                        </span>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        <SvgIcon v-if="data.type == NodeType.Db" name="Coin" color="#67c23a" />
 | 
					                        <SvgIcon v-if="data.type.value == SqlExecNodeType.Db" name="Coin" color="#67c23a" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        <SvgIcon name="Calendar" v-if="data.type == NodeType.TableMenu" color="#409eff" />
 | 
					                        <SvgIcon name="Calendar" v-if="data.type.value == SqlExecNodeType.TableMenu" color="#409eff" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        <el-tooltip
 | 
					                        <el-tooltip
 | 
				
			||||||
                            :show-after="500"
 | 
					                            :show-after="500"
 | 
				
			||||||
                            v-if="data.type == NodeType.Table"
 | 
					                            v-if="data.type.value == SqlExecNodeType.Table"
 | 
				
			||||||
                            effect="customized"
 | 
					                            effect="customized"
 | 
				
			||||||
                            :content="data.params.tableComment"
 | 
					                            :content="data.params.tableComment"
 | 
				
			||||||
                            placement="top-end"
 | 
					                            placement="top-end"
 | 
				
			||||||
@@ -74,11 +67,14 @@
 | 
				
			|||||||
                            <SvgIcon name="Calendar" color="#409eff" />
 | 
					                            <SvgIcon name="Calendar" color="#409eff" />
 | 
				
			||||||
                        </el-tooltip>
 | 
					                        </el-tooltip>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        <SvgIcon name="Files" v-if="data.type == NodeType.SqlMenu || data.type == NodeType.Sql" color="#f56c6c" />
 | 
					                        <SvgIcon name="Files" v-if="data.type.value == SqlExecNodeType.SqlMenu || data.type.value == SqlExecNodeType.Sql" color="#f56c6c" />
 | 
				
			||||||
                    </template>
 | 
					                    </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <template #suffix="{ data }">
 | 
					                    <template #suffix="{ data }">
 | 
				
			||||||
                        <span class="db-table-size" v-if="data.type == NodeType.Table">{{ ` ${data.params.size}` }}</span>
 | 
					                        <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>
 | 
					                    </template>
 | 
				
			||||||
                </tag-tree>
 | 
					                </tag-tree>
 | 
				
			||||||
            </el-col>
 | 
					            </el-col>
 | 
				
			||||||
@@ -119,7 +115,7 @@ import { defineAsyncComponent, onMounted, reactive, ref, toRefs, onBeforeUnmount
 | 
				
			|||||||
import { ElMessage } from 'element-plus';
 | 
					import { ElMessage } from 'element-plus';
 | 
				
			||||||
import { formatByteSize } from '@/common/utils/format';
 | 
					import { formatByteSize } from '@/common/utils/format';
 | 
				
			||||||
import { DbInst, TabInfo, TabType, registerDbCompletionItemProvider } from './db';
 | 
					import { DbInst, TabInfo, TabType, registerDbCompletionItemProvider } from './db';
 | 
				
			||||||
import { TagTreeNode } from '../component/tag';
 | 
					import { TagTreeNode, NodeType } from '../component/tag';
 | 
				
			||||||
import TagTree from '../component/TagTree.vue';
 | 
					import TagTree from '../component/TagTree.vue';
 | 
				
			||||||
import { dbApi } from './api';
 | 
					import { dbApi } from './api';
 | 
				
			||||||
import { dispposeCompletionItemProvider } from '../../../components/monaco/completionItemProvider';
 | 
					import { dispposeCompletionItemProvider } from '../../../components/monaco/completionItemProvider';
 | 
				
			||||||
@@ -129,7 +125,7 @@ const TableData = defineAsyncComponent(() => import('./component/tab/TableData.v
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * 树节点类型
 | 
					 * 树节点类型
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class NodeType {
 | 
					class SqlExecNodeType {
 | 
				
			||||||
    static DbInst = 1;
 | 
					    static DbInst = 1;
 | 
				
			||||||
    static Db = 2;
 | 
					    static Db = 2;
 | 
				
			||||||
    static TableMenu = 3;
 | 
					    static TableMenu = 3;
 | 
				
			||||||
@@ -137,10 +133,115 @@ class NodeType {
 | 
				
			|||||||
    static Table = 5;
 | 
					    static Table = 5;
 | 
				
			||||||
    static Sql = 6;
 | 
					    static Sql = 6;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ContextmenuClickId {
 | 
					class ContextmenuClickId {
 | 
				
			||||||
    static ReloadTable = 0;
 | 
					    static ReloadTable = 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// node节点点击时,触发改变db事件
 | 
				
			||||||
 | 
					const changeDb = (nodeData: TagTreeNode) => {
 | 
				
			||||||
 | 
					    const params = nodeData.params;
 | 
				
			||||||
 | 
					    changeSchema({ id: params.id, 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,
 | 
				
			||||||
 | 
					                dbs: dbs,
 | 
				
			||||||
 | 
					                db: x,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    .withNodeClickFunc(changeDb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 数据库节点
 | 
				
			||||||
 | 
					const NodeTypeDb = new NodeType(SqlExecNodeType.Db)
 | 
				
			||||||
 | 
					    .withLoadNodesFunc(async (parentNode: TagTreeNode) => {
 | 
				
			||||||
 | 
					        const params = parentNode.params;
 | 
				
			||||||
 | 
					        return [
 | 
				
			||||||
 | 
					            new TagTreeNode(`${params.id}.${params.db}.table-menu`, '表', NodeTypeTableMenu).withParams(params),
 | 
				
			||||||
 | 
					            new TagTreeNode(getSqlMenuNodeKey(params.id, params.db), 'SQL', NodeTypeSqlMenu).withParams(params),
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    .withNodeClickFunc(changeDb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 数据库表菜单节点
 | 
				
			||||||
 | 
					const NodeTypeTableMenu = new NodeType(SqlExecNodeType.TableMenu)
 | 
				
			||||||
 | 
					    .withContextMenuItems([{ contextMenuClickId: ContextmenuClickId.ReloadTable, txt: '刷新', icon: 'RefreshRight' }] as any)
 | 
				
			||||||
 | 
					    .withLoadNodesFunc(async (parentNode: TagTreeNode) => {
 | 
				
			||||||
 | 
					        const params = parentNode.params;
 | 
				
			||||||
 | 
					        const { 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),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        // 设置父节点参数的表大小
 | 
				
			||||||
 | 
					        parentNode.params.dbTableSize = formatByteSize(dbTableSize);
 | 
				
			||||||
 | 
					        return tablesNode;
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    .withNodeClickFunc(changeDb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 数据库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,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    .withNodeClickFunc(changeDb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 表节点类型
 | 
				
			||||||
 | 
					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 tagTreeRef: any = ref(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const tabs: Map<string, TabInfo> = new Map();
 | 
					const tabs: Map<string, TabInfo> = new Map();
 | 
				
			||||||
@@ -200,97 +301,16 @@ const getInsts = async () => {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 加载树节点
 | 
					 * 加载标签树节点
 | 
				
			||||||
 * @param {Object} node
 | 
					 | 
				
			||||||
 * @param {Object} resolve
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
const loadNode = async (node: any) => {
 | 
					const loadTags = async () => {
 | 
				
			||||||
    // 一级为tagPath
 | 
					    await getInsts();
 | 
				
			||||||
    if (node.level === 0) {
 | 
					    const tagPaths = instMap.keys();
 | 
				
			||||||
        await getInsts();
 | 
					    const tagNodes = [];
 | 
				
			||||||
        const tagPaths = instMap.keys();
 | 
					    for (let tagPath of tagPaths) {
 | 
				
			||||||
        const tagNodes = [];
 | 
					        tagNodes.push(new TagTreeNode(tagPath, tagPath, NodeTypeTagPath));
 | 
				
			||||||
        for (let tagPath of tagPaths) {
 | 
					 | 
				
			||||||
            tagNodes.push(new TagTreeNode(tagPath, tagPath));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return tagNodes;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return tagNodes;
 | 
				
			||||||
    const data = node.data;
 | 
					 | 
				
			||||||
    const nodeType = data.type;
 | 
					 | 
				
			||||||
    const params = data.params;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 点击tagPath -> 加载数据库实例信息列表
 | 
					 | 
				
			||||||
    if (nodeType === TagTreeNode.TagPath) {
 | 
					 | 
				
			||||||
        const dbInfos = instMap.get(data.key);
 | 
					 | 
				
			||||||
        return dbInfos?.map((x: any) => {
 | 
					 | 
				
			||||||
            return new TagTreeNode(`${data.key}.${x.id}`, x.name, NodeType.DbInst).withParams(x);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 点击数据库实例 -> 加载库列表
 | 
					 | 
				
			||||||
    if (nodeType === NodeType.DbInst) {
 | 
					 | 
				
			||||||
        const dbs = params.database.split(' ')?.sort();
 | 
					 | 
				
			||||||
        return dbs.map((x: any) => {
 | 
					 | 
				
			||||||
            return new TagTreeNode(`${data.key}.${x}`, x, NodeType.Db).withParams({
 | 
					 | 
				
			||||||
                tagPath: params.tagPath,
 | 
					 | 
				
			||||||
                id: params.id,
 | 
					 | 
				
			||||||
                name: params.name,
 | 
					 | 
				
			||||||
                type: params.type,
 | 
					 | 
				
			||||||
                dbs: dbs,
 | 
					 | 
				
			||||||
                db: x,
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 点击数据库 -> 加载 表&Sql 菜单
 | 
					 | 
				
			||||||
    if (nodeType === NodeType.Db) {
 | 
					 | 
				
			||||||
        return [
 | 
					 | 
				
			||||||
            new TagTreeNode(`${params.id}.${params.db}.table-menu`, '表', NodeType.TableMenu).withParams(params),
 | 
					 | 
				
			||||||
            new TagTreeNode(getSqlMenuNodeKey(params.id, params.db), 'SQL', NodeType.SqlMenu).withParams(params),
 | 
					 | 
				
			||||||
        ];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 点击表菜单 -> 加载表列表
 | 
					 | 
				
			||||||
    if (nodeType === NodeType.TableMenu) {
 | 
					 | 
				
			||||||
        return await getTables(params);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (nodeType === NodeType.SqlMenu) {
 | 
					 | 
				
			||||||
        return await loadSqls(params.id, params.db, params.dbs);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return [];
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const nodeClick = async (data: any) => {
 | 
					 | 
				
			||||||
    const params = data.params;
 | 
					 | 
				
			||||||
    const nodeKey = data.key;
 | 
					 | 
				
			||||||
    const dataType = data.type;
 | 
					 | 
				
			||||||
    // 点击数据库,修改当前数据库信息
 | 
					 | 
				
			||||||
    if (dataType === NodeType.Db || dataType === NodeType.SqlMenu || dataType === NodeType.TableMenu || dataType === NodeType.DbInst) {
 | 
					 | 
				
			||||||
        changeSchema({ id: params.id, name: params.name, type: params.type, tagPath: params.tagPath, databases: params.database }, params.db);
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 点击表加载表数据tab
 | 
					 | 
				
			||||||
    if (dataType === NodeType.Table) {
 | 
					 | 
				
			||||||
        await loadTableData({ id: params.id, nodeKey: nodeKey }, params.db, params.tableName);
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 点击表加载表数据tab
 | 
					 | 
				
			||||||
    if (dataType === NodeType.Sql) {
 | 
					 | 
				
			||||||
        await addQueryTab({ id: params.id, nodeKey: nodeKey, dbs: params.dbs }, params.db, params.sqlName);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const getContextmenuItems = (data: any) => {
 | 
					 | 
				
			||||||
    const dataType = data.type;
 | 
					 | 
				
			||||||
    if (dataType === NodeType.TableMenu) {
 | 
					 | 
				
			||||||
        return [{ contextMenuClickId: ContextmenuClickId.ReloadTable, txt: '刷新', icon: 'RefreshRight' }];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return [];
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 当前右击菜单点击事件
 | 
					// 当前右击菜单点击事件
 | 
				
			||||||
@@ -301,39 +321,6 @@ const onCurrentContextmenuClick = (clickData: any) => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getTables = async (params: any) => {
 | 
					 | 
				
			||||||
    const { id, db } = params;
 | 
					 | 
				
			||||||
    let tables = await DbInst.getInst(id).loadTables(db, state.reloadStatus);
 | 
					 | 
				
			||||||
    state.reloadStatus = false;
 | 
					 | 
				
			||||||
    return tables.map((x: any) => {
 | 
					 | 
				
			||||||
        return new TagTreeNode(`${id}.${db}.${x.tableName}`, x.tableName, NodeType.Table).withIsLeaf(true).withParams({
 | 
					 | 
				
			||||||
            id,
 | 
					 | 
				
			||||||
            db,
 | 
					 | 
				
			||||||
            tableName: x.tableName,
 | 
					 | 
				
			||||||
            tableComment: x.tableComment,
 | 
					 | 
				
			||||||
            size: formatByteSize(x.dataLength + x.indexLength, 1),
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * 加载用户保存的sql脚本
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param inst
 | 
					 | 
				
			||||||
 * @param schema
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
const loadSqls = async (id: any, db: string, dbs: any) => {
 | 
					 | 
				
			||||||
    const sqls = await dbApi.getSqlNames.request({ id: id, db: db });
 | 
					 | 
				
			||||||
    return sqls.map((x: any) => {
 | 
					 | 
				
			||||||
        return new TagTreeNode(`${id}.${db}.${x.name}`, x.name, NodeType.Sql).withIsLeaf(true).withParams({
 | 
					 | 
				
			||||||
            id,
 | 
					 | 
				
			||||||
            db,
 | 
					 | 
				
			||||||
            dbs,
 | 
					 | 
				
			||||||
            sqlName: x.name,
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// 选择数据库
 | 
					// 选择数据库
 | 
				
			||||||
const changeSchema = (inst: any, schema: string) => {
 | 
					const changeSchema = (inst: any, schema: string) => {
 | 
				
			||||||
    state.nowDbInst = DbInst.getOrNewInst(inst);
 | 
					    state.nowDbInst = DbInst.getOrNewInst(inst);
 | 
				
			||||||
@@ -372,6 +359,7 @@ const addQueryTab = async (inst: any, db: string, sqlName: string = '') => {
 | 
				
			|||||||
        ElMessage.warning('请选择数据库实例及对应的schema');
 | 
					        ElMessage.warning('请选择数据库实例及对应的schema');
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    changeSchema(inst, db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const dbId = inst.id;
 | 
					    const dbId = inst.id;
 | 
				
			||||||
    let label;
 | 
					    let label;
 | 
				
			||||||
@@ -470,31 +458,9 @@ const reloadTables = (nodeKey: string) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<style lang="scss">
 | 
					<style lang="scss">
 | 
				
			||||||
.db-sql-exec {
 | 
					.db-sql-exec {
 | 
				
			||||||
    .sql-file-exec {
 | 
					 | 
				
			||||||
        display: inline-flex;
 | 
					 | 
				
			||||||
        flex-direction: row;
 | 
					 | 
				
			||||||
        align-items: center;
 | 
					 | 
				
			||||||
        justify-content: center;
 | 
					 | 
				
			||||||
        vertical-align: middle;
 | 
					 | 
				
			||||||
        position: relative;
 | 
					 | 
				
			||||||
        text-decoration: none;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .db-table-size {
 | 
					    .db-table-size {
 | 
				
			||||||
        color: #c4c9c4;
 | 
					        color: #c4c9c4;
 | 
				
			||||||
        font-size: 10px;
 | 
					        font-size: 9px;
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .sqlEditor {
 | 
					 | 
				
			||||||
        font-size: 8pt;
 | 
					 | 
				
			||||||
        font-weight: 600;
 | 
					 | 
				
			||||||
        border: 1px solid #ccc;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .editor-move-resize {
 | 
					 | 
				
			||||||
        cursor: n-resize;
 | 
					 | 
				
			||||||
        height: 3px;
 | 
					 | 
				
			||||||
        text-align: center;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #data-exec {
 | 
					    #data-exec {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,9 +2,9 @@
 | 
				
			|||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
        <el-row>
 | 
					        <el-row>
 | 
				
			||||||
            <el-col :span="4">
 | 
					            <el-col :span="4">
 | 
				
			||||||
                <tag-tree @node-click="nodeClick" :load="loadNode">
 | 
					                <tag-tree :loadTags="loadTags">
 | 
				
			||||||
                    <template #prefix="{ data }">
 | 
					                    <template #prefix="{ data }">
 | 
				
			||||||
                        <span v-if="data.type == NodeType.Mongo">
 | 
					                        <span v-if="data.type.value == MongoNodeType.Mongo">
 | 
				
			||||||
                            <el-popover :show-after="500" placement="right-start" title="mongo实例信息" trigger="hover" :width="210">
 | 
					                            <el-popover :show-after="500" placement="right-start" title="mongo实例信息" trigger="hover" :width="210">
 | 
				
			||||||
                                <template #reference>
 | 
					                                <template #reference>
 | 
				
			||||||
                                    <SvgIcon name="iconfont icon-op-mongo" :size="18" />
 | 
					                                    <SvgIcon name="iconfont icon-op-mongo" :size="18" />
 | 
				
			||||||
@@ -18,14 +18,18 @@
 | 
				
			|||||||
                            </el-popover>
 | 
					                            </el-popover>
 | 
				
			||||||
                        </span>
 | 
					                        </span>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        <SvgIcon v-if="data.type == NodeType.Dbs" name="Coin" color="#67c23a" />
 | 
					                        <SvgIcon v-if="data.type.value == MongoNodeType.Dbs" name="Coin" color="#67c23a" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        <SvgIcon v-if="data.type == NodeType.Coll || data.type == NodeType.CollMenu" name="Document" class="color-primary" />
 | 
					                        <SvgIcon
 | 
				
			||||||
 | 
					                            v-if="data.type.value == MongoNodeType.Coll || data.type.value == MongoNodeType.CollMenu"
 | 
				
			||||||
 | 
					                            name="Document"
 | 
				
			||||||
 | 
					                            class="color-primary"
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
                    </template>
 | 
					                    </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <template #label="{ data }">
 | 
					                    <template #label="{ data }">
 | 
				
			||||||
                        <span v-if="data.type == NodeType.Dbs">
 | 
					                        <span v-if="data.type.value == MongoNodeType.Dbs">
 | 
				
			||||||
                            {{ data.params.dbName }}
 | 
					                            {{ data.params.database }}
 | 
				
			||||||
                            <span style="color: #8492a6; font-size: 13px"> [{{ formatByteSize(data.params.size) }}] </span>
 | 
					                            <span style="color: #8492a6; font-size: 13px"> [{{ formatByteSize(data.params.size) }}] </span>
 | 
				
			||||||
                        </span>
 | 
					                        </span>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -161,7 +165,7 @@ import { computed, defineAsyncComponent, reactive, ref, toRefs } from 'vue';
 | 
				
			|||||||
import { ElMessage } from 'element-plus';
 | 
					import { ElMessage } from 'element-plus';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { isTrue, notBlank } from '@/common/assert';
 | 
					import { isTrue, notBlank } from '@/common/assert';
 | 
				
			||||||
import { TagTreeNode } from '../component/tag';
 | 
					import { TagTreeNode, NodeType } from '../component/tag';
 | 
				
			||||||
import TagTree from '../component/TagTree.vue';
 | 
					import TagTree from '../component/TagTree.vue';
 | 
				
			||||||
import { formatByteSize } from '@/common/utils/format';
 | 
					import { formatByteSize } from '@/common/utils/format';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -175,13 +179,63 @@ const perms = {
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * 树节点类型
 | 
					 * 树节点类型
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class NodeType {
 | 
					class MongoNodeType {
 | 
				
			||||||
    static Mongo = 1;
 | 
					    static Mongo = 1;
 | 
				
			||||||
    static Dbs = 2;
 | 
					    static Dbs = 2;
 | 
				
			||||||
    static CollMenu = 3;
 | 
					    static CollMenu = 3;
 | 
				
			||||||
    static Coll = 4;
 | 
					    static Coll = 4;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// tagpath 节点类型
 | 
				
			||||||
 | 
					const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
 | 
				
			||||||
 | 
					    // 点击标签 -> 显示mongo信息列表
 | 
				
			||||||
 | 
					    const mongoInfos = instMap.get(parentNode.key);
 | 
				
			||||||
 | 
					    if (!mongoInfos) {
 | 
				
			||||||
 | 
					        return [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return mongoInfos?.map((x: any) => {
 | 
				
			||||||
 | 
					        return new TagTreeNode(`${parentNode.key}.${x.id}`, x.name, NodeTypeMongo).withParams(x);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const NodeTypeMongo = new NodeType(MongoNodeType.Mongo).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
 | 
				
			||||||
 | 
					    const inst = parentNode.params;
 | 
				
			||||||
 | 
					    // 点击mongo -> 加载mongo数据库列表
 | 
				
			||||||
 | 
					    const res = await mongoApi.databases.request({ id: inst.id });
 | 
				
			||||||
 | 
					    return res.Databases.map((x: any) => {
 | 
				
			||||||
 | 
					        const database = x.Name;
 | 
				
			||||||
 | 
					        return new TagTreeNode(`${inst.id}.${database}`, database, NodeTypeDbs).withParams({
 | 
				
			||||||
 | 
					            id: inst.id,
 | 
				
			||||||
 | 
					            database,
 | 
				
			||||||
 | 
					            size: x.SizeOnDisk,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const NodeTypeDbs = new NodeType(MongoNodeType.Dbs).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
 | 
				
			||||||
 | 
					    const params = parentNode.params;
 | 
				
			||||||
 | 
					    // 点击数据库列表 -> 加载数据库下拥有的菜单列表
 | 
				
			||||||
 | 
					    return [new TagTreeNode(`${params.id}.${params.database}.mongo-coll`, '集合', NodeTypeCollMenu).withParams(params)];
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const NodeTypeCollMenu = new NodeType(MongoNodeType.CollMenu).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
 | 
				
			||||||
 | 
					    const { id, database } = parentNode.params;
 | 
				
			||||||
 | 
					    // 点击数据库集合节点 -> 加载集合列表
 | 
				
			||||||
 | 
					    const colls = await mongoApi.collections.request({ id, database });
 | 
				
			||||||
 | 
					    return colls.map((x: any) => {
 | 
				
			||||||
 | 
					        return new TagTreeNode(`${id}.${database}.${x}`, x, NodeTypeColl).withIsLeaf(true).withParams({
 | 
				
			||||||
 | 
					            id,
 | 
				
			||||||
 | 
					            database,
 | 
				
			||||||
 | 
					            collection: x,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const NodeTypeColl = new NodeType(MongoNodeType.Coll).withNodeClickFunc((nodeData: TagTreeNode) => {
 | 
				
			||||||
 | 
					    const { id, database, collection } = nodeData.params;
 | 
				
			||||||
 | 
					    changeCollection(id, database, collection);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const findParamInputRef: any = ref(null);
 | 
					const findParamInputRef: any = ref(null);
 | 
				
			||||||
const state = reactive({
 | 
					const state = reactive({
 | 
				
			||||||
    tags: [],
 | 
					    tags: [],
 | 
				
			||||||
@@ -237,89 +291,16 @@ const getInsts = async () => {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 加载文件树节点
 | 
					 * 加载标签树树节点
 | 
				
			||||||
 * @param {Object} node
 | 
					 | 
				
			||||||
 * @param {Object} resolve
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
const loadNode = async (node: any) => {
 | 
					const loadTags = async () => {
 | 
				
			||||||
    // 一级为tagPath
 | 
					    await getInsts();
 | 
				
			||||||
    if (node.level === 0) {
 | 
					    const tagPaths = instMap.keys();
 | 
				
			||||||
        await getInsts();
 | 
					    const tagNodes = [];
 | 
				
			||||||
        const tagPaths = instMap.keys();
 | 
					    for (let tagPath of tagPaths) {
 | 
				
			||||||
        const tagNodes = [];
 | 
					        tagNodes.push(new TagTreeNode(tagPath, tagPath, NodeTypeTagPath));
 | 
				
			||||||
        for (let tagPath of tagPaths) {
 | 
					 | 
				
			||||||
            tagNodes.push(new TagTreeNode(tagPath, tagPath));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return tagNodes;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const data = node.data;
 | 
					 | 
				
			||||||
    const params = data.params;
 | 
					 | 
				
			||||||
    const nodeType = data.type;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 点击标签 -> 显示mongo信息列表
 | 
					 | 
				
			||||||
    if (nodeType === TagTreeNode.TagPath) {
 | 
					 | 
				
			||||||
        const mongoInfos = instMap.get(data.key);
 | 
					 | 
				
			||||||
        return mongoInfos?.map((x: any) => {
 | 
					 | 
				
			||||||
            return new TagTreeNode(`${data.key}.${x.id}`, x.name, NodeType.Mongo).withParams(x);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 点击mongo -> 加载mongo数据库列表
 | 
					 | 
				
			||||||
    if (nodeType === NodeType.Mongo) {
 | 
					 | 
				
			||||||
        return await getDatabases(params);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 点击数据库列表 -> 加载数据库下拥有的菜单列表
 | 
					 | 
				
			||||||
    if (nodeType === NodeType.Dbs) {
 | 
					 | 
				
			||||||
        return [new TagTreeNode(`${params.id}.${params.dbName}.mongo-coll`, '集合', NodeType.CollMenu).withParams(params)];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 点击数据库集合节点 -> 加载集合列表
 | 
					 | 
				
			||||||
    if (nodeType === NodeType.CollMenu) {
 | 
					 | 
				
			||||||
        return await getCollections(params.id, params.dbName);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return [];
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * 获取实例的所有库信息
 | 
					 | 
				
			||||||
 * @param inst 实例信息
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
const getDatabases = async (inst: any) => {
 | 
					 | 
				
			||||||
    const res = await mongoApi.databases.request({ id: inst.id });
 | 
					 | 
				
			||||||
    return res.Databases.map((x: any) => {
 | 
					 | 
				
			||||||
        const dbName = x.Name;
 | 
					 | 
				
			||||||
        return new TagTreeNode(`${inst.id}.${dbName}`, dbName, NodeType.Dbs).withParams({
 | 
					 | 
				
			||||||
            id: inst.id,
 | 
					 | 
				
			||||||
            dbName,
 | 
					 | 
				
			||||||
            size: x.SizeOnDisk,
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * 获取集合列表信息
 | 
					 | 
				
			||||||
 * @param inst
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
const getCollections = async (id: any, database: string) => {
 | 
					 | 
				
			||||||
    const colls = await mongoApi.collections.request({ id, database });
 | 
					 | 
				
			||||||
    return colls.map((x: any) => {
 | 
					 | 
				
			||||||
        return new TagTreeNode(`${id}.${database}.${x}`, x, NodeType.Coll).withIsLeaf(true).withParams({
 | 
					 | 
				
			||||||
            id,
 | 
					 | 
				
			||||||
            database,
 | 
					 | 
				
			||||||
            collection: x,
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const nodeClick = async (data: any) => {
 | 
					 | 
				
			||||||
    // 点击集合
 | 
					 | 
				
			||||||
    if (data.type === NodeType.Coll) {
 | 
					 | 
				
			||||||
        const { id, database, collection } = data.params;
 | 
					 | 
				
			||||||
        await changeCollection(id, database, collection);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return tagNodes;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const changeCollection = async (id: any, schema: string, collection: string) => {
 | 
					const changeCollection = async (id: any, schema: string, collection: string) => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,9 +4,9 @@
 | 
				
			|||||||
            <el-col :span="4">
 | 
					            <el-col :span="4">
 | 
				
			||||||
                <el-row type="flex" justify="space-between">
 | 
					                <el-row type="flex" justify="space-between">
 | 
				
			||||||
                    <el-col :span="24" class="flex-auto">
 | 
					                    <el-col :span="24" class="flex-auto">
 | 
				
			||||||
                        <tag-tree @node-click="nodeClick" :load="loadNode">
 | 
					                        <tag-tree :loadTags="loadTags">
 | 
				
			||||||
                            <template #prefix="{ data }">
 | 
					                            <template #prefix="{ data }">
 | 
				
			||||||
                                <span v-if="data.type == NodeType.Redis">
 | 
					                                <span v-if="data.type.value == RedisNodeType.Redis">
 | 
				
			||||||
                                    <el-popover :show-after="500" placement="right-start" title="redis实例信息" trigger="hover" :width="210">
 | 
					                                    <el-popover :show-after="500" placement="right-start" title="redis实例信息" trigger="hover" :width="210">
 | 
				
			||||||
                                        <template #reference>
 | 
					                                        <template #reference>
 | 
				
			||||||
                                            <SvgIcon name="iconfont icon-op-redis" :size="18" />
 | 
					                                            <SvgIcon name="iconfont icon-op-redis" :size="18" />
 | 
				
			||||||
@@ -22,7 +22,7 @@
 | 
				
			|||||||
                                    </el-popover>
 | 
					                                    </el-popover>
 | 
				
			||||||
                                </span>
 | 
					                                </span>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                <SvgIcon v-if="data.type == NodeType.Db" name="Coin" color="#67c23a" />
 | 
					                                <SvgIcon v-if="data.type.value == RedisNodeType.Db" name="Coin" color="#67c23a" />
 | 
				
			||||||
                            </template>
 | 
					                            </template>
 | 
				
			||||||
                        </tag-tree>
 | 
					                        </tag-tree>
 | 
				
			||||||
                    </el-col>
 | 
					                    </el-col>
 | 
				
			||||||
@@ -184,7 +184,7 @@ import { redisApi } from './api';
 | 
				
			|||||||
import { ref, defineAsyncComponent, toRefs, reactive, onMounted, nextTick } from 'vue';
 | 
					import { ref, defineAsyncComponent, toRefs, reactive, onMounted, nextTick } from 'vue';
 | 
				
			||||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
					import { ElMessage, ElMessageBox } from 'element-plus';
 | 
				
			||||||
import { isTrue, notBlank, notNull } from '@/common/assert';
 | 
					import { isTrue, notBlank, notNull } from '@/common/assert';
 | 
				
			||||||
import { TagTreeNode } from '../component/tag';
 | 
					import { TagTreeNode, NodeType } from '../component/tag';
 | 
				
			||||||
import TagTree from '../component/TagTree.vue';
 | 
					import TagTree from '../component/TagTree.vue';
 | 
				
			||||||
import { keysToTree, sortByTreeNodes, keysToList } from './utils';
 | 
					import { keysToTree, sortByTreeNodes, keysToList } from './utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -193,11 +193,61 @@ const KeyDetail = defineAsyncComponent(() => import('./KeyDetail.vue'));
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * 树节点类型
 | 
					 * 树节点类型
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class NodeType {
 | 
					class RedisNodeType {
 | 
				
			||||||
    static Redis = 1;
 | 
					    static Redis = 1;
 | 
				
			||||||
    static Db = 2;
 | 
					    static Db = 2;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// tagpath 节点类型
 | 
				
			||||||
 | 
					const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
 | 
				
			||||||
 | 
					    const redisInfos = instMap.get(parentNode.key);
 | 
				
			||||||
 | 
					    if (!redisInfos) {
 | 
				
			||||||
 | 
					        return [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return redisInfos.map((x: any) => {
 | 
				
			||||||
 | 
					        return new TagTreeNode(`${parentNode.key}.${x.id}`, x.name, NodeTypeRedis).withParams(x);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// redis实例节点类型
 | 
				
			||||||
 | 
					const NodeTypeRedis = new NodeType(RedisNodeType.Redis).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
 | 
				
			||||||
 | 
					    const redisInfo = parentNode.params;
 | 
				
			||||||
 | 
					    let dbs: TagTreeNode[] = redisInfo.db.split(',').map((x: string) => {
 | 
				
			||||||
 | 
					        return new TagTreeNode(x, `db${x}`, NodeTypeDb).withIsLeaf(true).withParams({
 | 
				
			||||||
 | 
					            id: redisInfo.id,
 | 
				
			||||||
 | 
					            db: x,
 | 
				
			||||||
 | 
					            name: `db${x}`,
 | 
				
			||||||
 | 
					            keys: 0,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (redisInfo.mode == 'cluster') {
 | 
				
			||||||
 | 
					        return dbs;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const res = await redisApi.redisInfo.request({ id: redisInfo.id, host: redisInfo.host, section: 'Keyspace' });
 | 
				
			||||||
 | 
					    for (let db in res.Keyspace) {
 | 
				
			||||||
 | 
					        for (let d of dbs) {
 | 
				
			||||||
 | 
					            if (db == d.params.name) {
 | 
				
			||||||
 | 
					                d.params.keys = res.Keyspace[db]?.split(',')[0]?.split('=')[1] || 0;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // 替换label
 | 
				
			||||||
 | 
					    dbs.forEach((e: any) => {
 | 
				
			||||||
 | 
					        e.label = `${e.params.name} [${e.params.keys}]`;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    return dbs;
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 库节点类型
 | 
				
			||||||
 | 
					const NodeTypeDb = new NodeType(RedisNodeType.Db).withNodeClickFunc((nodeData: TagTreeNode) => {
 | 
				
			||||||
 | 
					    resetScanParam();
 | 
				
			||||||
 | 
					    state.scanParam.id = nodeData.params.id;
 | 
				
			||||||
 | 
					    state.scanParam.db = nodeData.params.db;
 | 
				
			||||||
 | 
					    scan();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const treeProps = {
 | 
					const treeProps = {
 | 
				
			||||||
    label: 'name',
 | 
					    label: 'name',
 | 
				
			||||||
    children: 'children',
 | 
					    children: 'children',
 | 
				
			||||||
@@ -270,80 +320,16 @@ const getInsts = async () => {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 加载文件树节点
 | 
					 * 加载标签树节点
 | 
				
			||||||
 * @param {Object} node
 | 
					 | 
				
			||||||
 * @param {Object} resolve
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
const loadNode = async (node: any) => {
 | 
					const loadTags = async () => {
 | 
				
			||||||
    // 一级为tagPath
 | 
					    await getInsts();
 | 
				
			||||||
    if (node.level === 0) {
 | 
					    const tagPaths = instMap.keys();
 | 
				
			||||||
        await getInsts();
 | 
					    const tagNodes = [];
 | 
				
			||||||
        const tagPaths = instMap.keys();
 | 
					    for (let tagPath of tagPaths) {
 | 
				
			||||||
        const tagNodes = [];
 | 
					        tagNodes.push(new TagTreeNode(tagPath, tagPath, NodeTypeTagPath));
 | 
				
			||||||
        for (let tagPath of tagPaths) {
 | 
					 | 
				
			||||||
            tagNodes.push(new TagTreeNode(tagPath, tagPath));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return tagNodes;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return tagNodes;
 | 
				
			||||||
    const data = node.data;
 | 
					 | 
				
			||||||
    // 点击tagPath -> 加载数据库信息列表
 | 
					 | 
				
			||||||
    if (data.type === TagTreeNode.TagPath) {
 | 
					 | 
				
			||||||
        const redisInfos = instMap.get(data.key);
 | 
					 | 
				
			||||||
        return redisInfos?.map((x: any) => {
 | 
					 | 
				
			||||||
            return new TagTreeNode(`${data.key}.${x.id}`, x.name, NodeType.Redis).withParams(x);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 点击redis实例 -> 加载库列表
 | 
					 | 
				
			||||||
    if (data.type === NodeType.Redis) {
 | 
					 | 
				
			||||||
        return await getDbs(data.params);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return [];
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const nodeClick = (data: any) => {
 | 
					 | 
				
			||||||
    // 点击库事件
 | 
					 | 
				
			||||||
    if (data.type === NodeType.Db) {
 | 
					 | 
				
			||||||
        resetScanParam();
 | 
					 | 
				
			||||||
        state.scanParam.id = data.params.id;
 | 
					 | 
				
			||||||
        state.scanParam.db = data.params.db;
 | 
					 | 
				
			||||||
        scan();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * 获取所有库信息
 | 
					 | 
				
			||||||
 * @param redisInfo redis信息
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
const getDbs = async (redisInfo: any) => {
 | 
					 | 
				
			||||||
    let dbs: TagTreeNode[] = redisInfo.db.split(',').map((x: string) => {
 | 
					 | 
				
			||||||
        return new TagTreeNode(x, `db${x}`, NodeType.Db).withIsLeaf(true).withParams({
 | 
					 | 
				
			||||||
            id: redisInfo.id,
 | 
					 | 
				
			||||||
            db: x,
 | 
					 | 
				
			||||||
            name: `db${x}`,
 | 
					 | 
				
			||||||
            keys: 0,
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (redisInfo.mode == 'cluster') {
 | 
					 | 
				
			||||||
        return dbs;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const res = await redisApi.redisInfo.request({ id: redisInfo.id, host: redisInfo.host, section: 'Keyspace' });
 | 
					 | 
				
			||||||
    for (let db in res.Keyspace) {
 | 
					 | 
				
			||||||
        for (let d of dbs) {
 | 
					 | 
				
			||||||
            if (db == d.params.name) {
 | 
					 | 
				
			||||||
                d.params.keys = res.Keyspace[db]?.split(',')[0]?.split('=')[1] || 0;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    // 替换label
 | 
					 | 
				
			||||||
    dbs.forEach((e: any) => {
 | 
					 | 
				
			||||||
        e.label = `${e.params.name} [${e.params.keys}]`;
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    return dbs;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const scan = async (appendKey = false) => {
 | 
					const scan = async (appendKey = false) => {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user