mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-04 00:10:25 +08:00
feat: 代码小调整
This commit is contained in:
106
mayfly_go_web/src/views/ops/component/TagMenu.vue
Normal file
106
mayfly_go_web/src/views/ops/component/TagMenu.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<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 tags" :index="tag.tagPath" :key="tag.tagPath"
|
||||
@click.stop="clickTag(tag.tagPath)">
|
||||
<template #title>
|
||||
<el-icon>
|
||||
<FolderOpened v-if="opend[tag.tagPath]" color="#e6a23c" />
|
||||
<Folder v-else />
|
||||
</el-icon>
|
||||
<span>{{ tag.tagPath }}</span>
|
||||
</template>
|
||||
<slot :tag="tag" name="submenu"></slot>
|
||||
</el-sub-menu>
|
||||
</el-menu>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, toRefs } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
instanceMenuMaxHeight: {
|
||||
type: [Number, String],
|
||||
},
|
||||
tags: {
|
||||
type: Object, required: true
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
const state = reactive({
|
||||
instanceMenuMaxHeight: props.instanceMenuMaxHeight,
|
||||
tags: props.tags,
|
||||
opend: {},
|
||||
})
|
||||
|
||||
const {
|
||||
opend,
|
||||
} = toRefs(state)
|
||||
|
||||
const clickTag = (tagPath: string) => {
|
||||
if (state.opend[tagPath] === undefined) {
|
||||
state.opend[tagPath] = true;
|
||||
return;
|
||||
}
|
||||
const opend = state.opend[tagPath]
|
||||
state.opend[tagPath] = !opend
|
||||
}
|
||||
|
||||
</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>
|
||||
@@ -219,7 +219,8 @@
|
||||
layout="prev, pager, next, total, jumper" v-model:current-page="dt.pageNum"
|
||||
:page-size="defalutLimit"></el-pagination>
|
||||
</el-row>
|
||||
<div style=" font-size: 12px; padding: 0 10px; color: #606266"><span>{{ dt.sql }}</span></div>
|
||||
<div style=" font-size: 12px; padding: 0 10px; color: #606266"><span>{{ dt.sql }}</span>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-container>
|
||||
@@ -258,7 +259,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, nextTick, onMounted, reactive, watch } from 'vue';
|
||||
import { computed, nextTick, onMounted, reactive } from 'vue';
|
||||
import { dbApi } from './api';
|
||||
|
||||
import { format as sqlFormatter } from 'sql-formatter';
|
||||
@@ -1754,7 +1755,7 @@ const loadSchemaTables = async (inst: any, schema: string, fn: Function) => {
|
||||
} else {
|
||||
tables.forEach((a: any) => a.show = true)
|
||||
}
|
||||
fn(state.instances.tables[id+schema])
|
||||
fn(state.instances.tables[id + schema])
|
||||
}
|
||||
|
||||
// 选择数据库实例
|
||||
|
||||
@@ -1,19 +1,9 @@
|
||||
<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" ref="menuRef">
|
||||
<!-- 第一级: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>
|
||||
<tag-menu :instanceMenuMaxHeight="instanceMenuMaxHeight" :tags="instances.tags">
|
||||
<template #submenu="props">
|
||||
<!-- 第二级:数据库实例 -->
|
||||
<el-sub-menu v-for="inst in instances.tree[tag.tagId]" :index="'instance-' + inst.id"
|
||||
:key="'instance-' + inst.id" @click="changeInstance(inst, ()=>{})">
|
||||
<el-sub-menu v-for="inst in instances.tree[props.tag.tagId]" :index="'instance-' + inst.id"
|
||||
:key="'instance-' + inst.id" @click.stop="changeInstance(inst, () => { })">
|
||||
<template #title>
|
||||
<el-popover placement="right-start" title="数据库实例信息" trigger="hover" :width="210">
|
||||
<template #reference>
|
||||
@@ -34,9 +24,9 @@
|
||||
</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)">
|
||||
<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.stop="changeSchema(inst, schema)">
|
||||
<template #title>
|
||||
<el-icon>
|
||||
<Coin color="#67c23a" />
|
||||
@@ -46,9 +36,9 @@
|
||||
<!-- 第四级 01:表 -->
|
||||
<el-sub-menu :index="inst.id + schema + '-table'">
|
||||
<template #title>
|
||||
<div style="width: 100%" @click="loadTableNames(inst, schema, ()=>{})">
|
||||
<div style="width: 100%" @click="loadTableNames(inst, schema, () => { })">
|
||||
<el-icon>
|
||||
<Calendar color="#409eff"/>
|
||||
<Calendar color="#409eff" />
|
||||
</el-icon>
|
||||
<span>表</span>
|
||||
<el-icon v-show="state.loading[inst.id + schema]" class="is-loading">
|
||||
@@ -96,9 +86,8 @@
|
||||
</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)">
|
||||
<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%">
|
||||
<el-icon>
|
||||
@@ -112,16 +101,14 @@
|
||||
</el-sub-menu>
|
||||
</el-sub-menu>
|
||||
</el-sub-menu>
|
||||
</el-sub-menu>
|
||||
</el-menu>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
</tag-menu>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {nextTick, onBeforeMount, onMounted, reactive, ref, Ref, watch} from 'vue';
|
||||
import {store} from '@/store';
|
||||
import { nextTick, onBeforeMount, onMounted, reactive, ref, Ref, watch } from 'vue';
|
||||
import { store } from '@/store';
|
||||
import TagMenu from '../../component/TagMenu.vue';
|
||||
|
||||
const props = defineProps({
|
||||
instanceMenuMaxHeight: {
|
||||
@@ -158,7 +145,7 @@ const initLoadInstances = () => {
|
||||
* @param inst 选中的实例对象
|
||||
* @param fn 选中的实例对象后的回调函数
|
||||
*/
|
||||
const changeInstance = (inst : any, fn: Function) => {
|
||||
const changeInstance = (inst: any, fn: Function) => {
|
||||
emits('changeInstance', inst, fn)
|
||||
}
|
||||
/**
|
||||
@@ -178,7 +165,7 @@ const changeSchema = (inst: any, schema: string) => {
|
||||
*/
|
||||
const loadTableNames = async (inst: any, schema: string, fn: Function) => {
|
||||
state.loading[inst.id + schema] = true
|
||||
await emits('loadTableNames', inst, schema, (res: any[])=>{
|
||||
await emits('loadTableNames', inst, schema, (res: any[]) => {
|
||||
state.loading[inst.id + schema] = false
|
||||
fn && fn(res)
|
||||
})
|
||||
@@ -208,17 +195,17 @@ const filterTableName = (instId: number, schema: string, event?: any) => {
|
||||
const selectDb = async (val?: any) => {
|
||||
let info = val || store.state.sqlExecInfo.dbOptInfo;
|
||||
if (info && info.dbId) {
|
||||
const {tagPath, dbId, db} = info
|
||||
const { tagPath, dbId, db } = info
|
||||
menuRef.value.open(tagPath);
|
||||
menuRef.value.open('instance-' + dbId);
|
||||
await changeInstance({id: dbId}, () => {
|
||||
await changeInstance({ id: dbId }, () => {
|
||||
// 加载数据库
|
||||
nextTick(async () => {
|
||||
menuRef.value.open(dbId + db)
|
||||
state.nowSchema = (dbId+db)
|
||||
state.nowSchema = (dbId + db)
|
||||
// 加载集合列表
|
||||
await nextTick(async () => {
|
||||
await loadTableNames({id: dbId}, db, (res: any[]) => {
|
||||
await loadTableNames({ id: dbId }, db, (res: any[]) => {
|
||||
// 展开集合列表
|
||||
menuRef.value.open(dbId + db + '-table')
|
||||
console.log(res)
|
||||
@@ -229,11 +216,11 @@ const selectDb = async (val?: any) => {
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
onMounted(() => {
|
||||
selectDb();
|
||||
})
|
||||
|
||||
watch(()=>store.state.sqlExecInfo.dbOptInfo, async newValue => {
|
||||
watch(() => store.state.sqlExecInfo.dbOptInfo, async newValue => {
|
||||
await selectDb(newValue)
|
||||
})
|
||||
|
||||
@@ -241,46 +228,6 @@ watch(()=>store.state.sqlExecInfo.dbOptInfo, async newValue => {
|
||||
</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;
|
||||
|
||||
@@ -1,27 +1,8 @@
|
||||
<template>
|
||||
<div class="instances-box layout-aside">
|
||||
<el-row type="flex" justify="space-between">
|
||||
<el-col :span="24" :style="{
|
||||
maxHeight: state.instanceMenuMaxHeight,
|
||||
height: state.instanceMenuMaxHeight,
|
||||
overflow: 'auto'
|
||||
}" class="el-scrollbar flex-auto">
|
||||
|
||||
<el-menu background-color="transparent" ref="menuRef">
|
||||
<!-- 第一级: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="'mongo-instance-' + inst.id"
|
||||
:key="'mongo-instance-' + inst.id"
|
||||
@click.prevent="changeInstance(inst, ()=>{})"
|
||||
>
|
||||
<tag-menu :instanceMenuMaxHeight="state.instanceMenuMaxHeight" :tags="instances.tags">
|
||||
<template #submenu="props">
|
||||
<el-sub-menu v-for="inst in instances.tree[props.tag.tagId]" :index="'mongo-instance-' + inst.id"
|
||||
:key="'mongo-instance-' + inst.id" @click.stop="changeInstance(inst, () => { })">
|
||||
<template #title>
|
||||
<el-popover placement="right-start" title="mongo数据库实例信息" trigger="hover" :width="210">
|
||||
<template #reference>
|
||||
@@ -38,9 +19,9 @@
|
||||
</el-popover>
|
||||
</template>
|
||||
<!-- 第三级:数据库 -->
|
||||
<el-sub-menu v-for="db in instances.dbs[inst.id]" :index="inst.id + db.Name"
|
||||
:key="inst.id + db.Name" :class="state.nowSchema === (inst.id + db.Name) && 'checked'"
|
||||
@click.prevent="changeSchema(inst, db.Name)">
|
||||
<el-sub-menu v-for="db in instances.dbs[inst.id]" :index="inst.id + db.Name" :key="inst.id + db.Name"
|
||||
:class="state.nowSchema === (inst.id + db.Name) && 'checked'"
|
||||
@click.stop="changeSchema(inst, db.Name)">
|
||||
<template #title>
|
||||
<el-icon>
|
||||
<Coin color="#67c23a" />
|
||||
@@ -55,9 +36,9 @@
|
||||
<!-- 第四级 01:表 -->
|
||||
<el-sub-menu :index="inst.id + db.Name + '-table'">
|
||||
<template #title>
|
||||
<div style="width: 100%" @click="loadTableNames(inst, db.Name, ()=>{})">
|
||||
<div style="width: 100%" @click="loadTableNames(inst, db.Name, () => { })">
|
||||
<el-icon>
|
||||
<Calendar color="#409eff"/>
|
||||
<Calendar color="#409eff" />
|
||||
</el-icon>
|
||||
<span>集合</span>
|
||||
<el-icon v-show="state.loading[inst.id + db.Name]" class="is-loading">
|
||||
@@ -93,17 +74,15 @@
|
||||
</el-sub-menu>
|
||||
</el-sub-menu>
|
||||
</el-sub-menu>
|
||||
</el-sub-menu>
|
||||
</el-menu>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
</tag-menu>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {nextTick, onBeforeMount, onMounted, reactive, ref, Ref, watch} from 'vue';
|
||||
import { nextTick, onBeforeMount, onMounted, reactive, ref, Ref, watch } from 'vue';
|
||||
import { formatByteSize } from '@/common/utils/format';
|
||||
import {store} from '@/store';
|
||||
import { store } from '@/store';
|
||||
import TagMenu from '../component/TagMenu.vue';
|
||||
|
||||
const props = defineProps({
|
||||
instances: {
|
||||
@@ -119,7 +98,7 @@ onBeforeMount(async () => {
|
||||
})
|
||||
|
||||
const setHeight = () => {
|
||||
state.instanceMenuMaxHeight = window.innerHeight - 140 + 'px';
|
||||
state.instanceMenuMaxHeight = window.innerHeight - 115 + 'px';
|
||||
}
|
||||
|
||||
const menuRef = ref(null) as Ref
|
||||
@@ -144,7 +123,7 @@ const initLoadInstances = () => {
|
||||
* @param inst 选中的实例对象
|
||||
* @param fn 选中的实例对象后的回调事件
|
||||
*/
|
||||
const changeInstance = (inst : any, fn: Function) => {
|
||||
const changeInstance = (inst: any, fn: Function) => {
|
||||
emits('changeInstance', inst, fn)
|
||||
}
|
||||
/**
|
||||
@@ -164,7 +143,7 @@ const changeSchema = (inst: any, schema: string) => {
|
||||
*/
|
||||
const loadTableNames = async (inst: any, schema: string, fn: Function) => {
|
||||
state.loading[inst.id + schema] = true
|
||||
await emits('loadTableNames', inst, schema, (res: any)=>{
|
||||
await emits('loadTableNames', inst, schema, (res: any) => {
|
||||
state.loading[inst.id + schema] = false
|
||||
fn && fn(res)
|
||||
})
|
||||
@@ -194,20 +173,20 @@ const filterTableName = (instId: number, schema: string, event?: any) => {
|
||||
const selectDb = async (val?: any) => {
|
||||
let info = val || store.state.mongoDbOptInfo.dbOptInfo;
|
||||
if (info && info.dbId) {
|
||||
const {tagPath, dbId, db} = info
|
||||
const { tagPath, dbId, db } = info
|
||||
menuRef.value.open(tagPath);
|
||||
menuRef.value.open('mongo-instance-' + dbId);
|
||||
await changeInstance({id: dbId}, () => {
|
||||
await changeInstance({ id: dbId }, () => {
|
||||
// 加载数据库
|
||||
nextTick(async () => {
|
||||
menuRef.value.open(dbId + db)
|
||||
// 加载集合列表
|
||||
await nextTick(async () => {
|
||||
await loadTableNames({id: dbId}, db, (res: any[]) => {
|
||||
await loadTableNames({ id: dbId }, db, (res: any[]) => {
|
||||
// 展开集合列表
|
||||
menuRef.value.open(dbId + db + '-table')
|
||||
// 加载第一张集合数据
|
||||
loadTableData({id: dbId}, db, res[0].tableName)
|
||||
loadTableData({ id: dbId }, db, res[0].tableName)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -215,57 +194,17 @@ const selectDb = async (val?: any) => {
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
onMounted(() => {
|
||||
selectDb();
|
||||
})
|
||||
|
||||
watch(()=>store.state.mongoDbOptInfo.dbOptInfo, async newValue => {
|
||||
watch(() => store.state.mongoDbOptInfo.dbOptInfo, async newValue => {
|
||||
await selectDb(newValue)
|
||||
})
|
||||
|
||||
</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;
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card>
|
||||
<el-row>
|
||||
<el-col :span="3">
|
||||
<redis-instance-tree
|
||||
@init-load-instances="initLoadInstances"
|
||||
@change-instance="changeInstance"
|
||||
@change-schema="loadInitSchema"
|
||||
:instances="state.instances"
|
||||
/>
|
||||
<el-col :span="4">
|
||||
<redis-instance-tree @init-load-instances="initLoadInstances" @change-instance="changeInstance"
|
||||
@change-schema="loadInitSchema" :instances="state.instances" />
|
||||
</el-col>
|
||||
<el-col :span="19" style="border-left: 1px solid var(--el-card-border-color)">
|
||||
<el-col :span="20" style="border-left: 1px solid var(--el-card-border-color);">
|
||||
<el-col class="mt10">
|
||||
<el-form class="search-form" label-position="right" :inline="true" label-width="60px">
|
||||
<el-form-item label="key" label-width="40px">
|
||||
@@ -42,7 +37,8 @@
|
||||
</div>
|
||||
</el-form>
|
||||
</el-col>
|
||||
<el-table v-loading="state.loading" :data="state.keys" stripe :highlight-current-row="true" style="cursor: pointer">
|
||||
<el-table v-loading="state.loading" :data="state.keys" stripe :highlight-current-row="true"
|
||||
style="cursor: pointer">
|
||||
<el-table-column show-overflow-tooltip prop="key" label="key"></el-table-column>
|
||||
<el-table-column prop="type" label="type" width="80">
|
||||
<template #default="scope">
|
||||
@@ -66,8 +62,6 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
</el-card>
|
||||
|
||||
<div style="text-align: center; margin-top: 10px"></div>
|
||||
|
||||
<hash-value v-model:visible="hashValueDialog.visible" :operationType="dataEdit.operationType"
|
||||
@@ -142,7 +136,7 @@ const state = reactive({
|
||||
},
|
||||
keys: [],
|
||||
dbsize: 0,
|
||||
instances:{tags:{}, tree:{}, dbs:{}, tables:{}}
|
||||
instances: { tags: {}, tree: {}, dbs: {}, tables: {} }
|
||||
});
|
||||
|
||||
const {
|
||||
@@ -319,31 +313,31 @@ const getTypeColor = (type: string) => {
|
||||
};
|
||||
|
||||
|
||||
const initLoadInstances = async ()=>{
|
||||
const initLoadInstances = async () => {
|
||||
const res = await redisApi.redisList.request({});
|
||||
if(!res.total) return
|
||||
state.instances = {tags:{}, tree:{}, dbs:{}, tables:{}} ; // 初始化变量
|
||||
if (!res.total) return
|
||||
state.instances = { tags: {}, tree: {}, dbs: {}, tables: {} }; // 初始化变量
|
||||
for (const db of res.list) {
|
||||
let arr = state.instances.tree[db.tagId] || []
|
||||
const {tagId, tagPath} = db
|
||||
const { tagId, tagPath } = db
|
||||
// tags
|
||||
state.instances.tags[db.tagId]={tagId, tagPath}
|
||||
state.instances.tags[db.tagId] = { tagId, tagPath }
|
||||
// 实例
|
||||
arr.push(db)
|
||||
state.instances.tree[db.tagId] = arr;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const changeInstance = async (inst: any, fn: Function) => {
|
||||
let dbs = state.instances.dbs[inst.id] || []
|
||||
if(dbs.length <=0 ){
|
||||
const res = await redisApi.redisInfo.request({ id: inst.id, host:inst.host });
|
||||
for (let db in res.Keyspace){
|
||||
dbs.push({
|
||||
name: db,
|
||||
keys: res.Keyspace[db]?.split(',')[0]?.split('=')[1] || 0
|
||||
let dbs = inst.db.split(',').map((x: string) => {
|
||||
return { name: `db${x}`, keys: 0 }
|
||||
})
|
||||
const res = await redisApi.redisInfo.request({ id: inst.id, host: inst.host, section: "Keyspace" });
|
||||
for (let db in res.Keyspace) {
|
||||
for (let d of dbs) {
|
||||
if (db == d.name) {
|
||||
d.keys = res.Keyspace[db]?.split(',')[0]?.split('=')[1] || 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,9 +346,9 @@ const changeInstance = async (inst: any, fn: Function) => {
|
||||
}
|
||||
|
||||
/** 初始化加载db数据 */
|
||||
const loadInitSchema = (inst: any, schema: string)=>{
|
||||
const loadInitSchema = (inst: any, schema: string) => {
|
||||
state.scanParam.id = inst.id
|
||||
state.scanParam.db = schema.replace('db','')
|
||||
state.scanParam.db = schema.replace('db', '')
|
||||
scan()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,68 +1,46 @@
|
||||
<template>
|
||||
<div class="instances-box layout-aside">
|
||||
<el-row type="flex" justify="space-between">
|
||||
<el-col :span="24" :style="{
|
||||
maxHeight: state.instanceMenuMaxHeight,
|
||||
height: state.instanceMenuMaxHeight,
|
||||
overflow:'auto'
|
||||
}" class="el-scrollbar flex-auto">
|
||||
|
||||
<el-menu background-color="transparent" ref="menuRef">
|
||||
<!-- 第一级:tag -->
|
||||
<el-sub-menu v-for="tag of instances.tags" :index="tag.tagPath" :key="tag.tagPath">
|
||||
<tag-menu :instanceMenuMaxHeight="state.instanceMenuMaxHeight" :tags="instances.tags">
|
||||
<template #submenu="props">
|
||||
<el-sub-menu v-for="inst in instances.tree[props.tag.tagId]" :index="'redis-instance-' + inst.id"
|
||||
:key="'redis-instance-' + inst.id" @click.stop="changeInstance(inst)">
|
||||
<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="'redis-instance-' + inst.id"
|
||||
:key="'redis-instance-' + inst.id"
|
||||
@click.prevent="changeInstance(inst)"
|
||||
>
|
||||
<template #title>
|
||||
<el-popover
|
||||
placement="right-start"
|
||||
title="mongo数据库实例信息"
|
||||
trigger="hover"
|
||||
:width="210"
|
||||
>
|
||||
<el-popover placement="right-start" title="redis实例信息" trigger="hover" :width="210">
|
||||
<template #reference>
|
||||
<span> <el-icon><MostlyCloudy color="#409eff"/></el-icon>{{ inst.name }}</span>
|
||||
<span> <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.name}}</el-form-item>
|
||||
<el-form-item label="链接:">{{inst.host}}</el-form-item>
|
||||
<el-form-item label="名称:">{{ inst.name }}</el-form-item>
|
||||
<el-form-item label="链接:">{{ inst.host }}</el-form-item>
|
||||
<el-form-item label="备注:">{{ inst.remark }}</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
</el-popover>
|
||||
</template>
|
||||
<!-- 第三级:数据库 -->
|
||||
<el-sub-menu v-for="db in instances.dbs[inst.id]"
|
||||
:index="inst.id + db.name"
|
||||
:key="inst.id + db.name"
|
||||
:class="state.nowSchema === (inst.id+db.name) && 'checked'"
|
||||
@click.prevent="changeSchema(inst, db.name)"
|
||||
>
|
||||
<el-menu-item v-for="db in instances.dbs[inst.id]" :index="inst.id + db.name" :key="inst.id + db.name"
|
||||
:class="state.nowSchema === (inst.id + db.name) && 'checked'"
|
||||
@click="changeSchema(inst, db.name)">
|
||||
<template #title>
|
||||
<el-icon><Coin color="#67c23a"/></el-icon>
|
||||
<el-icon>
|
||||
<Coin color="#67c23a" />
|
||||
</el-icon>
|
||||
<span class="checked-schema">
|
||||
{{ db.name }} [{{db.keys}}]
|
||||
{{ db.name }} [{{ db.keys }}]
|
||||
</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</el-sub-menu>
|
||||
</el-sub-menu>
|
||||
</el-sub-menu>
|
||||
</el-menu>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
</tag-menu>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {onBeforeMount, onMounted, reactive, Ref, ref, watch} from 'vue';
|
||||
import {store} from '@/store';
|
||||
import { onBeforeMount, onMounted, reactive, Ref, ref, watch } from 'vue';
|
||||
import { store } from '@/store';
|
||||
import TagMenu from '../component/TagMenu.vue';
|
||||
|
||||
defineProps({
|
||||
instances: {
|
||||
@@ -70,15 +48,15 @@ defineProps({
|
||||
},
|
||||
})
|
||||
|
||||
const emits = defineEmits(['initLoadInstances','changeInstance','changeSchema'])
|
||||
const emits = defineEmits(['initLoadInstances', 'changeInstance', 'changeSchema'])
|
||||
|
||||
onBeforeMount(async ()=>{
|
||||
onBeforeMount(async () => {
|
||||
await initLoadInstances()
|
||||
setHeight()
|
||||
})
|
||||
|
||||
const setHeight = () => {
|
||||
state.instanceMenuMaxHeight = window.innerHeight - 140 + 'px';
|
||||
state.instanceMenuMaxHeight = window.innerHeight - 115 + 'px';
|
||||
}
|
||||
|
||||
const menuRef = ref(null) as Ref;
|
||||
@@ -102,7 +80,7 @@ const initLoadInstances = () => {
|
||||
* @param inst 选中的实例对象
|
||||
* @param fn 选中的实例后的回调函数
|
||||
*/
|
||||
const changeInstance = (inst : any, fn?:Function) => {
|
||||
const changeInstance = (inst: any, fn?: Function) => {
|
||||
emits('changeInstance', inst, fn)
|
||||
}
|
||||
/**
|
||||
@@ -110,7 +88,7 @@ const changeInstance = (inst : any, fn?:Function) => {
|
||||
* @param inst 选中的实例对象
|
||||
* @param schema 选中的数据库schema
|
||||
*/
|
||||
const changeSchema = (inst : any, schema: string) => {
|
||||
const changeSchema = (inst: any, schema: string) => {
|
||||
state.nowSchema = inst.id + schema
|
||||
emits('changeSchema', inst, schema)
|
||||
}
|
||||
@@ -118,61 +96,28 @@ const changeSchema = (inst : any, schema: string) => {
|
||||
const selectDb = async (val?: any) => {
|
||||
const info = val || store.state.redisDbOptInfo.dbOptInfo
|
||||
if (info && info.dbId) {
|
||||
const {tagPath, dbId} = info
|
||||
const { tagPath, dbId } = info
|
||||
menuRef.value.open(tagPath);
|
||||
menuRef.value.open('redis-instance-' + dbId);
|
||||
await changeInstance({id: dbId}, async (dbs: any[]) => {
|
||||
await changeSchema({id: dbId}, dbs[0]?.name)
|
||||
await changeInstance({ id: dbId }, async (dbs: any[]) => {
|
||||
await changeSchema({ id: dbId }, dbs[0]?.name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
onMounted(() => {
|
||||
selectDb();
|
||||
})
|
||||
|
||||
watch(()=>store.state.redisDbOptInfo.dbOptInfo, async newValue =>{
|
||||
watch(() => store.state.redisDbOptInfo.dbOptInfo, async newValue => {
|
||||
await selectDb(newValue)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.instances-box {
|
||||
.el-menu{
|
||||
width: 275px;
|
||||
}
|
||||
.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{
|
||||
.instances-pop-form {
|
||||
.el-form-item {
|
||||
margin-bottom: unset;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,36 +76,42 @@ func (r *Redis) RedisInfo(rc *req.Ctx) {
|
||||
g := rc.GinCtx
|
||||
ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")), 0)
|
||||
|
||||
var res string
|
||||
var err error
|
||||
|
||||
section := rc.GinCtx.Query("section")
|
||||
mode := ri.Info.Mode
|
||||
ctx := context.Background()
|
||||
var redisCli *redis.Client
|
||||
|
||||
if mode == "" || mode == entity.RedisModeStandalone || mode == entity.RedisModeSentinel {
|
||||
res, err = ri.Cli.Info(ctx).Result()
|
||||
redisCli = ri.Cli
|
||||
} else if mode == entity.RedisModeCluster {
|
||||
host := rc.GinCtx.Query("host")
|
||||
biz.NotEmpty(host, "集群模式host信息不能为空")
|
||||
clusterClient := ri.ClusterCli
|
||||
var redisClient *redis.Client
|
||||
// 遍历集群的master节点找到该redis client
|
||||
clusterClient.ForEachMaster(ctx, func(ctx context.Context, client *redis.Client) error {
|
||||
if host == client.Options().Addr {
|
||||
redisClient = client
|
||||
redisCli = client
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if redisClient == nil {
|
||||
if redisCli == nil {
|
||||
// 遍历集群的slave节点找到该redis client
|
||||
clusterClient.ForEachSlave(ctx, func(ctx context.Context, client *redis.Client) error {
|
||||
if host == client.Options().Addr {
|
||||
redisClient = client
|
||||
redisCli = client
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
biz.NotNil(redisClient, "该实例不在该集群中")
|
||||
res, err = redisClient.Info(ctx).Result()
|
||||
biz.NotNil(redisCli, "该实例不在该集群中")
|
||||
}
|
||||
|
||||
var res string
|
||||
var err error
|
||||
if section == "" {
|
||||
res, err = ri.Cli.Info(ctx).Result()
|
||||
} else {
|
||||
res, err = ri.Cli.Info(ctx, section).Result()
|
||||
}
|
||||
|
||||
biz.ErrIsNilAppendErr(err, "获取redis info失败: %s")
|
||||
|
||||
@@ -7,8 +7,6 @@ type Redis struct {
|
||||
Name *string `json:"name"`
|
||||
Host *string `json:"host"`
|
||||
Db string `json:"db"`
|
||||
ProjectId *int64 `json:"projectId"`
|
||||
Project *string `json:"project"`
|
||||
Mode *string `json:"mode"`
|
||||
EnableSshTunnel *int8 `json:"enableSshTunnel"` // 是否启用ssh隧道
|
||||
SshTunnelMachineId *uint64 `json:"sshTunnelMachineId"` // ssh隧道机器id
|
||||
|
||||
Reference in New Issue
Block a user