Files
mayfly-go/mayfly_go_web/src/views/ops/db/component/InstanceTree.vue
2023-02-06 17:14:16 +08:00

252 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="instances-box layout-aside">
<el-row type="flex" justify="space-between">
<el-col :span="24"
:style="{ maxHeight: instanceMenuMaxHeight, height: instanceMenuMaxHeight, overflow: 'auto' }"
class="el-scrollbar flex-auto">
<el-menu background-color="transparent" :collapse-transition="false">
<!-- 第一级tag -->
<el-sub-menu v-for="tag of instances.tags" :index="tag.tagPath" :key="tag.tagPath">
<template #title>
<el-icon>
<FolderOpened color="#e6a23c" />
</el-icon>
<span>{{ tag.tagPath }}</span>
</template>
<!-- 第二级数据库实例 -->
<el-sub-menu v-for="inst in instances.tree[tag.tagId]" :index="'instance-' + inst.id"
:key="'instance-' + inst.id" @click="changeInstance(inst)">
<template #title>
<el-popover placement="right-start" title="数据库实例信息" trigger="hover" :width="210">
<template #reference>
<span>&nbsp;&nbsp;<el-icon>
<MostlyCloudy color="#409eff" />
</el-icon>{{ inst.name }}</span>
</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>
<el-form-item v-if="inst.remark" label="备注:">{{
inst.remark
}}</el-form-item>
</el-form>
</template>
</el-popover>
</template>
<!-- 第三级数据库 -->
<el-sub-menu v-for="schema in instances.dbs[inst.id]" :index="inst.id + schema"
:key="inst.id + schema" :class="state.nowSchema === (inst.id + schema) && 'checked'"
@click="changeSchema(inst, schema)">
<template #title>
&nbsp;&nbsp;&nbsp;&nbsp;<el-icon>
<Coin color="#67c23a" />
</el-icon>
<span class="checked-schema">{{ schema }}</span>
</template>
<!-- 第四级 01 -->
<el-sub-menu :index="inst.id + schema + '-table'">
<template #title>
<div style="width: 100%" @click="loadTableNames(inst, schema)">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<el-icon>
<Calendar color="#409eff" />
</el-icon>
<span></span>
<el-icon v-show="state.loading[inst.id + schema]" class="is-loading">
<Loading />
</el-icon>
</div>
</template>
<el-menu-item :index="inst.id + schema + '-tableSearch'"
:key="inst.id + schema + '-tableSearch'">
<template #title>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<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]" />
</template>
</el-menu-item>
<template v-for="tb in instances.tables[inst.id + schema]">
<el-menu-item :index="inst.id + schema + tb.tableName"
:key="inst.id + schema + tb.tableName" v-if="tb.show"
@click="loadTableData(inst, schema, tb.tableName)">
<template #title>
<div style="width: 100%">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<el-icon>
<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>
</template>
</el-menu-item>
</template>
</el-sub-menu>
<!-- 第四级 02sql -->
<el-sub-menu :index="inst.id + schema + '-sql'">
<template #title>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<el-icon>
<List color="#f56c6c" />
</el-icon>
<span>sql</span>
</template>
<template v-for="sql in instances.sqls[inst.id + schema]">
<el-menu-item :index="inst.id + schema + sql.name"
:key="inst.id + schema + sql.name" v-if="sql.show"
@click="loadSql(inst, schema, sql.name)">
<template #title>
<div style="width: 100%">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<el-icon>
<Calendar color="#409eff" />
</el-icon>
<span>{{ sql.name }}</span>
</div>
</template>
</el-menu-item>
</template>
</el-sub-menu>
</el-sub-menu>
</el-sub-menu>
</el-sub-menu>
</el-menu>
</el-col>
</el-row>
</div>
</template>
<script lang="ts" setup>
import { onBeforeMount, reactive } from 'vue';
const props = defineProps({
instanceMenuMaxHeight: {
type: [Number, String],
},
instances: {
type: Object, required: true
},
})
const emits = defineEmits(['initLoadInstances', 'changeInstance', 'loadTableNames', 'loadTableData', 'changeSchema'])
onBeforeMount(async () => {
await initLoadInstances()
})
const state = reactive({
nowSchema: '',
filterParam: {},
loading: {}
})
/**
* 初始化加载实例数据
*/
const initLoadInstances = () => {
emits('initLoadInstances')
}
/**
* 改变选中的数据库实例
* @param inst 选中的实例对象
*/
const changeInstance = (inst: any) => {
emits('changeInstance', inst)
}
/**
* 改变选中的数据库schema
* @param inst 选中的实例对象
* @param schema 选中的数据库schema
*/
const changeSchema = (inst: any, schema: string) => {
state.nowSchema = inst.id + schema
emits('changeSchema', inst, schema)
}
/**
* 加载schema下所有表
* @param inst 数据库实例
* @param schema database名
*/
const loadTableNames = async (inst: any, schema: string) => {
state.loading[inst.id + schema] = true
await emits('loadTableNames', inst, schema, () => {
state.loading[inst.id + schema] = false
})
}
/**
* 加载选中表数据
* @param inst 数据库实例
* @param schema database名
* @param tableName 表名
*/
const loadTableData = (inst: any, schema: string, tableName: string) => {
emits('loadTableData', inst, schema, tableName)
}
const filterTableName = (instId: number, schema: string, event?: any) => {
if (event) {
state.filterParam[instId + schema] = event.target.value
}
let param = state.filterParam[instId + schema] as string
param = param?.replace('/', '\/')
const key = instId + schema;
props.instances.tables[key].forEach((a: any) => {
a.show = param ? eval('/' + param.split('').join('[_\w]*') + '[_\w]*/ig').test(a.tableName) : true
})
}
</script>
<style lang="scss">
.instances-box {
.el-menu {
width: 100%;
}
.el-sub-menu {
.checked {
.checked-schema {
color: var(--el-color-primary);
}
}
}
.el-sub-menu__title {
padding-left: 0 !important;
height: 30px !important;
line-height: 30px !important;
}
.el-menu--vertical:not(.el-menu--collapse):not(.el-menu--popup-container) .el-sub-menu__title {
padding-right: 10px;
}
.el-menu-item {
padding-left: 0 !important;
height: 20px !important;
line-height: 20px !important;
}
.el-icon {
margin: 0;
}
.el-sub-menu__icon-arrow {
top: inherit;
right: 10px;
}
}
.instances-pop-form {
.el-form-item {
margin-bottom: unset;
}
}
</style>