feat: DBMS 数据库管理、redis管理、mongo管理,新增【数据操作】快捷跳转

This commit is contained in:
刘宗洋
2022-10-18 17:21:42 +08:00
parent 6c197edddd
commit 9013fff804
16 changed files with 377 additions and 45 deletions

View File

@@ -5,6 +5,9 @@ import themeConfig from '@/store/modules/themeConfig.ts';
import routesList from '@/store/modules/routesList.ts';
import keepAliveNames from '@/store/modules/keepAliveNames.ts';
import userInfos from '@/store/modules/userInfos.ts';
import sqlExecInfo from '@/store/modules/mysqlDbOptInfo.ts';
import redisDbOptInfo from '@/store/modules/redisDbOptInfo.ts';
import mongoDbOptInfo from '@/store/modules/mongoDbOptInfo.ts';
export const key: InjectionKey<Store<RootStateTypes>> = Symbol();
@@ -14,6 +17,9 @@ export const store = createStore<RootStateTypes>({
routesList,
keepAliveNames,
userInfos,
sqlExecInfo,
redisDbOptInfo,
mongoDbOptInfo,
},
});

View File

@@ -71,6 +71,16 @@ export interface UserInfosState {
userInfos: object;
}
// 数据操作信息
export interface DbOptInfoState {
dbOptInfo: {
projectId?: number,
envId?: number,
dbId?: number,
db?: string,
}
}
// 后端返回原始路由(未处理时)
// export interface RequestOldRoutesState {
// requestOldRoutes: Array<object>;
@@ -82,5 +92,8 @@ export interface RootStateTypes {
routesList: RoutesListState;
keepAliveNames: KeepAliveNamesState;
userInfos: UserInfosState;
sqlExecInfo: DbOptInfoState;
redisDbOptInfo: DbOptInfoState;
mongoDbOptInfo: DbOptInfoState;
// requestOldRoutes: RequestOldRoutesState;
}

View File

@@ -0,0 +1,31 @@
import { Module } from 'vuex';
// 此处加上 `.ts` 后缀报错,具体原因不详
import {DbOptInfoState, RootStateTypes} from '@/store/interface';
const mongoDbOptInfoModule: Module<DbOptInfoState, RootStateTypes> = {
namespaced: true,
state: {
dbOptInfo: {
projectId: 0,
envId: 0,
dbId: 0,
db: '0',
},
},
mutations: {
// 设置用户信息
getMongoDbOptInfo(state: any, data: object) {
state.dbOptInfo = data;
},
},
actions: {
// 设置用户信息
async setMongoDbOptInfo({ commit }, data: object) {
if (data) {
commit('getMongoDbOptInfo', data);
}
},
},
};
export default mongoDbOptInfoModule;

View File

@@ -0,0 +1,31 @@
import { Module } from 'vuex';
// 此处加上 `.ts` 后缀报错,具体原因不详
import { DbOptInfoState, RootStateTypes } from '@/store/interface';
const sqlExecInfoModule: Module<DbOptInfoState, RootStateTypes> = {
namespaced: true,
state: {
dbOptInfo: {
projectId: 0,
envId: 0,
dbId: 0,
db: '0',
}
},
mutations: {
// 设置用户信息
getSqlExecInfo(state: any, data: object) {
state.dbOptInfo = data;
},
},
actions: {
// 设置用户信息
async setSqlExecInfo({ commit }, data: object) {
if (data) {
commit('getSqlExecInfo', data);
}
},
},
};
export default sqlExecInfoModule;

View File

@@ -0,0 +1,31 @@
import { Module } from 'vuex';
// 此处加上 `.ts` 后缀报错,具体原因不详
import {DbOptInfoState, RootStateTypes} from '@/store/interface';
const redisDbOptInfoModule: Module<DbOptInfoState, RootStateTypes> = {
namespaced: true,
state: {
dbOptInfo: {
projectId: 0,
envId: 0,
dbId: 0,
db: '0',
},
},
mutations: {
// 设置用户信息
getRedisDbOptInfo(state: any, data: object) {
state.dbOptInfo = data;
},
},
actions: {
// 设置用户信息
async setRedisDbOptInfo({ commit }, data: object) {
if (data) {
commit('getRedisDbOptInfo', data);
}
},
},
};
export default redisDbOptInfoModule;

