mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-04 00:10:25 +08:00
feat:mongo新增实例树
This commit is contained in:
@@ -1,69 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="toolbar">
|
<el-row>
|
||||||
<el-row type="flex" justify="space-between">
|
<el-col :span="4">
|
||||||
<el-col :span="24">
|
<mongo-instance-tree
|
||||||
<el-form class="search-form" label-position="right" :inline="true">
|
@init-load-instances="loadInstances"
|
||||||
<el-form-item label="标签">
|
@change-instance="changeInstance"
|
||||||
<el-select @change="changeTag" @focus="getTags" v-model="query.tagPath" placeholder="请选择标签"
|
@change-schema="changeDatabase"
|
||||||
filterable style="width: 250px">
|
@load-table-names="loadTableNames"
|
||||||
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
|
@load-table-data="changeCollection"
|
||||||
</el-select>
|
:instances="state.instances"/>
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="实例" label-width="40px">
|
|
||||||
<el-select v-model="mongoId" placeholder="请选择mongo" @change="changeMongo">
|
|
||||||
<el-option v-for="item in mongoList" :key="item.id" :label="item.name" :value="item.id">
|
|
||||||
<span style="float: left">{{ item.name }}</span>
|
|
||||||
<span style="float: right; color: #8492a6; margin-left: 6px; font-size: 13px">{{ `
|
|
||||||
[${item.uri}]`
|
|
||||||
}}</span>
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="库" label-width="20px">
|
|
||||||
<el-select v-model="database" placeholder="请选择库" @change="changeDatabase" filterable>
|
|
||||||
<el-option v-for="item in databases" :key="item.Name" :label="item.Name"
|
|
||||||
:value="item.Name">
|
|
||||||
<span style="float: left">{{ item.Name }}</span>
|
|
||||||
<span style="float: right; color: #8492a6; margin-left: 4px; font-size: 13px">{{
|
|
||||||
` [${formatByteSize(item.SizeOnDisk)}]`
|
|
||||||
}}</span>
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="集合" label-width="40px">
|
|
||||||
<el-select v-model="collection" placeholder="请选择集合" @change="changeCollection" filterable>
|
|
||||||
<el-option v-for="item in collections" :key="item" :label="item" :value="item">
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
<el-col :span="20">
|
||||||
</div>
|
|
||||||
|
|
||||||
<el-container id="data-exec" style="border: 1px solid #eee; margin-top: 1px">
|
<el-container id="data-exec" style="border: 1px solid #eee; margin-top: 1px">
|
||||||
<el-tabs @tab-remove="removeDataTab" @tab-click="onDataTabClick" style="width: 100%; margin-left: 5px"
|
<el-tabs @tab-remove="removeDataTab" @tab-click="onDataTabClick" style="width: 100%; margin-left: 5px"
|
||||||
v-model="activeName">
|
v-model="state.activeName">
|
||||||
<el-tab-pane closable v-for="dt in dataTabs" :key="dt.name" :label="dt.name" :name="dt.name">
|
<el-tab-pane closable v-for="dt in state.dataTabs" :key="dt.key" :label="dt.label" :name="dt.key">
|
||||||
<el-row v-if="mongoId">
|
|
||||||
<el-link @click="findCommand(activeName)" icon="refresh" :underline="false" class="ml5">
|
|
||||||
</el-link>
|
|
||||||
<el-link @click="showInsertDocDialog" class="ml5" type="primary" icon="plus" :underline="false">
|
|
||||||
</el-link>
|
|
||||||
</el-row>
|
|
||||||
<el-row class="mt5 mb5">
|
<el-row class="mt5 mb5">
|
||||||
|
<el-col :span="2">
|
||||||
|
<el-link @click="findCommand(state.activeName)" icon="refresh" :underline="false" class="">
|
||||||
|
</el-link>
|
||||||
|
<el-link @click="showInsertDocDialog" class="" type="primary" icon="plus" :underline="false">
|
||||||
|
</el-link>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="22">
|
||||||
<el-input ref="findParamInputRef" v-model="dt.findParamStr" placeholder="点击输入相应查询条件"
|
<el-input ref="findParamInputRef" v-model="dt.findParamStr" placeholder="点击输入相应查询条件"
|
||||||
@focus="showFindDialog(dt.name)">
|
@focus="showFindDialog(dt.key)">
|
||||||
<template #prepend>查询参数</template>
|
<template #prepend>查询参数</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="6" v-for="item in dt.datas" :key="item">
|
<el-col :span="6" v-for="item in dt.datas" :key="item">
|
||||||
<el-card :body-style="{ padding: '0px', position: 'relative' }">
|
<el-card :body-style="{ padding: '0px', position: 'relative' }">
|
||||||
<el-input type="textarea" v-model="item.value" :rows="12" />
|
<el-input type="textarea" v-model="item.value" :rows="10" />
|
||||||
<div style="padding: 3px; float: right" class="mr5 mongo-doc-btns">
|
<div style="padding: 3px; float: right" class="mr5 mongo-doc-btns">
|
||||||
<div>
|
<div>
|
||||||
<el-link @click="onJsonEditor(item)" :underline="false" type="success"
|
<el-link @click="onJsonEditor(item)" :underline="false" type="success"
|
||||||
@@ -90,6 +60,9 @@
|
|||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</el-container>
|
</el-container>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
</el-row>
|
||||||
|
|
||||||
<el-dialog width="600px" title="find参数" v-model="findDialog.visible">
|
<el-dialog width="600px" title="find参数" v-model="findDialog.visible">
|
||||||
<el-form label-width="70px">
|
<el-form label-width="70px">
|
||||||
@@ -116,7 +89,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<el-dialog width="60%" :title="`新增'${activeName}'集合文档`" v-model="insertDocDialog.visible"
|
<el-dialog width="60%" :title="`新增'${state.activeName}'集合文档`" v-model="insertDocDialog.visible"
|
||||||
:close-on-click-modal="false">
|
:close-on-click-modal="false">
|
||||||
<monaco-editor v-model="insertDocDialog.doc" language="json" />
|
<monaco-editor v-model="insertDocDialog.doc" language="json" />
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -127,9 +100,9 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<el-dialog width="60%" title="json编辑器" v-model="jsoneditorDialog.visible" @close="onCloseJsonEditDialog"
|
<el-dialog width="60%" title="json编辑器" v-model="jsonEditorDialog.visible" @close="onCloseJsonEditDialog"
|
||||||
:close-on-click-modal="false">
|
:close-on-click-modal="false">
|
||||||
<monaco-editor v-model="jsoneditorDialog.doc" language="json" />
|
<monaco-editor v-model="jsonEditorDialog.doc" language="json" />
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<div style="text-align: center; margin-top: 10px"></div>
|
<div style="text-align: center; margin-top: 10px"></div>
|
||||||
@@ -138,14 +111,13 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {mongoApi} from './api';
|
import {mongoApi} from './api';
|
||||||
import { toRefs, ref, reactive, watch } from 'vue';
|
import {reactive, ref, toRefs} from 'vue';
|
||||||
import {ElMessage} from 'element-plus';
|
import {ElMessage} from 'element-plus';
|
||||||
|
|
||||||
import { isTrue, notBlank, notNull } from '@/common/assert';
|
import {isTrue, notBlank} from '@/common/assert';
|
||||||
import { formatByteSize } from '@/common/utils/format';
|
|
||||||
import { tagApi } from '../tag/api.ts';
|
|
||||||
import {useStore} from '@/store/index.ts';
|
import {useStore} from '@/store/index.ts';
|
||||||
import MonacoEditor from '@/components/monaco/MonacoEditor.vue';
|
import MonacoEditor from '@/components/monaco/MonacoEditor.vue';
|
||||||
|
import MongoInstanceTree from '@/views/ops/mongo/MongoInstanceTree.vue';
|
||||||
|
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
const findParamInputRef: any = ref(null);
|
const findParamInputRef: any = ref(null);
|
||||||
@@ -159,8 +131,6 @@ const state = reactive({
|
|||||||
database: '', // 当前选择操作的库
|
database: '', // 当前选择操作的库
|
||||||
collection: '', //当前选中的collection
|
collection: '', //当前选中的collection
|
||||||
activeName: '', // 当前操作的tab
|
activeName: '', // 当前操作的tab
|
||||||
databases: [] as any,
|
|
||||||
collections: [] as any,
|
|
||||||
dataTabs: {} as any, // 数据tabs
|
dataTabs: {} as any, // 数据tabs
|
||||||
findDialog: {
|
findDialog: {
|
||||||
visible: false,
|
visible: false,
|
||||||
@@ -175,77 +145,49 @@ const state = reactive({
|
|||||||
visible: false,
|
visible: false,
|
||||||
doc: '',
|
doc: '',
|
||||||
},
|
},
|
||||||
jsoneditorDialog: {
|
jsonEditorDialog: {
|
||||||
visible: false,
|
visible: false,
|
||||||
doc: '',
|
doc: '',
|
||||||
item: {} as any,
|
item: {} as any,
|
||||||
},
|
},
|
||||||
|
instances:{tags:{}, tree:{}, dbs:{}, tables:{}}
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
tags,
|
|
||||||
mongoList,
|
|
||||||
query,
|
|
||||||
mongoId,
|
|
||||||
database,
|
|
||||||
collection,
|
|
||||||
activeName,
|
|
||||||
databases,
|
|
||||||
collections,
|
|
||||||
dataTabs,
|
|
||||||
findDialog,
|
findDialog,
|
||||||
insertDocDialog,
|
insertDocDialog,
|
||||||
jsoneditorDialog,
|
jsonEditorDialog,
|
||||||
} = toRefs(state)
|
} = toRefs(state)
|
||||||
|
|
||||||
const searchMongo = async () => {
|
const changeInstance = async (inst: any) => {
|
||||||
notNull(state.query.tagPath, '请先选择标签');
|
if (inst) {
|
||||||
const res = await mongoApi.mongoList.request(state.query);
|
if (!state.instances.dbs[inst.id]) {
|
||||||
state.mongoList = res.list;
|
const res = await mongoApi.databases.request({id: inst.id});
|
||||||
};
|
state.instances.dbs[inst.id] = res.Databases;
|
||||||
|
console.log(res.Databases)
|
||||||
const changeTag = (tagPath: string) => {
|
|
||||||
state.databases = [];
|
|
||||||
state.collections = [];
|
|
||||||
state.mongoId = null;
|
|
||||||
state.collection = '';
|
|
||||||
state.database = '';
|
|
||||||
state.dataTabs = {};
|
|
||||||
if (tagPath != null) {
|
|
||||||
searchMongo();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeDatabase = async (inst: any, database: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTags = async () => {
|
const loadTableNames = async (inst: any, database: string, fn:Function) => {
|
||||||
state.tags = await tagApi.getAccountTags.request(null);
|
let tbs = await mongoApi.collections.request({ id: inst.id, database });
|
||||||
};
|
let tables = [];
|
||||||
|
for(let tb of tbs){
|
||||||
|
tables.push({tableName: tb, show: true})
|
||||||
|
}
|
||||||
|
state.instances.tables[inst.id+database] = tables
|
||||||
|
fn()
|
||||||
|
}
|
||||||
|
|
||||||
const changeMongo = () => {
|
const changeCollection = (inst: any, schema: string, collection: string) => {
|
||||||
state.databases = [];
|
state.collection = collection
|
||||||
state.collections = [];
|
state.mongoId = inst.id
|
||||||
state.dataTabs = {};
|
state.database = schema
|
||||||
getDatabases();
|
let key = inst.id + schema +collection
|
||||||
};
|
let dataTab = state.dataTabs[key];
|
||||||
|
|
||||||
const getDatabases = async () => {
|
|
||||||
const res = await mongoApi.databases.request({ id: state.mongoId });
|
|
||||||
state.databases = res.Databases;
|
|
||||||
};
|
|
||||||
|
|
||||||
const changeDatabase = () => {
|
|
||||||
state.collections = [];
|
|
||||||
state.collection = '';
|
|
||||||
state.dataTabs = {};
|
|
||||||
getCollections();
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCollections = async () => {
|
|
||||||
state.collections = await mongoApi.collections.request({ id: state.mongoId, database: state.database });
|
|
||||||
};
|
|
||||||
|
|
||||||
const changeCollection = () => {
|
|
||||||
const collection = state.collection;
|
|
||||||
let dataTab = state.dataTabs[collection];
|
|
||||||
if (!dataTab) {
|
if (!dataTab) {
|
||||||
// 默认查询参数
|
// 默认查询参数
|
||||||
const findParam = {
|
const findParam = {
|
||||||
@@ -253,29 +195,30 @@ const changeCollection = () => {
|
|||||||
sort: '{"_id": -1}',
|
sort: '{"_id": -1}',
|
||||||
skip: 0,
|
skip: 0,
|
||||||
limit: 12,
|
limit: 12,
|
||||||
},
|
};
|
||||||
dataTab = {
|
state.dataTabs[key] = {
|
||||||
name: collection,
|
key: key,
|
||||||
|
label: schema+'.'+collection,
|
||||||
|
name: inst.id+schema+collection,
|
||||||
datas: [],
|
datas: [],
|
||||||
findParamStr: JSON.stringify(findParam),
|
findParamStr: JSON.stringify(findParam),
|
||||||
findParam,
|
findParam,
|
||||||
};
|
};
|
||||||
state.dataTabs[collection] = dataTab;
|
|
||||||
}
|
}
|
||||||
state.activeName = collection;
|
state.activeName = key;
|
||||||
findCommand(collection);
|
findCommand(key);
|
||||||
};
|
};
|
||||||
|
|
||||||
const showFindDialog = (collection: string) => {
|
const showFindDialog = (key: string) => {
|
||||||
// 获取当前tab的索引位置,将其输入框失去焦点,防止输入以及重复获取焦点
|
// 获取当前tab的索引位置,将其输入框失去焦点,防止输入以及重复获取焦点
|
||||||
const dataTabNames = Object.keys(state.dataTabs);
|
const dataTabNames = Object.keys(state.dataTabs);
|
||||||
for (let i = 0; i < dataTabNames.length; i++) {
|
for (let i = 0; i < dataTabNames.length; i++) {
|
||||||
if (collection == dataTabNames[i]) {
|
if (key == dataTabNames[i]) {
|
||||||
findParamInputRef.value[i].blur();
|
findParamInputRef.value[i].blur();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.findDialog.findParam = state.dataTabs[collection].findParam;
|
state.findDialog.findParam = state.dataTabs[key].findParam;
|
||||||
state.findDialog.visible = true;
|
state.findDialog.visible = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -286,8 +229,8 @@ const confirmFindDialog = () => {
|
|||||||
findCommand(state.activeName);
|
findCommand(state.activeName);
|
||||||
};
|
};
|
||||||
|
|
||||||
const findCommand = async (collection: string) => {
|
const findCommand = async (key: string) => {
|
||||||
const dataTab = state.dataTabs[collection];
|
const dataTab = state.dataTabs[key];
|
||||||
const findParma = dataTab.findParam;
|
const findParma = dataTab.findParam;
|
||||||
let filter, sort;
|
let filter, sort;
|
||||||
try {
|
try {
|
||||||
@@ -300,13 +243,13 @@ const findCommand = async (collection: string) => {
|
|||||||
const datas = await mongoApi.findCommand.request({
|
const datas = await mongoApi.findCommand.request({
|
||||||
id: state.mongoId,
|
id: state.mongoId,
|
||||||
database: state.database,
|
database: state.database,
|
||||||
collection,
|
collection: state.collection,
|
||||||
filter,
|
filter,
|
||||||
sort,
|
sort,
|
||||||
limit: findParma.limit || 12,
|
limit: findParma.limit || 12,
|
||||||
skip: findParma.skip || 0,
|
skip: findParma.skip || 0,
|
||||||
});
|
});
|
||||||
state.dataTabs[collection].datas = wrapDatas(datas);
|
state.dataTabs[key].datas = wrapDatas(datas);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -357,13 +300,13 @@ const onInsertDoc = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onJsonEditor = (item: any) => {
|
const onJsonEditor = (item: any) => {
|
||||||
state.jsoneditorDialog.item = item;
|
state.jsonEditorDialog.item = item;
|
||||||
state.jsoneditorDialog.doc = item.value;
|
state.jsonEditorDialog.doc = item.value;
|
||||||
state.jsoneditorDialog.visible = true;
|
state.jsonEditorDialog.visible = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCloseJsonEditDialog = () => {
|
const onCloseJsonEditDialog = () => {
|
||||||
state.jsoneditorDialog.item.value = JSON.stringify(JSON.parse(state.jsoneditorDialog.doc), null, 4);
|
state.jsonEditorDialog.item.value = JSON.stringify(JSON.parse(state.jsonEditorDialog.doc), null, 4);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSaveDoc = async (doc: string) => {
|
const onSaveDoc = async (doc: string) => {
|
||||||
@@ -440,31 +383,21 @@ const removeDataTab = (targetName: string) => {
|
|||||||
delete state.dataTabs[targetName];
|
delete state.dataTabs[targetName];
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载选中的tagPath
|
const loadInstances = async () => {
|
||||||
const setSelects = async (mongoDbOptInfo: any) => {
|
const res = await mongoApi.mongoList.request({pageNum: 1, pageSize: 1000,});
|
||||||
const { tagPath, dbId, db } = mongoDbOptInfo.dbOptInfo;
|
if(!res.total) return
|
||||||
state.query.tagPath = tagPath
|
state.instances = {tags:{}, tree:{}, dbs:{}, tables:{}} ; // 初始化变量
|
||||||
await searchMongo();
|
for (const db of res.list) {
|
||||||
state.mongoId = dbId
|
let arr = state.instances.tree[db.tagId] || []
|
||||||
await getDatabases();
|
const {tagId, tagPath} = db
|
||||||
state.database = db
|
// tags
|
||||||
await getCollections();
|
state.instances.tags[db.tagId]={tagId, tagPath}
|
||||||
if (state.collection) {
|
// 实例
|
||||||
state.collection = ''
|
arr.push(db)
|
||||||
state.dataTabs = {}
|
state.instances.tree[db.tagId] = arr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断如果有数据则加载下拉选项
|
|
||||||
let mongoDbOptInfo = store.state.mongoDbOptInfo
|
|
||||||
if (mongoDbOptInfo.dbOptInfo.tagPath) {
|
|
||||||
setSelects(mongoDbOptInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听选中操作的db变化,并加载下拉选项
|
|
||||||
watch(store.state.mongoDbOptInfo, async (newValue) => {
|
|
||||||
await setSelects(newValue)
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
225
mayfly_go_web/src/views/ops/mongo/MongoInstanceTree.vue
Normal file
225
mayfly_go_web/src/views/ops/mongo/MongoInstanceTree.vue
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
<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" :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="'mongo-instance-' + inst.id"
|
||||||
|
:key="'mongo-instance-' + inst.id"
|
||||||
|
@click.prevent="changeInstance(inst)"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<el-popover
|
||||||
|
placement="right-start"
|
||||||
|
title="mongo数据库实例信息"
|
||||||
|
trigger="hover"
|
||||||
|
:width="210"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<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.uri}}</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)"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<el-icon><Coin color="#67c23a"/></el-icon>
|
||||||
|
<span class="checked-schema">
|
||||||
|
{{ db.Name }}
|
||||||
|
<span style="color: #8492a6;font-size: 13px">[{{formatByteSize(db.SizeOnDisk)}}]</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<!-- 第四级 01:表 -->
|
||||||
|
<el-sub-menu :index="inst.id + db.Name + '-table'" >
|
||||||
|
<template #title>
|
||||||
|
<div style="width: 100%" @click="loadTableNames(inst, db.Name)">
|
||||||
|
<el-icon><Calendar color="#409eff"/></el-icon>
|
||||||
|
<span>集合</span>
|
||||||
|
<el-icon v-show="state.loading[inst.id + db.Name]" class="is-loading"><Loading /></el-icon>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-menu-item :index="inst.id + db.Name + '-tableSearch'"
|
||||||
|
:key="inst.id + db.Name + '-tableSearch'">
|
||||||
|
<template #title>
|
||||||
|
|
||||||
|
<el-input size="small" placeholder="过滤" clearable
|
||||||
|
@change="filterTableName(inst.id, db.Name)"
|
||||||
|
@keyup="e => filterTableName(inst.id, db.Name, e)"
|
||||||
|
v-model="state.filterParam[inst.id+db.Name]"/>
|
||||||
|
</template>
|
||||||
|
</el-menu-item>
|
||||||
|
|
||||||
|
<template v-for="tb in instances.tables[inst.id+db.Name]" >
|
||||||
|
<el-menu-item :index="inst.id + db.Name + tb.tableName"
|
||||||
|
:key="inst.id + db.Name + tb.tableName"
|
||||||
|
v-if="tb.show"
|
||||||
|
@click="loadTableData(inst, db.Name, tb.tableName)"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<div style="width: 100%" >
|
||||||
|
<el-icon><Calendar color="#409eff"/></el-icon>
|
||||||
|
<span :title="tb.tableComment||''">{{tb.tableName}}</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';
|
||||||
|
import {formatByteSize} from '@/common/utils/format';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
instances: {
|
||||||
|
type: Object, required: true
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const emits = defineEmits(['initLoadInstances','changeInstance','loadTableNames','loadTableData','changeSchema'])
|
||||||
|
|
||||||
|
onBeforeMount(async ()=>{
|
||||||
|
await initLoadInstances()
|
||||||
|
setHeight()
|
||||||
|
})
|
||||||
|
|
||||||
|
const setHeight = () => {
|
||||||
|
state.instanceMenuMaxHeight = window.innerHeight - 140 + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
instanceMenuMaxHeight: '800px',
|
||||||
|
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: 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{
|
||||||
|
margin-bottom: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -408,8 +408,6 @@ const valChange = () => {
|
|||||||
|
|
||||||
const openDataOps = (row: any) => {
|
const openDataOps = (row: any) => {
|
||||||
state.dbOps.db = row.Name
|
state.dbOps.db = row.Name
|
||||||
|
|
||||||
debugger
|
|
||||||
let data = {
|
let data = {
|
||||||
tagPath: state.currentData.tagPath,
|
tagPath: state.currentData.tagPath,
|
||||||
dbId: state.dbOps.dbId,
|
dbId: state.dbOps.dbId,
|
||||||
|
|||||||
Reference in New Issue
Block a user