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,96 +1,69 @@
 | 
			
		||||
<template>
 | 
			
		||||
    <div>
 | 
			
		||||
        <div class="toolbar">
 | 
			
		||||
            <el-row type="flex" justify="space-between">
 | 
			
		||||
                <el-col :span="24">
 | 
			
		||||
                    <el-form class="search-form" label-position="right" :inline="true">
 | 
			
		||||
                        <el-form-item label="标签">
 | 
			
		||||
                            <el-select @change="changeTag" @focus="getTags" v-model="query.tagPath" placeholder="请选择标签"
 | 
			
		||||
                                filterable style="width: 250px">
 | 
			
		||||
                                <el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
 | 
			
		||||
                            </el-select>
 | 
			
		||||
                        </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-row>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <el-container id="data-exec" style="border: 1px solid #eee; margin-top: 1px">
 | 
			
		||||
      <el-row>
 | 
			
		||||
        <el-col :span="4">
 | 
			
		||||
          <mongo-instance-tree
 | 
			
		||||
              @init-load-instances="loadInstances"
 | 
			
		||||
              @change-instance="changeInstance"
 | 
			
		||||
              @change-schema="changeDatabase"
 | 
			
		||||
              @load-table-names="loadTableNames"
 | 
			
		||||
              @load-table-data="changeCollection"
 | 
			
		||||
              :instances="state.instances"/>
 | 
			
		||||
        </el-col>
 | 
			
		||||
        <el-col :span="20">
 | 
			
		||||
          <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"
 | 
			
		||||
                v-model="activeName">
 | 
			
		||||
                <el-tab-pane closable v-for="dt in dataTabs" :key="dt.name" :label="dt.name" :name="dt.name">
 | 
			
		||||
                    <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-input ref="findParamInputRef" v-model="dt.findParamStr" placeholder="点击输入相应查询条件"
 | 
			
		||||
                            @focus="showFindDialog(dt.name)">
 | 
			
		||||
                            <template #prepend>查询参数</template>
 | 
			
		||||
                        </el-input>
 | 
			
		||||
                    </el-row>
 | 
			
		||||
                    <el-row>
 | 
			
		||||
                        <el-col :span="6" v-for="item in dt.datas" :key="item">
 | 
			
		||||
                            <el-card :body-style="{ padding: '0px', position: 'relative' }">
 | 
			
		||||
                                <el-input type="textarea" v-model="item.value" :rows="12" />
 | 
			
		||||
                                <div style="padding: 3px; float: right" class="mr5 mongo-doc-btns">
 | 
			
		||||
                                    <div>
 | 
			
		||||
                                        <el-link @click="onJsonEditor(item)" :underline="false" type="success"
 | 
			
		||||
                                            icon="MagicStick"></el-link>
 | 
			
		||||
                     v-model="state.activeName">
 | 
			
		||||
              <el-tab-pane closable v-for="dt in state.dataTabs" :key="dt.key" :label="dt.label" :name="dt.key">
 | 
			
		||||
                
 | 
			
		||||
                <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="点击输入相应查询条件"
 | 
			
		||||
                            @focus="showFindDialog(dt.key)">
 | 
			
		||||
                    <template #prepend>查询参数</template>
 | 
			
		||||
                  </el-input>
 | 
			
		||||
                  </el-col>
 | 
			
		||||
                </el-row>
 | 
			
		||||
                <el-row>
 | 
			
		||||
                  <el-col :span="6" v-for="item in dt.datas" :key="item">
 | 
			
		||||
                    <el-card :body-style="{ padding: '0px', position: 'relative' }">
 | 
			
		||||
                      <el-input type="textarea" v-model="item.value" :rows="10" />
 | 
			
		||||
                      <div style="padding: 3px; float: right" class="mr5 mongo-doc-btns">
 | 
			
		||||
                        <div>
 | 
			
		||||
                          <el-link @click="onJsonEditor(item)" :underline="false" type="success"
 | 
			
		||||
                                   icon="MagicStick"></el-link>
 | 
			
		||||
 | 
			
		||||
                                        <el-divider direction="vertical" border-style="dashed" />
 | 
			
		||||
                          <el-divider direction="vertical" border-style="dashed" />
 | 
			
		||||
 | 
			
		||||
                                        <el-link @click="onSaveDoc(item.value)" :underline="false" type="warning"
 | 
			
		||||
                                            icon="DocumentChecked"></el-link>
 | 
			
		||||
                          <el-link @click="onSaveDoc(item.value)" :underline="false" type="warning"
 | 
			
		||||
                                   icon="DocumentChecked"></el-link>
 | 
			
		||||
 | 
			
		||||
                                        <el-divider direction="vertical" border-style="dashed" />
 | 
			
		||||
                          <el-divider direction="vertical" border-style="dashed" />
 | 
			
		||||
 | 
			
		||||
                                        <el-popconfirm @confirm="onDeleteDoc(item.value)" title="确定删除该文档?">
 | 
			
		||||
                                            <template #reference>
 | 
			
		||||
                                                <el-link :underline="false" type="danger" icon="DocumentDelete">
 | 
			
		||||
                                                </el-link>
 | 
			
		||||
                                            </template>
 | 
			
		||||
                                        </el-popconfirm>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </el-card>
 | 
			
		||||
                        </el-col>
 | 
			
		||||
                    </el-row>
 | 
			
		||||
                </el-tab-pane>
 | 
			
		||||
                          <el-popconfirm @confirm="onDeleteDoc(item.value)" title="确定删除该文档?">
 | 
			
		||||
                            <template #reference>
 | 
			
		||||
                              <el-link :underline="false" type="danger" icon="DocumentDelete">
 | 
			
		||||
                              </el-link>
 | 
			
		||||
                            </template>
 | 
			
		||||
                          </el-popconfirm>
 | 
			
		||||
                        </div>
 | 
			
		||||
                      </div>
 | 
			
		||||
                    </el-card>
 | 
			
		||||
                  </el-col>
 | 
			
		||||
                </el-row>
 | 
			
		||||
              </el-tab-pane>
 | 
			
		||||
            </el-tabs>
 | 
			
		||||
        </el-container>
 | 
			
		||||
          </el-container>
 | 
			
		||||
        </el-col>
 | 
			
		||||
 | 
			
		||||
      </el-row>
 | 
			
		||||
        
 | 
			
		||||
        <el-dialog width="600px" title="find参数" v-model="findDialog.visible">
 | 
			
		||||
            <el-form label-width="70px">
 | 
			
		||||
                <el-form-item label="filter">
 | 
			
		||||
