update: 数据源管理-管理模块

This commit is contained in:
easy 2023-02-20 19:58:37 +08:00
parent 67df928602
commit 58e5eec93b
5 changed files with 439 additions and 18 deletions

View File

@ -3,9 +3,22 @@ import server from '@/utils/request';
// 获取数据源列表
export const getDataSourceList_api = (data: object) => server.post(`/datasource/config/_query/`, data);
// 获取数据源信息
export const getDataSourceInfo_api = (id: string) => server.get(`/datasource/config/${id}`);
// 获取数据库类型字典
export const getDataTypeDict_api = () => server.get(`/datasource/config/types`);
// 修改数据源状态
export const changeStatus_api = (id:string, status:'_disable'|'_enable') => server.put(`/datasource/config/${id}/${status}`);
export const changeStatus_api = (id: string, status: '_disable' | '_enable') => server.put(`/datasource/config/${id}/${status}`);
// 新增/更新数据源
export const saveDataSource_api = (data: any) => data.id ? server.patch(`datasource/config`, data) : server.post(`/datasource/config`, data)
// 删除数据源
export const delDataSource_api = (id: string) => server.remove(`/datasource/config/${id}`);
// 获取左侧树
export const rdbTree_api = (id: string) => server.get(`/datasource/rdb/${id}/tables?includeColumns=false`);
// 获取右侧表格
export const rdbTables_api = (id: string,key:string) => server.get(`/datasource/rdb/${id}/table/${key}`);
// 保存表格
export const saveTable_api = (id: string,data:object) => server.patch(`/datasource/rdb/${id}/table`,data);

View File

