初始版本提交

This commit is contained in:
yaoyn
2024-02-05 09:15:37 +08:00
parent b52d4414be
commit 445292105f
1848 changed files with 236859 additions and 75 deletions

View File

@ -0,0 +1,572 @@
<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(''));
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 [registerModal, { openModal }] = useModal();
const [register, { validate, getFieldsValue, setFieldsValue, updateSchema }] = useForm({
labelWidth: 100,
schemas: designType === 'data' ? formSchemaData : 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>

View File

@ -0,0 +1,298 @@
<template>
<BasicModal @register="registerModal" v-bind="$attrs" wrapClassName="form-modal">
<template #title>
<div class="step-form-form">
<a href="https://www.learun.cn/" target="_blank">
<DesignLogo />
</a>
<span></span>
<span>{{ t('表单生成代码配置') }}</span>
<a-steps :current="current">
<a-step :title="t('基本信息')" />
<a-step :title="t('界面设计')" />
<a-step :title="t('代码预览')" />
<a-step :title="t('菜单设置')" />
</a-steps>
<div class="btn-box">
<a-button type="primary" @click="handleStepPrev" v-show="current !== 0">{{
t('上一步')
}}</a-button>
<a-button type="primary" @click="handleStepNext" v-show="current < 3">{{
t('下一步')
}}</a-button>
<a-button type="primary" @click="handleSave" v-show="current === 3">{{
t('保存')
}}</a-button>
<a-button type="primary" danger @click="handleClose">{{ t('关闭') }}</a-button>
</div>
</div>
</template>
<div class="step-container">
<StructureConfigStep
ref="structureConfigStepRef"
v-show="current === 0"
:isFormGenerator="true"
/>
<ViewDesignStep ref="viewDesignStepRef" v-show="current === 1" :isFormGenerator="true" />
<PreviewCodeStep ref="previewCodeStepRef" v-show="current === 2" :isFormGenerator="true" />
<MenuConfigStep ref="menuConfigStepRef" v-show="current === 3" />
</div>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, reactive, provide, watch, Ref } from 'vue';
import PreviewCodeStep from '/@/views/generator/dev/components/PreviewCodeStep.vue';
import {
StructureConfigStep,
ViewDesignStep,
MenuConfigStep,
} from '/@/components/CreateCodeStep';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { GeneratorModel } from '/@/api/system/generator/model';
import { FormProps } from '/@/components/Form';
import { buildOption } from '/@/utils/helper/designHelper';
import { buildCode } from '/@/utils/helper/generatorHelper';
import { CustomFormConfig, GeneratorConfig } from '/@/model/generator/generatorConfig';
import { TableInfo } from '/@/components/Designer';
import { getFormTemplate } from '/@/api/form/design';
import { dataFirstGeneratorCode } from '/@/api/system/generator';
import { useUserStore } from '/@/store/modules/user';
import { FormTypeEnum } from '/@/enums/formtypeEnum';
import * as antd from '/@/components/Designer/src/types';
import { useI18n } from '/@/hooks/web/useI18n';
import DesignLogo from '/@/components/ModalPanel/src/DesignLogo.vue';
const { t } = useI18n();
const userStore = useUserStore();
const current = ref(0);
const structureConfigStepRef = ref();
const viewDesignStepRef = ref();
const previewCodeStepRef = ref();
const menuConfigStepRef = ref();
const tableInfo = ref<TableInfo[]>([]);
const widgetForm = JSON.parse(JSON.stringify(antd.widgetForm));
const emits = defineEmits(['success', 'register', 'close']);
const isGetInfo = ref<boolean>(false);
// const templateInfo = inject('getTemplateInfo');
let generatorConfig = reactive<GeneratorConfig>({
databaseId: '', //数据库id
tableConfigs: [],
tableStructureConfigs: [],
formJson: {},
formEventConfig: {},
listConfig: {
isLeftMenu: false,
queryConfigs: [],
leftMenuConfig: {
datasourceType: 'static',
listFieldName: undefined,
apiConfig: {},
dictionaryItemId: undefined,
menuName: '',
parentIcon: '',
childIcon: '',
staticData: [],
},
columnConfigs: [],
buttonConfigs: [],
defaultOrder: true,
isPage: true,
},
menuConfig: {},
outputConfig: {
creator: userStore.getUserInfo.name,
isMenu: true,
},
});
let customFormConfig = reactive<CustomFormConfig>({
name: '',
category: '',
formDesignType: 0,
formType: FormTypeEnum.CUSTOM_FORM,
formJson: generatorConfig,
remark: '',
});
provide<GeneratorConfig>('generatorConfig', generatorConfig);
provide<CustomFormConfig>('customFormConfig', customFormConfig);
provide<Ref<TableInfo[]>>('tableInfo', tableInfo);
provide('widgetForm', widgetForm);
provide<Ref<number>>('current', current); //当前步骤
watch(
() => generatorConfig,
() => {
console.log(generatorConfig, 'generatorConfig:>>watch');
},
{
deep: true,
},
);
watch(
() => customFormConfig,
() => {
console.log(customFormConfig, 'customFormConfig:>>watch');
},
{
deep: true,
},
);
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
setModalProps({
confirmLoading: false,
defaultFullscreen: true,
destroyOnClose: true,
draggable: false,
showOkBtn: false,
showCancelBtn: false,
footer: null,
closable: false,
});
const res = await getFormTemplate(data.formId);
const formJson = JSON.parse(res.formJson);
generatorConfig.databaseId = formJson.databaseId;
generatorConfig.formJson = formJson.formJson;
generatorConfig.tableConfigs = formJson.tableConfigs;
generatorConfig.tableStructureConfigs = formJson.tableStructureConfigs;
generatorConfig.formEventConfig = formJson.formEventConfig;
generatorConfig.outputConfig.dataAuthList = formJson.dataAuthList;
generatorConfig.outputConfig.isDataAuth = formJson.isDataAuth;
isGetInfo.value = true;
});
function handleClose() {
closeModal();
emits('close');
}
//上一步
function handleStepPrev() {
current.value--;
}
//下一步
async function handleStepNext() {
const isOk = await stepValidate[current.value]();
if (!isOk) {
return;
}
current.value++;
}
async function handleSave() {
const isOk = await stepValidate[3]();
if (
generatorConfig.formJson?.hiddenComponent &&
generatorConfig.formJson?.hiddenComponent.length
) {
generatorConfig.formJson.list.push(...generatorConfig.formJson.hiddenComponent);
}
if (!isOk) {
return;
}
const data = generatorConfig as GeneratorModel;
data.frontCode = buildCode(
generatorConfig,
tableInfo.value,
buildOption(generatorConfig.formJson) as FormProps,
);
await dataFirstGeneratorCode(data);
closeModal();
emits('success');
emits('close');
}
const stepValidate = {
//数据表配置 验证
0: () => structureConfigStepRef.value.validateStep(),
1: () => viewDesignStepRef.value.validateStep(),
2: () => previewCodeStepRef.value.validateStep(),
3: () => menuConfigStepRef.value.validateStep(),
};
</script>
<style lang="less" scoped>
@keyframes rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes rotationReverse {
from {
transform: rotate(0deg);
}
to {
transform: rotate(-360deg);
}
}
:deep(.ant-steps-item-process) {
.ant-steps-item-icon {
border: 2px solid #fff;
outline: 2px dashed #1890ff !important;
line-height: 30px;
animation: rotation 4s linear infinite;
.ant-steps-icon {
display: inline-block;
animation: rotationReverse 4s linear infinite;
}
}
}
.step-form-content {
padding: 24px;
background-color: @component-background;
}
.step-form-form {
display: flex;
align-items: center;
font-weight: 400;
a {
margin-left: -16px;
}
span {
font-size: 16px;
margin: 0 20px 0 -5px;
white-space: nowrap;
}
:deep(.ant-steps) {
width: calc(100% - 750px);
}
:deep(.ant-steps-item-container) {
padding: 2px 0 2px 2px;
}
.btn-box {
position: absolute;
right: 10px;
:deep(.ant-btn) {
margin-right: 10px;
}
}
}
.step-container {
height: 100%;
}
.step1 {
padding: 14px;
box-sizing: border-box;
}
</style>

