1.登录时是否输入租户码默认是需要输入。
2.切换租户的部分逻辑抽取成独立工具类。
3.表单编辑模块整合部分代码,将两个tabpane抽出来成独立文件。
4.修正bug-表单的列表配置为空时编辑页面报错,现改成为空时赋值{}。
5.系统数据迁移复制功能:去掉一些无用的代码;改成多租户时才允许以租户模式导入。
590 lines
17 KiB
Vue
590 lines
17 KiB
Vue
<template>
|
||
<div class="step1">
|
||
<div class="step1-form">
|
||
<BasicForm @register="register" />
|
||
</div>
|
||
<template v-if="designType === 'data'">
|
||
<Divider />
|
||
<p>{{ t('添加数据库表(请先选择数据库-第一个选择的为主库)') }}</p>
|
||
<div>
|
||
<a-table
|
||
:columns="columns"
|
||
:data-source="generatorConfig?.tableConfigs"
|
||
:pagination="false"
|
||
>
|
||
<template #bodyCell="{ column, record, index }">
|
||
<template v-if="column.key === 'order'">
|
||
<span>
|
||
{{ index + 1 }}
|
||
</span>
|
||
</template>
|
||
<template v-if="column.key === 'isMain'">
|
||
<span>
|
||
<a-tag :color="record.isMain ? 'blue' : 'orange'">
|
||
{{ record.isMain ? t('主表') : t('附表') }}
|
||
</a-tag>
|
||
</span>
|
||
</template>
|
||
<template v-else-if="column.key === 'relationField'">
|
||
<template v-if="index > 0">
|
||
<Select
|
||
style="width: 200px"
|
||
v-model:value="record[column.key]"
|
||
:placeholder="t('请选择附表关联主表字段')"
|
||
>
|
||
<SelectOption
|
||
v-for="(name, idx) in selectOptions[record.tableName]"
|
||
:key="idx"
|
||
:value="name"
|
||
>
|
||
{{ name }}
|
||
</SelectOption>
|
||
</Select>
|
||
</template>
|
||
</template>
|
||
<template v-else-if="column.key === 'relationTableField'">
|
||
<template v-if="index > 0">
|
||
<Select
|
||
style="width: 200px"
|
||
v-model:value="record[column.key]"
|
||
:placeholder="t('请选择主表字段')"
|
||
>
|
||
<SelectOption
|
||
v-for="(name, idx) in selectOptions[mainTable]"
|
||
:key="idx"
|
||
:value="name"
|
||
>
|
||
{{ name }}
|
||
</SelectOption>
|
||
</Select>
|
||
</template>
|
||
</template>
|
||
<template v-else-if="column.key === 'action'">
|
||
<DeleteTwoTone two-tone-color="#ff8080" @click="remove(index)" />
|
||
</template>
|
||
</template>
|
||
</a-table>
|
||
<a-button type="dashed" block @click="add">
|
||
<PlusOutlined />
|
||
{{ t('新增') }}
|
||
</a-button>
|
||
</div>
|
||
<SelectDatabase @register="registerModal" @success="handleSelectSuccess" />
|
||
</template>
|
||
</div>
|
||
</template>
|
||
<script lang="ts" setup>
|
||
import { computed, inject, Ref, ref, toRaw, watch } from 'vue';
|
||
import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
|
||
import { getDatabaselinkMultiTableColumns } from '/@/api/system/databaselink';
|
||
import { getAuthList } from '/@/api/system/authorize';
|
||
import { Divider } from 'ant-design-vue';
|
||
import { PlusOutlined, DeleteTwoTone } from '@ant-design/icons-vue';
|
||
import { useModal } from '/@/components/Modal';
|
||
import { SelectDatabase } from '/@/components/CreateCodeStep';
|
||
import { Select } from 'ant-design-vue';
|
||
import { debounce, uniqBy } from 'lodash-es';
|
||
import { TableConfig } from '/@/model/generator/tableConfig';
|
||
import { CustomFormConfig, GeneratorConfig } from '/@/model/generator/generatorConfig';
|
||
import { useMessage } from '/@/hooks/web/useMessage';
|
||
import { TableInfo, FieldInfo, upperFieldDb } from '/@/components/Designer';
|
||
import { JavaTypeConvertTsType } from '/@/utils/helper/designHelper';
|
||
import { useI18n } from '/@/hooks/web/useI18n';
|
||
const { t } = useI18n();
|
||
const SelectOption = Select.Option;
|
||
|
||
const { notification } = useMessage();
|
||
|
||
const dataAuthPlaceholder = computed(() => {
|
||
return generatorConfig!.isDataAuth ? t('请选择已有通用数据权限') : t('请先启用数据权限');
|
||
});
|
||
const dataAuthHelpMessage = `
|
||
1.启用数据权限会判断主表是否包含RuleUserlD字段如果存在,则不进行表结构修改,如果不存在,则会对主表进行字段添加。
|
||
2.RuleUserlD主要用来控制每一条记录的权限所属人新增时,默认将当前登录认作为权限所属人。
|
||
3.在表单设计中会添加“批量设置权限所属人”功能启用后,拥有该按钮权限的人员可以设置每一条记录的权限所属人。
|
||
`;
|
||
const formSchemaData: FormSchema[] = [
|
||
{
|
||
field: 'name',
|
||
label: t('表单名称'),
|
||
required: true,
|
||
component: 'Input',
|
||
colProps: {
|
||
span: 12,
|
||
},
|
||
componentProps: {
|
||
placeholder: t('请输入表单名称'),
|
||
onChange: debounce((val: ChangeEvent) => {
|
||
customFormConfig!.name = val.target.value;
|
||
}, 200),
|
||
},
|
||
},
|
||
{
|
||
field: 'category',
|
||
label: t('表单分类'),
|
||
component: 'DicSelect',
|
||
required: true,
|
||
componentProps: {
|
||
placeholder: t('请选择表单分类'),
|
||
itemId: '1419276800524424444',
|
||
isShowAdd: false,
|
||
onChange: debounce((id) => {
|
||
customFormConfig!.category = id;
|
||
}, 200),
|
||
},
|
||
colProps: { span: 12 },
|
||
},
|
||
{
|
||
field: 'remark',
|
||
label: t('功能描述'),
|
||
component: 'Input',
|
||
colProps: { span: 12 },
|
||
componentProps: {
|
||
placeholder: t('请输入功能描述'),
|
||
onChange: debounce((val: ChangeEvent) => {
|
||
customFormConfig!.remark = val.target.value;
|
||
}, 200),
|
||
},
|
||
},
|
||
{
|
||
field: 'databaseId',
|
||
label: t('数据库'),
|
||
component: 'DbSelect',
|
||
required: true,
|
||
colProps: { span: 12 },
|
||
componentProps: {
|
||
placeholder: t('请选择数据库'),
|
||
onChange: debounce((val, option) => {
|
||
generatorConfig!.databaseId = val;
|
||
if (option) isFieldUpper.value = upperFieldDb.includes(option.dbType);
|
||
mainTableName.value = isFieldUpper.value
|
||
? mainTableName.value.toUpperCase()
|
||
: mainTableName.value;
|
||
}, 200),
|
||
},
|
||
},
|
||
{
|
||
field: 'isDataAuth',
|
||
label: t('数据权限'),
|
||
component: 'Switch',
|
||
required: false,
|
||
colProps: { span: 12 },
|
||
helpMessage: dataAuthHelpMessage,
|
||
helpComponentProps: { maxWidth: '400px' },
|
||
componentProps: {
|
||
checkedValue: true,
|
||
unCheckedValue: false,
|
||
onChange: (val) => {
|
||
if (!val) {
|
||
setFieldsValue({ dataAuthList: [] });
|
||
generatorConfig!.dataAuthList = [];
|
||
}
|
||
generatorConfig!.isDataAuth = val;
|
||
},
|
||
},
|
||
},
|
||
{
|
||
field: 'dataAuthList',
|
||
label: t('权限选择'),
|
||
component: 'ApiSelect',
|
||
required: false,
|
||
colProps: { span: 12 },
|
||
componentProps: {
|
||
mode: 'multiple',
|
||
placeholder: dataAuthPlaceholder,
|
||
api: getAuthList,
|
||
labelField: 'name',
|
||
valueField: 'id',
|
||
getPopupContainer: () => document.body,
|
||
onChange: (val) => {
|
||
generatorConfig!.dataAuthList = val;
|
||
},
|
||
},
|
||
dynamicDisabled: ({ values }) => {
|
||
return !values.isDataAuth;
|
||
},
|
||
},
|
||
];
|
||
|
||
const formSchema: FormSchema[] = [
|
||
{
|
||
field: 'name',
|
||
label: t('表单名称'),
|
||
required: true,
|
||
component: 'Input',
|
||
colProps: {
|
||
span: 12,
|
||
},
|
||
componentProps: {
|
||
placeholder: t('请输入表单名称'),
|
||
onChange: debounce((val: ChangeEvent) => {
|
||
customFormConfig!.name = val.target.value;
|
||
}, 200),
|
||
},
|
||
},
|
||
{
|
||
field: 'category',
|
||
label: t('表单分类'),
|
||
component: 'DicSelect',
|
||
required: true,
|
||
componentProps: {
|
||
placeholder: t('请选择表单分类'),
|
||
itemId: '1419276800524424444',
|
||
isShowAdd: false,
|
||
onChange: debounce((id) => {
|
||
customFormConfig!.category = id;
|
||
}, 200),
|
||
},
|
||
colProps: { span: 12 },
|
||
},
|
||
{
|
||
field: 'remark',
|
||
label: t('功能描述'),
|
||
component: 'Input',
|
||
colProps: { span: 12 },
|
||
componentProps: {
|
||
placeholder: t('请输入功能描述'),
|
||
onChange: debounce((val: ChangeEvent) => {
|
||
customFormConfig!.remark = val.target.value;
|
||
}, 200),
|
||
},
|
||
},
|
||
{
|
||
field: 'isDataAuth',
|
||
label: t('数据权限'),
|
||
component: 'Switch',
|
||
required: false,
|
||
colProps: { span: 12 },
|
||
helpMessage: dataAuthHelpMessage,
|
||
helpComponentProps: { maxWidth: '400px' },
|
||
componentProps: {
|
||
checkedValue: true,
|
||
unCheckedValue: false,
|
||
onChange: (val) => {
|
||
if (!val) {
|
||
setFieldsValue({ dataAuthList: [] });
|
||
generatorConfig!.dataAuthList = [];
|
||
}
|
||
generatorConfig!.isDataAuth = val;
|
||
},
|
||
},
|
||
},
|
||
{
|
||
field: 'dataAuthList',
|
||
label: t('权限选择'),
|
||
component: 'ApiSelect',
|
||
required: false,
|
||
colProps: { span: 12 },
|
||
componentProps: {
|
||
mode: 'multiple',
|
||
placeholder: dataAuthPlaceholder,
|
||
api: getAuthList,
|
||
labelField: 'name',
|
||
valueField: 'id',
|
||
getPopupContainer: () => document.body,
|
||
onChange: (val) => {
|
||
generatorConfig!.dataAuthList = val;
|
||
},
|
||
},
|
||
dynamicDisabled: ({ values }) => {
|
||
return !values.isDataAuth;
|
||
},
|
||
},
|
||
];
|
||
|
||
const columns = [
|
||
{
|
||
title: t('序号'),
|
||
dataIndex: 'order',
|
||
key: 'order',
|
||
width: 80,
|
||
},
|
||
{
|
||
title: t('类别'),
|
||
dataIndex: 'isMain',
|
||
key: 'isMain',
|
||
width: 80,
|
||
},
|
||
{
|
||
title: t('表名'),
|
||
dataIndex: 'tableName',
|
||
key: 'tableName',
|
||
},
|
||
{
|
||
title: t('关联字段'),
|
||
dataIndex: 'relationField',
|
||
key: 'relationField',
|
||
},
|
||
{
|
||
title: t('关联表字段'),
|
||
key: 'relationTableField',
|
||
dataIndex: 'relationTableField',
|
||
},
|
||
{
|
||
title: t('操作'),
|
||
key: 'action',
|
||
align: 'center',
|
||
},
|
||
];
|
||
|
||
const selectOptions = ref({});
|
||
const selectTableName = computed(() =>
|
||
generatorConfig!.tableConfigs!.map((item) => item.tableName),
|
||
);
|
||
|
||
const mainTable = computed(
|
||
() => generatorConfig!.tableConfigs!.find((item) => item.isMain)!.tableName,
|
||
);
|
||
|
||
const generatorConfig = inject<GeneratorConfig>('generatorConfig');
|
||
const customFormConfig = inject<CustomFormConfig>('customFormConfig');
|
||
const tableInfo = inject<Ref<TableInfo[]>>('tableInfo');
|
||
const designType = inject<string>('designType');
|
||
const isFieldUpper = inject<Ref<boolean>>('isFieldUpper', ref(false));
|
||
let mainTableName = inject<Ref<string>>('mainTableName', ref(''));
|
||
const formType = inject<Ref<number>>('formType');
|
||
|
||
watch(
|
||
() => generatorConfig?.databaseId,
|
||
(val, oldVal) => {
|
||
const { tableConfigs } = generatorConfig!;
|
||
if (
|
||
designType === 'data' &&
|
||
val &&
|
||
val !== oldVal &&
|
||
tableConfigs &&
|
||
tableConfigs.length > 0
|
||
) {
|
||
getDatabaselinkColumns(val);
|
||
}
|
||
},
|
||
);
|
||
watch(
|
||
() => generatorConfig?.formJson,
|
||
(val) => {
|
||
if (!val) return;
|
||
if (val.list.length > 0 || val.hiddenComponent.length > 0) {
|
||
updateSchema({
|
||
field: 'databaseId',
|
||
componentProps: {
|
||
disabled: true,
|
||
},
|
||
});
|
||
} else {
|
||
updateSchema({
|
||
field: 'databaseId',
|
||
componentProps: {
|
||
disabled: false,
|
||
},
|
||
});
|
||
}
|
||
},
|
||
{
|
||
deep: true,
|
||
},
|
||
);
|
||
|
||
const filterFormSchema=(formSchema:FormSchema[])=>{
|
||
customFormConfig.formType=formType.value;
|
||
const rtSchema=[];
|
||
for(let i=0;i<formSchema.length;i++){
|
||
let item=formSchema[i];
|
||
if(item.field=='category'){
|
||
if(formType.value==1){
|
||
rtSchema.push(item);
|
||
}
|
||
}else{
|
||
rtSchema.push(item);
|
||
}
|
||
}
|
||
return rtSchema;
|
||
};
|
||
|
||
const [registerModal, { openModal }] = useModal();
|
||
|
||
const [register, { validate, getFieldsValue, setFieldsValue, updateSchema }] = useForm({
|
||
labelWidth: 100,
|
||
schemas: designType === 'data' ? filterFormSchema(formSchemaData) : filterFormSchema(formSchema),
|
||
showActionButtonGroup: false,
|
||
});
|
||
|
||
const add = async () => {
|
||
try {
|
||
const values = await validate();
|
||
openModal(true, { databaseId: values.databaseId, selectTableName: selectTableName.value });
|
||
} catch (error) {}
|
||
};
|
||
|
||
const handleSelectSuccess = (selectRows: TableConfig[]) => {
|
||
if (generatorConfig?.tableConfigs && generatorConfig?.tableConfigs.length === 0) {
|
||
generatorConfig!.tableConfigs = [...toRaw(selectRows)];
|
||
} else {
|
||
generatorConfig!.tableConfigs = uniqBy(
|
||
generatorConfig!.tableConfigs!.concat([...selectRows]),
|
||
'tableName',
|
||
);
|
||
}
|
||
const formData = getFieldsValue();
|
||
getDatabaselinkColumns(formData.databaseId);
|
||
};
|
||
|
||
const remove = (index) => {
|
||
// TODO 这里删除 可能会引起 表单设计步骤所有的设计 出错。3个解决方案
|
||
//1 删除后提示用户 表单设计全部清空!
|
||
//2 删除之后根据所删除的表名 去表单设计Json匹配 所绑定的组件 全部将绑定表置空!
|
||
//3 表单设计后 不允许删除表配置!
|
||
|
||
//如果删除的数据是主表 并且还有其他表 默认顺序 找下一张表 将其设为主表
|
||
if (generatorConfig!.tableConfigs![index].isMain && generatorConfig!.tableConfigs!.length > 1) {
|
||
const nextObj = generatorConfig!.tableConfigs![index + 1];
|
||
nextObj.isMain = true;
|
||
nextObj.relationField = '';
|
||
nextObj.relationTableField = '';
|
||
|
||
const nextTable = tableInfo!.value![index + 1];
|
||
nextTable.isMain = true;
|
||
|
||
generatorConfig!.tableConfigs!.splice(index, 1);
|
||
tableInfo?.value.splice(index, 1);
|
||
} else {
|
||
generatorConfig!.tableConfigs!.splice(index, 1);
|
||
tableInfo?.value.splice(index, 1);
|
||
}
|
||
};
|
||
const getDatabaselinkColumns = (databaseId) => {
|
||
getDatabaselinkMultiTableColumns({
|
||
id: databaseId,
|
||
tableNames: selectTableName.value.join(','),
|
||
}).then((result) => {
|
||
for (const key in result) {
|
||
const columnInfo = result[key];
|
||
|
||
//如果已经写入过的表格 不再添加
|
||
if (!tableInfo?.value.find((x) => x.name === key)) {
|
||
const fields = columnInfo.map((field) => {
|
||
const filedInfo: FieldInfo = {
|
||
name: field.column,
|
||
length: field.dataLength,
|
||
type: JavaTypeConvertTsType(field.dataType),
|
||
isPk: field.primaryKey,
|
||
isNullable: field.nullable,
|
||
};
|
||
return filedInfo;
|
||
});
|
||
|
||
tableInfo?.value.push({
|
||
name: key,
|
||
isMain: generatorConfig!.tableConfigs!.find((x) => x.tableName === key)
|
||
?.isMain as boolean,
|
||
fields: fields,
|
||
});
|
||
|
||
const thisTableConfig = generatorConfig!.tableConfigs!.find((x) => x.tableName === key);
|
||
|
||
const col = columnInfo.find((x) => x.primaryKey);
|
||
thisTableConfig!.pkField = col.column;
|
||
thisTableConfig!.pkType = col.dataType;
|
||
}
|
||
|
||
selectOptions.value[key] = columnInfo.map((x) => x.column);
|
||
}
|
||
});
|
||
};
|
||
|
||
//验证当前步骤的数据
|
||
const validateStep = async (): Promise<boolean> => {
|
||
try {
|
||
await validate();
|
||
if (designType !== 'data') {
|
||
return true;
|
||
}
|
||
const { tableConfigs } = generatorConfig as GeneratorConfig;
|
||
|
||
//判断tableconfig 是否为空 或者 一条数据都没有
|
||
if (!tableConfigs || tableConfigs!.length === 0) {
|
||
notification.error({
|
||
message: t('提示'),
|
||
description: t('数据表配置不能为空!'),
|
||
}); //提示消息
|
||
return false;
|
||
}
|
||
|
||
for (const config of tableConfigs!) {
|
||
//如果是主表 可以不需要关联字段等
|
||
if (config.isMain) {
|
||
if (!config.tableName) {
|
||
notification.error({
|
||
message: t('提示'),
|
||
description: t('主表表名未能配置成功!'),
|
||
}); //提示消息
|
||
return false;
|
||
}
|
||
} else {
|
||
//子表需要验证关联字段 已经关联表 是否选择好
|
||
if (!config.tableName) {
|
||
notification.error({
|
||
message: t('提示'),
|
||
description: t('子表表名未能配置成功!'),
|
||
}); //提示消息
|
||
return false;
|
||
}
|
||
|
||
if (!config.relationField) {
|
||
notification.error({
|
||
message: t('提示'),
|
||
description: t(`{name} 表 关联字段未选中`, { name: config.tableName }),
|
||
}); //提示消息
|
||
return false;
|
||
}
|
||
|
||
if (!config.relationTableField) {
|
||
notification.error({
|
||
message: t('提示'),
|
||
description: t(`{name} 表 关联表字段未选中`, { name: config.tableName }),
|
||
}); //提示消息
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
return false;
|
||
}
|
||
return true;
|
||
};
|
||
|
||
defineExpose({ validateStep, setFieldsValue });
|
||
</script>
|
||
<style lang="less" scoped>
|
||
.step1 {
|
||
h3 {
|
||
margin: 0 0 12px;
|
||
font-size: 16px;
|
||
line-height: 32px;
|
||
color: @text-color;
|
||
}
|
||
|
||
h4 {
|
||
margin: 0 0 4px;
|
||
font-size: 14px;
|
||
line-height: 22px;
|
||
color: @text-color;
|
||
}
|
||
|
||
p {
|
||
color: @text-color;
|
||
}
|
||
}
|
||
|
||
.pay-select {
|
||
width: 20%;
|
||
}
|
||
|
||
.pay-input {
|
||
width: 70%;
|
||
}
|
||
|
||
:deep(.ant-table-tbody > tr > td) {
|
||
padding: 16px 8px;
|
||
}
|
||
</style>
|