图形化建表

zzq
滕嵩 2024-06-06 15:25:28 +08:00
parent 70e08d2686
commit d12e16a726
10 changed files with 526 additions and 170 deletions

View File

@ -9,6 +9,8 @@ enum Api {
GETDATABASEDETAIL = '/api/SysDatabaseLink/GetDataBaseForm', //编辑时获取详情数据
TEStCONNECTTION = '/api/SysDatabaseLink/TestConnection', //测试连接串是否正确
createPicTable = '/api/createTable/createTable', //图形化建表
updateTable = '/api/CreateTable/UpdateTable', //图形化建表-修改
loadTableRecordInfo = '/api/CreateTable/LoadTableRecordInfo', //图形化建表记录
}
/**
@ -38,3 +40,9 @@ export const delDataBaseInfo = (params: AccountListItem) =>
export const createPicTable = (params: AccountListItem) =>
defHttp.post<AccountListGetResultModel>({ url: Api.createPicTable, params });
export const updateTable = (params: AccountParams) =>
defHttp.post<AccountListGetResultModel>({ url: Api.updateTable, params });
export const loadTableRecordInfo = (params: AccountParams) =>
defHttp.get<AccountListGetResultModel>({ url: Api.loadTableRecordInfo, params });

View File

@ -137,7 +137,6 @@
v-bind="item.componentProps"
:is="item.component"
v-model:value="formConfig.currentItem[item.name]"
:key="formConfig.currentItem.component + '===' + formConfig.currentItem.label"
/>
</FormItem>
</div>

View File

@ -185,7 +185,7 @@ export const baseFormItemProps: IBaseFormAttrs[] = [
label: '控件-FormItem',
component: Select,
componentProps: {
options: baseComponents.concat(customComponents[0]).map((item) => ({
options: baseComponents.concat(customComponents[1]).map((item) => ({
value: item.component,
label: item.label,
key: item.component + '===' + item.label,

View File

@ -20,27 +20,14 @@ loginUser:当前登录者信息
callback:,使http使';
`;
export const formItemPropsScript = `
export const formItemPropsScript__ = `
// 示例代码,只支持ES5语法
// 获取表单是新增还是编辑
var isUpdate = utils.isUpdate();
// 组件id
var id = utils.prop
// 组件变更数据
var data = utils.data
// 子组件变更行号 rowIndex
var rowIndex = utils.rowIndex
// 获取主表数据
var value = utils.get('组件id')
// 获取子表行数据
var childRow = utils.get('子表组件id.行号')
// 获取子表单元格数据
var childCell = utils.get('子表组件id.行号.单元格组件id')
// 设置主表数据
utils.set({path:'组件id',value:'xxxx'})
// 添加子表行数据
var row = {}
utils.set({path:'子表组件id',value:row,type:'addTable'})
@ -48,13 +35,6 @@ utils.set({path:'子表组件id',value:row,type:'addTable'})
utils.set({path:'子表组件id.行号',type:'deleteTable'})
// 设置子表某一个单元格数据
utils.set({path:'子表组件id.行号.单元格组件id',value:'xxxxxx'})
// 获取主表字段显示值(针对下拉框等)
var label = utils.getLabel('组件id')
// 获取子表单元格显示值
var cellLabel = utils.getLabel('子表组件id.行号.单元格组件id')
// 去掉子表某一行删除按钮
utils.set({path:'子表组件id.行号.hasNoDeleteBtn',value:false})
// 让子表某一行变成不可编辑
@ -62,47 +42,20 @@ utils.set({path:'子表组件id.行号.disabled',value:true})
// 让子表某一行除了某一列外不可以编辑
utils.set({path:'子表组件id.行号.disabled',value:true})
utils.set({path:'子表组件id.行号.abledList',value:['编辑列组件id']})
// 显示表单加载动画
utils.loading('数据加载中')
// 关闭表单加载状态
utils.hideLoading('数据加载中')
// get请求
// 需要用到回调方法
var getCallback = function(res){ console.log(res)//请求结果 }
utils.httpGet({url:api,params:{},getCallback})
// post请求
// 需要用到回调方法
var postCallback = function(res){ console.log(res)//请求结果 }
utils.httpPost({url:api,params:{},data:{},postCallback})
// put请求
// 需要用到回调方法
var putCallback = function(res){ console.log(res)//请求结果 }
utils.httpPut({url:api,params:{},data:{},putCallback})
// post请求
// 需要用到回调方法
var deleteCallback = function(res){ console.log(res)//请求结果 }
utils.httpDelete({url:api,params:{},data:{},deleteCallback})
// 如果用来上述的请求,用了回掉方法,请最后写
return 'callback'
`;
export function addBreakLines(text: string): string {
// 根据换行符分割文本为行
const lines = text.split(/\r?\n/);
// 在每行后面添加
return lines.map((line) => line + '<br />').join('');
};
export const formItemPropsScript = `
// 获取表单是新增还是编辑
var isUpdate = utils.isUpdate();
// 组件变更数据
var data = utils.data();
// 数据设置
// 获取主表数据
utils.getValue('组件的字段标识');
// 设置主表数据
setFieldsValue(utils.setValue('组件的字段标识', '设置的值'));
export const formItemPropsScript__ = `
// 组件设置
// 设置组件为隐藏
formColumns.value = utils.setHide('组件的字段标识', false);
@ -125,4 +78,26 @@ utils.message('提示信息', '提示类型');
var loginUser = utils.loginUser();
loginUser{ account: , name: }
// 回调方法
// get请求
// 需要用到回调方法
// 例子let resGet = await utils.httpGet('/api/FormScheme/LoadFormPage', { page: 1, limit: 10 });
let resGet = await utils.httpGet(url, params);
url:api, params:
// post请求
let resPost = await utils.httpPost(url, params);
url:api, params:
// put请求
// 需要用到回调方法
let resPut = await utils.httpPut(url, params);
url:api, params:
`;
export function addBreakLines(text: string): string {
// 根据换行符分割文本为行
const lines = text.split(/\r?\n/);
// 在每行后面添加
return lines.map((line) => line + '<br />').join('');
};

View File

@ -553,6 +553,8 @@
cardLayout: cardLayout,
createOrModifyList: createOrModifyList,
buttonLayout: buttonLayout,
record: isUpdate.value ? getFieldsValue() : {},
cardValues: cardValues,
};
utils.setCallModalData(callModalData);
eval(codeClick);

View File

@ -1,5 +1,6 @@
import { useMessage } from '@/hooks/web/useMessage';
import { useUserStore } from '@/store/modules/user';
import { defHttp } from '@/utils/http/axios';
// utils.ts
let c_formModalVisible: Boolean = false;
@ -20,6 +21,8 @@ let c_scrollValue = '';
let c_cardLayout: any = [];
let c_createOrModifyList: any = [];
let c_buttonLayout: any = [];
let c_record: any = {};
let c_cardValues: any = {};
const { createMessage } = useMessage();
const userStore = useUserStore();
@ -28,8 +31,21 @@ const userInfo: any = userStore.getUserInfo;
// 通过field获取组件
function getComponentByFiled(data: string): any {
return c_formColumns.find(item => item.field === data);
};
}
function getChildComponentByFiled(data: string): any {
return c_subTableData.find(item => item.field === data);
}
function setChildComponentByFiled(childComponet: any, field: string): any {
c_formColumns.forEach((item, index1) => {
if (item.columns) {
item.columns[0].children.forEach((i, index2) => {
if (i.field == field) {
c_formColumns[index1].columns[0].children[index2] = childComponet;
}
});
}
});
}
export const utils = {
setCallModalData: function setCallModalData(data: any) {
c_formModalVisible = data.formModalVisible.value;
@ -50,6 +66,8 @@ export const utils = {
c_cardLayout = data.cardLayout.value;
c_createOrModifyList = data.createOrModifyList.value;
c_buttonLayout = data.buttonLayout.value;
c_record = data.record;
c_cardValues = data.cardValues.value;
},
formModalVisible: (): Boolean => {
return c_formModalVisible;
@ -105,13 +123,35 @@ export const utils = {
buttonLayout: (): any => {
return c_buttonLayout;
},
record: (): any => {
return c_record;
},
cardValues: (): any => {
return c_cardValues;
},
// 数据-----------------------------
// 获取数据
// 获取主表数据
getValue: (field: any): any => {
return c_record[field];
},
// 设置主表数据
setValue: (field: any, value: any): any => {
c_record[field] = value;
return c_record;
},
// 获取子表数据
getChildValue: (field: any): any => {
return c_record[field];
},
// 设置子表数据
setChildValue: (field: any, value: any): any => {
c_record[field] = value;
return c_record;
},
// 组件-----------------------------
// 获取组件
getComponent: (field: any): any => {
return getComponentByFiled(field);
return getComponentByFiled(field) || getChildComponentByFiled(field);
},
// 隐藏与否
setHide: (field: any, hideStatus: Boolean): any => {
@ -123,6 +163,12 @@ export const utils = {
c_formColumns[index] = componet;
}
});
} else {
const childComponet: any = getChildComponentByFiled(field);
if (childComponet) {
childComponet.ifShow = hideStatus;
setChildComponentByFiled(childComponet, field);
}
}
return c_formColumns;
},
@ -136,6 +182,12 @@ export const utils = {
c_formColumns[index] = componet;
}
});
} else {
const childComponet: any = getChildComponentByFiled(field);
if (childComponet) {
childComponet.dynamicDisabled = disabledStatus;
setChildComponentByFiled(childComponet, field);
}
}
return c_formColumns;
},
@ -149,6 +201,12 @@ export const utils = {
c_formColumns[index] = componet;
}
});
} else {
const childComponet: any = getChildComponentByFiled(field);
if (childComponet) {
childComponet.required = requiredStatus;
setChildComponentByFiled(childComponet, field);
}
}
return c_formColumns;
},
@ -169,4 +227,26 @@ export const utils = {
loginUser: () => {
return { account: userInfo.account, name: userInfo.name };
},
// 回调方法-----------------------------
// get请求
httpGet: (api: string, params: any) => {
return defHttp.get<any>({
url: api,
params,
});
},
// post请求
httpPost: (api: string, params: any) => {
return defHttp.post<any>({
url: api,
params,
});
},
// put请求
httpPut: (api: string, params: any) => {
return defHttp.put<any>({
url: api,
params,
});
},
};

View File

@ -1,16 +1,34 @@
import { BasicColumn } from '@/components/Table';
import { h } from 'vue';
import { Tag } from 'ant-design-vue';
import { BasicColumn, FormSchema } from '@/components/Table';
export const columns: BasicColumn[] = [
// 图形化建表table
export const graphicaltableColumns: BasicColumn[] = [
{
title: '字段名',
dataIndex: 'name',
key: 'name',
title: '名',
dataIndex: 'tableName',
width: 200,
},
{
title: '类型',
dataIndex: 'type',
key: 'type',
width: 200,
},
{
title: '行数据',
dataIndex: 'columnJson',
ifShow: false,
},
{
title: '创建人',
dataIndex: 'createUser',
width: 200,
},
];
// 图形化建表-搜索
export const searchFormSchema: FormSchema[] = [
{
field: 'keyword',
label: '名称',
component: 'Input',
colProps: { span: 8 },
},
];

View File

@ -1,38 +1,31 @@
<template>
<div class="box-container">
<div class="flex mb-3">
<div class="tab-label"><span class="red">*</span>表名</div>
<a-input v-model:value="tableName" placeholder="请输入表名" />
</div>
<BasicTable class="w-3/4 xl:w-5/5" @register="registerTable">
<PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
<BasicTable
class="w-4/4 xl:w-5/5"
@register="registerTable"
@fetch-success="onFetchSuccess"
:searchInfo="searchInfo"
>
<template #toolbar>
<a-button type="primary" @click="handleAdd"> </a-button>
<a-button type="success" @click="handleSubmit"> </a-button>
<a-button type="primary" @click="handleAdd"></a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'name'">
<a-input v-model:value="record.name" placeholder="请输入字段名" />
</template>
<template v-if="column.key === 'type'">
<a-select
style="width: 100%"
ref="select"
placeholder="请选择"
v-model:value="record.type"
:options="codesTypeArr"
/>
</template>
<template v-if="column.key === 'length'">
<a-input v-model:value="record.length" placeholder="请输入长度" />
{{ record.type ? '空间类型' : '非空间类型' }}
</template>
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
label: '删除',
color: 'error',
label: '查看',
onClick: () => {
handleDelete(record);
handleView(record);
},
},
{
label: '编辑',
onClick: () => {
handleEdit(record);
},
},
]"
@ -40,95 +33,86 @@
</template>
</template>
</BasicTable>
</div>
<Modal @register="registerModal" @submit="submit" />
</PageWrapper>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import { ref, reactive } from 'vue';
import { BasicTable, useTable, TableAction } from '@/components/Table';
import { columns } from './index.data';
import { createPicTable } from '@/api/database/index';
import { useModal } from '@/components/Modal';
//
import Modal from './modal/index.vue';
// ts
import { searchFormSchema, graphicaltableColumns } from './index.data';
// api
import { loadTableRecordInfo } from '@/api/database/index';
const tableName: any = ref('');
const codesTypeArr: any = ref([
{ label: 'varchar', value: 'varchar' },
{ label: 'int', value: 'int' },
{ label: 'date', value: 'date' },
]);
const [registerTable, { reload, setTableData, getDataSource }] = useTable({
rowKey: '',
columns,
//
const isUpdate = ref(false);
//
const isDetail = ref(false);
//
const [registerModal, { openModal }] = useModal();
//
const searchInfo = reactive<Recordable>({});
const [registerTable, { reload, clearSelectedRowKeys }] = useTable({
api: loadTableRecordInfo,
columns: graphicaltableColumns,
rowKey: 'id',
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
},
useSearchForm: false,
showTableSetting: false,
//
showIndexColumn: true,
striped: false,
useSearchForm: true,
showTableSetting: true,
bordered: true,
actionColumn: {
width: 200,
width: 180,
title: '操作',
dataIndex: 'action',
key: 'action',
},
handleSearchInfoFn(info) {
return info;
},
});
function handleAdd() {
let viesObj: any = getDataSource() || [];
viesObj.push({ name: '', type: '', length: 255, id: new Date().getTime() });
setTableData(viesObj);
reload();
}
function handleDelete(record: Recordable) {
let arr = getDataSource();
let newArr: any = [];
arr.forEach((item) => {
if (item.id !== record.id) {
newArr.push(item);
}
});
setTableData(newArr);
// -
function handleAdd() {
isUpdate.value = false;
isDetail.value = false;
openModal(true, {
isUpdate,
isDetail,
});
}
// -
function handleView(record) {
isUpdate.value = true;
isDetail.value = true;
openModal(true, {
isUpdate,
isDetail,
record,
});
}
// -
function handleEdit(record) {
isUpdate.value = true;
isDetail.value = false;
openModal(true, {
isUpdate,
isDetail,
record,
});
}
// -
function submit() {
clearSelectedRowKeys();
reload();
}
function handleSubmit() {
let arr = getDataSource();
console.log('record', arr);
let newArr: any = [];
arr.forEach((item) => {
newArr.push({
name: item.name,
type: item.type,
});
});
const param: any = {
tableName: tableName.value,
tableInfos: newArr,
};
console.log('param', param);
createPicTable(param).then((res: Recordable) => {
console.log('res', res);
});
}
onMounted(() => {
const arr: any = [{ name: '', type: '', length: 255, id: new Date().getTime() }];
setTableData(arr);
});
</script>
<style>
.box-container {
width: 80%;
margin: 20px auto 0;
}
.tab-label {
width: 100px;
display: flex;
align-items: center;
justify-content: end;
}
.red {
color: red;
}
</style>

View File

@ -0,0 +1,248 @@
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
:centered="true"
:canFullscreen="false"
:defaultFullscreen="false"
:showCancelBtn="!isDetail"
:showOkBtn="!isDetail"
:draggable="false"
:title="getTitle"
:useWrapper="true"
:width="700"
@ok="handleOk"
>
<div class="box-container">
<div class="flex mb-3">
<div class="tab-label"><span class="red">*</span>表名</div>
<a-input v-model:value="tableName" placeholder="请输入表名" :disabled="isUpdate" />
</div>
</div>
<BasicTable class="w-3/4 xl:w-5/5" @register="registerTable">
<template #toolbar>
<a-button type="primary" @click="handleAdd" v-if="!isDetail"> </a-button>
<!-- <a-button type="success" @click="handleSubmit"> </a-button> -->
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'name' && !isDetail">
<a-input v-model:value="record.name" placeholder="请输入字段名" />
</template>
<template v-if="column.key === 'type' && !isDetail">
<a-select
style="width: 100%"
ref="select"
placeholder="请选择"
v-model:value="record.type"
:options="codesTypeArr"
/>
</template>
<template v-if="column.key === 'type' && isDetail">
{{ getCodesTypeArrByType(record.type) }}
</template>
<template v-if="column.key === 'explanation' && !isDetail">
<a-input
v-model:value="record.explanation"
placeholder="请输入备注,备注内不能含有英文字符的,和:"
/>
</template>
<template v-if="column.key === 'action' && (!record.isOldColumn || !isUpdate)">
<TableAction
:actions="[
{
label: '删除',
color: 'error',
onClick: () => {
handleDelete(record);
},
},
]"
/>
</template>
</template>
</BasicTable>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, onMounted, defineEmits } from 'vue';
import { BasicModal, useModalInner } from '@/components/Modal';
import { BasicTable, useTable, TableAction } from '@/components/Table';
import { columns, columns_isDetail } from './modal.data';
import { useMessage } from '@/hooks/web/useMessage';
// api
import { createPicTable, updateTable } from '@/api/database/index';
import { getLoad } from '@/api/sys/sysDataItemDetail';
const emit = defineEmits(['submit']);
const { createMessage } = useMessage();
//
const isUpdate = ref(false);
//
const isDetail = ref(false);
const tableName: any = ref('');
const tableColumns: any = ref([]);
const codesTypeArr: any = ref([
{ label: 'varchar', value: 'varchar' },
{ label: 'int', value: 'int' },
{ label: 'date', value: 'date' },
]);
//
const [registerModal, { setModalProps, closeModal }] = useModalInner((data) => {
setModalProps({ confirmLoading: false });
isUpdate.value = data.isUpdate;
isDetail.value = data.isDetail;
//
if (!data.record) {
tableName.value = null;
setTableData([]);
handleAdd();
reload();
} else {
//
tableName.value = data.record.tableName;
const lines = data.record.columnJson.split(':');
let arr: any = [];
for (const line of lines) {
const i = line.split(',');
arr.push({
name: i[0],
type: i[1],
explanation: i[2],
length: 255,
id: new Date().getTime(),
isOldColumn: true,
});
}
setTableData(arr);
reload();
}
//
if (isDetail.value) {
tableColumns.value = columns_isDetail;
} else {
tableColumns.value = columns;
}
});
//
const [registerTable, { reload, setTableData, getDataSource }] = useTable({
rowKey: '',
columns: tableColumns,
formConfig: {
labelWidth: 120,
},
canResize: false,
useSearchForm: false,
showTableSetting: false,
showIndexColumn: true,
bordered: true,
handleSearchInfoFn(info) {
return info;
},
});
//
function handleAdd() {
let viesObj: any = getDataSource() || [];
viesObj.push({ name: '', type: '', explanation: '', length: 255, id: new Date().getTime() });
setTableData(viesObj);
reload();
}
//
function handleDelete(record: Recordable) {
let arr = getDataSource();
let newArr: any = [];
arr.forEach((item) => {
if (item.id !== record.id) {
newArr.push(item);
}
});
setTableData(newArr);
reload();
}
//
function handleOk() {
let arr = getDataSource();
// console.log('record', arr);
let newArr: any = [];
let flag = true;
arr.forEach((item) => {
if (
item.explanation &&
(item.explanation.indexOf(':') > -1 || item.explanation.indexOf(',') > -1)
) {
createMessage.warn('备注内不能含有英文字符的,和:');
flag = false;
}
newArr.push({
name: item.name,
type: item.type,
explanation: item.explanation,
});
});
if (flag) {
const param: any = {
tableName: tableName.value,
tableInfos: newArr,
};
console.log('param', param);
if (isUpdate.value) {
updateTable(param).then((res: Recordable) => {
console.log('res', res);
setTableData([]);
tableName.value = null;
emit('submit');
closeModal();
});
} else {
createPicTable(param).then((res: Recordable) => {
console.log('res', res);
setTableData([]);
tableName.value = null;
emit('submit');
closeModal();
});
}
}
}
//
async function getCodesTypeArr() {
getLoad({ code: 'clounm_type' }).then((res) => {
if (res) {
codesTypeArr.value = [];
res.forEach((item) => {
codesTypeArr.value.push({ label: item.itemName, value: item.itemValue });
});
}
});
}
//
function getCodesTypeArrByType(type) {
for (const codesType of codesTypeArr.value) {
if (codesType.value === type) {
return codesType.label;
}
}
return '';
}
onMounted(() => {
getCodesTypeArr();
});
</script>
<style>
.box-container {
width: 80%;
margin: 20px auto 0;
}
.tab-label {
width: 100px;
display: flex;
align-items: center;
justify-content: end;
}
.red {
color: red;
}
</style>

View File

@ -0,0 +1,42 @@
import { BasicColumn } from '@/components/Table';
export const columns: BasicColumn[] = [
{
title: '字段名',
dataIndex: 'name',
key: 'name',
},
{
title: '类型',
dataIndex: 'type',
key: 'type',
},
{
title: '备注',
dataIndex: 'explanation',
key: 'explanation',
},
{
title: '操作',
dataIndex: 'action',
key: 'action',
},
];
export const columns_isDetail: BasicColumn[] = [
{
title: '字段名',
dataIndex: 'name',
key: 'name',
},
{
title: '类型',
dataIndex: 'type',
key: 'type',
},
{
title: '备注',
dataIndex: 'explanation',
key: 'explanation',
},
];