增加数据库函数和存储过程的增删改查支持,优化数据源管理

This commit is contained in:
暮光:城中城
2021-04-25 21:56:27 +08:00
parent f270a9bb12
commit 744e877309
22 changed files with 1007 additions and 98 deletions

View File

@@ -42,6 +42,7 @@
<el-dropdown v-if="data.type == 1" @command="databaseActionDropdown">
<i class="el-icon-more" @click.stop=""></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item icon="el-icon-coin" :command="{command: 'procedure', node: node}">函数管理</el-dropdown-item>
<el-dropdown-item icon="el-icon-refresh" :command="{command: 'refresh', node: node}">刷新</el-dropdown-item>
<!-- <el-dropdown-item icon="el-icon-upload2" :command="{command: 'upload', node: node}">导入</el-dropdown-item>-->
<!-- <el-dropdown-item icon="el-icon-download" :command="{command: 'download', node: node}">导出</el-dropdown-item>-->
@@ -166,6 +167,8 @@
}
}
this.datasourceOptions = datasourceOptions;
this.choiceDatasourceId = '';
this.databaseList = [];
},
datasourceChangeEvents() {
this.nowDatasourceShow = this.choiceDatasourceId;
@@ -207,6 +210,10 @@
this.loadGetTableList(param.node.data, () => {
setTimeout(() => param.node.loading = false, 500);
});
} else if (param.command == 'procedure') {
let nodeData = param.node.data;
let procedureParam = {sourceId: this.choiceDatasourceId,dbName: nodeData.dbName, host: nodeData.host};
this.$router.push({path: '/procedure/list', query: procedureParam});
} else {
this.$message.warning("暂未支持的选项");
}

View File

@@ -38,6 +38,9 @@ export default {
manageDatasourceList: data => {
return request({url: '/zyplayer-doc-db/datasource/list', method: 'post', data: Qs.stringify(data)});
},
manageDatasourceGroupList: data => {
return request({url: '/zyplayer-doc-db/datasource/groups', method: 'post', data: Qs.stringify(data)});
},
manageUpdateDatasource: data => {
return request({url: '/zyplayer-doc-db/datasource/update', method: 'post', data: Qs.stringify(data)});
},
@@ -79,6 +82,18 @@ export default {
},
dbUserAuthList: data => {
return request({url: '/zyplayer-doc-db/auth/list', method: 'post', data: Qs.stringify(data)});
},
procedureList: data => {
return request({url: '/zyplayer-doc-db/procedure/list', method: 'post', data: Qs.stringify(data)});
},
deleteProcedure: data => {
return request({url: '/zyplayer-doc-db/procedure/delete', method: 'post', data: Qs.stringify(data)});
},
procedureDetail: data => {
return request({url: '/zyplayer-doc-db/procedure/detail', method: 'post', data: Qs.stringify(data)});
},
saveProcedure: data => {
return request({url: '/zyplayer-doc-db/procedure/save', method: 'post', data: Qs.stringify(data)});
},
systemUpgradeInfo: data => {
return request({url: '/system/info/upgrade', method: 'post', data: Qs.stringify(data)});

View File

@@ -8,6 +8,8 @@ import PageTableView from './components/layouts/PageTableView'
import TableInfo from './views/table/Info.vue'
import TableDatabase from './views/table/Database.vue'
import TableProcedure from './views/table/Procedure.vue'
import TableProcedureEdit from './views/table/ProcedureEdit.vue'
import DataDatasourceManage from './views/data/DatasourceManage.vue'
import DataExport from './views/data/Export.vue'
@@ -35,6 +37,8 @@ let routes = [
children: [
{path: '/table/info', name: '表信息',component: TableInfo},
{path: '/table/database', name: '库信息',component: TableDatabase},
{path: '/procedure/list', name: '存储过程',component: TableProcedure},
{path: '/procedure/edit', name: '编辑存储过程',component: TableProcedureEdit},
{path: '/data/datasourceManage', name: '数据源管理',component: DataDatasourceManage},
{path: '/data/export', name: '数据库导出',component: DataExport},
{path: '/data/executor', name: 'SQL执行器',component: DataExecutor},

View File

@@ -1,26 +1,43 @@
<template>
<div style="padding: 0 10px;">
<el-card>
<div slot="header" class="clearfix">
<span>数据源管理</span>
<el-button style="float: right;margin-left: 5px;" :loading="loadDataListLoading" v-on:click="getDatasourceList" plain icon="el-icon-refresh" size="small">刷新</el-button>
<el-button style="float: right;" v-on:click="addDatasource" type="primary" icon="el-icon-circle-plus-outline" size="small">新增</el-button>
</div>
<el-table :data="datasourceList" stripe border style="width: 100%; margin-bottom: 5px;">
<el-table-column prop="name" label="名字" width="200"></el-table-column>
<el-table-column prop="groupName" label="分组" width="100"></el-table-column>
<el-table-column prop="driverClassName" label="驱动类" width="200"></el-table-column>
<el-table-column prop="sourceUrl" label="数据源URL"></el-table-column>
<el-table-column prop="sourceName" label="账号" width="200"></el-table-column>
<el-table-column label="操作" width="220">
<template slot-scope="scope">
<el-button v-on:click="editDatasource(scope.row)" type="primary" size="mini">修改</el-button>
<el-button v-on:click="editDbAuth(scope.row)" type="success" size="mini">权限</el-button>
<el-button v-on:click="deleteDatasource(scope.row)" type="danger" size="mini">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<el-form :inline="true">
<el-form-item label="名字">
<el-input v-model="searchParam.name" placeholder="名字"></el-input>
</el-form-item>
<el-form-item label="分组">
<el-select v-model="searchParam.groupName" placeholder="分组">
<el-option value="">全部</el-option>
<el-option :value="item" v-for="item in datasourceGroupList"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="loadDataListLoading" @click="getDatasourceList" icon="el-icon-search">查询</el-button>
<el-button @click="addDatasource" icon="el-icon-circle-plus-outline">新增</el-button>
</el-form-item>
</el-form>
<el-table :data="datasourceList" stripe border style="width: 100%; margin-bottom: 5px;">
<el-table-column prop="name" label="名字"></el-table-column>
<el-table-column prop="groupName" label="分组"></el-table-column>
<el-table-column prop="driverClassName" label="驱动类"></el-table-column>
<el-table-column prop="sourceName" label="账号"></el-table-column>
<el-table-column label="操作" width="220">
<template slot-scope="scope">
<el-button v-on:click="editDatasource(scope.row)" type="primary" size="mini">修改</el-button>
<el-button v-on:click="editDbAuth(scope.row)" type="success" size="mini">权限</el-button>
<el-button v-on:click="deleteDatasource(scope.row)" type="danger" size="mini">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
style="margin-top: 10px;"
@size-change="handlePageSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 30, 50]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="tableTotalCount">
</el-pagination>
<!--增加数据源弹窗-->
<el-dialog :inline="true" :title="newDatasource.id>0?'编辑数据源':'新增数据源'" :visible.sync="datasourceDialogVisible" width="760px">
<el-form label-width="120px">
@@ -110,8 +127,15 @@
data() {
return {
loadDataListLoading: false,
datasourceDialogVisible: false,
datasourceList: [],
datasourceDialogVisible: false,
datasourceList: [],
searchParam: {
name: '',
groupName: ''
},
pageSize: 30,
currentPage: 1,
tableTotalCount: 0,
newDatasource: {},
urlPlaceholder: "数据源URL",
@@ -130,6 +154,7 @@
},
mounted: function () {
this.getDatasourceList();
this.getDatasourceGroupList();
},
methods: {
editDbAuth(row) {
@@ -248,17 +273,31 @@
this.urlPlaceholder = "例jdbc:postgresql://127.0.0.1:5432/user_info";
}
},
handleCurrentChange(to) {
this.currentPage = to;
this.getDatasourceList();
},
handlePageSizeChange(to) {
this.pageSize = to;
this.getDatasourceList();
},
getDatasourceList() {
this.loadDataListLoading = true;
datasourceApi.manageDatasourceList({}).then(json => {
let param = {...this.searchParam, pageNum: this.currentPage, pageSize: this.pageSize};
datasourceApi.manageDatasourceList(param).then(json => {
if (this.currentPage == 1) {
this.tableTotalCount = json.total || 0;
}
this.datasourceList = json.data || [];
let datasourceGroupList = [];
this.datasourceList.filter(item => !!item.groupName).forEach(item => datasourceGroupList.push(item.groupName));
this.datasourceGroupList = Array.from(new Set(datasourceGroupList));
setTimeout(() => {
this.loadDataListLoading = false;
}, 800);
});
}, 800);
});
},
getDatasourceGroupList() {
datasourceApi.manageDatasourceGroupList({}).then(json => {
this.datasourceGroupList = json.data || [];
});
},
}
}

View File

@@ -2,18 +2,18 @@
<div class="table-database-vue">
<el-card style="margin: 10px;" shadow="never">
<div slot="header" class="clearfix">库信息</div>
<el-form label-width="100px">
<el-form-item label="数据源:">{{vueQueryParam.host}}</el-form-item>
<el-form-item label="数据库:">{{vueQueryParam.dbName}}</el-form-item>
</el-form>
<el-form :inline="true" label-width="100px">
<el-form-item label="关键字:">
<el-input v-model="keyword" placeholder="输入字段名或注释搜索相关的表或字段信息" style="width: 500px;"></el-input>
</el-form-item>
<el-form-item>
<el-button class="search-submit" type="primary" icon="el-icon-search" @click="searchSubmit">模糊搜索</el-button>
</el-form-item>
</el-form>
<el-row style="margin-bottom: 20px;">
<el-col :span="12"><span class="label">数据源</span>{{vueQueryParam.host}}</el-col>
<el-col :span="12"><span class="label">数据库</span>{{vueQueryParam.dbName}}</el-col>
</el-row>
<el-row>
<el-col :span="24">
<span class="label">关键字</span>
<el-input v-model="keyword" placeholder="输入字段名或注释搜索库中相关的表或字段信息" style="width: 350px;margin-right: 10px;"></el-input>
<el-button class="search-submit" type="primary" icon="el-icon-search" @click="searchSubmit">模糊搜索</el-button>
<el-button icon="el-icon-coin" @click="funcManage">函数管理</el-button>
</el-col>
</el-row>
</el-card>
<div style="padding: 10px;" v-loading="columnListLoading">
<el-table :data="columnList" stripe border style="width: 100%; margin-bottom: 5px;">
@@ -65,10 +65,14 @@
this.columnListLoading = false;
});
},
funcManage() {
this.$router.push({path: '/procedure/list', query: this.vueQueryParam});
},
}
}
</script>
<style>
.table-database-vue .el-table td, .table-database-vue .el-table th{padding: 5px 0;}
.table-database-vue .label{width: 140px; text-align: right;}
</style>