View File

@ -0,0 +1,112 @@
// 自定义表单
<template>
<BasicModal @register="registerModal" v-bind="$attrs">
<GroupCard :growCardList="items" />
<DataFirstModal
v-if="state.isShowDataFirst"
@register="registerDataFirst"
@success="handleSuccess"
@close="handleClose('isShowDataFirst')"
/>
<CodeFirstModal
v-if="state.isShowCodeFirst"
@register="registerCodeFirst"
@success="handleSuccess"
@close="handleClose('isShowCodeFirst')"
/>
<SimpleTemplateModal
v-if="state.isShowSimpleTemplate"
@register="registerSimpleTemplate"
@success="handleSuccess"
@close="handleClose('isShowSimpleTemplate')"
/>
</BasicModal>
</template>
<script lang="ts" setup>
import { reactive } from 'vue';
import { BasicModal, useModal, useModalInner } from '/@/components/Modal';
import GroupCard from '/@/views/generator/dev/components/GroupCard.vue';
import DataFirstModal from './components/DataFirstModal.vue';
import CodeFirstModal from './components/CodeFirstModal.vue';
import SimpleTemplateModal from './components/SimpleTemplateModal.vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { useRouter } from 'vue-router';
import interfaceFirstPng from '/@/assets/images/design/interfaceFirst.png';
import dataFirst from '/@/assets/images/design/dataFirst.png';
import simpleTemplate from '/@/assets/images/design/simpleTemplate.png';
import quickDevelop from '/@/assets/images/design/quickDevelop.png';
const router = useRouter();
const { t } = useI18n();
const [registerDataFirst, { openModal: openDataFirstModal }] = useModal();
const [registerCodeFirst, { openModal: openCodeFirstModal }] = useModal();
const [registerSimpleTemplate, { openModal: openSimpleTemplateModal }] = useModal();
const emits = defineEmits(['success', 'register']);
const items = [
{
name: t('界面优先'),
content: t('以界面为基础设计并生成单表或多表的基础功能'),
btnName: t('设计功能'),
image: interfaceFirstPng,
func: () => {
openCodeFirstModal(true, { undoAble: true });
},
},
{
name: t('数据优先'),
content: t('以数据为基础设置生成单表或多表的基础功能'),
btnName: t('设计功能'),
image: dataFirst,
func: () => {
openDataFirstModal(true, { undoAble: true });
},
},
{
name: t('简易模板'),
content: t('通过简单操作生成不需要考虑数据库的基础功能'),
btnName: t('设计功能'),
image: simpleTemplate,
func: () => {
openSimpleTemplateModal(true, { undoAble: true });
},
},
{
name: t('快速生成代码'),
content: t('根据数据表结构自动快速生成功能代码'),
btnName: t('设计功能'),
image: quickDevelop,
func: () => {
router.push({ path: '/dataconfig/database' });
},
},
];
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
setModalProps({
confirmLoading: false,
canFullscreen: false,
defaultFullscreen: true,
draggable: false,
title: data.title,
showOkBtn: false,
showCancelBtn: false,
});
});
const state = reactive({
isShowDataFirst: true,
isShowCodeFirst: true,
isShowSimpleTemplate: true,
});
const handleClose = (modal) => {
state[modal] = !state[modal];
setTimeout(() => {
state[modal] = !state[modal];
}, 100);
};
const handleSuccess = () => {
emits('success');
closeModal();
};
</script>