View File

@@ -22,7 +22,7 @@
</template>
<script lang="ts">
import { toRefs, reactive, defineComponent, onMounted } from 'vue';
import {toRefs, reactive, defineComponent, onMounted, watch} from 'vue';
import { projectApi } from '../project/api';
export default defineComponent({
@@ -52,8 +52,27 @@ export default defineComponent({
envId: null,
});
// 动态选中项目和环境
const setData = async (projectId: null, envId: null) => {
if (projectId) {
state.projectId = projectId;
if (envId) {
state.envs = await projectApi.projectEnvs.request({projectId});
state.envId = envId;
}
}
}
watch(() => props.data, (newValue)=>{
setData(newValue.projectId, newValue.envId)
})
onMounted(async () => {
state.projects = await projectApi.accountProjects.request(null);
// 初始化容器时可能会选中项目和环境
if(props.data?.projectId && props.data?.envId){
await setData(props.data.projectId, props.data.envId)
}
});
const changeProject = async (projectId: any) => {
@@ -72,6 +91,7 @@ export default defineComponent({
...toRefs(state),
changeProject,
changeEnv,
setData,
};
},
});

View File

@@ -31,20 +31,22 @@
<el-table-column prop="type" label="类型" min-width="90"></el-table-column>
<el-table-column prop="database" label="数据库" min-width="80">
<template #default="scope">
<el-popover :width="250" placement="right" trigger="click">
<el-popover placement="right" trigger="click" :width="300">
<template #reference>
<el-link type="primary" :underline="false" plain>查看</el-link>
<el-link type="primary" :underline="false" plain @click="selectDb(scope.row.dbs)">查看</el-link>
</template>
<el-tag
@click="showTableInfo(scope.row, db)"
effect="plain"
type="success"
size="small"
v-for="db in scope.row.dbs"
:key="db"
style="cursor: pointer; margin-left: 3px; margin-bottom: 3px;"
>{{ db }}</el-tag
<el-input v-model="filterDb.param" @keyup="filterSchema" class="w-50 m-2" placeholder="搜索" size="small" >
<template #prefix>
<el-icon class="el-input__icon"><search-icon /></el-icon>
</template>
</el-input>
<div class="el-tag--plain el-tag--success"
v-for="db in filterDb.list" :key="db"
style="border:1px var(--color-success-light-3) solid; margin-top: 3px;border-radius: 5px; padding: 2px;position: relative"
>
<el-link type="success" plain size="small" :underline="false" @click="showTableInfo(scope.row, db)">{{ db }}</el-link>
<el-link type="primary" plain size="small" :underline="false" @click="openSqlExec(scope.row, db)" style="position: absolute; right: 4px">数据操作</el-link>
</div>
</el-popover>
</template>
</el-table-column>
@@ -271,15 +273,20 @@ import SqlExecBox from './component/SqlExecBox.ts';
import config from '@/common/config';
import { getSession } from '@/common/utils/storage';
import { isTrue } from '@/common/assert';
import { Search as SearchIcon } from '@element-plus/icons-vue'
import router from '@/router';
import {store} from '@/store';
export default defineComponent({
name: 'DbList',
components: {
DbEdit,
CreateTable,
SearchIcon,
},
setup() {
const state = reactive({
row: {},
dbId: 0,
db: '',
permissions: {
@@ -296,6 +303,7 @@ export default defineComponent({
* 查询条件
*/
query: {
projectId:null,
pageNum: 1,
pageSize: 10,
},
@@ -356,6 +364,11 @@ export default defineComponent({
tableCreateDialog: {
visible: false,
},
filterDb:{
param:'',
cache:[],
list:[],
}
});
onMounted(async () => {
@@ -545,6 +558,7 @@ export default defineComponent({
try {
state.tableInfoDialog.infos = await dbApi.tableInfos.request({ id: row.id, db });
state.dbId = row.id;
state.row = row;
state.db = db;
} catch (e) {
state.tableInfoDialog.visible = false;
@@ -613,6 +627,37 @@ export default defineComponent({
});
} catch (err) {}
};
const openSqlExec = (row: any, db: any) => {
// 判断db是否发生改变
let oldDb = store.state.sqlExecInfo.dbOptInfo.db;
if(db && oldDb !== db){
const {projectId, envId, id} = row;
let params = {
projectId,
envId,
dbId: id,
db
}
store.dispatch('sqlExecInfo/setSqlExecInfo', params);
}
router.push({name: 'SqlExec'});
}
// 点击查看时初始化数据
const selectDb = (row: any) => {
state.filterDb.param = ''
state.filterDb.cache = row;
state.filterDb.list = row;
}
// 输入字符过滤schema
const filterSchema = () => {
if(state.filterDb.param){
state.filterDb.list = state.filterDb.cache.filter((a)=>{return String(a).toLowerCase().indexOf(state.filterDb.param) > -1 })
}else{
state.filterDb.list = state.filterDb.cache;
}
}
return {
...toRefs(state),
@@ -639,6 +684,9 @@ export default defineComponent({
showCreateDdl,
dropTable,
formatByteSize,
openSqlExec,
selectDb,
filterSchema,
};
},
});

View File

@@ -3,7 +3,7 @@
<div class="toolbar">
<el-row type="flex" justify="space-between">
<el-col :span="24">
<project-env-select @changeProjectEnv="changeProjectEnv">
<project-env-select @changeProjectEnv="changeProjectEnv" :data="{ projectId, envId:params.envId }">
<template #default>
<el-form-item label="资源">
<el-select v-model="dbId" placeholder="请选择资源实例" @change="changeDbInstance" filterable style="width: 150px">
@@ -299,7 +299,7 @@
</template>
<script lang="ts">
import { onMounted, toRefs, reactive, defineComponent, ref } from 'vue';
import { onMounted, toRefs, reactive, defineComponent, ref, watch } from 'vue';
import { dbApi } from './api';
import 'codemirror/addon/hint/show-hint.css';
@@ -321,6 +321,7 @@ import config from '@/common/config';
import { getSession } from '@/common/utils/storage';
import SqlExecBox from './component/SqlExecBox';
import { dateStrFormat } from '@/common/utils/date.ts';
import { useStore } from '@/store/index.ts';
export default defineComponent({
name: 'SqlExec',
@@ -328,12 +329,14 @@ export default defineComponent({
ProjectEnvSelect,
},
setup() {
const store = useStore();
const codeTextarea: any = ref(null);
const token = getSession('token');
let codemirror = null as any;
const tableMap = new Map();
const state = reactive({
projectId: null,
token: token,
defalutLimit: 20, // 默认查询数量
dbs: [], // 数据库实例列表
@@ -1218,7 +1221,39 @@ export default defineComponent({
const res = await dbApi.dbs.request(state.params);
state.dbs = res.list;
};
// 加载选中的db
const setSelects = async (sqlExecInfo: any) =>{
// 保存sql
let sql = codemirror?.getValue()
if( sql && sql.length > 0 && state.dbId){
await saveSql();
}
// 设置项目id和环境id
const { projectId, envId, dbId, db} = sqlExecInfo.dbOptInfo;
state.projectId = projectId;
state.params.envId = envId
// 查询有哪些数据库实例
await search()
// 加载数据库所有schema
changeDbInstance(dbId);
state.dbId = dbId
state.db = db
// 加载schema下所有表
changeDb(db)
}
// 判断如果有数据则加载下拉选项
let sqlExecInfo = store.state.sqlExecInfo
if(sqlExecInfo.dbOptInfo.envId){
setSelects(sqlExecInfo)
}
// 监听选中操作的db变化并加载下拉选项
watch(store.state.sqlExecInfo,async (newValue) => {
await setSelects(newValue)
})
return {
...toRefs(state),
codeTextarea,

View File

@@ -3,7 +3,7 @@
<div class="toolbar">
<el-row type="flex" justify="space-between">
<el-col :span="24">
<project-env-select @changeProjectEnv="changeProjectEnv">
<project-env-select @changeProjectEnv="changeProjectEnv" :data="{ projectId, envId:query.envId }">
<template #default>
<el-form-item label="实例" label-width="40px">
<el-select v-model="mongoId" placeholder="请选择mongo" @change="changeMongo">
@@ -130,13 +130,14 @@
<script lang="ts">
import { mongoApi } from './api';
import { toRefs, ref, reactive, defineComponent } from 'vue';
import {toRefs, ref, reactive, defineComponent, watch} from 'vue';
import { ElMessage } from 'element-plus';
import ProjectEnvSelect from '../component/ProjectEnvSelect.vue';
import { isTrue, notBlank, notNull } from '@/common/assert';
import { formatByteSize } from '@/common/utils/format';
import JsonEdit from '@/components/jsonedit/index.vue';
import { useStore } from '@/store/index.ts';
export default defineComponent({
name: 'MongoDataOp',
@@ -145,8 +146,10 @@ export default defineComponent({
JsonEdit,
},
setup() {
const store = useStore();
const findParamInputRef: any = ref(null);
const state = reactive({
projectId: null,
loading: false,
mongoList: [],
query: {
@@ -416,6 +419,34 @@ export default defineComponent({
delete state.dataTabs[targetName];
};
// 加载选中的db
const setSelects = async (mongoDbOptInfo: any) =>{
// 设置项目id和环境id
const { projectId, envId, dbId, db} = mongoDbOptInfo.dbOptInfo;
state.projectId = projectId;
state.query.envId = envId
await searchMongo();
state.mongoId = dbId
await getDatabases();
state.database = db
await getCollections();
if(state.collection){
state.collection = ''
state.dataTabs = {}
}
}
// 判断如果有数据则加载下拉选项
let mongoDbOptInfo = store.state.mongoDbOptInfo
if(mongoDbOptInfo.dbOptInfo.envId){
setSelects(mongoDbOptInfo)
}
// 监听选中操作的db变化并加载下拉选项
watch(store.state.mongoDbOptInfo,async (newValue) => {
await setSelects(newValue)
})
return {
...toRefs(state),
findParamInputRef,

View File

@@ -35,7 +35,7 @@
<el-table-column label="操作" width>
<template #default="scope">
<el-link type="primary" @click="showDatabases(scope.row.id)" plain size="small" :underline="false">数据库</el-link>
<el-link type="primary" @click="showDatabases(scope.row.id, scope.row)" plain size="small" :underline="false">数据库</el-link>
</template>
</el-table-column>
</el-table>
@@ -61,11 +61,13 @@
</el-table-column>
<el-table-column min-width="80" property="Empty" label="是否为空" />
<el-table-column min-width="80" label="操作">
<el-table-column min-width="150" label="操作">
<template #default="scope">
<el-link type="success" @click="showDatabaseStats(scope.row.Name)" plain size="small" :underline="false">stats</el-link>
<el-divider direction="vertical" border-style="dashed" />
<el-link type="primary" @click="showCollections(scope.row.Name)" plain size="small" :underline="false">集合</el-link>
<el-divider direction="vertical" border-style="dashed" />
<el-link type="primary" @click="openDataOps(scope.row)" plain size="small" :underline="false">数据操作</el-link>
</template>
</el-table-column>
</el-table>
@@ -195,6 +197,8 @@ import { ElMessage, ElMessageBox } from 'element-plus';
import { projectApi } from '../project/api.ts';
import MongoEdit from './MongoEdit.vue';
import { formatByteSize } from '@/common/utils/format';
import {store} from '@/store';
import router from '@/router';
export default defineComponent({
name: 'MongoList',
@@ -203,6 +207,12 @@ export default defineComponent({
},
setup() {
const state = reactive({
dbOps: {
projectId: null,
envId: null,
dbId: 0,
db: '',
},
projects: [],
list: [],
total: 0,
@@ -265,7 +275,12 @@ export default defineComponent({
state.currentData = item;
};
const showDatabases = async (id: number) => {
const showDatabases = async (id: number, row: any) => {
const {projectId, envId} = row;
state.dbOps.projectId = projectId
state.dbOps.envId = envId
state.dbOps.dbId = id
state.databaseDialog.data = (await mongoApi.databases.request({ id })).Databases;
state.databaseDialog.title = `数据库列表`;
state.databaseDialog.visible = true;
@@ -391,6 +406,24 @@ export default defineComponent({
state.currentData = null;
search();
};
const openDataOps = ( row: any) => {
state.dbOps.db = row.Name
let data = {
projectId: state.dbOps.projectId,
envId: state.dbOps.envId,
dbId: state.dbOps.dbId,
db: state.dbOps.db,
}
console.log(data)
// 判断db是否发生改变
let oldDb = store.state.mongoDbOptInfo.dbOptInfo.db;
if(oldDb !== row.Name){
store.dispatch('mongoDbOptInfo/setMongoDbOptInfo', data);
}
router.push({name: 'MongoDataOp'});
}
return {
...toRefs(state),
@@ -409,6 +442,7 @@ export default defineComponent({
deleteMongo,
editMongo,
valChange,
openDataOps,
};
},
});

View File

@@ -4,11 +4,16 @@
<div style="float: left">
<el-row type="flex" justify="space-between">
<el-col :span="24">
<project-env-select @changeProjectEnv="changeProjectEnv" @clear="clearRedis">
<project-env-select @changeProjectEnv="changeProjectEnv" @clear="clearRedis" :data="{ projectId, envId:query.envId }" >
<template #default>
<el-form-item label="redis" label-width="40px">
<el-select v-model="scanParam.id" placeholder="请选择redis" @change="changeRedis" @clear="clearRedis" clearable>
<el-option v-for="item in redisList" :key="item.id" :label="item.host" :value="item.id"> </el-option>
<el-option v-for="item in redisList" :key="item.id" :value="item.id" :label=" item.remark ">
<span style="float: left">{{ item.remark }}</span>
<span style="float: right; color: #8492a6; margin-left: 6px; font-size: 13px">{{
`${item.host}`
}}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="库" label-width="20px">
@@ -125,7 +130,7 @@
<script lang="ts">
import { redisApi } from './api';
import { toRefs, reactive, defineComponent } from 'vue';
import {toRefs, reactive, defineComponent, watch} from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import ProjectEnvSelect from '../component/ProjectEnvSelect.vue';
import HashValue from './HashValue.vue';
@@ -133,6 +138,7 @@ import StringValue from './StringValue.vue';
import SetValue from './SetValue.vue';
import ListValue from './ListValue.vue';
import { isTrue, notBlank, notNull } from '@/common/assert';
import { useStore } from '@/store/index.ts';
export default defineComponent({
name: 'DataOperation',
@@ -144,7 +150,9 @@ export default defineComponent({
ProjectEnvSelect,
},
setup() {
let store = useStore();
const state = reactive({
projectId: null,
loading: false,
redisList: [],
dbList: [],
@@ -153,7 +161,7 @@ export default defineComponent({
},
scanParam: {
id: null,
db: null,
db: '',
match: null,
count: 10,
cursor: {},
@@ -200,7 +208,7 @@ export default defineComponent({
const changeRedis = (id: number) => {
resetScanParam(id);
state.scanParam.db = null;
state.scanParam.db = '';
state.dbList = (state.redisList.find((x: any) => x.id == id) as any).db.split(',');
state.keys = [];
state.dbsize = 0;
@@ -244,7 +252,7 @@ export default defineComponent({
state.redisList = [];
state.scanParam.id = null;
resetScanParam();
state.scanParam.db = null;
state.scanParam.db = '';
state.keys = [];
state.dbsize = 0;
};
@@ -383,7 +391,33 @@ export default defineComponent({
}
};
return {
// 加载选中的db
const setSelects = async (redisDbOptInfo: any) =>{
// 设置项目id和环境id
const { projectId, envId, dbId} = redisDbOptInfo.dbOptInfo;
state.projectId = projectId;
state.query.envId = envId
await searchRedis()
state.scanParam.id = dbId
changeRedis(dbId)
if(!state.scanParam.db){
state.scanParam.db = '0'
}
changeDb()
}
// 判断如果有数据则加载下拉选项
let redisDbOptInfo = store.state.redisDbOptInfo
if(redisDbOptInfo.dbOptInfo.envId){
setSelects(redisDbOptInfo)
}
// 监听选中操作的db变化并加载下拉选项
watch(store.state.redisDbOptInfo,async (newValue) => {
await setSelects(newValue)
})
return {
...toRefs(state),
changeProjectEnv,
changeRedis,

View File

@@ -82,7 +82,7 @@ export default defineComponent({
require: true,
},
db: {
type: [Number],
type: [String],
require: true,
},
keyInfo: {
@@ -98,7 +98,7 @@ export default defineComponent({
dialogVisible: false,
operationType: 1,
redisId: 0,
db: 0,
db: '0',
key: {
key: '',
type: 'hash',
@@ -107,7 +107,7 @@ export default defineComponent({
scanParam: {
key: '',
id: 0,
db: 0,
db: '0',
cursor: 0,
match: '',
count: 10,
@@ -261,4 +261,4 @@ export default defineComponent({
max-width: 70px;
}
}
</style>
</style>

View File

@@ -75,7 +75,7 @@ export default defineComponent({
require: true,
},
db: {
type: [Number],
type: [String],
require: true,
},
keyInfo: {
@@ -95,7 +95,7 @@ export default defineComponent({
dialogVisible: false,
operationType: 1,
redisId: '',
db: 0,
db: '0',
key: {
key: '',
type: 'string',
@@ -210,4 +210,4 @@ export default defineComponent({
max-width: 70px;
}
}
</style>
</style>

View File

@@ -29,18 +29,17 @@
</template>
</el-table-column>
<el-table-column prop="creator" label="创建人" min-width="100"></el-table-column>
<el-table-column label="更多" min-width="130" fixed="right">
<el-table-column label="更多" min-width="155" fixed="right">
<template #default="scope">
<el-link
v-if="scope.row.mode == 'standalone' || scope.row.mode == 'sentinel'"
type="primary"
@click="info(scope.row)"
:underline="false"
>单机信息</el-link
>
<el-link @click="onShowClusterInfo(scope.row)" v-if="scope.row.mode == 'cluster'" type="success" :underline="false"
>集群信息</el-link
>
>单机信息</el-link>
<el-link @click="onShowClusterInfo(scope.row)" v-if="scope.row.mode == 'cluster'" type="success" :underline="false">集群信息</el-link>
<el-divider direction="vertical" border-style="dashed" />
<el-link @click="openDataOpt(scope.row)" type="success" :underline="false">数据操作</el-link>
</template>
</el-table-column>
</el-table>
@@ -153,6 +152,8 @@ import { toRefs, reactive, defineComponent, onMounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { projectApi } from '../project/api.ts';
import RedisEdit from './RedisEdit.vue';
import {store} from '@/store';
import router from '@/router';
export default defineComponent({
name: 'RedisList',
@@ -284,6 +285,22 @@ export default defineComponent({
state.currentData = null;
search();
};
// 打开redis数据操作页
const openDataOpt = (row : any) => {
const {projectId, envId, id, db} = row;
// 判断db是否发生改变
let oldDbId = store.state.redisDbOptInfo.dbOptInfo.dbId;
if(oldDbId !== id){
let params = {
projectId,
envId,
dbId: id,
db
}
store.dispatch('redisDbOptInfo/setRedisDbOptInfo', params);
}
router.push({name: 'DataOperation'});
}
return {
...toRefs(state),
@@ -296,6 +313,7 @@ export default defineComponent({
deleteRedis,
editRedis,
valChange,
openDataOpt,
};
},
});

View File

@@ -54,7 +54,7 @@ export default defineComponent({
require: true,
},
db: {
type: [Number],
type: [String],
require: true,
},
keyInfo: {
@@ -74,7 +74,7 @@ export default defineComponent({
dialogVisible: false,
operationType: 1,
redisId: '',
db: 0,
db: '0',
key: {
key: '',
type: 'string',
@@ -161,4 +161,4 @@ export default defineComponent({
max-width: 70px;
}
}
</style>
</style>

View File

@@ -49,7 +49,7 @@ export default defineComponent({
require: true,
},
db: {
type: [Number],
type: [String],
require: true,
},
keyInfo: {
@@ -66,7 +66,7 @@ export default defineComponent({
dialogVisible: false,
operationType: 1,
redisId: '',
db: 0,
db: '0',
key: {
key: '',
type: 'string',
@@ -180,4 +180,4 @@ export default defineComponent({
max-width: 70px;
}
}
</style>
</style>