View File

@@ -0,0 +1,180 @@
<template>
<div class="table-procedure-vue">
<el-card style="margin: 10px;" shadow="never">
<div slot="header" class="clearfix">库信息</div>
<el-row>
<el-col :span="12"><span class="label">数据源</span>{{vueQueryParam.host}}</el-col>
<el-col :span="12"><span class="label">数据库</span>{{vueQueryParam.dbName}}</el-col>
</el-row>
</el-card>
<el-card style="margin: 10px;" shadow="never" header="函数管理">
<el-form :inline="true">
<el-form-item label="名称">
<el-input v-model="searchParam.name" placeholder="名字"></el-input>
</el-form-item>
<el-form-item label="类型">
<el-select v-model="searchParam.type" placeholder="类型">
<el-option value="">全部</el-option>
<el-option value="PROCEDURE" label="存储过程"></el-option>
<el-option value="FUNCTION" label="函数"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="procedureListLoading" @click="searchProcedureList" icon="el-icon-search">查询</el-button>
<el-button @click="createProc" icon="el-icon-circle-plus-outline">新建函数</el-button>
</el-form-item>
</el-form>
<el-table :data="procedureList" stripe border style="width: 100%;">
<el-table-column prop="name" label="名称" width="200"></el-table-column>
<el-table-column prop="type" label="类型" width="200">
<!--<template slot-scope="scope">{{scope.row.type=='PROCEDURE' ? '过程' : '函数'}}</template>-->
</el-table-column>
<el-table-column prop="definer" label="定义者"></el-table-column>
<el-table-column prop="created" label="创建时间"></el-table-column>
<el-table-column prop="action" label="操作">
<template slot-scope="scope">
<el-button type="primary" @click="doEditProc(scope.row)">编辑</el-button>
<el-button type="danger" @click="doDeleteProc(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
style="margin-top: 10px;"
@size-change="handlePageSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 30, 50]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="tableTotalCount">
</el-pagination>
</el-card>
<!--新建函数弹窗-->
<el-dialog :inline="true" title="新建函数" :visible.sync="newProcedureDialogVisible" width="760px">
<el-form label-width="120px">
<el-form-item label="类型:">
<el-select v-model="newProcedureInfo.type" placeholder="请选择类型" style="width: 100%">
<el-option value="PROCEDURE" label="存储过程"></el-option>
<el-option value="FUNCTION" label="函数"></el-option>
</el-select>
</el-form-item>
<el-form-item label="函数名:">
<el-input v-model="newProcedureInfo.name" placeholder="请输入函数名"></el-input>
</el-form-item>
</el-form>
<div slot="footer" style="text-align: center;">
<el-button @click="newProcedureOk" type="primary">下一步</el-button>
<el-button @click="newProcedureDialogVisible=false" plain>取消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import datasourceApi from '../../common/api/datasource'
export default {
data() {
return {
procedureListLoading: false,
vueQueryParam: {},
procedureList: [],
newProcedureDialogVisible: false,
newProcedureInfo: {
type: '',
name: '',
},
searchParam: {
name: '',
type: ''
},
pageSize: 30,
currentPage: 1,
tableTotalCount: 0,
};
},
mounted: function () {
// 延迟设置展开的目录edit比app先初始化
setTimeout(() => {
this.$emit('initLoadDataList', {
sourceId: this.vueQueryParam.sourceId,
host: this.vueQueryParam.host,
dbName: this.vueQueryParam.dbName
});
}, 500);
this.initQueryParam(this.$route);
this.searchProcedureList();
},
methods: {
initQueryParam(to) {
this.vueQueryParam = to.query;
let newName = {key: this.$route.fullPath, val: '函数管理-' + this.vueQueryParam.dbName};
this.$store.commit('global/addTableName', newName);
},
handleCurrentChange(to) {
this.currentPage = to;
this.searchProcedureList();
},
handlePageSizeChange(to) {
this.pageSize = to;
this.searchProcedureList();
},
searchProcedureList() {
this.procedureListLoading = true;
let param = {...this.vueQueryParam, ...this.searchParam, pageNum: this.currentPage, pageSize: this.pageSize};
datasourceApi.procedureList(param).then(json => {
if (this.currentPage == 1) {
this.tableTotalCount = json.total || 0;
}
this.procedureList = json.data || [];
this.procedureListLoading = false;
});
},
doEditProc(item) {
let param = {...this.vueQueryParam, typeName: item.type, procName: item.name};
this.$router.push({path: '/procedure/edit', query: param});
},
createProc() {
this.newProcedureDialogVisible = true;
},
newProcedureOk() {
if (!this.newProcedureInfo.type) {
this.$message.error("请先选择类型");return;
}
if (!this.newProcedureInfo.name) {
this.$message.error("请先输入函数名");return;
}
let param = {
...this.vueQueryParam,
typeName: this.newProcedureInfo.type,
procName: this.newProcedureInfo.name
};
this.newProcedureDialogVisible = false;
this.newProcedureInfo = {type: '', name: ''};
this.$router.push({path: '/procedure/edit', query: param});
},
doDeleteProc(item) {
let param = {
sourceId: this.vueQueryParam.sourceId,
dbName: this.vueQueryParam.dbName,
typeName: item.type,
procName: item.name
};
this.$confirm('确定要删除此存储过程吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
datasourceApi.deleteProcedure(param).then(json => {
this.$message.success("删除成功!");
this.searchProcedureList();
});
}).catch(() => {});
},
}
}
</script>
<style>
.table-procedure-vue .el-table td, .table-database-vue .el-table th{padding: 5px 0;}
</style>