View File

@ -0,0 +1,123 @@
<template>
<BasicModal @register="registerCategoryModal" v-bind="$attrs">
<BasicTable @register="registerTable">
<template #action="{ record }">
<a-button type="link" v-if="record.activityFlag === 1" @click="handlePreview(record.id)"
>{{ t('预览') }}
</a-button>
<div v-else class="flex justify-around">
<a-button type="link" @click="handlePreview(record.id)">{{ t('预览') }} </a-button>
<a-popconfirm
:title="t('确认要将当前表单更新到此历史版本吗?')"
:ok-text="t('确定')"
:cancel-text="t('取消')"
@confirm="handleVersion(record)"
>
<a-button type="link">{{ t('更新到此版本') }} </a-button>
</a-popconfirm>
</div>
</template>
</BasicTable>
</BasicModal>
<PreviewModal @register="registerPreviewModal" />
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { BasicModal, useModal, useModalInner } from '/@/components/Modal';
import PreviewModal from './PreviewModal.vue';
import { BasicTable, useTable, BasicColumn } from '/@/components/Table';
import { getFormHistoryInfo, setFormHistoryVersion } from '/@/api/form/design';
import { getFormHistory } from '/@/api/form/design';
import { useMessage } from '/@/hooks/web/useMessage';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
const columns: BasicColumn[] = [
{
dataIndex: 'activityFlag',
title: t('版本'),
customRender: ({ record }) =>
`${record.activityFlag === 1 ? t('当前版本') : t('非当前版本')}`, //1-当前版本 0-非当前版本
},
{
dataIndex: 'createUserName',
title: t('创建人'),
},
{
dataIndex: 'createDate',
title: t('创建时间'),
},
];
const formId = ref<string>('');
const { notification } = useMessage();
const [registerPreviewModal, { openModal }] = useModal();
const [registerCategoryModal, { setModalProps }] = useModalInner(async (data) => {
formId.value = data.formId;
setModalProps({
confirmLoading: false,
draggable: false,
title: data.title,
showOkBtn: false,
showCancelBtn: false,
destroyOnClose: true,
width: 800,
});
});
const [registerTable, { reload }] = useTable({
api: getFormHistory,
beforeFetch: () => {
return formId.value;
},
rowKey: 'id',
columns,
showTableSetting: false,
bordered: true,
maxHeight: 200,
actionColumn: {
width: 180,
title: t('操作'),
dataIndex: 'action',
slots: { customRender: 'action' },
},
});
const handlePreview = async (id) => {
if (!id) {
notification.warning({
message: t('提示'),
description: t('请先选择要预览的数据项'),
}); //提示消息
return;
}
const templateJson = await getFormHistoryInfo(id);
openModal(true, { title: t('预览'), formJson: templateJson.formJson });
};
const handleVersion = (data) => {
setFormHistoryVersion({ formId: data.formId, id: data.id }).then((res) => {
if (res) {
notification.success({
message: t('提示'),
description: t('更新版本成功'),
});
reload();
} else {
notification.error({
message: t('提示'),
description: t('更新版本失败'),
});
}
});
};
</script>
<style lang="less" scoped>
:deep(.vben-basic-table-header__toolbar) {
justify-content: space-between;
}
:deep(.current-row) td {
background: #e6f2ff !important;
}
</style>

View File

@ -0,0 +1,44 @@
<template>
<div>
<BasicModal @register="registerModal" v-bind="$attrs" title="预览">
<SimpleForm
ref="formRef"
:key="show"
:formProps="state.formProps"
:formModel="state.formModel"
/>
</BasicModal>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref, provide } from 'vue';
import SimpleForm from '/@/components/SimpleForm/src/SimpleForm.vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { buildOption } from '/@/utils/helper/designHelper';
const state = reactive({
formProps: {},
formModel: {},
});
provide<boolean>('isCustomForm', true);
const show = ref(1);
const formRef = ref();
const [registerModal, { setModalProps }] = useModalInner(async (data) => {
state.formProps = buildOption(JSON.parse(data.formJson).formJson, false);
show.value += 1;
await formRef.value.setDefaultValue();
formRef.value.resetFields();
setModalProps({
confirmLoading: false,
draggable: false,
title: data.title,
destroyOnClose: true,
showOkBtn: false,
showCancelBtn: false,
width: 800,
});
});
</script>

View File