@ -0,0 +1,381 @@
<template>
<a-card class="mangement-container">
<div class="left">
<a-input-search
v-model:value="leftData.searchValue"
placeholder="请输入"
style="margin-bottom: 24px"
/>
<a-tree
showLine
autoExpandParent
:tree-data="leftData.treeData"
v-model:selectedKeys="leftData.selectedKeys"
@select="leftData.onSelect"
>
<template #title="{ dataRef }">
<div
v-if="dataRef.root"
style="
justify-content: space-between;
display: flex;
align-items: center;
"
>
<span>
{{ dataRef.title }}
</span>
<AIcon
type="PlusOutlined"
style="color: #1d39c4"
@click="leftData.addTable"
/>
</div>
<span v-else>
{{ dataRef.title }}
</span>
</template>
</a-tree>
</div>
<div class="right">
<div class="btns">
<a-button type="primary" @click="table.clickSave"
>保存</a-button
>
</div>
<JTable
ref="tableRef"
:columns="table.columns"
model="TABLE"
:dataSource="table.data"
>
<template #previousName="slotProps">
<a-input
:disabled="slotProps.scale === 0"
v-model:value="slotProps.previousName"
placeholder="请输入名称"
/>
</template>
<template #type="slotProps">
<a-input
v-model:value="slotProps.type"
placeholder="请输入类型"
/>
</template>
<template #length="slotProps">
<a-input-number v-model:value="slotProps.length" />
</template>
<template #precision="slotProps">
<a-input-number v-model:value="slotProps.precision" />
</template>
<template #notnull="slotProps">
<a-radio-group
v-model:value="slotProps.notnull"
button-style="solid"
>
<a-radio-button :value="true"></a-radio-button>
<a-radio-button :value="false"></a-radio-button>
</a-radio-group>
</template>
<template #comment="slotProps">
<a-input
v-model:value="slotProps.comment"
placeholder="请输入说明"
/>
</template>
<template #action="slotProps">
<PermissionButton
:uhasPermission="`{permission}:delete`"
type="link"
:tooltip="{ title: '删除' }"
:popConfirm="{
title: `确认删除`,
onConfirm: () => table.clickDel(slotProps),
}"
:disabled="slotProps.status"
>
<AIcon type="DeleteOutlined" />
</PermissionButton>
</template>
</JTable>
<a-botton class="add-row" @click="table.addRow"
><AIcon type="PlusOutlined" /> 新增行</a-botton
>
</div>
</a-card>
<div class="dialogs">
<a-modal
v-model:visible="dialog.visible"
title="新增"
@ok="dialog.handleOk"
>
<a-form :model="dialog.form" ref="addFormRef">
<a-form-item
label="名称"
name="name"
:rules="[
{
required: true,
message: '请输入名称',
trigger: 'change',
},
{
max: 64,
message: '最多可输入64个字符',
trigger: 'change',
},
{
pattern: /^[0-9].*$/,
message: '不能以数字开头',
trigger: 'change',
},
{
pattern: /^\w+$/,
message: '名称只能由数字、字母、下划线、中划线组成',
trigger: 'change',
},
]"
>
<a-input
v-model:value="dialog.form.name"
placeholder="请输入名称"
/>
</a-form-item>
</a-form>
</a-modal>
</div>
</template>
<script setup lang="ts" name="Management">
import {
getDataSourceInfo_api,
rdbTree_api,
rdbTables_api,
saveTable_api,
} from '@/api/system/dataSource';
import { FormInstance, message } from 'ant-design-vue';
import { DataNode } from 'ant-design-vue/lib/tree';
import { dictItemType, sourceItemType } from '../typing';
const id = useRoute().query.id as string;
const info = reactive({
data: {} as sourceItemType,
init: () => {
id &&
getDataSourceInfo_api(id).then((resp: any) => {
info.data = resp.result;
});
},
});
const leftData = reactive({
searchValue: '',
sourceTree: [] as dictItemType[],
treeData: [] as DataNode[],
selectedKeys: [] as string[],
oldKey: '',
init: () => {
leftData.getTree();
watch(
[
() => leftData.searchValue,
() => leftData.sourceTree,
() => info.data,
],
(n) => {
if (leftData.sourceTree.length < 1 || !info.data.shareConfig)
return;
let filterArr = [];
if (leftData.searchValue) {
filterArr = leftData.sourceTree.filter((item) =>
item.name.includes(n[0]),
);
} else filterArr = leftData.sourceTree;
leftData.treeData = [
{
title: info.data.shareConfig.schema,
key: info.data.shareConfig.schema,
root: true,
children: filterArr.map((item) => ({
title: item.name,
key: item.name,
})),
},
];
leftData.selectedKeys = [filterArr[0].name];
leftData.onSelect([filterArr[0].name]);
},
{},
);
},
getTree: () => {
rdbTree_api(id)
.then((resp: any) => {
leftData.sourceTree = resp.result;
})
.catch(() => {});
},
onSelect: (selectedKeys: string[], e?: any) => {
if (e?.node?.root) {
leftData.selectedKeys = [leftData.oldKey];
return;
}
leftData.oldKey = selectedKeys[0];
const key = selectedKeys[0];
table.getTabelData(key);
},
addTable: (e: Event) => {
e.stopPropagation();
},
});
const table = reactive({
columns: [
{
title: '列名',
dataIndex: 'previousName',
key: 'previousName',
scopedSlots: true,
},
{
title: '类型',
dataIndex: 'type',
key: 'type',
scopedSlots: true,
},
{
title: '长度',
dataIndex: 'length',
key: 'length',
scopedSlots: true,
},
{
title: '精度',
dataIndex: 'precision',
key: 'precision',
scopedSlots: true,
},
{
title: '不能为空',
dataIndex: 'notnull',
key: 'notnull',
scopedSlots: true,
},
{
title: '说明',
dataIndex: 'comment',
key: 'comment',
scopedSlots: true,
},
{
title: '操作',
dataIndex: 'action',
key: 'action',
scopedSlots: true,
},
],
data: [] as any,
getTabelData: (key: string) => {
rdbTables_api(id, key).then((resp: any) => {
table.data = resp.result.columns;
});
},
addRow: () => {
table.data.push({
precision: 0,
length: 0,
notnull: false,
});
},
clickSave: () => {
const params = {
name: leftData.selectedKeys[0],
columns: table.data,
};
saveTable_api(id, params).then(() => {
table.getTabelData(params.name);
});
},
clickDel: (row: any) => {},
});
const addFormRef = ref<FormInstance >();
const dialog = reactive({
visible: false,
form: {
name: '',
},
handleOk: () => {
addFormRef.value && addFormRef.value.validate().then(()=>{
const name = dialog.form.name
leftData.sourceTree.unshift({
id:name,
name
})
leftData.oldKey = name;
leftData.selectedKeys = [name]
table.data = []
})
},
});
init();
function init() {
info.init();
leftData.init();
}
</script>
<style lang="less" scoped>
.mangement-container {
padding: 24px;
background-color: transparent;
:deep(.ant-card-body) {
display: flex;
background-color: #fff;
.left {
flex-basis: 280px;
padding-right: 24px;
box-sizing: border-box;
.ant-tree-treenode {
width: 100%;
.ant-tree-switcher-noop {
display: none;
}
.ant-tree-node-content-wrapper {
width: 100%;
.ant-tree-title {
width: 100%;
}
}
&:first-child .ant-tree-node-selected {
background-color: transparent;
}
}
}
.right {
width: calc(100% - 280px);
box-sizing: border-box;
border-left: 1px solid #f0f0f0;
.btns {
display: flex;
justify-content: right;
padding: 0px 24px;
}
.add-row {
display: block;
text-align: center;
width: 100%;
cursor: pointer;
}
}
}
}
</style>

