feat: 调整单个数据库资源可配置多个数据库

This commit is contained in:
meilin.huang
2022-05-08 14:10:57 +08:00
parent 483f5b7604
commit 9db3db31be
26 changed files with 329 additions and 143 deletions

View File

@@ -1,14 +1,14 @@
<template>
<div>
<el-form class="search-form" label-position="right" :inline="true" label-width="60px">
<el-form class="search-form" label-position="right" :inline="true">
<el-form-item prop="project" label="项目" label-width="40px">
<el-select v-model="projectId" placeholder="请选择项目" @change="changeProject" filterable>
<el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item prop="env" label="环境" label-width="40px">
<el-select style="width: 100px" v-model="envId" placeholder="环境" @change="changeEnv" filterable>
<el-form-item prop="env" label="env" label-width="33px">
<el-select style="width: 80px" v-model="envId" placeholder="环境" @change="changeEnv" filterable>
<el-option v-for="item in envs" :key="item.id" :label="item.name" :value="item.id">
<span style="float: left">{{ item.name }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.remark }}</span>
@@ -22,7 +22,7 @@
</template>
<script lang="ts">
import { toRefs, reactive, watch, defineComponent, onMounted } from 'vue';
import { toRefs, reactive, defineComponent, onMounted } from 'vue';
import { projectApi } from '../project/api';
export default defineComponent({
@@ -52,8 +52,6 @@ export default defineComponent({
envId: null,
});
watch(props, (newValue, oldValue) => {});
onMounted(async () => {
state.projects = await projectApi.accountProjects.request(null);
});

View File

@@ -83,6 +83,9 @@ export default defineComponent({
dbId: {
type: Number,
},
db: {
type: String,
}
},
setup(props: any, { emit }) {
const formRef: any = ref();
@@ -202,6 +205,7 @@ export default defineComponent({
SqlExecBox({
sql: sql,
dbId: props.dbId as any,
db: props.db,
runSuccessCallback: () => {
ElMessage.success('创建成功');
proxy.$parent.tableInfo({ id: props.dbId });

View File

@@ -1,6 +1,6 @@
<template>
<div>
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false" width="35%">
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false" :destroy-on-close="true" width="35%">
<el-form :model="form" ref="dbForm" :rules="rules" label-width="85px">
<el-form-item prop="projectId" label="项目:" required>
<el-select style="width: 100%" v-model="form.projectId" placeholder="请选择项目" @change="changeProject" filterable>
@@ -30,24 +30,45 @@
<el-form-item prop="username" label="用户名:" required>
<el-input v-model.trim="form.username" placeholder="请输入用户名"></el-input>
</el-form-item>
<el-form-item prop="password" label="密码:" required>
<el-form-item prop="password" label="密码:">
<el-input
type="password"
show-password
v-model.trim="form.password"
placeholder="请输入密码"
placeholder="请输入密码,新增为必填项"
autocomplete="new-password"
></el-input>
</el-form-item>
<el-form-item prop="database" label="数据库名:" required>
<el-input v-model.trim="form.database" placeholder="请输入数据库名"></el-input>
<el-tag
v-for="db in databaseList"
:key="db"
class="ml5 mt5"
type="success"
effect="plain"
closable
:disable-transitions="false"
@close="handleClose(db)"
>
{{ db }}
</el-tag>
<el-input
v-if="inputDbVisible"
ref="InputDbRef"
v-model="inputDbValue"
style="width: 120px; margin-left: 5px; margin-top: 5px"
size="small"
@keyup.enter="handleInputDbConfirm"
@blur="handleInputDbConfirm"
/>
<el-button v-else class="ml5 mt5" size="small" @click="showInputDb"> + 添加数据库 </el-button>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" :loading="btnLoading" @click="btnOk"> </el-button>
<el-button @click="cancel()"> </el-button>
<el-button type="primary" :loading="btnLoading" @click="btnOk"> </el-button>
</div>
</template>
</el-dialog>
@@ -55,10 +76,11 @@
</template>
<script lang="ts">
import { toRefs, reactive, watch, defineComponent, ref } from 'vue';
import { toRefs, reactive, nextTick, watch, defineComponent, ref } from 'vue';
import { dbApi } from './api';
import { projectApi } from '../project/api.ts';
import { ElMessage } from 'element-plus';
import type { ElInput } from 'element-plus';
export default defineComponent({
name: 'DbEdit',
@@ -78,16 +100,22 @@ export default defineComponent({
},
setup(props: any, { emit }) {
const dbForm: any = ref(null);
const InputDbRef = ref<InstanceType<typeof ElInput>>();
const state = reactive({
dialogVisible: false,
projects: [],
envs: [],
databaseList: [] as any,
inputDbVisible: false,
inputDbValue: '',
form: {
id: null,
name: null,
port: 3306,
username: null,
password: null,
database: '',
project: null,
projectId: null,
envId: null,
@@ -144,17 +172,17 @@ export default defineComponent({
trigger: ['change', 'blur'],
},
],
password: [
{
required: true,
message: '请输入密码',
trigger: ['change', 'blur'],
},
],
// password: [
// {
// required: true,
// message: '请输入密码',
// trigger: ['change', 'blur'],
// },
// ],
database: [
{
required: true,
message: '请输入数据库',
message: '请添加数据库',
trigger: ['change', 'blur'],
},
],
@@ -162,17 +190,47 @@ export default defineComponent({
});
watch(props, async (newValue) => {
state.dialogVisible = newValue.visible;
state.projects = newValue.projects;
if (newValue.db) {
getEnvs(newValue.db.projectId);
state.form = { ...newValue.db };
// 将数据库名使用空格切割,获取所有数据库列表
state.databaseList = newValue.db.database.split(' ');
} else {
state.envs = [];
state.form = { port: 3306 } as any;
}
state.dialogVisible = newValue.visible;
});
const handleClose = (db: string) => {
state.databaseList.splice(state.databaseList.indexOf(db), 1);
changeDatabase();
};
const showInputDb = () => {
state.inputDbVisible = true;
nextTick(() => {
InputDbRef.value!.input!.focus();
});
};
const handleInputDbConfirm = () => {
if (state.inputDbValue) {
state.databaseList.push(state.inputDbValue);
changeDatabase();
}
state.inputDbVisible = false;
state.inputDbValue = '';
};
/**
* 改变表单中的数据库字段,方便表单错误提示。如全部删光,可提示请添加数据库
*/
const changeDatabase = () => {
state.form.database = state.databaseList.length == 0 ? '' : state.databaseList.join(' ');
};
const getEnvs = async (projectId: any) => {
state.envs = await projectApi.projectEnvs.request({ projectId });
};
@@ -216,10 +274,17 @@ export default defineComponent({
});
};
const resetInputDb = () => {
state.inputDbVisible = false;
state.databaseList = [];
state.inputDbValue = '';
};
const cancel = () => {
emit('update:visible', false);
emit('cancel');
setTimeout(() => {
resetInputDb();
dbForm.value.resetFields();
// 重置对象属性为null
state.form = {} as any;
@@ -229,6 +294,10 @@ export default defineComponent({
return {
...toRefs(state),
dbForm,
InputDbRef,
handleClose,
showInputDb,
handleInputDbConfirm,
changeProject,
changeEnv,
btnOk,

View File

@@ -14,9 +14,6 @@
</el-select>
</el-form-item>
<el-form-item>
<el-input v-model="query.database" placeholder="请输入数据库" auto-complete="off" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button v-waves type="primary" icon="search" @click="search()">查询</el-button>
</el-form-item>
@@ -39,7 +36,20 @@
</template>
</el-table-column>
<el-table-column prop="type" label="类型" min-width="80"></el-table-column>
<el-table-column prop="database" label="数据库" min-width="120"></el-table-column>
<el-table-column prop="database" label="数据库" min-width="120">
<template #default="scope">
<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"
>{{ db }}</el-tag
>
</template>
</el-table-column>
<el-table-column prop="username" label="用户名" min-width="100"></el-table-column>
<el-table-column min-width="115" prop="creator" label="创建账号"></el-table-column>
@@ -49,11 +59,8 @@
</template>
</el-table-column>
<el-table-column fixed="right" label="更多信息" min-width="100">
<template #default="scope">
<el-link @click.prevent="tableInfo(scope.row)" type="success">表信息</el-link>
</template>
</el-table-column>
<!-- <el-table-column fixed="right" label="更多信息" min-width="100">
</el-table-column> -->
</el-table>
<el-row style="margin-top: 20px" type="flex" justify="end">
<el-pagination
@@ -67,12 +74,7 @@
</el-row>
</el-card>
<el-dialog
width="75%"
:title="`${chooseData ? chooseData.database : ''} 表信息`"
:before-close="closeTableInfo"
v-model="tableInfoDialog.visible"
>
<el-dialog width="75%" :title="`${db} 表信息`" :before-close="closeTableInfo" v-model="tableInfoDialog.visible">
<el-row class="mb10">
<el-button type="primary" size="small" @click="tableCreateDialog.visible = true">创建表</el-button>
</el-row>
@@ -173,6 +175,7 @@ export default defineComponent({
setup() {
const state = reactive({
dbId: 0,
db: '',
permissions: {
saveDb: 'db:save',
delDb: 'db:del',
@@ -235,6 +238,11 @@ export default defineComponent({
const search = async () => {
let res: any = await dbApi.dbs.request(state.query);
// 切割数据库
res.list.forEach((e: any) => {
e.popoverSelectDbVisible = false;
e.dbs = e.database.split(' ');
});
state.datas = res.list;
state.total = res.total;
};
@@ -247,10 +255,10 @@ export default defineComponent({
const editDb = (isAdd = false) => {
if (isAdd) {
state.dbEditDialog.data = null;
state.dbEditDialog.title = '新增数据库';
state.dbEditDialog.title = '新增数据库资源';
} else {
state.dbEditDialog.data = state.chooseData;
state.dbEditDialog.title = '修改数据库';
state.dbEditDialog.title = '修改数据库资源';
}
state.dbEditDialog.visible = true;
};
@@ -274,9 +282,10 @@ export default defineComponent({
} catch (err) {}
};
const tableInfo = async (row: any) => {
state.tableInfoDialog.infos = await dbApi.tableInfos.request({ id: row.id });
const showTableInfo = async (row: any, db: string) => {
state.tableInfoDialog.infos = await dbApi.tableInfos.request({ id: row.id, db });
state.dbId = row.id;
state.db = db;
state.tableInfoDialog.visible = true;
};
@@ -289,6 +298,7 @@ export default defineComponent({
state.chooseTableName = row.tableName;
state.columnDialog.columns = await dbApi.columnMetadata.request({
id: state.chooseId,
db: state.db,
tableName: row.tableName,
});
@@ -299,6 +309,7 @@ export default defineComponent({
state.chooseTableName = row.tableName;
state.indexDialog.indexs = await dbApi.tableIndex.request({
id: state.chooseId,
db: state.db,
tableName: row.tableName,
});
@@ -309,6 +320,7 @@ export default defineComponent({
state.chooseTableName = row.tableName;
const res = await dbApi.tableDdl.request({
id: state.chooseId,
db: state.db,
tableName: row.tableName,
});
state.ddlDialog.ddl = res[0]['Create Table'];
@@ -329,8 +341,9 @@ export default defineComponent({
SqlExecBox({
sql: `DROP TABLE ${tableName}`,
dbId: state.chooseId,
db: state.db,
runSuccessCallback: async () => {
state.tableInfoDialog.infos = await dbApi.tableInfos.request({ id: state.chooseId });
state.tableInfoDialog.infos = await dbApi.tableInfos.request({ id: state.chooseId, db: state.db });
},
});
} catch (err) {}
@@ -345,7 +358,7 @@ export default defineComponent({
editDb,
valChange,
deleteDb,
tableInfo,
showTableInfo,
closeTableInfo,
showColumns,
showTableIndex,

View File

@@ -7,21 +7,33 @@
<div class="toolbar">
<el-row type="flex" justify="space-between">
<el-col :span="24">
<project-env-select @changeProjectEnv="changeProjectEnv" @clear="clearDb">
<project-env-select @changeProjectEnv="changeProjectEnv">
<template #default>
<el-form-item label="数据库">
<el-select v-model="dbId" placeholder="请选择数据库" @change="changeDb" @clear="clearDb" clearable filterable>
<el-option v-for="item in dbs" :key="item.id" :label="item.database" :value="item.id">
<span style="float: left">{{ item.database }}</span>
<el-form-item label="资源">
<el-select
v-model="dbId"
placeholder="请选择资源实例"
@change="changeDbInstance"
filterable
style="width: 150px"
>
<el-option v-for="item in dbs" :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.name} [${item.type}]`
`${item.host}:${item.port} ${item.type}`
}}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item label-width="40" label="">
<el-select v-model="tableName" placeholder="选择表查看表数据" @change="changeTable" filterable style="width: 300px">
<el-form-item label="数据库">
<el-select v-model="db" placeholder="选择数据" @change="changeDb" @clear="clearDb" clearable filterable style="width: 150px">
<el-option v-for="item in databaseList" :key="item" :label="item" :value="item"> </el-option>
</el-select>
</el-form-item>
<el-form-item label-width="20" label="表">
<el-select v-model="tableName" placeholder="选择表查看表数据" @change="changeTable" filterable style="width: 250px">
<el-option
v-for="item in tableMetadata"
:key="item.tableName"
@@ -139,7 +151,12 @@
</el-tooltip>
</el-row>
<el-row class="mt5">
<el-input v-model="dt.condition" placeholder="若需条件过滤,可选择列并点击对应的字段并输入需要过滤的内容点击查询按钮即可" clearable size="small">
<el-input
v-model="dt.condition"
placeholder="若需条件过滤,可选择列并点击对应的字段并输入需要过滤的内容点击查询按钮即可"
clearable
size="small"
>
<template #prepend>
<el-popover trigger="click" :width="270" placement="right">
<template #reference>
@@ -245,9 +262,11 @@ export default defineComponent({
const state = reactive({
token: token,
defalutLimit: 25, // 默认查询数量
dbs: [],
dbs: [], // 数据库实例列表
databaseList: [], // 数据库实例拥有的数据库列表1数据库实例 -> 多数据库
db: '', // 当前操作的数据库
tables: [],
dbId: null,
dbId: null, // 当前选中操作的数据库实例
tableName: '',
tableMetadata: [],
columnMetadata: [],
@@ -344,6 +363,8 @@ export default defineComponent({
const changeProjectEnv = (projectId: any, envId: any) => {
state.dbs = [];
state.dbId = null;
state.db = '';
state.databaseList = [];
clearDb();
if (envId != null) {
state.params.envId = envId;
@@ -403,6 +424,7 @@ export default defineComponent({
const runSql = (sql: string) => {
return dbApi.sqlExec.request({
id: state.dbId,
db: state.db,
sql: sql.trim(),
});
};
@@ -540,26 +562,35 @@ export default defineComponent({
return selectSql;
};
/**
* 选择数据库实例事件
*/
const changeDbInstance = (dbId: any) => {
state.db = '';
state.databaseList = (state.dbs.find((e: any) => e.id == dbId) as any).database.split(' ');
clearDb();
};
/**
* 更改数据库事件
*/
const changeDb = (id: number) => {
if (!id) {
const changeDb = (db: string) => {
if (!db) {
return;
}
clearDb();
dbApi.tableMetadata.request({ id }).then((res) => {
dbApi.tableMetadata.request({ id: state.dbId, db }).then((res) => {
state.tableMetadata = res;
});
dbApi.hintTables
.request({
id: state.dbId,
db,
})
.then((res) => {
state.cmOptions.hintOptions.tables = res;
});
getSqlNames();
};
@@ -631,6 +662,7 @@ export default defineComponent({
}
columns = await dbApi.columnMetadata.request({
id: state.dbId,
db: state.db,
tableName: tableName,
});
tableMap.set(tableName, columns);
@@ -709,7 +741,7 @@ export default defineComponent({
*/
const getUserSql = () => {
notBlank(state.dbId, '请先选择数据库');
dbApi.getSql.request({ id: state.dbId, type: 1, name: state.sqlName }).then((res) => {
dbApi.getSql.request({ id: state.dbId, type: 1, name: state.sqlName, db: state.db }).then((res) => {
if (res) {
setCodermirrorValue(res.sql);
} else {
@@ -733,6 +765,7 @@ export default defineComponent({
dbApi.getSqlNames
.request({
id: state.dbId,
db: state.db,
})
.then((res) => {
if (res && res.length > 0) {
@@ -750,13 +783,14 @@ export default defineComponent({
const saveSql = async () => {
const sql = codemirror.getValue();
notEmpty(sql, 'sql内容不能为空');
notBlank(state.dbId, '请先选择数据库');
await dbApi.saveSql.request({ id: state.dbId, sql: sql, type: 1, name: state.sqlName });
notBlank(state.dbId, '请先选择数据库实例');
await dbApi.saveSql.request({ id: state.dbId, db: state.db, sql: sql, type: 1, name: state.sqlName });
ElMessage.success('保存成功');
dbApi.getSqlNames
.request({
id: state.dbId,
db: state.db,
})
.then((res) => {
if (res) {
@@ -773,7 +807,7 @@ export default defineComponent({
cancelButtonText: '取消',
type: 'warning',
});
await dbApi.deleteDbSql.request({ id: state.dbId, name: state.sqlName });
await dbApi.deleteDbSql.request({ id: state.dbId, name: state.sqlName, db: state.db });
ElMessage.success('删除成功');
getSqlNames();
} catch (err) {}
@@ -909,6 +943,7 @@ export default defineComponent({
SqlExecBox({
sql: sql,
dbId: state.dbId as any,
db: state.db,
runSuccessCallback: successFunc,
cancelCallback: cancelFunc,
});
@@ -991,6 +1026,7 @@ export default defineComponent({
changeSqlTemplate,
deleteSql,
saveSql,
changeDbInstance,
changeDb,
clearDb,
formatSql,

View File

@@ -4,6 +4,7 @@ import SqlExecDialog from './SqlExecDialog.vue'
export type SqlExecProps = {
sql: string
dbId: number,
db: string,
runSuccessCallback?: Function,
cancelCallback?: Function
}

View File

@@ -40,6 +40,9 @@ export default defineComponent({
dbId: {
type: [Number],
},
db: {
type: String,
},
sql: {
type: String,
},
@@ -49,6 +52,7 @@ export default defineComponent({
dialogVisible: false,
sqlValue: '',
dbId: 0,
db: '',
btnLoading: false,
cmOptions: {
tabSize: 4,
@@ -77,6 +81,7 @@ export default defineComponent({
state.btnLoading = true;
await dbApi.sqlExec.request({
id: state.dbId,
db: state.db,
sql: state.sqlValue.trim(),
});
runSuccess = true;
@@ -110,6 +115,7 @@ export default defineComponent({
cancelCallback = props.cancelCallback;
state.sqlValue = sqlFormatter(props.sql);
state.dbId = props.dbId;
state.db = props.db;
state.dialogVisible = true;
};

View File

@@ -32,8 +32,8 @@
<template #footer>
<div class="dialog-footer">
<el-button type="primary" :loading="btnLoading" @click="btnOk"> </el-button>
<el-button @click="cancel()"> </el-button>
<el-button type="primary" :loading="btnLoading" @click="btnOk"> </el-button>
</div>
</template>
</el-dialog>

View File

@@ -326,8 +326,6 @@ export default defineComponent({
return {
...toRefs(state),
choose,
// monitor,
// closeMonitor,
showTerminal,
openFormDialog,
deleteMachine,

View File

@@ -35,6 +35,7 @@
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel()" :disabled="submitDisabled" size="small"> </el-button>
<el-button
v-auth="'machine:script:save'"
type="primary"
@@ -44,7 +45,6 @@
:disabled="submitDisabled"
> </el-button
>
<el-button @click="cancel()" :disabled="submitDisabled" size="small"> </el-button>
</div>
</template>
</el-dialog>

View File

@@ -71,8 +71,8 @@
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="addProject" type="primary"> </el-button>
<el-button @click="cancelAddProject()"> </el-button>
<el-button @click="addProject" type="primary"> </el-button>
</div>
</template>
</el-dialog>
@@ -103,8 +103,8 @@
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button v-auth="permissions.saveEnv" @click="addEnv" type="primary" :loading="btnLoading"> </el-button>
<el-button @click="cancelAddEnv()"> </el-button>
<el-button v-auth="permissions.saveEnv" @click="addEnv" type="primary" :loading="btnLoading"> </el-button>
</div>
</template>
</el-dialog>
@@ -163,8 +163,8 @@
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button v-auth="permissions.saveMember" @click="addMember" type="primary" :loading="btnLoading"> </el-button>
<el-button @click="cancelAddMember()"> </el-button>
<el-button v-auth="permissions.saveMember" @click="addMember" type="primary" :loading="btnLoading"> </el-button>
</div>
</template>
</el-dialog>

View File

@@ -32,8 +32,8 @@
<template #footer>
<div class="dialog-footer">
<el-button type="primary" :loading="btnLoading" @click="btnOk"> </el-button>
<el-button @click="cancel()"> </el-button>
<el-button type="primary" :loading="btnLoading" @click="btnOk"> </el-button>
</div>
</template>
</el-dialog>

View File

@@ -8,8 +8,8 @@
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="saveValue" type="primary"> </el-button>
<el-button @click="cancel()"> </el-button>
<el-button @click="saveValue" type="primary"> </el-button>
</div>
</template>
</el-dialog>

View File

@@ -82,10 +82,12 @@
</el-row>
</el-form>
<div style="text-align: center" class="dialog-footer mt10">
<el-button type="primary" :loading="btnLoading" @click="btnOk"> </el-button>
<el-button @click="cancel()"> </el-button>
</div>
<template #footer>
<div class="dialog-footer mt10">
<el-button @click="cancel()"> </el-button>
<el-button type="primary" :loading="btnLoading" @click="btnOk"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>

View File

@@ -19,8 +19,8 @@
</el-tree>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="btnOk"> </el-button>
<el-button @click="cancel"> </el-button>
<el-button type="primary" @click="btnOk"> </el-button>
</div>
</template>
</el-dialog>

View File

@@ -14,8 +14,8 @@
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" :loading="btnLoading" @click="btnOk"> </el-button>
<el-button @click="cancel()"> </el-button>
<el-button type="primary" :loading="btnLoading" @click="btnOk"> </el-button>
</div>
</template>
</el-dialog>