View File

@@ -0,0 +1,135 @@
<template>
<div class="table-procedure-edit-vue">
<el-card style="margin: 10px;" shadow="never">
<div slot="header" class="clearfix">函数信息</div>
<el-row>
<el-col :span="6"><span class="label">数据源</span>{{vueQueryParam.host}}</el-col>
<el-col :span="6"><span class="label">数据库</span>{{vueQueryParam.dbName}}</el-col>
<el-col :span="6"><span class="label">类型</span>{{vueQueryParam.typeName}}</el-col>
<el-col :span="6"><span class="label">名称</span>{{vueQueryParam.procName}}</el-col>
</el-row>
</el-card>
<el-card style="margin: 10px;" shadow="never" v-loading="procedureInfoLoading">
<div slot="header" class="clearfix">
<span style="margin-right: 20px;">编辑函数</span>
<el-button type="" @click="saveProcedure" icon="el-icon-document-checked" size="mini">保存</el-button>
<!-- <el-button type="" @click="" icon="el-icon-video-play" size="mini">运行</el-button>-->
<!-- <el-button type="" @click="" icon="el-icon-video-pause" size="mini">停止</el-button>-->
</div>
<pre id="sqlExecutorEditor" style="width: 100%;height: 500px;margin-top: 0;"></pre>
</el-card>
<!--错误信息弹窗-->
<el-dialog title="保存函数失败" :visible.sync="saveProcedureErrVisible" :footer="null">
<div style="width: 700px;max-height: 500px;overflow: auto;">
<pre>{{saveProcedureErrInfo}}</pre>
</div>
</el-dialog>
</div>
</template>
<script>
import '../../common/lib/ace/ace'
import '../../common/lib/ace/theme-monokai'
import '../../common/lib/ace/mode-sql'
import '../../common/lib/ace/ext-language_tools'
import '../../common/lib/ace/snippets/sql'
import datasourceApi from '../../common/api/datasource'
export default {
data() {
return {
procedureInfoLoading: false,
vueQueryParam: {},
procedureInfo: {},
sqlExecutorEditor: {},
saveProcedureErrInfo: '',
saveProcedureErrVisible: false,
};
},
mounted: function () {
// 延迟设置展开的目录edit比app先初始化
setTimeout(() => {
this.$emit('initLoadDataList', {
sourceId: this.vueQueryParam.sourceId,
host: this.vueQueryParam.host,
dbName: this.vueQueryParam.dbName
});
}, 500);
let that = this;
this.sqlExecutorEditor = this.initAceEditor("sqlExecutorEditor", 20);
this.sqlExecutorEditor.setFontSize(16);
this.sqlExecutorEditor.commands.addCommand({
name: "execute-sql",
bindKey: {win: "Ctrl-S", mac: "Command-S"},
exec: function (editor) {
that.saveProcedure();
}
});
this.initQueryParam(this.$route);
this.searchProcedureDetail();
},
methods: {
initQueryParam(to) {
this.vueQueryParam = to.query;
let newName = {key: this.$route.fullPath, val: '编辑函数-' + this.vueQueryParam.procName};
this.$store.commit('global/addTableName', newName);
},
searchProcedureDetail() {
this.procedureInfoLoading = true;
datasourceApi.procedureDetail(this.vueQueryParam).then(json => {
this.procedureInfo = json.data || {};
this.procedureInfoLoading = false;
this.sqlExecutorEditor.setValue(this.procedureInfo.body, 1);
});
},
saveProcedure() {
this.procedureInfoLoading = true;
let param = {...this.vueQueryParam, procSql: this.sqlExecutorEditor.getValue()};
datasourceApi.saveProcedure(param).then(json => {
this.procedureInfoLoading = false;
let executeInfo = json.data || {};
if (!!executeInfo.errMsg) {
this.saveProcedureErrInfo = executeInfo.errMsg;
this.saveProcedureErrVisible = true;
} else {
this.$message.success("保存成功!");
}
});
},
doDeleteProc(item) {
let param = {
sourceId: this.vueQueryParam.sourceId,
dbName: this.vueQueryParam.dbName,
typeName: item.type,
procName: item.name
};
this.$confirm('确定要删除此存储过程吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
datasourceApi.deleteProcedure(param).then(json => {
this.$message.success("删除成功!");
});
}).catch(() => {});
},
initAceEditor(editor, minLines) {
return ace.edit(editor, {
theme: "ace/theme/monokai",
mode: "ace/mode/sql",
wrap: true,
autoScrollEditorIntoView: true,
enableBasicAutocompletion: true,
enableSnippets: true,
enableLiveAutocompletion: true,
minLines: minLines,
maxLines: 40,
});
},
}
}
</script>
<style>
.table-procedure-edit-vue .el-table td, .table-database-vue .el-table th{padding: 5px 0;}
</style>