View File

@ -34,9 +34,9 @@
>
<a-select
v-model:value="form.data.typeId"
style="width: 120px"
:options="form.typeOptions"
placeholder="请选择类型"
:disabled="!!form.data.id"
/>
</a-form-item>
</a-col>
@ -83,7 +83,7 @@
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="24">
<a-row :gutter="24" v-show="form.data.typeId">
<a-col :span="12">
<a-form-item
:name="['shareConfig', 'username']"
@ -179,28 +179,42 @@
</template>
<script setup lang="ts">
import { getDataTypeDict_api } from '@/api/system/dataSource';
import {
getDataTypeDict_api,
saveDataSource_api,
} from '@/api/system/dataSource';
import { FormInstance, message } from 'ant-design-vue';
import type { dictItemType, optionItemType, sourceItemType } from '../typing';
const emits = defineEmits(['confirm']);
//
const dialog = {
title: '',
loading: ref<boolean>(false),
visible: ref<boolean>(false),
handleOk: () => {},
handleOk: () => {
formRef.value?.validate().then(() => {
form.submit();
});
},
//
changeVisible: (row: sourceItemType) => {
openDialog: (row: sourceItemType) => {
if (row.id) dialog.title = '编辑数据源';
else dialog.title = '新增数据源';
form.data = { ...row };
dialog.visible.value = true;
nextTick(() => {
formRef.value?.clearValidate();
dialog.visible.value = true;
});
},
};
//
defineExpose({
openDialog: dialog.changeVisible,
openDialog: dialog.openDialog,
});
const formRef = ref<FormInstance>();
const form = reactive({
data: {
shareConfig: {},
@ -217,8 +231,16 @@ const form = reactive({
}));
});
},
submit: () => {
dialog.loading.value = true;
saveDataSource_api(form.data)
.then(() => {
message.success('操作成功');
emits('confirm');
dialog.visible.value = false;
})
.finally(() => (dialog.loading.value = false));
},
});
form.getTypeOption();
</script>
<style scoped></style>

View File

@ -66,6 +66,7 @@
`/system/DataSource/Management?id=${slotProps.id}`,
)
"
:disabled="slotProps?.typeId === 'rabbitmq' || !table.getRowStatus(slotProps)"
>
<AIcon type="icon-ziyuankuguanli" />
</PermissionButton>
@ -131,6 +132,7 @@ import {
getDataSourceList_api,
getDataTypeDict_api,
changeStatus_api,
delDataSource_api
} from '@/api/system/dataSource';
import { message } from 'ant-design-vue';
@ -226,6 +228,7 @@ const table = {
title: '说明',
dataIndex: 'description',
key: 'description',
ellipsis: true,
},
{
title: '状态',
@ -240,6 +243,7 @@ const table = {
key: 'action',
scopedSlots: true,
width: '200px',
fixed:'right'
},
],
@ -265,16 +269,16 @@ const table = {
},
//
openDialog: (row: sourceItemType | {}) => {
editDialogRef.value.openDialog({shareConfig:{},...row});
editDialogRef.value.openDialog({ shareConfig: {}, ...row });
},
//
clickDel: (row: sourceItemType) => {
// delRelation_api(row.id).then((resp: any) => {
// if (resp.status === 200) {
// tableRef.value?.reload();
// message.success('!');
// }
// });
delDataSource_api(row.id as string).then((resp: any) => {
if (resp.status === 200) {
tableRef.value?.reload();
message.success('操作成功!');
}
});
},
clickChangeStatus: (row: sourceItemType) => {
const status = row.state.value === 'enabled' ? '_disable' : '_enable';

View File

@ -1,6 +1,7 @@
export type dictItemType = {
id: string,
name: string
name: string,
children?:dictItemType
}
export type optionItemType = {
label: string,