@ -0,0 +1,355 @@
<template>
<BasicModal @register="registerModal" v-bind="$attrs" wrapClassName="form-modal">
<template #title>
<div class="step-form-form">
<a href="https://www.learun.cn/" target="_blank">
<DesignLogo />
</a>
<span></span>
<span>{{ t('表单设计') }} - {{ t('界面优先') }}</span>
<a-steps :current="current" size="mini">
<a-step :title="t('基础信息')" />
<a-step :title="t('表单设计')" />
<a-step :title="t('表单事件')" />
<a-step :title="t('结构配置')" />
</a-steps>
<div class="btn-box">
<a-button @click="handleStepPrev" v-show="current !== 0">{{ t('上一步') }}</a-button>
<a-button type="primary" @click="handleStepNext" v-show="current < 3">
{{ t('下一步') }}
</a-button>
<a-button type="primary" @click="handleSave" v-show="current === 3">
{{ t('保存') }}
</a-button>
<a-button type="primary" danger @click="handleClose">{{ t('关闭') }}</a-button>
</div>
</div>
</template>
<div class="step-container">
<BasicConfigStep ref="basicConfigStepRef" v-show="current === 0" />
<FormDesignStep ref="formDesignStepRef" v-show="current === 1" />
<FormEventStep ref="formEventStepRef" v-show="current === 2" />
<StructureConfigStep
ref="structureConfigStepRef"
v-show="current === 3"
:isUpdate="isUpdate"
:beforeTableNames="beforeTableNames"
@validate-table="handleSave"
/>
</div>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, reactive, provide, Ref, toRaw, watch } from 'vue';
import { FormDesignStep, FormEventStep, StructureConfigStep } from '/@/components/CreateCodeStep';
import BasicConfigStep from '../BasicConfigStep.vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import {
CustomFormConfig,
CustomFormJson,
GeneratorConfig,
} from '/@/model/generator/generatorConfig';
import { TableInfo } from '/@/components/Designer';
import {
addCodeFirstFormTemplate,
updateCodeFirstFormTemplate,
getFormTemplate,
} from '/@/api/form/design';
import { noHaveTableAndField } from '/@/components/Designer';
import * as antd from '/@/components/Designer/src/types';
import { FormTypeEnum } from '/@/enums/formtypeEnum';
import DesignLogo from '/@/components/ModalPanel/src/DesignLogo.vue';
import { random } from 'lodash-es';
import { useI18n } from '/@/hooks/web/useI18n';
import { FormJson } from '/@/model/generator/codeGenerator';
import { FormEventColumnConfig } from '/@/model/generator/formEventConfig';
import { changeCompsApiConfig, changeEventApiConfig, getMainTable } from '/@/utils/event/design';
const { t } = useI18n();
const current = ref(0);
const basicConfigStepRef = ref();
const formDesignStepRef = ref();
const formEventStepRef = ref();
const structureConfigStepRef = ref();
const widgetForm = ref(JSON.parse(JSON.stringify(antd.widgetForm))); //FormDesignStep -> designer和StructureConfigStep页面使用
const isUpdate = ref<boolean>(false);
const tableInfo = ref<TableInfo[]>([]);
const formId = ref<string>('');
const beforeTableNames = ref<string[]>([]);
const mainTableName = ref('table_' + random(10000, 99999));
let generatorConfig = reactive<CustomFormJson>({
databaseId: '', //数据库id
formJson: {} as FormJson,
tableStructureConfigs: [],
formEventConfig: {} as FormEventColumnConfig,
isDataAuth: false,
dataAuthList: [],
});
let customFormConfig = reactive<CustomFormConfig>({
name: '',
category: '',
formDesignType: 1,
formType: FormTypeEnum.CUSTOM_FORM,
formJson: generatorConfig,
remark: '',
isChange: false,
});
provide<GeneratorConfig>('generatorConfig', generatorConfig as GeneratorConfig);
provide<CustomFormConfig>('customFormConfig', customFormConfig);
provide<Ref<TableInfo[]>>('tableInfo', tableInfo);
provide<Ref<number>>('current', current); //当前步骤
provide<string>('designType', 'code');
provide('widgetForm', widgetForm);
provide<Ref<string>>('mainTableName', mainTableName);
provide<boolean>('isCustomForm', true); //是自定义表单
provide<Ref<boolean>>('isFieldUpper', ref(false));
watch(
() => generatorConfig,
(val) => {
customFormConfig.formJson = val;
},
{
deep: true,
},
);
const emits = defineEmits(['success', 'register', 'close']);
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
isUpdate.value = !!data.isUpdate;
formId.value = data.id;
customFormConfig.isChange = isUpdate.value;
if (formId.value) {
getFormTemplateInfo();
}
setModalProps({
confirmLoading: false,
canFullscreen: false,
defaultFullscreen: true,
destroyOnClose: true,
draggable: false,
showOkBtn: false,
showCancelBtn: false,
footer: null,
closable: false,
});
});
async function getFormTemplateInfo() {
const data = await getFormTemplate(formId.value);
customFormConfig.name = data.name;
customFormConfig.category = data.category;
customFormConfig.formDesignType = data.formDesignType;
customFormConfig.formJson = JSON.parse(data.formJson);
customFormConfig.remark = data.remark;
const { formJson } = customFormConfig;
generatorConfig.databaseId = formJson.databaseId;
generatorConfig.isDataAuth = formJson.isDataAuth;
generatorConfig.dataAuthList = formJson.dataAuthList;
generatorConfig.tableStructureConfigs = formJson.tableStructureConfigs;
generatorConfig.formJson = formJson.formJson;
generatorConfig.formEventConfig = formJson.formEventConfig!;
generatorConfig.formJson.list = generatorConfig.formJson.list.filter(
(x) => x.type !== 'hiddenComponent',
);
getMainTableFirst(generatorConfig.formJson.list);
widgetForm.value = generatorConfig.formJson;
basicConfigStepRef.value.setFieldsValue({
name: customFormConfig?.name,
category: customFormConfig?.category,
remark: customFormConfig?.remark,
databaseId: generatorConfig.databaseId,
isDataAuth: generatorConfig.isDataAuth,
dataAuthList: generatorConfig.dataAuthList,
});
beforeTableNames.value = generatorConfig.tableStructureConfigs!.map((x) => x.tableName);
}
function getMainTableFirst(list) {
list.some((x) => {
if (['tab', 'grid', 'card'].includes(x.type)) {
for (const child of x.layout!) {
return getMainTableFirst(child.list);
}
} else if (x.type === 'table-layout') {
for (const child of x.layout!) {
for (const el of child.list!) {
return getMainTableFirst(el.children);
}
}
} else if (
x.type !== 'form' &&
x.type !== 'one-for-one' &&
!noHaveTableAndField.includes(x.type)
) {
mainTableName.value = x.bindTable;
return true;
}
});
}
function handleClose() {
closeModal();
emits('close');
}
//上一步
function handleStepPrev() {
current.value--;
}
//下一步
async function handleStepNext() {
if (current.value === 2) {
current.value++;
return;
}
const isOk = await stepValidate[current.value]();
if (!isOk) {
return;
}
current.value++;
}
async function handleSave() {
const isOk = await stepValidate[3]();
if (!isOk) {
return;
}
let tableFieldConfigs = getMainTable(generatorConfig.tableStructureConfigs);
changeCompsApiConfig(generatorConfig!.formJson.list, 'code', tableFieldConfigs);
changeEventApiConfig(generatorConfig!.formEventConfig, 'code', tableFieldConfigs);
if (
generatorConfig.formJson?.hiddenComponent &&
generatorConfig.formJson?.hiddenComponent.length
) {
generatorConfig.formJson.list.push(...generatorConfig.formJson.hiddenComponent);
}
if (isUpdate.value) {
handleEditSuccess();
} else {
await addCodeFirstFormTemplate(toRaw(customFormConfig));
closeModal();
emits('success');
emits('close');
}
}
async function handleEditSuccess() {
await updateCodeFirstFormTemplate({ id: formId.value, ...toRaw(customFormConfig) });
closeModal();
emits('success');
emits('close');
}
const stepValidate = {
//数据表配置 验证
0: () => basicConfigStepRef.value.validateStep(),
1: () => formDesignStepRef.value.validateStep(),
2: () => formEventStepRef.value.validateStep(),
3: () => structureConfigStepRef.value.validateStep(),
};
</script>
<style lang="less" scoped>
@keyframes rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes rotationReverse {
from {
transform: rotate(0deg);
}
to {
transform: rotate(-360deg);
}
}
:deep(.ant-steps-item-process) {
.ant-steps-item-icon {
border: 2px solid #fff;
line-height: 30px;
animation: rotation 4s linear infinite;
position: relative;
&::before {
border: 2px dashed #1890ff;
content: '';
width: 36px;
height: 36px;
display: block;
border-radius: 50%;
position: absolute;
left: -4px;
top: -4px;
}
.ant-steps-icon {
display: inline-block;
animation: rotationReverse 4s linear infinite;
}
}
}
.step-form-content {
padding: 24px;
background-color: @component-background;
}
.step-form-form {
display: flex;
align-items: center;
font-weight: 400;
a {
margin-left: -16px;
}
span {
font-size: 16px;
margin: 0 20px 0 -5px;
white-space: nowrap;
}
:deep(.ant-steps) {
width: calc(100% - 750px);
}
:deep(.ant-steps-item-container) {
padding: 3px 0 3px 3px;
}
.btn-box {
position: absolute;
right: 10px;
:deep(.ant-btn) {
margin-right: 10px;
}
}
}
.step-container {
height: 100%;
}
.step1 {
padding: 0 14px;
box-sizing: border-box;
}
</style>

View File

@ -0,0 +1,286 @@
<template>
<BasicModal @register="registerModal" v-bind="$attrs" wrapClassName="form-modal">
<template #title>
<div class="step-form-form">
<a href="https://www.learun.cn/" target="_blank">
<DesignLogo />
</a>
<span></span>
<span>{{ t('表单设计') }} - {{ t('数据优先') }}</span>
<a-steps :current="current">
<a-step :title="t('基础信息')" />
<a-step :title="t('表单设计')" />
<a-step :title="t('表单事件')" />
</a-steps>
<div class="btn-box">
<a-button type="primary" @click="handleStepPrev" v-show="current !== 0">{{
t('上一步')
}}</a-button>
<a-button type="primary" @click="handleStepNext" v-show="current < 2">{{
t('下一步')
}}</a-button>
<a-button type="primary" @click="handleSave" v-show="current === 2">{{
t('保存')
}}</a-button>
<a-button type="primary" danger @click="handleClose">{{ t('关闭') }}</a-button>
</div>
</div>
</template>
<div class="step-container">
<BasicConfigStep ref="basicConfigStepRef" v-show="current === 0" />
<FormDesignStep ref="formDesignStepRef" v-show="current === 1" />
<FormEventStep ref="formEventStepRef" v-show="current === 2" />
</div>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, reactive, provide, watch, Ref, toRaw } from 'vue';
import BasicConfigStep from '../BasicConfigStep.vue';
import { FormDesignStep, FormEventStep } from '/@/components/CreateCodeStep';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { CustomFormConfig, GeneratorConfig } from '/@/model/generator/generatorConfig';
import { TableInfo } from '/@/components/Designer';
import {
addDataFirstFormTemplate,
updateDataFirstFormTemplate,
getFormTemplate,
} from '/@/api/form/design';
import { FormTypeEnum } from '/@/enums/formtypeEnum';
import * as antd from '/@/components/Designer/src/types';
import { useI18n } from '/@/hooks/web/useI18n';
import DesignLogo from '/@/components/ModalPanel/src/DesignLogo.vue';
const { t } = useI18n();
const current = ref(0);
const basicConfigStepRef = ref();
const formDesignStepRef = ref();
const formEventStepRef = ref();
const tableInfo = ref<TableInfo[]>([]);
const widgetForm = ref(JSON.parse(JSON.stringify(antd.widgetForm)));
const isUpdate = ref<boolean>(false);
const formId = ref<string>('');
const emits = defineEmits(['success', 'register', 'close']);
let generatorConfig = reactive<GeneratorConfig>({
databaseId: '', //数据库id
tableConfigs: [],
formJson: {},
formEventConfig: {},
isDataAuth: false,
dataAuthList: [],
});
let customFormConfig = reactive<CustomFormConfig>({
name: '',
category: '',
formDesignType: 0,
formType: FormTypeEnum.CUSTOM_FORM,
formJson: generatorConfig,
remark: '',
});
provide<GeneratorConfig>('generatorConfig', generatorConfig);
provide<CustomFormConfig>('customFormConfig', customFormConfig);
provide<Ref<TableInfo[]>>('tableInfo', tableInfo);
provide<string>('designType', 'data');
provide('widgetForm', widgetForm);
provide<Ref<number>>('current', current); //当前步骤
provide<boolean>('isCustomForm', true); //是自定义表单
watch(
() => generatorConfig,
(val) => {
customFormConfig.formJson = val;
},
{
deep: true,
},
);
const [registerModal, { setModalProps, closeModal }] = useModalInner((data) => {
isUpdate.value = !!data.isUpdate;
formId.value = data.id;
if (formId.value) getFormTemplateInfo();
setModalProps({
confirmLoading: false,
canFullscreen: false,
defaultFullscreen: true,
destroyOnClose: true,
draggable: false,
showOkBtn: false,
showCancelBtn: false,
footer: null,
closable: false,
});
});
async function getFormTemplateInfo() {
const data = await getFormTemplate(formId.value);
customFormConfig.name = data.name;
customFormConfig.category = data.category;
customFormConfig.formDesignType = data.formDesignType;
customFormConfig.formJson = JSON.parse(data.formJson);
customFormConfig.remark = data.remark;
const { formJson } = customFormConfig;
generatorConfig.databaseId = formJson.databaseId;
generatorConfig.isDataAuth = formJson.isDataAuth;
generatorConfig.dataAuthList = formJson.dataAuthList;
generatorConfig.tableConfigs = formJson.tableConfigs;
generatorConfig.formJson = formJson.formJson;
generatorConfig.formEventConfig = formJson.formEventConfig!;
generatorConfig.formJson.list = generatorConfig.formJson.list.filter(
(x) => x.type !== 'hiddenComponent',
);
widgetForm.value = generatorConfig.formJson;
basicConfigStepRef.value.setFieldsValue({
name: customFormConfig?.name,
category: customFormConfig?.category,
remark: customFormConfig?.remark,
databaseId: generatorConfig.databaseId,
isDataAuth: generatorConfig.isDataAuth,
dataAuthList: generatorConfig.dataAuthList,
});
}
function handleClose() {
closeModal();
emits('close');
}
//上一步
function handleStepPrev() {
current.value--;
}
//下一步
async function handleStepNext() {
const isOk = await stepValidate[current.value]();
if (!isOk) {
return;
}
current.value++;
}
async function handleSave() {
const isOk = await stepValidate[2]();
if (!isOk) {
return;
}
if (
generatorConfig.formJson?.hiddenComponent &&
generatorConfig.formJson?.hiddenComponent.length
) {
generatorConfig.formJson.list.push(...generatorConfig.formJson.hiddenComponent);
}
if (isUpdate.value) {
await updateDataFirstFormTemplate({ id: formId.value, ...toRaw(customFormConfig) });
} else {
await addDataFirstFormTemplate(toRaw(customFormConfig));
}
closeModal();
emits('close');
emits('success');
}
const stepValidate = {
//数据表配置 验证
0: () => basicConfigStepRef.value.validateStep(),
1: () => formDesignStepRef.value.validateStep(),
2: () => formEventStepRef.value.validateStep(),
};
</script>
<style lang="less" scoped>
@keyframes rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes rotationReverse {
from {
transform: rotate(0deg);
}
to {
transform: rotate(-360deg);
}
}
:deep(.ant-steps-item-process) {
.ant-steps-item-icon {
border: 2px solid #fff;
line-height: 30px;
animation: rotation 4s linear infinite;
position: relative;
&::before {
border: 2px dashed #1890ff;
content: '';
width: 36px;
height: 36px;
display: block;
border-radius: 50%;
position: absolute;
left: -4px;
top: -4px;
}
.ant-steps-icon {
display: inline-block;
animation: rotationReverse 4s linear infinite;
}
}
}
.step-form-content {
padding: 24px;
background-color: @component-background;
}
.step-form-form {
display: flex;
align-items: center;
font-weight: 400;
a {
margin-left: -16px;
}
span {
font-size: 16px;
margin: 0 20px 0 -5px;
white-space: nowrap;
}
:deep(.ant-steps) {
width: calc(100% - 750px);
}
:deep(.ant-steps-item-container) {
padding: 3px 0 3px 3px;
}
.btn-box {
position: absolute;
right: 10px;
:deep(.ant-btn) {
margin-right: 10px;
}
}
}
.step-container {
height: calc(100% - 50px);
}
.step1 {
padding: 14px;
box-sizing: border-box;
}
</style>

View File

@ -0,0 +1,395 @@
<template>
<BasicModal @register="registerModal" v-bind="$attrs" wrapClassName="form-modal">
<template #title>
<div class="step-form-form">
<a href="https://www.learun.cn/" target="_blank">
<DesignLogo />
</a>
<span></span>
<span>{{ t('表单设计') }} - {{ t('简易模板') }}</span>
<a-steps :current="current" size="mini">
<a-step :title="t('基础信息')" />
<a-step :title="t('表单设计')" />
<a-step :title="t('表单事件')" />
</a-steps>
<div class="btn-box">
<a-button @click="handleStepPrev" v-show="current !== 0">{{ t('上一步') }}</a-button>
<a-button type="primary" @click="handleStepNext" v-show="current < 2">
{{ t('下一步') }}
</a-button>
<a-button type="primary" @click="handleSave" v-show="current === 2">
{{ t('保存') }}
</a-button>
<a-button type="primary" danger @click="handleClose">{{ t('关闭') }}</a-button>
</div>
</div>
</template>
<div class="step-container">
<BasicConfigStep ref="basicConfigStepRef" v-show="current === 0" />
<FormDesignStep ref="formDesignStepRef" v-show="current === 1" />
<FormEventStep ref="formEventStepRef" v-show="current === 2" />
</div>
</BasicModal>
<TableNameModal @register="registerTableName" @success="handleEditSuccess" />
</template>
<script lang="ts" setup>
import { ref, reactive, provide, Ref, toRaw, watch, onMounted } from 'vue';
import { FormDesignStep, FormEventStep, TableNameModal } from '/@/components/CreateCodeStep';
import BasicConfigStep from '../BasicConfigStep.vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { GeneratorConfig, CustomFormConfig } from '/@/model/generator/generatorConfig';
import { TableInfo, upperFieldDb } from '/@/components/Designer';
import { validateTableName, getMasterInfo } from '/@/api/system/generator';
import {
addCodeFirstFormTemplate,
updateCodeFirstFormTemplate,
getFormTemplate,
} from '/@/api/form/design';
import { FormTypeEnum } from '/@/enums/formtypeEnum';
import * as antd from '/@/components/Designer/src/types';
import { random } from 'lodash-es';
import { noHaveTableAndField } from '/@/components/Designer';
import { useI18n } from '/@/hooks/web/useI18n';
import { useModal } from '/@/components/Modal';
import DesignLogo from '/@/components/ModalPanel/src/DesignLogo.vue';
const { t } = useI18n();
const current = ref(0);
const basicConfigStepRef = ref();
const formDesignStepRef = ref();
const formEventStepRef = ref();
const widgetForm = ref(JSON.parse(JSON.stringify(antd.widgetForm))); //FormDesignStep -> designer和StructureConfigStep页面使用
const mainTableName = ref('table_' + random(10000, 99999));
const tableInfo = ref<TableInfo[]>([]);
const isUpdate = ref<boolean>(false);
const formId = ref<string>('');
const beforeTableNames = ref<string[]>([]);
const isFieldUpper = ref<boolean>(false);
let generatorConfig = reactive<GeneratorConfig>({
databaseId: 'master',
formJson: {},
tableStructureConfigs: [],
formEventConfig: {},
isDataAuth: false,
dataAuthList: [],
});
let customFormConfig = reactive<CustomFormConfig>({
name: '',
category: '',
formDesignType: 2,
formType: FormTypeEnum.CUSTOM_FORM,
formJson: generatorConfig,
remark: '',
isChange: false,
});
const [registerTableName, { openModal }] = useModal();
provide<GeneratorConfig>('generatorConfig', generatorConfig);
provide<Ref<TableInfo[]>>('tableInfo', tableInfo);
provide<CustomFormConfig>('customFormConfig', customFormConfig);
provide<Ref<number>>('current', current); //当前步骤
provide<string>('designType', 'template');
provide('widgetForm', widgetForm);
provide<Ref<string>>('mainTableName', mainTableName);
provide<boolean>('isCustomForm', true); //是自定义表单
provide<Ref<boolean>>('isFieldUpper', isFieldUpper);
watch(
() => generatorConfig,
(val) => {
customFormConfig.formJson = val;
},
{
deep: true,
},
);
const emits = defineEmits(['success', 'register', 'close']);
onMounted(async () => {
const res = await getMasterInfo();
isFieldUpper.value = upperFieldDb.includes(res?.dbType || '');
mainTableName.value = isFieldUpper.value
? mainTableName.value.toUpperCase()
: mainTableName.value;
});
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
isUpdate.value = !!data.isUpdate;
formId.value = data.id;
customFormConfig.isChange = isUpdate.value;
if (formId.value) {
getFormTemplateInfo();
}
setModalProps({
confirmLoading: false,
canFullscreen: false,
defaultFullscreen: true,
destroyOnClose: true,
draggable: false,
showOkBtn: false,
showCancelBtn: false,
footer: null,
closable: false,
});
});
async function getFormTemplateInfo() {
const data = await getFormTemplate(formId.value);
customFormConfig.name = data.name;
customFormConfig.category = data.category;
customFormConfig.formDesignType = data.formDesignType;
customFormConfig.formJson = JSON.parse(data.formJson);
customFormConfig.remark = data.remark;
const { formJson } = customFormConfig;
generatorConfig.databaseId = formJson.databaseId;
generatorConfig.isDataAuth = formJson.isDataAuth;
generatorConfig.dataAuthList = formJson.dataAuthList;
generatorConfig.tableStructureConfigs = formJson.tableStructureConfigs;
generatorConfig.formJson = formJson.formJson;
generatorConfig.formEventConfig = formJson.formEventConfig!;
generatorConfig.formJson.list = generatorConfig.formJson.list.filter(
(x) => x.type !== 'hiddenComponent',
);
beforeTableNames.value = generatorConfig.tableStructureConfigs?.map((x) => x.tableName) || [];
getMainTableFirst(generatorConfig.formJson.list);
widgetForm.value = generatorConfig.formJson;
basicConfigStepRef.value.setFieldsValue({
name: customFormConfig?.name,
category: customFormConfig?.category,
remark: customFormConfig?.remark,
databaseId: generatorConfig.databaseId,
isDataAuth: generatorConfig.isDataAuth,
dataAuthList: generatorConfig.dataAuthList,
});
}
function getMainTableFirst(list) {
list.some((x) => {
if (['tab', 'grid', 'card'].includes(x.type)) {
for (const child of x.layout!) {
return getMainTableFirst(child.list);
}
} else if (x.type === 'table-layout') {
for (const child of x.layout!) {
for (const el of child.list!) {
return getMainTableFirst(el.children);
}
}
} else if (
x.type !== 'form' &&
x.type !== 'one-for-one' &&
!noHaveTableAndField.includes(x.type)
) {
mainTableName.value = x.bindTable;
return true;
}
});
}
function handleClose() {
closeModal();
emits('close');
}
//上一步
function handleStepPrev() {
current.value--;
}
//下一步
async function handleStepNext() {
const isOk = await stepValidate[current.value]();
if (!isOk) {
return;
}
current.value++;
}
async function handleSave() {
const isOk = await stepValidate[2]();
if (!isOk) {
return;
}
const tableNames: string[] = generatorConfig.tableStructureConfigs!.map((x) => x.tableName);
const testTableNames = tableNames.filter((x) => {
return !beforeTableNames.value.includes(x);
});
if (!isUpdate.value || (isUpdate.value && testTableNames.length)) {
const names = isUpdate.value ? testTableNames.toString() : tableNames.toString();
validateName(names);
} else {
submitTemplate();
}
}
const validateName = (tableNames) => {
const params = {
id: 'master',
tableNames,
};
validateTableName(params, 'none')
.then(() => {
submitTemplate();
})
.catch((err) => {
if (!err) return;
const info = err.message.split('[')[1];
const existTableName = info.substring(0, info.length - 1).split(',');
const newTableName: string[] = [];
generatorConfig?.tableStructureConfigs!.map((table) => {
const tableName = 'table_' + random(10000, 99999);
if (existTableName.includes(table.tableName)) {
table.tableName = tableName;
}
newTableName.push(tableName);
generatorConfig.formJson.list.map((component) => {
editTableName(component, existTableName, tableName);
});
});
validateName(newTableName.toString());
});
};
const editTableName = (component, existTableName, tableName) => {
if (existTableName.includes(component.bindTable)) {
component.bindTable = tableName;
if (component.children || component.layout || component.list) {
editTableName(component.children, existTableName, tableName);
}
}
};
const submitTemplate = async () => {
if (
generatorConfig.formJson?.hiddenComponent &&
generatorConfig.formJson?.hiddenComponent.length
) {
generatorConfig.formJson.list.push(...generatorConfig.formJson.hiddenComponent);
}
if (isUpdate.value) {
openModal();
} else {
await addCodeFirstFormTemplate(toRaw(customFormConfig));
closeModal();
emits('success');
emits('close');
}
};
async function handleEditSuccess() {
await updateCodeFirstFormTemplate({ id: formId.value, ...toRaw(customFormConfig) });
closeModal();
emits('success');
emits('close');
}
const stepValidate = {
//数据表配置 验证
0: () => basicConfigStepRef.value.validateStep(),
1: () => formDesignStepRef.value.validateStep(),
2: () => formEventStepRef.value.validateStep(),
};
</script>
<style lang="less" scoped>
@keyframes rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes rotationReverse {
from {
transform: rotate(0deg);
}
to {
transform: rotate(-360deg);
}
}
:deep(.ant-steps-item-process) {
.ant-steps-item-icon {
border: 2px solid #fff;
line-height: 30px;
animation: rotation 4s linear infinite;
position: relative;
&::before {
border: 2px dashed #1890ff;
content: '';
width: 36px;
height: 36px;
display: block;
border-radius: 50%;
position: absolute;
left: -4px;
top: -4px;
}
.ant-steps-icon {
display: inline-block;
animation: rotationReverse 4s linear infinite;
}
}
}
.step-form-content {
padding: 24px;
background-color: @component-background;
}
.step-form-form {
display: flex;
align-items: center;
font-weight: 400;
a {
margin-left: -16px;
}
span {
font-size: 16px;
margin: 0 20px 0 -5px;
white-space: nowrap;
}
:deep(.ant-steps) {
width: calc(100% - 750px);
}
:deep(.ant-steps-item-container) {
padding: 3px 0 3px 3px;
}
.btn-box {
position: absolute;
right: 10px;
:deep(.ant-btn) {
margin-right: 10px;
}
}
}
.step-container {
height: 100%;
}
.step1 {
padding: 0 14px;
box-sizing: border-box;
}
</style>