Commit 8705f399 authored by 罗林杰's avatar 罗林杰

创建实时同步数据库到数据库模块

parent 2a044289
<template>
<div class="m-5 mr-0 overflow-hidden bg-white">
<BasicTree
title=""
ref="treeRef"
treeWrapperClassName="h-[calc(100%-35px)] overflow-auto"
:defaultExpandAll="true"
:treeData="treeData"
:fieldNames="{ key: 'businessId', title: 'workSpaceName' }"
@select="handleSelect"
/>
</div>
</template>
<script lang="ts" setup>
import { nextTick, onMounted, ref, unref } from 'vue';
import { BasicTree, TreeActionType, TreeItem } from '@/components/Tree';
import { Nullable } from '@vben/types';
import { TreeData } from './dataBaseData';
const emit = defineEmits(['select']);
const treeData = ref<TreeItem[]>([]);
const treeRef = ref<Nullable<TreeActionType>>(null);
function getTree() {
const tree = unref(treeRef);
if (!tree) {
throw new Error('tree is null!');
}
return tree;
}
async function fetch() {
treeData.value = handleTree(TreeData, 'businessId', undefined, undefined, undefined);
await nextTick(() => {
getTree().expandAll(true);
});
}
function handleTree(data, id, parentId, children, rootId) {
id = id || 'id';
parentId = parentId || 'parentId';
children = children || 'children';
rootId =
rootId ||
Math.min.apply(
Math,
data.map((item) => {
return item[parentId];
}),
) ||
0;
// 对源数据深度克隆
const cloneData = JSON.parse(JSON.stringify(data));
// 循环所有项
const treeData = cloneData.filter((father) => {
const branchArr = cloneData.filter((child) => {
// 返回每一项的子级数组
return father[id] === child[parentId];
});
branchArr.length > 0 ? (father.children = branchArr) : '';
// 返回第一层
return father[parentId] === rootId;
});
return treeData !== '' ? treeData : data;
}
/**选中的数据*/
function handleSelect(keys) {
emit('select', keys[0]);
}
onMounted(() => {
fetch();
});
</script>
import {BasicColumn, FormSchema} from "@/components/Table";
export const columns: BasicColumn[] = [
{
title: '名称',
dataIndex: 'name',
width: 120,
// slots: { customRender: 'name' },
},
{
title: '场景',
dataIndex: 'scene',
width: 150,
},
{
title: '权属工作组',
dataIndex: 'workgroup',
width: 120,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 150,
},
{
title: '更新时间',
dataIndex: 'updateTime',
width: 150,
},
{
title: '拥有者',
dataIndex: 'owner',
width: 100,
},
{
title: '发布状态',
dataIndex: 'releaseStatus',
width: 120,
},
];
export const searchFormSchema: FormSchema[] = [
{
field: 'name',
label: ' ',
component: 'Input',
colProps: { span: 5 },
componentProps: {
placeholder: '输入关键字搜索',
},
},
{
field: 'releaseStatus',
label: ' ',
component: 'Select',
colProps: { span: 5 },
componentProps: {
placeholder: '发布状态',
options: [
{ label: '未发布', value: '未发布' },
{ label: '已发布', value: '已发布' },
{ label: '审核中', value: '审核中' },
{ label: '已下线', value: '已下线' },
],
},
},
];
export const importFormSchema: any[] = [
{
label: '导入任务版本',
field: 'importVersionAlert',
slot: 'importVersionAlert',
colProps: { lg: 24, md: 24 },
},
{
field: 'importType',
label: '导入类型',
component: 'RadioGroup',
required: true,
colProps: { lg: 24, md: 24 },
defaultValue: '1',
componentProps: {
options: [
{ label: '同集群导入', value: '1' },
{ label: '其他集群导入', value: '2' },
],
},
},
{
field: 'importOptionsAlert',
slot: 'importOptionsAlert',
colProps: { lg: 24, md: 24 },
},
{
field: 'deptId',
label: '导入至',
component: 'TreeSelect',
colProps: { lg: 24, md: 24 },
componentProps: {
fieldNames: {
label: 'deptName',
value: 'businessId',
},
treeData: [
{
deptName: '数据加载',
businessId: '1',
children: [
{
deptName: '个人工作区',
businessId: '11',
children: [
{
deptName: '图标验收',
businessId: '111',
},
],
},
{
deptName: '共享工作区',
businessId: '12',
children: [
{
deptName: '学生成绩',
businessId: '122',
},
],
},
],
},
],
},
required: true,
},
{
field: 'fileMethods',
label: '导入文件选择',
slot: 'fileMethods',
colProps: { lg: 24, md: 24 },
},
{
field: 'fileRename',
label: '文件重名',
component: 'RadioGroup',
required: true,
defaultValue: '3',
colProps: { lg: 24, md: 24 },
componentProps: {
options: [
{ label: '全部放弃', value: '1' },
{ label: '全部替换', value: '2' },
{ label: '自动重命名', value: '3' },
],
},
},
];
export const MoveFormSchema: any[] = [
{
field: 'taskId',
label: '路径',
component: 'TreeSelect',
colProps: { lg: 24, md: 24 },
componentProps: {
// border: 'none',
fieldNames: {
label: 'workSpaceName',
value: 'businessId',
},
getPopupContainer: () => document.body,
},
required: true,
},
];
export const formSchemaNewFolder: any = [
{
field: 'path',
label: '路径',
component: 'TreeSelect',
rules: [
{
required: true,
message: '请选择上级菜单',
},
],
componentProps: {
fieldNames: {
label: 'workSpaceName',
value: 'businessId',
},
getPopupContainer: () => document.body,
},
},
{
field: 'name',
label: '文件夹名称',
component: 'Input',
colProps: { span: 8 },
componentProps: {
placeholder: '输入文件夹名称',
},
},
// {
// field: 'fileType',
// label: '权限模式',
// component: 'RadioGroup',
// defaultValue: '本级定义',
// colProps: { span: 8 },
// componentProps: {
// options: [
// { label: '本级定义', value: '本级定义' },
// { label: '资源自定义', value: '资源自定义' },
// ],
// placeholder: '输入描述',
// },
// },
// {
// field: 'group',
// label: '权属工作组',
// component: 'Select',
// defaultValue: '默认工作组',
// colProps: { span: 8 },
// componentProps: {
// placeholder: '输入描述',
// options: [{ label: '默认工作组', value: '默认工作组' }],
// },
// },
];
export const tableList: any[] = [
{
businessId: 1,
name: '图标加载',
scene: '离线加载',
releaseStatus: '未发布',
createTime: '2023/05/23 14:36:04',
updateTime: '2023/05/23 14:36:04',
owner: 'admin',
workgroup: '个人工作组',
},
{
businessId: 2,
name: '图标专利文件加载',
scene: '离线加载',
releaseStatus: '未发布',
createTime: '2023/05/23 14:36:04',
updateTime: '2023/05/23 14:36:05',
owner: 'admin',
workgroup: '个人工作组',
},
{
businessId: 3,
name: '版权证书加载',
scene: '离线加载',
releaseStatus: '已下线',
createTime: '2023/05/23 14:36:04',
updateTime: '2023/05/23 14:36:04',
owner: 'admin',
workgroup: '个人工作组',
},
{
businessId: 4,
name: '学生成绩表格',
scene: '文件加载',
releaseStatus: '已发布',
createTime: '2023/05/23 14:36:04',
updateTime: '2023/05/23 14:36:05',
owner: 'admin',
workgroup: '共享工作组',
},
{
businessId: 5,
name: '各科试卷加载',
scene: '文件加载',
releaseStatus: '已发布',
createTime: '2023/05/23 14:36:04',
updateTime: '2023/05/23 14:36:05',
owner: 'admin',
workgroup: '共享工作组',
},
{
businessId: 6,
name: '学生个人信息加载',
scene: '准实时加载',
releaseStatus: '已发布',
createTime: '2023/05/23 14:36:04',
updateTime: '2023/05/23 14:36:04',
owner: 'admin',
workgroup: '共享工作组',
},
];
export const TreeData: any[] = [
{
delFlag: '0',
flag: '1',
businessId: 100,
parentWorkSpaceName: '整合',
workSpaceName: '整合',
parentId: 0,
'code:': '001',
ancestors: '0',
orderNum: 0,
children: [],
selectType: null,
createTime: '2024-10-24 10:04:04',
createBy: 'admin',
},
{
delFlag: '0',
flag: '1',
businessId: 101,
parentWorkSpaceName: '数据加载',
workSpaceName: '数据加载',
parentId: 100,
'code:': '002',
ancestors: '0,100',
orderNum: 1,
children: [],
selectType: null,
createTime: '2024-10-24 10:04:04',
createBy: 'admin',
},
{
delFlag: '0',
flag: '1',
businessId: 201,
parentWorkSpaceName: '数据加载',
workSpaceName: '个人工作区',
parentId: 101,
'code:': '003',
ancestors: '0,100',
orderNum: 1,
children: [],
selectType: null,
createTime: '2024-10-24 10:04:04',
createBy: 'admin',
},
{
delFlag: '0',
flag: '1',
businessId: 202,
parentWorkSpaceName: '数据加载',
workSpaceName: '共享工作区',
parentId: 101,
'code:': '004',
ancestors: '0,100',
orderNum: 2,
children: [],
selectType: null,
createTime: '2024-10-24 10:04:04',
createBy: 'admin',
},
{
delFlag: '0',
flag: '1',
businessId: 301,
parentWorkSpaceName: '个人工作区',
workSpaceName: '图标验收',
parentId: 201,
'code:': '004',
ancestors: '0,100',
orderNum: 2,
children: [],
selectType: null,
createTime: '2024-10-24 10:04:04',
createBy: 'admin',
},
{
delFlag: '0',
flag: '1',
businessId: 302,
parentWorkSpaceName: '共享工作区',
workSpaceName: '学生成绩',
parentId: 202,
'code:': '004',
ancestors: '0,100',
orderNum: 2,
children: [],
selectType: null,
createTime: '2024-10-24 10:04:04',
createBy: 'admin',
},
];
export const cardList = [
{
title: '数据库离线加载',
scene: 'databaseOfflineLoading',
icon: 'iconoir:db',
color: '#9064e9',
description: '支持数据加载导数据仓库(数据加载功能),也支持数仓将数据写入业务库(卸载功能)',
},
{
title: '文件离线加载',
scene: 'fileOfflineLoading',
icon: 'tabler:file-isr',
color: '#9064e9',
description: '支持将文件定期加载入数据仓库中',
},
];
<template> <template>
<div>数据库到数据库</div> <PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
<GroupTree class="w-1/4 xl:w-1/5" @select="handleSelect" />
<BasicTable @register="registerTable" class="w-3/4 xl:w-4/5">
<template #toolbar>
<a-button
:disabled="getRowSelection().selectedRowKeys <= 0"
type="primary"
@click="handleRun"
>运行</a-button
>
<a-button
:disabled="getRowSelection().selectedRowKeys <= 0"
type="primary"
@click="handleDownline"
>暂停</a-button
>
<a-button
:disabled="getRowSelection().selectedRowKeys <= 0"
type="primary"
@click="handlePublish"
>发布</a-button
>
<a-button
:disabled="getRowSelection().selectedRowKeys <= 0"
type="primary"
@click="handleMove(0)"
>复制到</a-button
>
<a-button
:disabled="getRowSelection().selectedRowKeys <= 0"
type="primary"
@click="deleteButton"
>删除</a-button
>
<a-button
:disabled="getRowSelection().selectedRowKeys <= 0"
type="primary"
@click="handleMove(1)"
>移动</a-button
>
<a-button type="primary" @click="handleNewFolder">新建文件夹</a-button>
<a-button type="primary" @click="handleDataEntry">新建文件</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
label: '复制',
onClick: handleMove.bind(null, 0, record),
},
{
label: '移动',
onClick: handleMove.bind(null, 1, record),
},
{
label: '删除',
onClick: deleteButton.bind(null, record, 1),
},
]"
/>
</template>
</template>
</BasicTable>
<MoveFile @register="registerMoveFile" @success="handleMoveSuccess" />
<SceneSelectionModal @register="registerSceneSelectionModal" />
<NewFolder @register="registerNewFolder" />
</PageWrapper>
</template> </template>
<script lang="ts" setup>
import { onMounted } from 'vue';
import { BasicTable, useTable, TableAction } from '@/components/Table';
import { PageWrapper } from '@/components/Page';
import { useMessage } from '@/hooks/web/useMessage';
import { useModal } from '@/components/Modal';
import { tableList } from './dataBaseData';
import { columns, searchFormSchema } from './data';
import MoveFile from './moveFile.vue';
import NewFolder from './newFolder.vue';
import GroupTree from './GroupTree.vue';
import SceneSelectionModal from './sceneSelectionModal.vue';
<script> const { createMessage, createConfirm } = useMessage();
export default { const [registerMoveFile, { openModal: openMoveFileModal }] = useModal();
name: "index" const [registerSceneSelectionModal, { openModal: openSceneSelectionModal }] = useModal();
} const [registerNewFolder, { openModal: openNewFolderModal }] = useModal();
</script> const [
registerTable,
{ reload, updateTableDataRecord, getSearchInfo, getForm, getRowSelection },
] = useTable({
api: async () => {
// console.log('tableList', tableList);
const response = {
pageNu: '1',
pageSize: '10',
pages: '1',
total: tableList.length,
code: '',
message: '',
data: tableList,
};
return { ...response };
},
rowKey: 'businessId',
rowSelection: true,
columns,
formConfig: {
labelWidth: 10,
schemas: searchFormSchema,
autoSubmitOnEnter: true,
},
useSearchForm: true,
showTableSetting: false,
showIndexColumn: false,
bordered: true,
handleSearchInfoFn(info) {
// console.log('handleSearchInfoFn', info);
return info;
},
actionColumn: {
width: 260,
title: '操作',
dataIndex: 'action',
// slots: { customRender: 'action' },
},
});
function handleDataEntry() {
openSceneSelectionModal(true);
}
function handleNewFolder() {
openNewFolderModal(true, {
isUpdate: false,
});
}
<style scoped> /** 移动按钮*/
function handleMove(isMove, record: Recordable) {
// console.log('record',record)
openMoveFileModal(true, {
record,
isMove: isMove,
});
}
/**删除按钮*/
function deleteButton() {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '确认批量删除选中数据吗?',
onOk() {
createMessage.success('删除成功!');
reload();
},
});
}
</style> /** 部门树的select*/
function handleSelect() {
reload();
}
/** 移动*/
function handleMoveSuccess({ isMove, values }) {
const rowSelection = getRowSelection().selectedRowKeys;
if (rowSelection.length > 0) {
//批量移动
for (let i = 0; i < rowSelection.length; i++) {
const result = updateTableDataRecord(values[i].institutionId, values[i]);
}
} else {
//单个移动
const result = updateTableDataRecord(values.businessId, values);
}
reload();
}
onMounted(() => {});
</script>
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
:title="getTitle"
@ok="handleSubmit"
>
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue';
import { BasicModal, useModalInner } from '@/components/Modal';
import { BasicForm, useForm } from '@/components/Form';
import { MoveFormSchema } from './data';
import { useMessage } from '@/hooks/web/useMessage';
import { TreeData } from './dataBaseData';
const emit = defineEmits(['success', 'register']);
const { createMessage } = useMessage();
const rowData = ref([]);
let isMove = ref();
//获取接口数据并放在下拉框里(这里是打开了一个弹框)
//初始化表单
const [registerForm, { updateSchema, resetFields }] = useForm({
baseColProps: { span: 24 },
schemas: MoveFormSchema,
showActionButtonGroup: false,
actionColOptions: {
span: 23,
},
});
//初始化弹框
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
console.log('data', data);
// //每次点击弹窗 需要清空存储的数据
rowData.value = [];
isMove.value = data.isMove;
// //重置表单数据
resetFields();
setModalProps({ confirmLoading: false });
const treeList = handleTree(TreeData, 'businessId', undefined, undefined, undefined);
updateSchema([
{
field: 'taskId',
componentProps: {
treeData: treeList,
},
},
]);
console.log('treeList:', treeList);
});
const getTitle = computed(() => '移动');
/**确定按钮*/
async function handleSubmit() {
// console.log('isMove.value', isMove.value);
if (isMove.value === 1) {
createMessage.success('移动成功!');
} else {
createMessage.success('复制成功!');
}
closeModal();
}
/**数组对象转成树*/
function handleTree(data, id, parentId, children, rootId) {
id = id || 'id';
parentId = parentId || 'parentId';
children = children || 'children';
rootId =
rootId ||
Math.min.apply(
Math,
data.map((item) => {
return item[parentId];
}),
) ||
0;
// 对源数据深度克隆
const cloneData = JSON.parse(JSON.stringify(data));
// 循环所有项
const treeData = cloneData.filter((father) => {
const branchArr = cloneData.filter((child) => {
// 返回每一项的子级数组
return father[id] === child[parentId];
});
branchArr.length > 0 ? (father.children = branchArr) : '';
// 返回第一层
return father[parentId] === rootId;
});
return treeData !== '' ? treeData : data;
}
</script>
<template>
<BasicModal
width="40%"
v-bind="$attrs"
@register="registerModal"
:title="getTitle"
@ok="handleSubmit"
>
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, computed, unref } from 'vue';
import { BasicModal, useModalInner } from '@/components/Modal';
import { BasicForm, useForm } from '@/components/Form';
import { formSchemaNewFolder } from './data';
import { TreeData } from './dataBaseData';
defineOptions({ name: 'AccountModal' });
const emit = defineEmits(['success', 'register']);
const isUpdate = ref(true);
const rowId = ref('');
//获取接口数据并放在下拉框里(这里是打开了一个弹框)
//初始化表单
const [registerForm, { setFieldsValue, updateSchema, resetFields, validate }] = useForm({
labelWidth: 100,
baseColProps: { lg: 24, md: 24 },
schemas: formSchemaNewFolder,
showActionButtonGroup: false,
actionColOptions: {
span: 23,
},
});
//初始化弹框
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
resetFields();
setModalProps({ confirmLoading: false });
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
// 通过id获取行详情信息
// 塞值
setFieldsValue({
...data.record,
});
}
const treeList = handleTree(TreeData, 'businessId', undefined, undefined, undefined);
updateSchema([
{
field: 'path',
componentProps: {
treeData: treeList,
},
},
]);
});
/**数组对象转成树*/
function handleTree(data, id, parentId, children, rootId) {
id = id || 'id';
parentId = parentId || 'parentId';
children = children || 'children';
rootId =
rootId ||
Math.min.apply(
Math,
data.map((item) => {
return item[parentId];
}),
) ||
0;
// 对源数据深度克隆
const cloneData = JSON.parse(JSON.stringify(data));
// 循环所有项
const treeData = cloneData.filter((father) => {
const branchArr = cloneData.filter((child) => {
// 返回每一项的子级数组
return father[id] === child[parentId];
});
branchArr.length > 0 ? (father.children = branchArr) : '';
// 返回第一层
return father[parentId] === rootId;
});
return treeData !== '' ? treeData : data;
}
const getTitle = computed(() => (!unref(isUpdate) ? '新建主体' : '编辑主体'));
async function handleSubmit() {
try {
const values = await validate();
setModalProps({ confirmLoading: true });
// TODO custom api
closeModal();
emit('success', { isUpdate: unref(isUpdate), values: { ...values, id: rowId.value } });
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>
<style lang="scss" scoped>
.modalRow {
padding: 0 20px;
display: flex;
align-items: center;
justify-content: space-between;
.clearAll {
padding-right: 10px;
font-size: 16px;
}
.right {
display: flex;
align-items: center;
justify-content: space-between;
}
}
.addDialogBG {
margin: 10px;
border-radius: 10px;
padding: 20px;
background-color: #e8ecf7;
width: 98%;
height: 400px;
}
</style>
<template>
<BasicModal width="40%" v-bind="$attrs" @register="registerModal" title="场景选择">
<List>
<Row :gutter="16">
<template v-for="item in cardList" :key="item.title">
<Col :span="12">
<ListItem>
<Card
:hoverable="true"
class="sceneSelectionCard"
@click="handleNewModal(item.scene)"
>
<div>
<Icon
class="sceneSelectionIcon"
v-if="item.icon"
:icon="item.icon"
:color="item.color"
/>
</div>
<div class="sceneSelectionTitle">
{{ item.title }}
</div>
<div class="sceneSelectionDescription">
{{ item.description }}
</div>
</Card>
</ListItem>
</Col>
</template>
</Row>
</List>
</BasicModal>
</template>
<script lang="ts" setup>
import Icon from '@/components/Icon/Icon.vue';
import { BasicModal, useModalInner } from '@/components/Modal';
import { cardList } from './dataBaseData';
import { Card, Row, Col, List, ListItem } from 'ant-design-vue';
import { router } from '@/router';
const emit = defineEmits(['register']);
//初始化弹框
const [registerModal] = useModalInner(async () => {});
function handleNewModal(scene) {
router.push({
path: '/dataIntegration/dataLoading/dataEntryLake/' + scene,
});
}
</script>
<style lang="scss" scoped>
.sceneSelectionCard {
height: 200px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.sceneSelectionIcon {
font-size: 40px !important;
margin-bottom: 16px;
}
.sceneSelectionTitle {
font-size: 18px;
font-weight: bold;
margin-bottom: 8px;
}
.sceneSelectionDescription {
font-size: 14px;
color: gray;
text-align: left;
line-height: 1.2;
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment