440 lines
13 KiB
Vue
440 lines
13 KiB
Vue
|
|
<template>
|
||
|
|
<div class="db-manage-container">
|
||
|
|
<el-row :gutter="20">
|
||
|
|
<!-- 左侧数据库表列表 -->
|
||
|
|
<el-col :span="4">
|
||
|
|
<div class="table-list">
|
||
|
|
<h3>数据库表</h3>
|
||
|
|
<el-scrollbar height="400px">
|
||
|
|
<el-tree
|
||
|
|
:data="tables"
|
||
|
|
node-key="name"
|
||
|
|
:props="defaultProps"
|
||
|
|
@node-click="handleTableClick"
|
||
|
|
/>
|
||
|
|
</el-scrollbar>
|
||
|
|
</div>
|
||
|
|
</el-col>
|
||
|
|
|
||
|
|
<!-- 右侧主内容区 -->
|
||
|
|
<el-col :span="20">
|
||
|
|
<!-- 顶部下拉选择框 -->
|
||
|
|
<el-row :gutter="20" class="mb-20">
|
||
|
|
<el-col :span="12">
|
||
|
|
<el-select
|
||
|
|
v-model="selectedDatabase"
|
||
|
|
filterable
|
||
|
|
placeholder="请选择数据库"
|
||
|
|
class="w-100"
|
||
|
|
>
|
||
|
|
<template #prefix>
|
||
|
|
<el-icon><search /></el-icon>
|
||
|
|
</template>
|
||
|
|
<el-option
|
||
|
|
v-for="item in databases"
|
||
|
|
:key="item.ID"
|
||
|
|
:label="item.Name"
|
||
|
|
:value="item.ID"
|
||
|
|
>
|
||
|
|
<span>{{ item.Name }}</span>
|
||
|
|
<span class="option-actions">
|
||
|
|
<el-icon @click.stop="editDatabase(item)"><edit /></el-icon>
|
||
|
|
<el-icon @click.stop="deleteDatabase(item)"><delete /></el-icon>
|
||
|
|
</span>
|
||
|
|
</el-option>
|
||
|
|
<template #append>
|
||
|
|
<el-button icon="plus" @click="showAddDatabaseDialog" />
|
||
|
|
</template>
|
||
|
|
</el-select>
|
||
|
|
</el-col>
|
||
|
|
<el-col :span="12">
|
||
|
|
<el-select
|
||
|
|
v-model="selectedConnection"
|
||
|
|
filterable
|
||
|
|
placeholder="请选择连接"
|
||
|
|
class="w-100"
|
||
|
|
>
|
||
|
|
<template #prefix>
|
||
|
|
<el-icon><search /></el-icon>
|
||
|
|
</template>
|
||
|
|
<el-option
|
||
|
|
v-for="item in connections"
|
||
|
|
:key="item.id"
|
||
|
|
:label="item.name"
|
||
|
|
:value="item.id"
|
||
|
|
>
|
||
|
|
<span>{{ item.name }}</span>
|
||
|
|
<span class="option-actions">
|
||
|
|
<el-icon @click.stop="editConnection(item)"><edit /></el-icon>
|
||
|
|
<el-icon @click.stop="deleteConnection(item)"><delete /></el-icon>
|
||
|
|
</span>
|
||
|
|
</el-option>
|
||
|
|
<template #append>
|
||
|
|
<el-button icon="plus" @click="showAddConnectionDialog" />
|
||
|
|
</template>
|
||
|
|
</el-select>
|
||
|
|
</el-col>
|
||
|
|
</el-row>
|
||
|
|
|
||
|
|
<!-- SQL输入框和执行按钮 -->
|
||
|
|
<el-row class="mb-20">
|
||
|
|
<el-col :span="20">
|
||
|
|
<el-input
|
||
|
|
v-model="sqlQuery"
|
||
|
|
type="textarea"
|
||
|
|
:rows="3"
|
||
|
|
placeholder="请输入SQL语句"
|
||
|
|
/>
|
||
|
|
</el-col>
|
||
|
|
<el-col :span="4">
|
||
|
|
<el-button type="primary" @click="executeSql" class="w-100" :loading="executing">
|
||
|
|
执行
|
||
|
|
</el-button>
|
||
|
|
</el-col>
|
||
|
|
</el-row>
|
||
|
|
|
||
|
|
<!-- 结果表格 -->
|
||
|
|
<el-table
|
||
|
|
:data="tableData"
|
||
|
|
border
|
||
|
|
style="width: 100%"
|
||
|
|
height="400px"
|
||
|
|
v-horizontal-scroll="'always'"
|
||
|
|
v-loading="loading"
|
||
|
|
>
|
||
|
|
<el-table-column
|
||
|
|
v-for="column in tableColumns"
|
||
|
|
:key="column.prop"
|
||
|
|
:prop="column.prop"
|
||
|
|
:label="column.label"
|
||
|
|
/>
|
||
|
|
</el-table>
|
||
|
|
</el-col>
|
||
|
|
</el-row>
|
||
|
|
|
||
|
|
<!-- 添加/编辑数据库对话框 -->
|
||
|
|
<el-dialog
|
||
|
|
v-model="databaseDialogVisible"
|
||
|
|
:title="isEditDatabase ? '编辑数据库' : '添加数据库'"
|
||
|
|
width="50%"
|
||
|
|
:close-on-click-modal="false"
|
||
|
|
>
|
||
|
|
<el-form :model="databaseForm" label-width="100px">
|
||
|
|
<el-form-item label="数据库名称" required>
|
||
|
|
<el-input v-model="databaseForm.name" placeholder="请输入数据库名称" />
|
||
|
|
</el-form-item>
|
||
|
|
</el-form>
|
||
|
|
<template #footer>
|
||
|
|
<el-button @click="databaseDialogVisible = false">取消</el-button>
|
||
|
|
<el-button type="primary" @click="saveDatabase">保存</el-button>
|
||
|
|
</template>
|
||
|
|
</el-dialog>
|
||
|
|
|
||
|
|
<!-- 添加/编辑连接对话框 -->
|
||
|
|
<el-dialog
|
||
|
|
v-model="connectionDialogVisible"
|
||
|
|
:title="isEditConnection ? '编辑连接' : '添加连接'"
|
||
|
|
width="50%"
|
||
|
|
:close-on-click-modal="false"
|
||
|
|
>
|
||
|
|
<el-form :model="connectionForm" label-width="100px">
|
||
|
|
<el-form-item label="连接名称" required>
|
||
|
|
<el-input v-model="connectionForm.name" placeholder="请输入连接名称" />
|
||
|
|
</el-form-item>
|
||
|
|
<el-form-item label="连接地址" required>
|
||
|
|
<el-input v-model="connectionForm.host" placeholder="请输入连接地址" />
|
||
|
|
</el-form-item>
|
||
|
|
<el-form-item label="端口" required>
|
||
|
|
<el-input v-model="connectionForm.port" placeholder="请输入端口" />
|
||
|
|
</el-form-item>
|
||
|
|
<el-form-item label="用户名" required>
|
||
|
|
<el-input v-model="connectionForm.username" placeholder="请输入用户名" />
|
||
|
|
</el-form-item>
|
||
|
|
<el-form-item label="密码" required>
|
||
|
|
<el-input v-model="connectionForm.password" type="password" placeholder="请输入密码" />
|
||
|
|
</el-form-item>
|
||
|
|
</el-form>
|
||
|
|
<template #footer>
|
||
|
|
<el-button @click="connectionDialogVisible = false">取消</el-button>
|
||
|
|
<el-button type="primary" @click="saveConnection">保存</el-button>
|
||
|
|
</template>
|
||
|
|
</el-dialog>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script lang="ts">
|
||
|
|
import { defineComponent, ref, reactive } from 'vue';
|
||
|
|
import {FindDBManageListService,AddDBManageService,UpdateDBManageService,RunSQLService,GetSQLRunHistoryService } from '@/api/dbm';
|
||
|
|
import {DatabaseConfig} from '@/types/dbm';
|
||
|
|
|
||
|
|
import horizontalScroll from 'el-table-horizontal-scroll'
|
||
|
|
import {
|
||
|
|
Search, Edit, Delete, Plus
|
||
|
|
} from '@element-plus/icons-vue';
|
||
|
|
|
||
|
|
|
||
|
|
export default defineComponent({
|
||
|
|
name: 'DbManage',
|
||
|
|
components: {
|
||
|
|
Search, Edit, Delete, Plus,horizontalScroll
|
||
|
|
},
|
||
|
|
setup() {
|
||
|
|
// 数据库下拉框数据
|
||
|
|
const databases = ref<DatabaseConfig[]>([]);
|
||
|
|
const selectedDatabase = ref('');
|
||
|
|
|
||
|
|
// 连接下拉框数据
|
||
|
|
const connections = ref([
|
||
|
|
{ id: 1, name: '本地开发环境' },
|
||
|
|
{ id: 2, name: '测试环境' },
|
||
|
|
{ id: 3, name: '生产环境' },
|
||
|
|
]);
|
||
|
|
const selectedConnection = ref('');
|
||
|
|
|
||
|
|
// SQL查询相关
|
||
|
|
const sqlQuery = ref('');
|
||
|
|
const executing = ref(false);
|
||
|
|
const tableData = ref([]);
|
||
|
|
const tableColumns = ref([]);
|
||
|
|
const loading = ref(false);
|
||
|
|
|
||
|
|
// 数据库表列表
|
||
|
|
const tables = ref([
|
||
|
|
{ name: 'users', children: [{ name: 'id' }, { name: 'username' }, { name: 'email' }] },
|
||
|
|
{ name: 'products', children: [{ name: 'id' }, { name: 'name' }, { name: 'price' }] },
|
||
|
|
]);
|
||
|
|
const defaultProps = {
|
||
|
|
children: 'children',
|
||
|
|
label: 'name',
|
||
|
|
};
|
||
|
|
|
||
|
|
// 数据库对话框相关
|
||
|
|
const databaseDialogVisible = ref(false);
|
||
|
|
const isEditDatabase = ref(false);
|
||
|
|
const databaseForm = reactive({
|
||
|
|
id: 0,
|
||
|
|
name: '',
|
||
|
|
});
|
||
|
|
|
||
|
|
// 连接对话框相关
|
||
|
|
const connectionDialogVisible = ref(false);
|
||
|
|
const isEditConnection = ref(false);
|
||
|
|
const connectionForm = reactive({
|
||
|
|
id: 0,
|
||
|
|
name: '',
|
||
|
|
host: '',
|
||
|
|
port: '',
|
||
|
|
username: '',
|
||
|
|
password: '',
|
||
|
|
});
|
||
|
|
|
||
|
|
// 方法定义
|
||
|
|
const showAddDatabaseDialog = () => {
|
||
|
|
isEditDatabase.value = false;
|
||
|
|
databaseForm.id = 0;
|
||
|
|
databaseForm.name = '';
|
||
|
|
databaseDialogVisible.value = true;
|
||
|
|
};
|
||
|
|
|
||
|
|
const editDatabase = (item) => {
|
||
|
|
isEditDatabase.value = true;
|
||
|
|
databaseForm.id = item.id;
|
||
|
|
databaseForm.name = item.name;
|
||
|
|
databaseDialogVisible.value = true;
|
||
|
|
};
|
||
|
|
|
||
|
|
const GetDBManageList = async () => {
|
||
|
|
let req = {"get_type":0, "token":localStorage.getItem("token"), "id":localStorage.getItem("userId")}
|
||
|
|
await FindDBManageListService(req).then((res: any) => {
|
||
|
|
if (res.code === 0) {
|
||
|
|
databases.value = res.data;
|
||
|
|
for (let i = 0; i < databases.value.length; i++) {
|
||
|
|
let type = "MySQL";
|
||
|
|
switch(databases.value[i].DB_Type) {
|
||
|
|
case 0:
|
||
|
|
type = "MySQL";
|
||
|
|
break;
|
||
|
|
case 1:
|
||
|
|
type = "PostgreSQL";
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
databases.value[i].Name = type + " - "+ databases.value[i].DB_IP + ":" + databases.value[i].DB_Port + " - " + databases.value[i].DB_NAME;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
console.error('获取数据库列表失败:', res.message);
|
||
|
|
}
|
||
|
|
}).catch((error: any) => {
|
||
|
|
console.error('请求错误:', error);
|
||
|
|
});
|
||
|
|
};
|
||
|
|
GetDBManageList();
|
||
|
|
|
||
|
|
const deleteDatabase = (item) => {
|
||
|
|
// 实际项目中这里应该调用API删除
|
||
|
|
databases.value = databases.value.filter(db => db.ID !== item.id);
|
||
|
|
};
|
||
|
|
|
||
|
|
const saveDatabase = () => {
|
||
|
|
if (isEditDatabase.value) {
|
||
|
|
// 更新
|
||
|
|
const index = databases.value.findIndex(db => db.ID === databaseForm.id);
|
||
|
|
if (index !== -1) {
|
||
|
|
databases.value[index].Name = databaseForm.name;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
// 新增
|
||
|
|
// databases.value.push({
|
||
|
|
// id: databases.value.length + 1,
|
||
|
|
// name: databaseForm.name,
|
||
|
|
// });
|
||
|
|
}
|
||
|
|
databaseDialogVisible.value = false;
|
||
|
|
};
|
||
|
|
|
||
|
|
const showAddConnectionDialog = () => {
|
||
|
|
isEditConnection.value = false;
|
||
|
|
connectionForm.id = 0;
|
||
|
|
connectionForm.name = '';
|
||
|
|
connectionForm.host = '';
|
||
|
|
connectionForm.port = '';
|
||
|
|
connectionForm.username = '';
|
||
|
|
connectionForm.password = '';
|
||
|
|
connectionDialogVisible.value = true;
|
||
|
|
};
|
||
|
|
|
||
|
|
const editConnection = (item) => {
|
||
|
|
isEditConnection.value = true;
|
||
|
|
connectionForm.id = item.id;
|
||
|
|
connectionForm.name = item.name;
|
||
|
|
connectionForm.host = item.host || '';
|
||
|
|
connectionForm.port = item.port || '';
|
||
|
|
connectionForm.username = item.username || '';
|
||
|
|
connectionForm.password = item.password || '';
|
||
|
|
connectionDialogVisible.value = true;
|
||
|
|
};
|
||
|
|
|
||
|
|
const deleteConnection = (item) => {
|
||
|
|
// 实际项目中这里应该调用API删除
|
||
|
|
connections.value = connections.value.filter(conn => conn.id !== item.id);
|
||
|
|
};
|
||
|
|
|
||
|
|
const saveConnection = () => {
|
||
|
|
if (isEditConnection.value) {
|
||
|
|
// 更新
|
||
|
|
const index = connections.value.findIndex(conn => conn.id === connectionForm.id);
|
||
|
|
if (index !== -1) {
|
||
|
|
connections.value[index] = { ...connectionForm };
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
// 新增
|
||
|
|
connections.value.push({
|
||
|
|
id: connections.value.length + 1,
|
||
|
|
...connectionForm,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
connectionDialogVisible.value = false;
|
||
|
|
};
|
||
|
|
|
||
|
|
const executeSql = () => {
|
||
|
|
executing.value = true;
|
||
|
|
loading.value = true;
|
||
|
|
let req = {
|
||
|
|
"token":localStorage.getItem("token"),
|
||
|
|
"id":localStorage.getItem("userId"),
|
||
|
|
"sql":sqlQuery.value,
|
||
|
|
"db_id":selectedDatabase.value,
|
||
|
|
};
|
||
|
|
try{
|
||
|
|
RunSQLService(req).then((res: any) => {
|
||
|
|
if (res.code === 0) {
|
||
|
|
tableData.value = res.data;
|
||
|
|
// tableColumns.value = res.columns;
|
||
|
|
let first = res.data[0];
|
||
|
|
let keys = Object.keys(first);
|
||
|
|
tableColumns.value = keys.map((key) => {
|
||
|
|
return { prop: key, label: key };
|
||
|
|
});
|
||
|
|
console.log(res.data);
|
||
|
|
} else {
|
||
|
|
console.error('执行SQL失败:', res.message);
|
||
|
|
}
|
||
|
|
}).catch((error: any) => {
|
||
|
|
console.error('请求错误:', error);
|
||
|
|
});
|
||
|
|
} catch (e) {
|
||
|
|
console.log(e);
|
||
|
|
}
|
||
|
|
|
||
|
|
executing.value = false;
|
||
|
|
loading.value = false;
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleTableClick = (data) => {
|
||
|
|
// 点击表名时自动生成查询语句
|
||
|
|
if (!data.children) {
|
||
|
|
sqlQuery.value = `SELECT * FROM ${data.name}`;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
return {
|
||
|
|
databases,
|
||
|
|
selectedDatabase,
|
||
|
|
connections,
|
||
|
|
selectedConnection,
|
||
|
|
sqlQuery,
|
||
|
|
executing,
|
||
|
|
tableData,
|
||
|
|
tableColumns,
|
||
|
|
loading,
|
||
|
|
tables,
|
||
|
|
defaultProps,
|
||
|
|
databaseDialogVisible,
|
||
|
|
isEditDatabase,
|
||
|
|
databaseForm,
|
||
|
|
connectionDialogVisible,
|
||
|
|
isEditConnection,
|
||
|
|
connectionForm,
|
||
|
|
showAddDatabaseDialog,
|
||
|
|
editDatabase,
|
||
|
|
deleteDatabase,
|
||
|
|
saveDatabase,
|
||
|
|
showAddConnectionDialog,
|
||
|
|
editConnection,
|
||
|
|
deleteConnection,
|
||
|
|
saveConnection,
|
||
|
|
executeSql,
|
||
|
|
handleTableClick,
|
||
|
|
};
|
||
|
|
},
|
||
|
|
});
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style scoped>
|
||
|
|
.db-manage-container {
|
||
|
|
padding: 20px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.table-list {
|
||
|
|
background: #fff;
|
||
|
|
padding: 10px;
|
||
|
|
border-radius: 4px;
|
||
|
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||
|
|
}
|
||
|
|
|
||
|
|
.mb-20 {
|
||
|
|
margin-bottom: 20px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.w-100 {
|
||
|
|
width: 100%;
|
||
|
|
}
|
||
|
|
|
||
|
|
.option-actions {
|
||
|
|
float: right;
|
||
|
|
}
|
||
|
|
|
||
|
|
.option-actions .el-icon {
|
||
|
|
margin-left: 10px;
|
||
|
|
cursor: pointer;
|
||
|
|
}
|
||
|
|
</style>
|