2023-01-31 10:37:40 +08:00
|
|
|
|
<template>
|
2023-02-13 21:11:16 +08:00
|
|
|
|
<tag-menu :instanceMenuMaxHeight="instanceMenuMaxHeight" :tags="tags" ref="menuRef">
|
2023-02-07 16:54:44 +08:00
|
|
|
|
<template #submenu="props">
|
|
|
|
|
|
<!-- 第二级:数据库实例 -->
|
2023-02-13 21:11:16 +08:00
|
|
|
|
<el-sub-menu v-for="inst in tree[props.tag.tagId]" :index="'instance-' + inst.id"
|
2023-02-07 16:54:44 +08:00
|
|
|
|
:key="'instance-' + inst.id" @click.stop="changeInstance(inst, () => { })">
|
|
|
|
|
|
<template #title>
|
|
|
|
|
|
<el-popover placement="right-start" title="数据库实例信息" trigger="hover" :width="210">
|
|
|
|
|
|
<template #reference>
|
2023-02-13 21:11:16 +08:00
|
|
|
|
<span class="ml10">
|
|
|
|
|
|
<el-icon>
|
2023-02-07 16:54:44 +08:00
|
|
|
|
<MostlyCloudy color="#409eff" />
|
2023-02-13 21:11:16 +08:00
|
|
|
|
</el-icon>{{ inst.name }}
|
|
|
|
|
|
</span>
|
2023-02-07 16:54:44 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
<template #default>
|
|
|
|
|
|
<el-form class="instances-pop-form" label-width="55px" :size="'small'">
|
|
|
|
|
|
<el-form-item label="类型:">{{ inst.type }}</el-form-item>
|
|
|
|
|
|
<el-form-item label="链接:">{{ inst.host }}:{{ inst.port }}</el-form-item>
|
|
|
|
|
|
<el-form-item label="用户:">{{ inst.username }}</el-form-item>
|
2023-02-13 21:11:16 +08:00
|
|
|
|
<el-form-item v-if="inst.remark" label="备注:">{{ inst.remark }}</el-form-item>
|
2023-02-07 16:54:44 +08:00
|
|
|
|
</el-form>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-popover>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<!-- 第三级:数据库 -->
|
2023-02-13 21:11:16 +08:00
|
|
|
|
<el-sub-menu v-for="schema in dbs[inst.id]" :index="inst.id + schema" :key="inst.id + schema"
|
2023-02-07 16:54:44 +08:00
|
|
|
|
:class="state.nowSchema === (inst.id + schema) && 'checked'"
|
|
|
|
|
|
@click.stop="changeSchema(inst, schema)">
|
|
|
|
|
|
<template #title>
|
2023-02-13 21:11:16 +08:00
|
|
|
|
<span class="checked-schema ml20">
|
|
|
|
|
|
<el-icon>
|
|
|
|
|
|
<Coin color="#67c23a" />
|
|
|
|
|
|
</el-icon>{{ schema }}</span>
|
2023-02-07 16:54:44 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
<!-- 第四级 01:表 -->
|
|
|
|
|
|
<el-sub-menu :index="inst.id + schema + '-table'">
|
2023-02-06 17:14:16 +08:00
|
|
|
|
<template #title>
|
2023-02-13 21:11:16 +08:00
|
|
|
|
<div class="ml30" style="width: 100%" @click="loadSchemaTables(inst, schema)">
|
|
|
|
|
|
<el-icon>
|
2023-02-07 16:54:44 +08:00
|
|
|
|
<Calendar color="#409eff" />
|
|
|
|
|
|
</el-icon>
|
|
|
|
|
|
<span>表</span>
|
|
|
|
|
|
<el-icon v-show="state.loading[inst.id + schema]" class="is-loading">
|
|
|
|
|
|
<Loading />
|
|
|
|
|
|
</el-icon>
|
|
|
|
|
|
</div>
|
2023-02-06 17:14:16 +08:00
|
|
|
|
</template>
|
2023-02-07 16:54:44 +08:00
|
|
|
|
<el-menu-item :index="inst.id + schema + '-tableSearch'"
|
|
|
|
|
|
:key="inst.id + schema + '-tableSearch'">
|
2023-02-06 17:14:16 +08:00
|
|
|
|
<template #title>
|
2023-02-13 21:11:16 +08:00
|
|
|
|
<span class="ml35">
|
|
|
|
|
|
<el-input size="small" placeholder="表名、备注过滤表" clearable
|
|
|
|
|
|
@change="filterTableName(inst.id, schema)"
|
|
|
|
|
|
@keyup="(e: any) => filterTableName(inst.id, schema, e)"
|
|
|
|
|
|
v-model="state.filterParam[inst.id + schema]" />
|
|
|
|
|
|
</span>
|
2023-02-06 17:14:16 +08:00
|
|
|
|
</template>
|
2023-02-07 16:54:44 +08:00
|
|
|
|
</el-menu-item>
|
|
|
|
|
|
|
2023-02-13 21:11:16 +08:00
|
|
|
|
<template v-for="tb in tables[inst.id + schema]">
|
2023-02-07 16:54:44 +08:00
|
|
|
|
<el-menu-item :index="inst.id + schema + tb.tableName"
|
|
|
|
|
|
:key="inst.id + schema + tb.tableName" v-if="tb.show"
|
2023-02-13 21:11:16 +08:00
|
|
|
|
@click="clickSchemaTable(inst, schema, tb.tableName)">
|
2023-02-06 17:14:16 +08:00
|
|
|
|
<template #title>
|
2023-02-13 21:11:16 +08:00
|
|
|
|
<div class="ml35" style="width: 100%">
|
|
|
|
|
|
<el-icon>
|
2023-02-07 16:54:44 +08:00
|
|
|
|
<Calendar color="#409eff" />
|
|
|
|
|
|
</el-icon>
|
|
|
|
|
|
<el-tooltip v-if="tb.tableComment" effect="customized"
|
|
|
|
|
|
:content="tb.tableComment" placement="right">
|
|
|
|
|
|
{{ tb.tableName }}
|
|
|
|
|
|
</el-tooltip>
|
|
|
|
|
|
<span v-else>{{ tb.tableName }}</span>
|
|
|
|
|
|
</div>
|
2023-02-06 17:14:16 +08:00
|
|
|
|
</template>
|
2023-02-07 16:54:44 +08:00
|
|
|
|
</el-menu-item>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-sub-menu>
|
|
|
|
|
|
<!-- 第四级 02:sql -->
|
2023-02-13 21:11:16 +08:00
|
|
|
|
<el-sub-menu @click.stop="loadSqls(inst, schema)" :index="inst.id + schema + '-sql'">
|
2023-02-07 16:54:44 +08:00
|
|
|
|
<template #title>
|
2023-02-13 21:11:16 +08:00
|
|
|
|
<span class="ml30">
|
|
|
|
|
|
<el-icon>
|
|
|
|
|
|
<List color="#f56c6c" />
|
|
|
|
|
|
</el-icon>
|
|
|
|
|
|
<span>sql</span>
|
|
|
|
|
|
</span>
|
2023-02-07 16:54:44 +08:00
|
|
|
|
</template>
|
2023-01-31 10:37:40 +08:00
|
|
|
|
|
2023-02-13 21:11:16 +08:00
|
|
|
|
<template v-for="sql in sqls[inst.id + schema]">
|
|
|
|
|
|
<el-menu-item v-if="sql.show" :index="inst.id + schema + sql.name"
|
|
|
|
|
|
:key="inst.id + schema + sql.name" @click="clickSqlName(inst, schema, sql.name)">
|
2023-02-07 16:54:44 +08:00
|
|
|
|
<template #title>
|
2023-02-13 21:11:16 +08:00
|
|
|
|
<div class="ml35" style="width: 100%">
|
|
|
|
|
|
<el-icon>
|
|
|
|
|
|
<Document />
|
2023-02-06 17:14:16 +08:00
|
|
|
|
</el-icon>
|
2023-02-07 16:54:44 +08:00
|
|
|
|
<span>{{ sql.name }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-menu-item>
|
|
|
|
|
|
</template>
|
2023-02-06 17:14:16 +08:00
|
|
|
|
</el-sub-menu>
|
2023-02-07 16:54:44 +08:00
|
|
|
|
</el-sub-menu>
|
|
|
|
|
|
</el-sub-menu>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</tag-menu>
|
2023-01-31 10:37:40 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
2023-02-13 21:11:16 +08:00
|
|
|
|
import { onBeforeMount, reactive, toRefs } from 'vue';
|
2023-02-07 16:54:44 +08:00
|
|
|
|
import TagMenu from '../../component/TagMenu.vue';
|
2023-02-13 21:11:16 +08:00
|
|
|
|
import { dbApi } from '../api';
|
|
|
|
|
|
import { DbInst } from '../db';
|
2023-01-31 10:37:40 +08:00
|
|
|
|
|
2023-02-13 21:11:16 +08:00
|
|
|
|
const emits = defineEmits(['changeInstance', 'clickSqlName', 'clickSchemaTable', 'changeSchema', 'loadSqlNames'])
|
2023-01-31 10:37:40 +08:00
|
|
|
|
|
2023-02-06 17:14:16 +08:00
|
|
|
|
onBeforeMount(async () => {
|
2023-02-13 21:11:16 +08:00
|
|
|
|
await loadInstances();
|
|
|
|
|
|
state.instanceMenuMaxHeight = window.innerHeight - 140 + 'px';
|
2023-01-31 10:37:40 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const state = reactive({
|
2023-02-13 21:11:16 +08:00
|
|
|
|
tags: {},
|
|
|
|
|
|
tree: {},
|
|
|
|
|
|
dbs: {},
|
|
|
|
|
|
tables: {},
|
|
|
|
|
|
sqls: {},
|
2023-02-06 17:14:16 +08:00
|
|
|
|
nowSchema: '',
|
|
|
|
|
|
filterParam: {},
|
2023-02-13 21:11:16 +08:00
|
|
|
|
loading: {},
|
|
|
|
|
|
instanceMenuMaxHeight: '850px',
|
2023-01-31 10:37:40 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
2023-02-13 21:11:16 +08:00
|
|
|
|
const {
|
|
|
|
|
|
instanceMenuMaxHeight,
|
|
|
|
|
|
tags,
|
|
|
|
|
|
tree,
|
|
|
|
|
|
dbs,
|
|
|
|
|
|
sqls,
|
|
|
|
|
|
tables,
|
|
|
|
|
|
} = toRefs(state)
|
|
|
|
|
|
|
|
|
|
|
|
// 加载实例数据
|
|
|
|
|
|
const loadInstances = async () => {
|
|
|
|
|
|
const res = await dbApi.dbs.request({ pageNum: 1, pageSize: 1000, })
|
|
|
|
|
|
if (!res.total) return
|
|
|
|
|
|
// state.instances = { tags: {}, tree: {}, dbs: {}, tables: {}, sqls: {} }; // 初始化变量
|
|
|
|
|
|
for (const db of res.list) {
|
|
|
|
|
|
let arr = state.tree[db.tagId] || []
|
|
|
|
|
|
const { tagId, tagPath } = db
|
|
|
|
|
|
// tags
|
|
|
|
|
|
state.tags[db.tagId] = { tagId, tagPath }
|
|
|
|
|
|
|
|
|
|
|
|
// tree
|
|
|
|
|
|
arr.push(db)
|
|
|
|
|
|
state.tree[db.tagId] = arr;
|
|
|
|
|
|
|
|
|
|
|
|
// dbs
|
|
|
|
|
|
state.dbs[db.id] = db.database.split(' ')
|
|
|
|
|
|
}
|
2023-01-31 10:37:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 改变选中的数据库实例
|
|
|
|
|
|
* @param inst 选中的实例对象
|
2023-02-07 10:32:42 +08:00
|
|
|
|
* @param fn 选中的实例对象后的回调函数
|
2023-01-31 10:37:40 +08:00
|
|
|
|
*/
|
2023-02-07 16:54:44 +08:00
|
|
|
|
const changeInstance = (inst: any, fn: Function) => {
|
|
|
|
|
|
emits('changeInstance', inst, fn)
|
2023-01-31 10:37:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 改变选中的数据库schema
|
|
|
|
|
|
* @param inst 选中的实例对象
|
|
|
|
|
|
* @param schema 选中的数据库schema
|
|
|
|
|
|
*/
|
2023-02-06 17:14:16 +08:00
|
|
|
|
const changeSchema = (inst: any, schema: string) => {
|
|
|
|
|
|
state.nowSchema = inst.id + schema
|
|
|
|
|
|
emits('changeSchema', inst, schema)
|
2023-01-31 10:37:40 +08:00
|
|
|
|
}
|
2023-02-13 21:11:16 +08:00
|
|
|
|
|
|
|
|
|
|
/** 加载schema下所有表
|
|
|
|
|
|
*
|
2023-01-31 10:37:40 +08:00
|
|
|
|
* @param inst 数据库实例
|
|
|
|
|
|
* @param schema database名
|
|
|
|
|
|
*/
|
2023-02-13 21:11:16 +08:00
|
|
|
|
const loadSchemaTables = async (inst: any, schema: string) => {
|
|
|
|
|
|
const key = getSchemaKey(inst.id, schema);
|
|
|
|
|
|
state.loading[key] = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
let { id } = inst
|
|
|
|
|
|
let tables = await DbInst.getInst(id, inst.type).loadTables(schema);
|
|
|
|
|
|
tables && tables.forEach((a: any) => a.show = true)
|
|
|
|
|
|
state.tables[key] = tables;
|
|
|
|
|
|
changeSchema(inst, schema);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
state.loading[key] = false
|
|
|
|
|
|
}
|
2023-01-31 10:37:40 +08:00
|
|
|
|
}
|
2023-02-13 21:11:16 +08:00
|
|
|
|
|
2023-01-31 10:37:40 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 加载选中表数据
|
|
|
|
|
|
* @param inst 数据库实例
|
|
|
|
|
|
* @param schema database名
|
|
|
|
|
|
* @param tableName 表名
|
|
|
|
|
|
*/
|
2023-02-13 21:11:16 +08:00
|
|
|
|
const clickSchemaTable = (inst: any, schema: string, tableName: string) => {
|
|
|
|
|
|
emits('clickSchemaTable', inst, schema, tableName)
|
2023-01-31 10:37:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const filterTableName = (instId: number, schema: string, event?: any) => {
|
2023-02-13 21:11:16 +08:00
|
|
|
|
const key = getSchemaKey(instId, schema)
|
2023-02-06 17:14:16 +08:00
|
|
|
|
if (event) {
|
2023-02-13 21:11:16 +08:00
|
|
|
|
state.filterParam[key] = event.target.value
|
2023-02-06 17:14:16 +08:00
|
|
|
|
}
|
2023-02-13 21:11:16 +08:00
|
|
|
|
let param = state.filterParam[key] as string
|
2023-02-06 17:14:16 +08:00
|
|
|
|
param = param?.replace('/', '\/')
|
2023-02-13 21:11:16 +08:00
|
|
|
|
state.tables[key].forEach((a: any) => {
|
|
|
|
|
|
a.show = param ? eval('/' + param.split('').join('[_\w]*') + '[_\w]*/ig').test(a.tableName) || eval('/' + param.split('').join('[_\w]*') + '[_\w]*/ig').test(a.tableComment) : true
|
2023-02-06 17:14:16 +08:00
|
|
|
|
})
|
2023-01-31 10:37:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-02-13 21:11:16 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 加载用户保存的sql脚本
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param inst
|
|
|
|
|
|
* @param schema
|
|
|
|
|
|
*/
|
|
|
|
|
|
const loadSqls = async (inst: any, schema: string) => {
|
|
|
|
|
|
const key = getSchemaKey(inst.id, schema)
|
|
|
|
|
|
let sqls = state.sqls[key];
|
|
|
|
|
|
if (!sqls) {
|
|
|
|
|
|
const sqls = await dbApi.getSqlNames.request({ id: inst.id, db: schema, })
|
|
|
|
|
|
sqls && sqls.forEach((a: any) => a.show = true)
|
|
|
|
|
|
state.sqls[key] = sqls;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
sqls.forEach((a: any) => a.show = true);
|
2023-02-07 16:54:44 +08:00
|
|
|
|
}
|
2023-02-07 10:32:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-02-13 21:11:16 +08:00
|
|
|
|
const reloadSqls = async (inst: any, schema: string) => {
|
|
|
|
|
|
const sqls = await dbApi.getSqlNames.request({ id: inst.id, db: schema, })
|
|
|
|
|
|
sqls && sqls.forEach((a: any) => a.show = true)
|
|
|
|
|
|
state.sqls[getSchemaKey(inst.id, schema)] = sqls;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 点击sql模板名称时间,加载用户保存的指定名称的sql内容,并回调子组件指定事件
|
|
|
|
|
|
*/
|
|
|
|
|
|
const clickSqlName = async (inst: any, schema: string, sqlName: string) => {
|
|
|
|
|
|
emits('clickSqlName', inst, schema, sqlName)
|
|
|
|
|
|
changeSchema(inst, schema);
|
|
|
|
|
|
}
|
2023-02-07 10:32:42 +08:00
|
|
|
|
|
2023-02-13 21:11:16 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 根据实例以及库获取对应的唯一id
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param inst 数据库实例
|
|
|
|
|
|
* @param schema 数据库
|
|
|
|
|
|
*/
|
|
|
|
|
|
const getSchemaKey = (instId: any, schema: string) => {
|
|
|
|
|
|
return instId + schema;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const getSchemas = (dbId: any) => {
|
|
|
|
|
|
return state.dbs[dbId] || []
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
defineExpose({
|
|
|
|
|
|
getSchemas,
|
|
|
|
|
|
reloadSqls,
|
|
|
|
|
|
})
|
2023-01-31 10:37:40 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss">
|
2023-02-06 17:14:16 +08:00
|
|
|
|
.instances-pop-form {
|
|
|
|
|
|
.el-form-item {
|
|
|
|
|
|
margin-bottom: unset;
|
|
|
|
|
|
}
|
2023-01-31 10:37:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
</style>
|