2021-06-07 17:22:07 +08:00
|
|
|
|
<template>
|
2023-11-02 12:46:21 +08:00
|
|
|
|
<div class="db-sql-exec">
|
2023-12-07 23:57:11 +08:00
|
|
|
|
<Splitpanes class="default-theme">
|
|
|
|
|
|
<Pane size="20" max-size="30">
|
2023-12-05 23:03:51 +08:00
|
|
|
|
<tag-tree :resource-type="TagResourceTypeEnum.Db.value" :tag-path-node-type="NodeTypeTagPath" ref="tagTreeRef">
|
2023-02-18 23:02:14 +08:00
|
|
|
|
<template #prefix="{ data }">
|
2023-11-03 17:09:20 +08:00
|
|
|
|
<span v-if="data.type.value == SqlExecNodeType.DbInst">
|
2023-12-20 17:29:16 +08:00
|
|
|
|
<el-popover
|
|
|
|
|
|
@show="showDbInfo(data.params)"
|
|
|
|
|
|
:show-after="500"
|
|
|
|
|
|
placement="right-start"
|
|
|
|
|
|
title="数据库实例信息"
|
|
|
|
|
|
trigger="hover"
|
|
|
|
|
|
:width="250"
|
|
|
|
|
|
>
|
2023-02-18 23:02:14 +08:00
|
|
|
|
<template #reference>
|
2023-12-07 11:48:17 +08:00
|
|
|
|
<SvgIcon :name="getDbDialect(data.params.type).getInfo().icon" :size="18" />
|
2023-02-18 23:02:14 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
<template #default>
|
2023-11-12 20:14:44 +08:00
|
|
|
|
<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>
|
2023-12-20 17:29:16 +08:00
|
|
|
|
<el-descriptions-item label="数据库版本">
|
|
|
|
|
|
<span v-loading="loadingServerInfo"> {{ `${dbServerInfo?.version}` }}</span>
|
|
|
|
|
|
</el-descriptions-item>
|
2023-11-12 20:14:44 +08:00
|
|
|
|
<el-descriptions-item label="user">
|
|
|
|
|
|
{{ data.params.username }}
|
|
|
|
|
|
</el-descriptions-item>
|
|
|
|
|
|
<el-descriptions-item label="备注">
|
|
|
|
|
|
{{ data.params.remark }}
|
|
|
|
|
|
</el-descriptions-item>
|
|
|
|
|
|
</el-descriptions>
|
2023-02-18 23:02:14 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
</el-popover>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
2023-11-12 20:14:44 +08:00
|
|
|
|
<SvgIcon v-if="data.icon" :name="data.icon.name" :color="data.icon.color" />
|
|
|
|
|
|
</template>
|
2023-02-18 23:02:14 +08:00
|
|
|
|
|
2023-11-02 12:46:21 +08:00
|
|
|
|
<template #suffix="{ data }">
|
2023-11-03 17:09:20 +08:00
|
|
|
|
<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>
|
2023-11-02 12:46:21 +08:00
|
|
|
|
</template>
|
2023-02-18 23:02:14 +08:00
|
|
|
|
</tag-tree>
|
2023-12-07 23:57:11 +08:00
|
|
|
|
</Pane>
|
2023-11-02 12:46:21 +08:00
|
|
|
|
|
2023-12-07 23:57:11 +08:00
|
|
|
|
<Pane>
|
2023-12-14 13:05:21 +08:00
|
|
|
|
<div class="card db-op pd5">
|
|
|
|
|
|
<el-row>
|
|
|
|
|
|
<el-col :span="24" v-if="state.db">
|
|
|
|
|
|
<el-descriptions :column="4" size="small" border>
|
|
|
|
|
|
<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 }, 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="getDbDialect(nowDbInst.type).getInfo().icon" :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">
|
|
|
|
|
|
<el-tabs
|
|
|
|
|
|
v-if="state.tabs.size > 0"
|
|
|
|
|
|
type="card"
|
|
|
|
|
|
@tab-remove="onRemoveTab"
|
|
|
|
|
|
@tab-change="onTabChange"
|
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
|
v-model="state.activeName"
|
|
|
|
|
|
class="h100"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-tab-pane class="h100" closable v-for="dt in state.tabs.values()" :label="dt.label" :name="dt.key" :key="dt.key">
|
2023-11-15 12:28:49 +08:00
|
|
|
|
<template #label>
|
2023-12-14 13:05:21 +08:00
|
|
|
|
<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="getDbDialect(dt.params.type).getInfo().icon" :size="18" />
|
|
|
|
|
|
{{ dt.params.host }}
|
|
|
|
|
|
</el-descriptions-item>
|
|
|
|
|
|
<el-descriptions-item label="库名">
|
|
|
|
|
|
{{ dt.params.dbName }}
|
|
|
|
|
|
</el-descriptions-item>
|
|
|
|
|
|
</el-descriptions>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-popover>
|
2023-11-15 12:28:49 +08:00
|
|
|
|
</template>
|
2023-12-14 13:05:21 +08:00
|
|
|
|
|
|
|
|
|
|
<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"
|
|
|
|
|
|
>
|
|
|
|
|
|
</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>
|
2023-11-15 12:28:49 +08:00
|
|
|
|
</div>
|
2023-12-07 23:57:11 +08:00
|
|
|
|
</Pane>
|
|
|
|
|
|
</Splitpanes>
|
2021-06-07 17:22:07 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
2022-10-29 20:08:15 +08:00
|
|
|
|
<script lang="ts" setup>
|
2023-12-20 17:41:24 +08:00
|
|
|
|
import { defineAsyncComponent, onBeforeUnmount, onMounted, reactive, ref, toRefs } from 'vue';
|
2023-11-24 17:03:08 +08:00
|
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
2023-11-02 12:46:21 +08:00
|
|
|
|
import { formatByteSize } from '@/common/utils/format';
|
2023-12-20 17:41:24 +08:00
|
|
|
|
import { DbInst, registerDbCompletionItemProvider, TabInfo, TabType } from './db';
|
|
|
|
|
|
import { NodeType, TagTreeNode } from '../component/tag';
|
2023-02-18 23:02:14 +08:00
|
|
|
|
import TagTree from '../component/TagTree.vue';
|
|
|
|
|
|
import { dbApi } from './api';
|
2023-11-15 12:28:49 +08:00
|
|
|
|
import { dispposeCompletionItemProvider } from '@/components/monaco/completionItemProvider';
|
2023-11-13 17:41:03 +08:00
|
|
|
|
import SvgIcon from '@/components/svgIcon/index.vue';
|
2023-11-18 15:22:25 +08:00
|
|
|
|
import { ContextmenuItem } from '@/components/contextmenu';
|
2023-11-26 21:21:35 +08:00
|
|
|
|
import { getDbDialect } from './dialect/index';
|
2023-12-05 23:03:51 +08:00
|
|
|
|
import { sleep } from '@/common/utils/loading';
|
|
|
|
|
|
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
2023-12-20 17:41:24 +08:00
|
|
|
|
import { Pane, Splitpanes } from 'splitpanes';
|
2023-12-11 17:08:52 +08:00
|
|
|
|
import { useEventListener } from '@vueuse/core';
|
2022-10-29 20:08:15 +08:00
|
|
|
|
|
2023-11-12 20:14:44 +08:00
|
|
|
|
const DbSqlEditor = defineAsyncComponent(() => import('./component/sqleditor/DbSqlEditor.vue'));
|
|
|
|
|
|
const DbTableDataOp = defineAsyncComponent(() => import('./component/table/DbTableDataOp.vue'));
|
|
|
|
|
|
const DbTablesOp = defineAsyncComponent(() => import('./component/table/DbTablesOp.vue'));
|
|
|
|
|
|
|
2023-02-18 23:02:14 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 树节点类型
|
|
|
|
|
|
*/
|
2023-11-03 17:09:20 +08:00
|
|
|
|
class SqlExecNodeType {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
static DbInst = 1;
|
|
|
|
|
|
static Db = 2;
|
2023-02-18 23:02:14 +08:00
|
|
|
|
static TableMenu = 3;
|
|
|
|
|
|
static SqlMenu = 4;
|
|
|
|
|
|
static Table = 5;
|
|
|
|
|
|
static Sql = 6;
|
2023-11-12 20:14:44 +08:00
|
|
|
|
static PgSchemaMenu = 7;
|
|
|
|
|
|
static PgSchema = 8;
|
2023-02-18 23:02:14 +08:00
|
|
|
|
}
|
2023-11-03 17:09:20 +08:00
|
|
|
|
|
2023-11-12 20:14:44 +08:00
|
|
|
|
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',
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-11-03 17:09:20 +08:00
|
|
|
|
// node节点点击时,触发改变db事件
|
2023-11-12 20:14:44 +08:00
|
|
|
|
const nodeClickChangeDb = (nodeData: TagTreeNode) => {
|
2023-11-03 17:09:20 +08:00
|
|
|
|
const params = nodeData.params;
|
2023-11-15 12:28:49 +08:00
|
|
|
|
if (params.db) {
|
2023-12-12 17:53:24 +08:00
|
|
|
|
changeDb({ id: params.id, host: `${params.host}`, name: params.name, type: params.type, tagPath: params.tagPath, databases: params.dbs }, params.db);
|
2023-11-15 12:28:49 +08:00
|
|
|
|
}
|
2023-11-03 17:09:20 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// tagpath 节点类型
|
|
|
|
|
|
const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
2023-12-05 23:03:51 +08:00
|
|
|
|
const dbInfoRes = await dbApi.dbs.request({ tagPath: parentNode.key });
|
|
|
|
|
|
const dbInfos = dbInfoRes.list;
|
2023-11-03 17:09:20 +08:00
|
|
|
|
if (!dbInfos) {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
2023-12-05 23:03:51 +08:00
|
|
|
|
|
|
|
|
|
|
// 防止过快加载会出现一闪而过,对眼睛不好
|
|
|
|
|
|
await sleep(100);
|
2023-11-03 17:09:20 +08:00
|
|
|
|
return dbInfos?.map((x: any) => {
|
2023-12-05 23:03:51 +08:00
|
|
|
|
x.tagPath = parentNode.key;
|
2023-11-03 17:09:20 +08:00
|
|
|
|
return new TagTreeNode(`${parentNode.key}.${x.id}`, x.name, NodeTypeDbInst).withParams(x);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 数据库实例节点类型
|
2023-11-15 12:28:49 +08:00
|
|
|
|
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);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2023-11-03 17:09:20 +08:00
|
|
|
|
|
|
|
|
|
|
// 数据库节点
|
|
|
|
|
|
const NodeTypeDb = new NodeType(SqlExecNodeType.Db)
|
|
|
|
|
|
.withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
|
|
|
|
|
const params = parentNode.params;
|
2023-12-21 15:02:35 +08:00
|
|
|
|
// pg类数据库会多一层schema
|
2023-12-06 14:50:02 +08:00
|
|
|
|
if (params.type == 'postgres' || params.type === 'dm') {
|
2023-12-21 15:02:35 +08:00
|
|
|
|
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;
|
|
|
|
|
|
nParams.dbs = schemaNames;
|
|
|
|
|
|
return new TagTreeNode(`${params.id}.${params.db}.schema.${sn}`, sn, NodeTypePostgresScheam).withParams(nParams).withIcon(SchemaIcon);
|
|
|
|
|
|
});
|
2023-11-12 20:14:44 +08:00
|
|
|
|
}
|
2023-11-03 17:09:20 +08:00
|
|
|
|
return [
|
2023-11-12 20:14:44 +08:00
|
|
|
|
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),
|
2023-11-03 17:09:20 +08:00
|
|
|
|
];
|
|
|
|
|
|
})
|
2023-11-12 20:14:44 +08:00
|
|
|
|
.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);
|
2023-11-03 17:09:20 +08:00
|
|
|
|
|
|
|
|
|
|
// 数据库表菜单节点
|
|
|
|
|
|
const NodeTypeTableMenu = new NodeType(SqlExecNodeType.TableMenu)
|
2023-11-12 20:14:44 +08:00
|
|
|
|
.withContextMenuItems([
|
2023-11-14 17:36:51 +08:00
|
|
|
|
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 });
|
|
|
|
|
|
}),
|
|
|
|
|
|
])
|
2023-11-03 17:09:20 +08:00
|
|
|
|
.withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
|
|
|
|
|
const params = parentNode.params;
|
2023-11-12 20:14:44 +08:00
|
|
|
|
let { id, db } = params;
|
2023-11-03 17:09:20 +08:00
|
|
|
|
// 获取当前库的所有表信息
|
|
|
|
|
|
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;
|
2023-11-12 20:14:44 +08:00
|
|
|
|
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),
|
|
|
|
|
|
})
|
2023-12-22 00:47:01 +08:00
|
|
|
|
.withIcon(TableIcon)
|
|
|
|
|
|
.withLabelRemark(`${x.tableName} ${x.tableComment ? '| ' + x.tableComment : ''}`);
|
2023-11-03 17:09:20 +08:00
|
|
|
|
});
|
|
|
|
|
|
// 设置父节点参数的表大小
|
|
|
|
|
|
parentNode.params.dbTableSize = formatByteSize(dbTableSize);
|
|
|
|
|
|
return tablesNode;
|
|
|
|
|
|
})
|
2023-11-12 20:14:44 +08:00
|
|
|
|
.withNodeClickFunc(nodeClickChangeDb);
|
2023-11-03 17:09:20 +08:00
|
|
|
|
|
|
|
|
|
|
// 数据库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) => {
|
2023-11-12 20:14:44 +08:00
|
|
|
|
return new TagTreeNode(`${id}.${db}.${x.name}`, x.name, NodeTypeSql)
|
|
|
|
|
|
.withIsLeaf(true)
|
|
|
|
|
|
.withParams({
|
|
|
|
|
|
id,
|
|
|
|
|
|
db,
|
|
|
|
|
|
dbs,
|
|
|
|
|
|
sqlName: x.name,
|
|
|
|
|
|
})
|
|
|
|
|
|
.withIcon(SqlIcon);
|
2023-11-03 17:09:20 +08:00
|
|
|
|
});
|
|
|
|
|
|
})
|
2023-11-12 20:14:44 +08:00
|
|
|
|
.withNodeClickFunc(nodeClickChangeDb);
|
2023-11-03 17:09:20 +08:00
|
|
|
|
|
|
|
|
|
|
// 表节点类型
|
|
|
|
|
|
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模板节点类型
|
2023-11-24 17:03:08 +08:00
|
|
|
|
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);
|
|
|
|
|
|
})
|
|
|
|
|
|
.withContextMenuItems([
|
|
|
|
|
|
new ContextmenuItem('delSql', '删除').withIcon('delete').withOnClick((data: any) => deleteSql(data.params.id, data.params.db, data.params.sqlName)),
|
|
|
|
|
|
]);
|
2023-11-03 17:09:20 +08:00
|
|
|
|
|
2023-07-06 20:59:22 +08:00
|
|
|
|
const tagTreeRef: any = ref(null);
|
2023-01-31 10:37:40 +08:00
|
|
|
|
|
2023-02-13 21:11:16 +08:00
|
|
|
|
const tabs: Map<string, TabInfo> = new Map();
|
2022-10-29 20:08:15 +08:00
|
|
|
|
const state = reactive({
|
2023-02-15 21:28:01 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 当前操作的数据库实例
|
|
|
|
|
|
*/
|
|
|
|
|
|
nowDbInst: {} as DbInst,
|
2023-02-13 21:11:16 +08:00
|
|
|
|
db: '', // 当前操作的数据库
|
2023-02-18 23:02:14 +08:00
|
|
|
|
activeName: '',
|
2023-03-28 15:22:05 +08:00
|
|
|
|
reloadStatus: false,
|
2023-02-13 21:11:16 +08:00
|
|
|
|
tabs,
|
2023-12-07 23:57:11 +08:00
|
|
|
|
dataTabsTableHeight: '600px',
|
2023-11-12 20:14:44 +08:00
|
|
|
|
tablesOpHeight: '600',
|
2023-12-20 17:29:16 +08:00
|
|
|
|
dbServerInfo: {
|
|
|
|
|
|
loading: true,
|
|
|
|
|
|
version: '',
|
|
|
|
|
|
},
|
2022-10-29 20:08:15 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2023-07-06 20:59:22 +08:00
|
|
|
|
const { nowDbInst } = toRefs(state);
|
2023-02-15 21:28:01 +08:00
|
|
|
|
|
2023-12-20 17:29:16 +08:00
|
|
|
|
const serverInfoReqParam = ref({
|
|
|
|
|
|
instanceId: 0,
|
|
|
|
|
|
});
|
|
|
|
|
|
const { execute: getDbServerInfo, isFetching: loadingServerInfo, data: dbServerInfo } = dbApi.getInstanceServerInfo.useApi<any>(serverInfoReqParam);
|
|
|
|
|
|
|
2023-02-13 21:11:16 +08:00
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
setHeight();
|
|
|
|
|
|
// 监听浏览器窗口大小变化,更新对应组件高度
|
2023-12-11 17:08:52 +08:00
|
|
|
|
useEventListener(window, 'resize', setHeight);
|
2022-11-05 22:18:59 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2023-09-19 23:00:32 +08:00
|
|
|
|
onBeforeUnmount(() => {
|
|
|
|
|
|
dispposeCompletionItemProvider('sql');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2023-02-13 21:11:16 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 设置editor高度和数据表高度
|
|
|
|
|
|
*/
|
|
|
|
|
|
const setHeight = () => {
|
2023-12-14 13:05:21 +08:00
|
|
|
|
state.dataTabsTableHeight = window.innerHeight - 270 + 'px';
|
|
|
|
|
|
state.tablesOpHeight = window.innerHeight - 225 + 'px';
|
2022-10-28 17:42:23 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2023-12-20 17:29:16 +08:00
|
|
|
|
const showDbInfo = async (db: any) => {
|
|
|
|
|
|
if (dbServerInfo.value) {
|
|
|
|
|
|
dbServerInfo.value.version = '';
|
|
|
|
|
|
}
|
|
|
|
|
|
serverInfoReqParam.value.instanceId = db.instanceId;
|
|
|
|
|
|
await getDbServerInfo();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-11-13 17:41:03 +08:00
|
|
|
|
// 选择数据库,改变当前正在操作的数据库信息
|
2023-11-12 20:14:44 +08:00
|
|
|
|
const changeDb = (db: any, dbName: string) => {
|
|
|
|
|
|
state.nowDbInst = DbInst.getOrNewInst(db);
|
2023-12-12 17:53:24 +08:00
|
|
|
|
state.nowDbInst.databases = db.databases;
|
2023-11-12 20:14:44 +08:00
|
|
|
|
state.db = dbName;
|
2023-07-06 20:59:22 +08:00
|
|
|
|
};
|
2022-10-28 17:42:23 +08:00
|
|
|
|
|
2023-02-13 21:11:16 +08:00
|
|
|
|
// 加载选中的表数据,即新增表数据操作tab
|
2023-11-12 20:14:44 +08:00
|
|
|
|
const loadTableData = async (db: any, dbName: string, tableName: string) => {
|
2023-02-13 21:11:16 +08:00
|
|
|
|
if (tableName == '') {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2023-11-15 12:28:49 +08:00
|
|
|
|
changeDb(db, dbName);
|
2022-11-07 21:57:51 +08:00
|
|
|
|
|
2023-11-12 20:14:44 +08:00
|
|
|
|
const key = `${db.id}:\`${dbName}\`.${tableName}`;
|
|
|
|
|
|
let tab = state.tabs.get(key);
|
|
|
|
|
|
state.activeName = key;
|
2023-02-13 21:11:16 +08:00
|
|
|
|
// 如果存在该表tab,则直接返回
|
|
|
|
|
|
if (tab) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
tab = new TabInfo();
|
2023-11-12 20:14:44 +08:00
|
|
|
|
tab.label = tableName;
|
|
|
|
|
|
tab.key = key;
|
|
|
|
|
|
tab.treeNodeKey = db.nodeKey;
|
|
|
|
|
|
tab.dbId = db.id;
|
|
|
|
|
|
tab.db = dbName;
|
2023-02-13 21:11:16 +08:00
|
|
|
|
tab.type = TabType.TableData;
|
2023-02-14 11:44:48 +08:00
|
|
|
|
tab.params = {
|
2023-11-13 17:41:03 +08:00
|
|
|
|
...getNowDbInfo(),
|
2023-07-06 20:59:22 +08:00
|
|
|
|
table: tableName,
|
|
|
|
|
|
};
|
2023-11-12 20:14:44 +08:00
|
|
|
|
state.tabs.set(key, tab);
|
2023-07-06 20:59:22 +08:00
|
|
|
|
};
|
2023-02-13 21:11:16 +08:00
|
|
|
|
|
2023-11-12 20:14:44 +08:00
|
|
|
|
// 新建查询tab
|
|
|
|
|
|
const addQueryTab = async (db: any, dbName: string, sqlName: string = '') => {
|
|
|
|
|
|
if (!dbName || !db.id) {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
ElMessage.warning('请选择数据库实例及对应的schema');
|
|
|
|
|
|
return;
|
2023-02-13 21:11:16 +08:00
|
|
|
|
}
|
2023-11-15 12:28:49 +08:00
|
|
|
|
changeDb(db, dbName);
|
2023-02-18 23:02:14 +08:00
|
|
|
|
|
2023-11-12 20:14:44 +08:00
|
|
|
|
const dbId = db.id;
|
2023-02-13 21:11:16 +08:00
|
|
|
|
let label;
|
2023-11-12 20:14:44 +08:00
|
|
|
|
let key;
|
2023-02-13 21:11:16 +08:00
|
|
|
|
// 存在sql模板名,则该模板名只允许一个tab
|
|
|
|
|
|
if (sqlName) {
|
2023-11-12 20:14:44 +08:00
|
|
|
|
label = `查询-${sqlName}`;
|
|
|
|
|
|
key = `查询:${dbId}:${dbName}.${sqlName}`;
|
2023-02-13 21:11:16 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
let count = 1;
|
|
|
|
|
|
state.tabs.forEach((v) => {
|
2023-02-14 11:44:48 +08:00
|
|
|
|
if (v.type == TabType.Query && !v.params.sqlName) {
|
2023-02-13 21:11:16 +08:00
|
|
|
|
count++;
|
2022-11-07 21:57:51 +08:00
|
|
|
|
}
|
2023-07-06 20:59:22 +08:00
|
|
|
|
});
|
2023-11-12 20:14:44 +08:00
|
|
|
|
label = `新查询-${count}`;
|
|
|
|
|
|
key = `新查询${count}:${dbId}:${dbName}`;
|
2023-02-13 21:11:16 +08:00
|
|
|
|
}
|
2023-11-12 20:14:44 +08:00
|
|
|
|
state.activeName = key;
|
|
|
|
|
|
let tab = state.tabs.get(key);
|
2023-02-13 21:11:16 +08:00
|
|
|
|
if (tab) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
tab = new TabInfo();
|
2023-11-12 20:14:44 +08:00
|
|
|
|
tab.key = key;
|
|
|
|
|
|
tab.label = label;
|
|
|
|
|
|
tab.treeNodeKey = db.nodeKey;
|
2023-02-13 21:11:16 +08:00
|
|
|
|
tab.dbId = dbId;
|
2023-11-12 20:14:44 +08:00
|
|
|
|
tab.db = dbName;
|
2023-02-13 21:11:16 +08:00
|
|
|
|
tab.type = TabType.Query;
|
2023-02-14 11:44:48 +08:00
|
|
|
|
tab.params = {
|
2023-11-13 17:41:03 +08:00
|
|
|
|
...getNowDbInfo(),
|
2023-02-13 21:11:16 +08:00
|
|
|
|
sqlName: sqlName,
|
2023-11-12 20:14:44 +08:00
|
|
|
|
dbs: db.dbs,
|
2023-07-06 20:59:22 +08:00
|
|
|
|
};
|
2023-11-12 20:14:44 +08:00
|
|
|
|
state.tabs.set(key, tab);
|
2023-10-19 19:00:23 +08:00
|
|
|
|
// 注册当前sql编辑框提示词
|
2023-12-12 12:41:44 +08:00
|
|
|
|
registerDbCompletionItemProvider(tab.dbId, tab.db, tab.params.dbs, nowDbInst.value.type);
|
2023-07-06 20:59:22 +08:00
|
|
|
|
};
|
2023-02-13 21:11:16 +08:00
|
|
|
|
|
2023-11-12 20:14:44 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 添加数据操作tab
|
|
|
|
|
|
* @param inst
|
|
|
|
|
|
*/
|
|
|
|
|
|
const addTablesOpTab = async (db: any) => {
|
|
|
|
|
|
const dbName = db.db;
|
|
|
|
|
|
if (!db || !db.id) {
|
|
|
|
|
|
ElMessage.warning('请选择数据库实例及对应的schema');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2023-11-15 12:28:49 +08:00
|
|
|
|
changeDb(db, dbName);
|
2023-11-12 20:14:44 +08:00
|
|
|
|
|
|
|
|
|
|
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 = {
|
2023-11-13 17:41:03 +08:00
|
|
|
|
...getNowDbInfo(),
|
2023-11-12 20:14:44 +08:00
|
|
|
|
id: db.id,
|
|
|
|
|
|
db: dbName,
|
|
|
|
|
|
type: db.type,
|
|
|
|
|
|
};
|
|
|
|
|
|
state.tabs.set(key, tab);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-02-15 21:28:01 +08:00
|
|
|
|
const onRemoveTab = (targetName: string) => {
|
2023-02-13 21:11:16 +08:00
|
|
|
|
let activeName = state.activeName;
|
2023-07-06 20:59:22 +08:00
|
|
|
|
const tabNames = [...state.tabs.keys()];
|
2023-02-13 21:11:16 +08:00
|
|
|
|
for (let i = 0; i < tabNames.length; i++) {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
const tabName = tabNames[i];
|
2023-02-13 21:11:16 +08:00
|
|
|
|
if (tabName !== targetName) {
|
|
|
|
|
|
continue;
|
2022-11-07 21:57:51 +08:00
|
|
|
|
}
|
2023-02-13 21:11:16 +08:00
|
|
|
|
const nextTab = tabNames[i + 1] || tabNames[i - 1];
|
|
|
|
|
|
if (nextTab) {
|
|
|
|
|
|
activeName = nextTab;
|
2023-02-15 21:28:01 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
activeName = '';
|
2023-02-13 21:11:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
state.tabs.delete(targetName);
|
|
|
|
|
|
state.activeName = activeName;
|
2023-11-12 20:14:44 +08:00
|
|
|
|
onTabChange();
|
2023-02-13 21:11:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-02-15 21:28:01 +08:00
|
|
|
|
const onTabChange = () => {
|
|
|
|
|
|
if (!state.activeName) {
|
|
|
|
|
|
state.nowDbInst = {} as DbInst;
|
|
|
|
|
|
state.db = '';
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2023-09-19 23:00:32 +08:00
|
|
|
|
|
2023-02-18 23:02:14 +08:00
|
|
|
|
const nowTab = state.tabs.get(state.activeName);
|
|
|
|
|
|
state.nowDbInst = DbInst.getInst(nowTab?.dbId);
|
|
|
|
|
|
state.db = nowTab?.db as string;
|
2023-09-19 23:00:32 +08:00
|
|
|
|
|
|
|
|
|
|
if (nowTab?.type == TabType.Query) {
|
|
|
|
|
|
// 注册sql提示
|
2023-12-12 12:41:44 +08:00
|
|
|
|
registerDbCompletionItemProvider(nowTab.dbId, nowTab.db, nowTab.params.dbs, nowDbInst.value.type);
|
2023-09-19 23:00:32 +08:00
|
|
|
|
}
|
2023-07-06 20:59:22 +08:00
|
|
|
|
};
|
2022-11-07 21:57:51 +08:00
|
|
|
|
|
2023-02-13 21:11:16 +08:00
|
|
|
|
const reloadSqls = (dbId: number, db: string) => {
|
2023-02-18 23:02:14 +08:00
|
|
|
|
tagTreeRef.value.reloadNode(getSqlMenuNodeKey(dbId, db));
|
2023-07-06 20:59:22 +08:00
|
|
|
|
};
|
2023-02-13 21:11:16 +08:00
|
|
|
|
|
2023-11-24 17:03:08 +08:00
|
|
|
|
const deleteSql = async (dbId: any, db: string, sqlName: string) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await ElMessageBox.confirm(`确定删除【${sqlName}】该SQL内容?`, '提示', {
|
|
|
|
|
|
confirmButtonText: '确定',
|
|
|
|
|
|
cancelButtonText: '取消',
|
|
|
|
|
|
type: 'warning',
|
|
|
|
|
|
});
|
|
|
|
|
|
await dbApi.deleteDbSql.request({ id: dbId, db: db, name: sqlName });
|
|
|
|
|
|
ElMessage.success('删除成功');
|
|
|
|
|
|
reloadSqls(dbId, db);
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
//
|
|
|
|
|
|
}
|
2023-07-06 20:59:22 +08:00
|
|
|
|
};
|
2023-02-13 21:11:16 +08:00
|
|
|
|
|
2023-02-18 23:02:14 +08:00
|
|
|
|
const getSqlMenuNodeKey = (dbId: number, db: string) => {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
return `${dbId}.${db}.sql-menu`;
|
|
|
|
|
|
};
|
2023-02-18 23:02:14 +08:00
|
|
|
|
|
2023-04-05 22:41:53 +08:00
|
|
|
|
const reloadTables = (nodeKey: string) => {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
state.reloadStatus = true;
|
2023-04-05 22:41:53 +08:00
|
|
|
|
tagTreeRef.value.reloadNode(nodeKey);
|
2023-07-06 20:59:22 +08:00
|
|
|
|
};
|
2023-11-13 17:41:03 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取当前操作的数据库信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
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,
|
|
|
|
|
|
};
|
|
|
|
|
|
};
|
2021-06-07 17:22:07 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
2022-03-15 22:27:39 +08:00
|
|
|
|
<style lang="scss">
|
2023-11-02 12:46:21 +08:00
|
|
|
|
.db-sql-exec {
|
|
|
|
|
|
.db-table-size {
|
|
|
|
|
|
color: #c4c9c4;
|
2023-11-03 17:09:20 +08:00
|
|
|
|
font-size: 9px;
|
2023-11-02 12:46:21 +08:00
|
|
|
|
}
|
2023-02-06 17:14:16 +08:00
|
|
|
|
|
2023-12-14 13:05:21 +08:00
|
|
|
|
.db-op {
|
|
|
|
|
|
height: calc(100vh - 108px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-02 12:46:21 +08:00
|
|
|
|
#data-exec {
|
2023-11-13 17:41:03 +08:00
|
|
|
|
.el-tabs {
|
|
|
|
|
|
--el-tabs-header-height: 30px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-02 12:46:21 +08:00
|
|
|
|
.el-tabs__header {
|
2023-11-13 17:41:03 +08:00
|
|
|
|
margin: 0 0 5px;
|
2023-11-02 12:46:21 +08:00
|
|
|
|
|
|
|
|
|
|
.el-tabs__item {
|
2023-11-12 20:14:44 +08:00
|
|
|
|
padding: 0 10px;
|
2023-11-02 12:46:21 +08:00
|
|
|
|
}
|
2023-02-06 17:14:16 +08:00
|
|
|
|
}
|
2023-11-13 17:41:03 +08:00
|
|
|
|
|
|
|
|
|
|
.el-tabs__nav-next,
|
|
|
|
|
|
.el-tabs__nav-prev {
|
|
|
|
|
|
line-height: 30px;
|
|
|
|
|
|
}
|
2023-01-31 10:37:40 +08:00
|
|
|
|
}
|
2022-11-22 17:15:08 +08:00
|
|
|
|
|
2023-11-02 12:46:21 +08:00
|
|
|
|
.update_field_active {
|
|
|
|
|
|
background-color: var(--el-color-success);
|
|
|
|
|
|
}
|
2023-02-18 23:02:14 +08:00
|
|
|
|
}
|
2021-06-07 17:22:07 +08:00
|
|
|
|
</style>
|