@@ -116,7 +89,7 @@
 | 
			
		||||
            </template>
 | 
			
		||||
        </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">
 | 
			
		||||
            <monaco-editor v-model="insertDocDialog.doc" language="json" />
 | 
			
		||||
            <template #footer>
 | 
			
		||||
@@ -127,9 +100,9 @@
 | 
			
		||||
            </template>
 | 
			
		||||
        </el-dialog>
 | 
			
		||||
 | 
			
		||||
        <el-dialog width="60%" title="json编辑器" v-model="jsoneditorDialog.visible" @close="onCloseJsonEditDialog"
 | 
			
		||||
            :close-on-click-modal="false">
 | 
			
		||||
            <monaco-editor v-model="jsoneditorDialog.doc" language="json" />
 | 
			
		||||
        <el-dialog width="60%" title="json编辑器" v-model="jsonEditorDialog.visible" @close="onCloseJsonEditDialog"
 | 
			
		||||
                   :close-on-click-modal="false">
 | 
			
		||||
            <monaco-editor v-model="jsonEditorDialog.doc" language="json" />
 | 
			
		||||
        </el-dialog>
 | 
			
		||||
 | 
			
		||||
        <div style="text-align: center; margin-top: 10px"></div>
 | 
			
		||||
@@ -137,15 +110,14 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { mongoApi } from './api';
 | 
			
		||||
import { toRefs, ref, reactive, watch } from 'vue';
 | 
			
		||||
import { ElMessage } from 'element-plus';
 | 
			
		||||
import {mongoApi} from './api';
 | 
			
		||||
import {reactive, ref, toRefs} from 'vue';
 | 
			
		||||
import {ElMessage} from 'element-plus';
 | 
			
		||||
 | 
			
		||||
import { isTrue, notBlank, notNull } from '@/common/assert';
 | 
			
		||||
import { formatByteSize } from '@/common/utils/format';
 | 
			
		||||
import { tagApi } from '../tag/api.ts';
 | 
			
		||||
import { useStore } from '@/store/index.ts';
 | 
			
		||||
import {isTrue, notBlank} from '@/common/assert';
 | 
			
		||||
import {useStore} from '@/store/index.ts';
 | 
			
		||||
import MonacoEditor from '@/components/monaco/MonacoEditor.vue';
 | 
			
		||||
import MongoInstanceTree from '@/views/ops/mongo/MongoInstanceTree.vue';
 | 
			
		||||
 | 
			
		||||
const store = useStore();
 | 
			
		||||
const findParamInputRef: any = ref(null);
 | 
			
		||||
@@ -159,8 +131,6 @@ const state = reactive({
 | 
			
		||||
    database: '', // 当前选择操作的库
 | 
			
		||||
    collection: '', //当前选中的collection
 | 
			
		||||
    activeName: '', // 当前操作的tab
 | 
			
		||||
    databases: [] as any,
 | 
			
		||||
    collections: [] as any,
 | 
			
		||||
    dataTabs: {} as any, // 数据tabs
 | 
			
		||||
    findDialog: {
 | 
			
		||||
        visible: false,
 | 
			
		||||
@@ -175,107 +145,80 @@ const state = reactive({
 | 
			
		||||
        visible: false,
 | 
			
		||||
        doc: '',
 | 
			
		||||
    },
 | 
			
		||||
    jsoneditorDialog: {
 | 
			
		||||
    jsonEditorDialog: {
 | 
			
		||||
        visible: false,
 | 
			
		||||
        doc: '',
 | 
			
		||||
        item: {} as any,
 | 
			
		||||
    },
 | 
			
		||||
    instances:{tags:{}, tree:{}, dbs:{}, tables:{}}
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const {
 | 
			
		||||
    tags,
 | 
			
		||||
    mongoList,
 | 
			
		||||
    query,
 | 
			
		||||
    mongoId,
 | 
			
		||||
    database,
 | 
			
		||||
    collection,
 | 
			
		||||
    activeName,
 | 
			
		||||
    databases,
 | 
			
		||||
    collections,
 | 
			
		||||
    dataTabs,
 | 
			
		||||
    findDialog,
 | 
			
		||||
    insertDocDialog,
 | 
			
		||||
    jsoneditorDialog,
 | 
			
		||||
    jsonEditorDialog,
 | 
			
		||||
} = toRefs(state)
 | 
			
		||||
 | 
			
		||||
const searchMongo = async () => {
 | 
			
		||||
    notNull(state.query.tagPath, '请先选择标签');
 | 
			
		||||
    const res = await mongoApi.mongoList.request(state.query);
 | 
			
		||||
    state.mongoList = res.list;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const changeTag = (tagPath: string) => {
 | 
			
		||||
    state.databases = [];
 | 
			
		||||
    state.collections = [];
 | 
			
		||||
    state.mongoId = null;
 | 
			
		||||
    state.collection = '';
 | 
			
		||||
    state.database = '';
 | 
			
		||||
    state.dataTabs = {};
 | 
			
		||||
    if (tagPath != null) {
 | 
			
		||||
        searchMongo();
 | 
			
		||||
const changeInstance = async (inst: any) => {
 | 
			
		||||
  if (inst) {
 | 
			
		||||
    if (!state.instances.dbs[inst.id]) {
 | 
			
		||||
      const res = await mongoApi.databases.request({id: inst.id});
 | 
			
		||||
      state.instances.dbs[inst.id] = res.Databases;
 | 
			
		||||
      console.log(res.Databases)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const changeDatabase = async (inst: any, database: string) => {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const getTags = async () => {
 | 
			
		||||
    state.tags = await tagApi.getAccountTags.request(null);
 | 
			
		||||
const loadTableNames = async (inst: any, database: string, fn:Function) => {
 | 
			
		||||
  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 changeCollection = (inst: any, schema: string, collection: string) => {
 | 
			
		||||
  state.collection = collection
 | 
			
		||||
  state.mongoId = inst.id
 | 
			
		||||
  state.database = schema
 | 
			
		||||
  let key = inst.id + schema +collection
 | 
			
		||||
  let dataTab = state.dataTabs[key];
 | 
			
		||||
  if (!dataTab) {
 | 
			
		||||
    // 默认查询参数
 | 
			
		||||
    const findParam = {
 | 
			
		||||
      filter: '{}',
 | 
			
		||||
      sort: '{"_id": -1}',
 | 
			
		||||
      skip: 0,
 | 
			
		||||
      limit: 12,
 | 
			
		||||
    };
 | 
			
		||||
    state.dataTabs[key] = {
 | 
			
		||||
      key: key,
 | 
			
		||||
      label: schema+'.'+collection,
 | 
			
		||||
      name: inst.id+schema+collection,
 | 
			
		||||
      datas: [],
 | 
			
		||||
      findParamStr: JSON.stringify(findParam),
 | 
			
		||||
      findParam,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
  state.activeName = key;
 | 
			
		||||
  findCommand(key);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const changeMongo = () => {
 | 
			
		||||
    state.databases = [];
 | 
			
		||||
    state.collections = [];
 | 
			
		||||
    state.dataTabs = {};
 | 
			
		||||
    getDatabases();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
        // 默认查询参数
 | 
			
		||||
        const findParam = {
 | 
			
		||||
            filter: '{}',
 | 
			
		||||
            sort: '{"_id": -1}',
 | 
			
		||||
            skip: 0,
 | 
			
		||||
            limit: 12,
 | 
			
		||||
        },
 | 
			
		||||
            dataTab = {
 | 
			
		||||
                name: collection,
 | 
			
		||||
                datas: [],
 | 
			
		||||
                findParamStr: JSON.stringify(findParam),
 | 
			
		||||
                findParam,
 | 
			
		||||
            };
 | 
			
		||||
        state.dataTabs[collection] = dataTab;
 | 
			
		||||
    }
 | 
			
		||||
    state.activeName = collection;
 | 
			
		||||
    findCommand(collection);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const showFindDialog = (collection: string) => {
 | 
			
		||||
const showFindDialog = (key: string) => {
 | 
			
		||||
    // 获取当前tab的索引位置,将其输入框失去焦点,防止输入以及重复获取焦点
 | 
			
		||||
    const dataTabNames = Object.keys(state.dataTabs);
 | 
			
		||||
    for (let i = 0; i < dataTabNames.length; i++) {
 | 
			
		||||
        if (collection == dataTabNames[i]) {
 | 
			
		||||
        if (key == dataTabNames[i]) {
 | 
			
		||||
            findParamInputRef.value[i].blur();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    state.findDialog.findParam = state.dataTabs[collection].findParam;
 | 
			
		||||
    state.findDialog.findParam = state.dataTabs[key].findParam;
 | 
			
		||||
    state.findDialog.visible = true;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -286,8 +229,8 @@ const confirmFindDialog = () => {
 | 
			
		||||
    findCommand(state.activeName);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const findCommand = async (collection: string) => {
 | 
			
		||||
    const dataTab = state.dataTabs[collection];
 | 
			
		||||
const findCommand = async (key: string) => {
 | 
			
		||||
    const dataTab = state.dataTabs[key];
 | 
			
		||||
    const findParma = dataTab.findParam;
 | 
			
		||||
    let filter, sort;
 | 
			
		||||
    try {
 | 
			
		||||
@@ -300,13 +243,13 @@ const findCommand = async (collection: string) => {
 | 
			
		||||
    const datas = await mongoApi.findCommand.request({
 | 
			
		||||
        id: state.mongoId,
 | 
			
		||||
        database: state.database,
 | 
			
		||||
        collection,
 | 
			
		||||
        collection: state.collection,
 | 
			
		||||
        filter,
 | 
			
		||||
        sort,
 | 
			
		||||
        limit: findParma.limit || 12,
 | 
			
		||||
        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) => {
 | 
			
		||||
    state.jsoneditorDialog.item = item;
 | 
			
		||||
    state.jsoneditorDialog.doc = item.value;
 | 
			
		||||
    state.jsoneditorDialog.visible = true;
 | 
			
		||||
    state.jsonEditorDialog.item = item;
 | 
			
		||||
    state.jsonEditorDialog.doc = item.value;
 | 
			
		||||
    state.jsonEditorDialog.visible = true;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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) => {
 | 
			
		||||
@@ -440,31 +383,21 @@ const removeDataTab = (targetName: string) => {
 | 
			
		||||
    delete state.dataTabs[targetName];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 加载选中的tagPath
 | 
			
		||||
const setSelects = async (mongoDbOptInfo: any) => {
 | 
			
		||||
    const { tagPath, dbId, db } = mongoDbOptInfo.dbOptInfo;
 | 
			
		||||
    state.query.tagPath = tagPath
 | 
			
		||||
    await searchMongo();
 | 
			
		||||
    state.mongoId = dbId
 | 
			
		||||
    await getDatabases();
 | 
			
		||||
    state.database = db
 | 
			
		||||
    await getCollections();
 | 
			
		||||
    if (state.collection) {
 | 
			
		||||
        state.collection = ''
 | 
			
		||||
        state.dataTabs = {}
 | 
			
		||||
    }
 | 
			
		||||
const loadInstances = async () => {
 | 
			
		||||
  const res = await mongoApi.mongoList.request({pageNum: 1, pageSize: 1000,});
 | 
			
		||||
  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
 | 
			
		||||
    // tags
 | 
			
		||||
    state.instances.tags[db.tagId]={tagId, tagPath}
 | 
			
		||||
    // 实例
 | 
			
		||||
    arr.push(db)
 | 
			
		||||
    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>
 | 
			
		||||
 | 
			
		||||
<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) => {
 | 
			
		||||
    state.dbOps.db = row.Name
 | 
			
		||||
 | 
			
		||||
    debugger
 | 
			
		||||
    let data = {
 | 
			
		||||
        tagPath: state.currentData.tagPath,
 | 
			
		||||
        dbId: state.dbOps.dbId,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user