初始版本提交
This commit is contained in:
12
src/components/CreateCodeStep/index.ts
Normal file
12
src/components/CreateCodeStep/index.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export { default as MenuConfigStep } from './src/MenuConfigStep.vue';
|
||||
export { default as StructureConfigStep } from './src/StructureConfigStep.vue';
|
||||
|
||||
export { default as ViewDesignStep } from './src/ViewDesignStep.vue';
|
||||
|
||||
export { default as FormDesignStep } from './src/FormDesignStep.vue';
|
||||
export { default as FormEventStep } from './src/FormEventStep.vue';
|
||||
|
||||
export { default as TableConfigStep } from './src/TableConfigStep.vue';
|
||||
|
||||
export { default as SelectDatabase } from './src/components/SelectDatabase.vue';
|
||||
export { default as TableNameModal } from './src/components/TableNameModal.vue';
|
||||
597
src/components/CreateCodeStep/src/FormDesignStep.vue
Normal file
597
src/components/CreateCodeStep/src/FormDesignStep.vue
Normal file
@ -0,0 +1,597 @@
|
||||
<template>
|
||||
<div style="height: 100%">
|
||||
<DesignForm ref="designFormRef" />
|
||||
<FormPreviewDrawer @register="registerDrawer" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DesignForm, noHaveTableAndField, remoteComponents } from '/@/components/Designer';
|
||||
import FormPreviewDrawer from './components/FormPreviewDrawer.vue';
|
||||
import { inject, ref, Ref, watch } from 'vue';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import { GeneratorConfig } from '/@/model/generator/generatorConfig';
|
||||
import { TableFieldConfig } from '/@/model/generator/tableStructureConfig';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { ComponentOptionModel, FormJson } from '/@/model/generator/codeGenerator';
|
||||
import { unionWith, cloneDeep } from 'lodash-es';
|
||||
import {
|
||||
noHaveField,
|
||||
shortTextComponents,
|
||||
longTextComponents,
|
||||
integerComponents,
|
||||
decimalsComponents,
|
||||
dateTimeComponents,
|
||||
timeComponents,
|
||||
} from '/@/components/Designer';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const generatorConfig = inject<GeneratorConfig>('generatorConfig');
|
||||
const designType = inject<string>('designType');
|
||||
const isCustomForm = inject<boolean>('isCustomForm', false);
|
||||
const widgetForm = inject<any>('widgetForm');
|
||||
const { notification } = useMessage();
|
||||
const current = inject<Ref<number>>('current') as Ref<number>;
|
||||
const designFormRef = ref();
|
||||
const rangeComponents = ['time-range', 'date-range'];
|
||||
watch(
|
||||
() => current.value,
|
||||
() => {
|
||||
if (designType == 'data' || (isCustomForm && designType == 'template')) return;
|
||||
designFormRef.value.setWidgetFormSelect(widgetForm.value);
|
||||
},
|
||||
);
|
||||
//注册抽屉 获取外部操作抽屉得方法
|
||||
const [registerDrawer] = useDrawer();
|
||||
|
||||
const setStructureConfig = () => {
|
||||
//新增时的配置信息
|
||||
const addStructureConfig = cloneDeep(generatorConfig!.tableStructureConfigs || []);
|
||||
generatorConfig!.tableStructureConfigs = [];
|
||||
let tableFieldConfigs = [] as TableFieldConfig[];
|
||||
|
||||
getTableStructure(generatorConfig?.formJson.list, tableFieldConfigs);
|
||||
if (generatorConfig?.formJson.hiddenComponent?.length) {
|
||||
generatorConfig?.formJson.hiddenComponent.map((item) => {
|
||||
if (
|
||||
!generatorConfig?.tableStructureConfigs?.length ||
|
||||
!generatorConfig?.tableStructureConfigs[0]?.isMain
|
||||
) {
|
||||
generatorConfig?.tableStructureConfigs?.unshift({
|
||||
tableName: item.bindTable,
|
||||
tableComment: '',
|
||||
isMain: true,
|
||||
tableFieldConfigs: [],
|
||||
});
|
||||
}
|
||||
if (generatorConfig?.tableStructureConfigs?.length) {
|
||||
generatorConfig?.tableStructureConfigs[0].tableFieldConfigs.push({
|
||||
key: item.key,
|
||||
fieldName: item.bindField.substr(0, 30),
|
||||
fieldLength: 500,
|
||||
fieldType: 0,
|
||||
fieldComment: t('隐藏组件'),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
let isDataAuth = isCustomForm
|
||||
? generatorConfig?.isDataAuth
|
||||
: generatorConfig?.outputConfig.isDataAuth;
|
||||
|
||||
//自定义表单界面优先、简易模板开启了数据权限则新增rule_user_id字段
|
||||
if (isDataAuth) {
|
||||
const hasRuleUserField =
|
||||
generatorConfig?.tableStructureConfigs?.length &&
|
||||
generatorConfig?.tableStructureConfigs[0].tableFieldConfigs.find(
|
||||
(x) => x.key === 'rule_user_id',
|
||||
);
|
||||
if (hasRuleUserField) return;
|
||||
generatorConfig?.tableStructureConfigs?.length &&
|
||||
generatorConfig?.tableStructureConfigs[0].tableFieldConfigs.push({
|
||||
key: 'rule_user_id',
|
||||
fieldName: 'rule_user_id',
|
||||
fieldLength: 500,
|
||||
fieldType: 7,
|
||||
fieldComment: t('数据权限所属人ID'),
|
||||
});
|
||||
} else {
|
||||
if (generatorConfig?.tableStructureConfigs?.length) {
|
||||
generatorConfig.tableStructureConfigs[0].tableFieldConfigs =
|
||||
generatorConfig?.tableStructureConfigs[0].tableFieldConfigs.filter(
|
||||
(x) => x.key !== 'rule_user_id',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (designType === 'code' && addStructureConfig.length) {
|
||||
//编辑回显
|
||||
addStructureConfig.forEach((addConfig) => {
|
||||
generatorConfig!.tableStructureConfigs!.forEach((config) => {
|
||||
if (addConfig.tableName === config.tableName) {
|
||||
config.tableComment = addConfig.tableComment;
|
||||
addConfig.tableFieldConfigs?.forEach((subAddConfig) => {
|
||||
config.tableFieldConfigs?.forEach((subConfig) => {
|
||||
if (subAddConfig.fieldName === subConfig.fieldName) {
|
||||
subConfig.fieldType = subAddConfig.fieldType;
|
||||
subConfig.fieldLength = subAddConfig.fieldLength;
|
||||
subConfig.fieldComment = subAddConfig.fieldComment;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const getTableStructure = (list, tableFieldConfigs) => {
|
||||
list?.map((item) => {
|
||||
if (['tab', 'grid', 'card'].includes(item.type)) {
|
||||
for (const child of item.layout!) {
|
||||
getTableStructure(child.list, tableFieldConfigs);
|
||||
}
|
||||
} else if (item.type === 'table-layout') {
|
||||
for (const child of item.layout!) {
|
||||
for (const el of child.list) {
|
||||
getTableStructure(el.children, tableFieldConfigs);
|
||||
}
|
||||
}
|
||||
} else if (item.type === 'form' || item.type === 'one-for-one') {
|
||||
let subTableFieldConfigs = [] as TableFieldConfig[];
|
||||
generatorConfig?.tableStructureConfigs?.push({
|
||||
key: item.key,
|
||||
tableName: item.bindTable,
|
||||
tableComment: '',
|
||||
isMain: false,
|
||||
tableFieldConfigs: subTableFieldConfigs,
|
||||
});
|
||||
if (item.type === 'form') {
|
||||
item.children?.map((subItem) => {
|
||||
if (!noHaveField.includes(subItem.type) || rangeComponents.includes(subItem.type)) {
|
||||
setTableFieldConfigs(subItem, subTableFieldConfigs);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
getSimpleTableStructure(item.children, subTableFieldConfigs);
|
||||
}
|
||||
} else if (
|
||||
(!noHaveField.includes(item.type) && item.type !== 'input') ||
|
||||
rangeComponents.includes(item.type) ||
|
||||
(item.type == 'input' && !item.options.isSave)
|
||||
) {
|
||||
if (
|
||||
!generatorConfig?.tableStructureConfigs?.length ||
|
||||
!generatorConfig?.tableStructureConfigs[0].isMain
|
||||
) {
|
||||
generatorConfig?.tableStructureConfigs?.unshift({
|
||||
tableName: item.bindTable,
|
||||
tableComment: '',
|
||||
isMain: true,
|
||||
tableFieldConfigs,
|
||||
});
|
||||
}
|
||||
|
||||
if (generatorConfig?.tableStructureConfigs?.length) {
|
||||
setTableFieldConfigs(item, generatorConfig?.tableStructureConfigs[0].tableFieldConfigs);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
const getSimpleTableStructure = (list, tableFieldConfigs) => {
|
||||
list?.map((item) => {
|
||||
if (['tab', 'grid', 'card'].includes(item.type)) {
|
||||
for (const child of item.layout!) {
|
||||
getSimpleTableStructure(child.list, tableFieldConfigs);
|
||||
}
|
||||
} else if (!noHaveField.includes(item.type) || rangeComponents.includes(item.type)) {
|
||||
setTableFieldConfigs(item, tableFieldConfigs);
|
||||
}
|
||||
});
|
||||
};
|
||||
const setTableFieldConfigs = (item, tableFieldConfigs) => {
|
||||
if (rangeComponents.includes(item.type)) {
|
||||
tableFieldConfigs.push({
|
||||
key: item.key,
|
||||
fieldStartName: item.bindStartTime,
|
||||
fieldName: item.bindStartTime.substr(0, 30),
|
||||
fieldLength: !getFieldType(item.type) ? 500 : null,
|
||||
fieldType: getFieldType(item.type),
|
||||
fieldComment: t(`{types}开始时间`, { types: item.label }),
|
||||
});
|
||||
tableFieldConfigs.push({
|
||||
key: item.key,
|
||||
fieldEndName: item.bindEndTime,
|
||||
fieldName: item.bindEndTime.substr(0, 30),
|
||||
fieldLength: !getFieldType(item.type) ? 500 : null,
|
||||
fieldType: getFieldType(item.type),
|
||||
fieldComment: t(`{types}结束时间`, { types: item.label }),
|
||||
});
|
||||
} else if (item.type === 'info' && item.options.infoType === 2) {
|
||||
//信息体组件 选择当前时间情况
|
||||
tableFieldConfigs.push({
|
||||
key: item.key,
|
||||
fieldName: item.bindField.substr(0, 30),
|
||||
fieldLength: null,
|
||||
fieldType: 5,
|
||||
fieldComment: item.label!,
|
||||
});
|
||||
} else {
|
||||
if (item.options.isSave && item.type == 'input') return;
|
||||
tableFieldConfigs.push({
|
||||
key: item.key,
|
||||
fieldName: item.bindField.substr(0, 30),
|
||||
fieldLength: !getFieldType(item.type) ? 500 : null,
|
||||
fieldType: getFieldType(item.type),
|
||||
fieldComment: item.label!,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const getFieldType = (type) => {
|
||||
switch (type) {
|
||||
case shortTextComponents.find((x) => x === type):
|
||||
return 0;
|
||||
case longTextComponents.find((x) => x === type):
|
||||
return 1;
|
||||
case integerComponents.find((x) => x === type):
|
||||
return 2;
|
||||
case decimalsComponents.find((x) => x === type):
|
||||
return 3;
|
||||
case dateTimeComponents.find((x) => x === type):
|
||||
return 5;
|
||||
case timeComponents.find((x) => x === type):
|
||||
return 8;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
//验证当前步骤的数据
|
||||
const validateStep = async (): Promise<boolean> => {
|
||||
if (designType !== 'data') {
|
||||
setStructureConfig();
|
||||
}
|
||||
const formJson = designFormRef.value.getJson() as FormJson;
|
||||
|
||||
//formJson 是否为空 或者 一个组件都没有
|
||||
if (!formJson || formJson.list.length === 0) {
|
||||
notification.error({
|
||||
message: t('提示'),
|
||||
description: t('表单设计不能为空!'),
|
||||
}); //提示消息
|
||||
return false;
|
||||
}
|
||||
|
||||
const { tableConfigs } = generatorConfig as GeneratorConfig;
|
||||
const mainTableName = tableConfigs?.find((x) => x.isMain)?.tableName;
|
||||
|
||||
//先判断所有非子表组件 是否包含主表字段 如果一个主表字段都没有 提示
|
||||
const getMainComponent = (list?) => {
|
||||
if (!list) return [];
|
||||
let mainComponents = [] as Recordable[];
|
||||
for (const item of list) {
|
||||
if (['tab', 'grid', 'card'].includes(item.type)) {
|
||||
for (const child of item.layout!) {
|
||||
mainComponents = unionWith(mainComponents, getMainComponent(child.list));
|
||||
}
|
||||
} else if (item.type === 'table-layout') {
|
||||
for (const child of item.layout!) {
|
||||
for (const el of child.list) {
|
||||
mainComponents = unionWith(mainComponents, getMainComponent(el.children));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (item.bindTable === mainTableName) {
|
||||
mainComponents.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return mainComponents;
|
||||
};
|
||||
|
||||
//获取子表中的字段
|
||||
const getSubField = (list, subTableFieldList = {}) => {
|
||||
for (const item of list) {
|
||||
if (['tab', 'grid', 'card'].includes(item.type)) {
|
||||
for (const child of item.layout!) {
|
||||
getSubField(child.list, subTableFieldList);
|
||||
}
|
||||
}
|
||||
if (item.type === 'form' || item.type === 'one-for-one') {
|
||||
if (!item.children.length) return;
|
||||
if (!subTableFieldList[item.bindTable]) subTableFieldList[item.bindTable] = [];
|
||||
item.children.forEach((x) => {
|
||||
if (['tab', 'grid', 'card'].includes(x.type)) {
|
||||
for (const child of x.layout!) {
|
||||
getSubField(child.list, subTableFieldList);
|
||||
}
|
||||
} else if (rangeComponents.includes(x.type)) {
|
||||
subTableFieldList[item.bindTable].push(x.bindStartTime);
|
||||
subTableFieldList[item.bindTable].push(x.bindEndTime);
|
||||
} else {
|
||||
if (x.bindField) subTableFieldList[item.bindTable].push(x.bindField);
|
||||
}
|
||||
});
|
||||
} else if (item.isSingleFormChild) {
|
||||
if (rangeComponents.includes(item.type)) {
|
||||
subTableFieldList[item.bindTable].push(item.bindStartTime);
|
||||
subTableFieldList[item.bindTable].push(item.bindEndTime);
|
||||
} else {
|
||||
if (item.bindField) subTableFieldList[item.bindTable].push(item.bindField);
|
||||
}
|
||||
}
|
||||
}
|
||||
return subTableFieldList;
|
||||
};
|
||||
|
||||
//判断子表的同一表中 是否有重复字段
|
||||
const subTableHasSameField = () => {
|
||||
const subTableFieldList = getSubField(formJson.list);
|
||||
const hasSameFieldList: boolean[] = [];
|
||||
if (!Object.keys(subTableFieldList!).length) return false;
|
||||
for (let key in subTableFieldList) {
|
||||
const res = subTableFieldList[key].length === new Set(subTableFieldList[key]).size;
|
||||
hasSameFieldList.push(res);
|
||||
}
|
||||
return hasSameFieldList.some((x) => !x);
|
||||
};
|
||||
|
||||
//主表中的所有字段
|
||||
const mainTableFieldList: string[] = [];
|
||||
getMainComponent(formJson.list).forEach((item) => {
|
||||
if (rangeComponents.includes(item.type)) {
|
||||
mainTableFieldList.push(item.bindStartTime);
|
||||
mainTableFieldList.push(item.bindEndTime);
|
||||
} else {
|
||||
mainTableFieldList.push(item.bindField);
|
||||
}
|
||||
});
|
||||
|
||||
//界面优先、简易模板 判断子表里面是否有组件
|
||||
const hasSubFormComponents = generatorConfig?.tableStructureConfigs
|
||||
?.filter((x) => !x.isMain)
|
||||
.every((x) => !!x.tableFieldConfigs.length);
|
||||
|
||||
if (!getMainComponent(formJson.list).length && designType === 'data') {
|
||||
notification.error({
|
||||
message: t('提示'),
|
||||
description: t('表单设计未绑定一个主表字段,最少得包含一个主表字段!'),
|
||||
}); //提示消息
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
generatorConfig?.tableStructureConfigs &&
|
||||
!generatorConfig?.tableStructureConfigs[0]?.isMain &&
|
||||
designType !== 'data'
|
||||
) {
|
||||
notification.error({
|
||||
message: t('提示'),
|
||||
description: t('表单设计未添加生成主表字段的组件,请先添加后再进行下一步。'),
|
||||
}); //提示消息
|
||||
return false;
|
||||
}
|
||||
//判断是否多表 但是没有子表单组件
|
||||
if (
|
||||
(designType === 'data' &&
|
||||
tableConfigs!.length > 1 &&
|
||||
sumSubFormComponent(formJson.list) !== tableConfigs!.length - 1) ||
|
||||
(designType !== 'data' && !hasSubFormComponents)
|
||||
) {
|
||||
notification.error({
|
||||
message: t('提示'),
|
||||
description: t('有子表未绑定组件!'),
|
||||
}); //提示消息
|
||||
return false;
|
||||
}
|
||||
if (designType === 'data' && mainTableFieldList.length > new Set(mainTableFieldList).size) {
|
||||
notification.error({
|
||||
message: t('提示'),
|
||||
description: t('主表中有组件绑定相同字段!'),
|
||||
}); //提示消息
|
||||
return false;
|
||||
}
|
||||
if (designType === 'data' && subTableHasSameField()) {
|
||||
notification.error({
|
||||
message: t('提示'),
|
||||
description: t('子表中有组件绑定相同字段!'),
|
||||
}); //提示消息
|
||||
return false;
|
||||
}
|
||||
//判断隐藏组件是否填写完整
|
||||
if (formJson.hiddenComponent?.length) {
|
||||
const isCompelete = formJson.hiddenComponent.every((com) => {
|
||||
return Object.values(com).every((val) => val !== '');
|
||||
});
|
||||
if (!isCompelete) {
|
||||
notification.error({
|
||||
message: t('提示'),
|
||||
description: t('隐藏组件需填写完整'),
|
||||
}); //提示消息
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//一个个组件遍历
|
||||
const message = validateComponent(formJson.list);
|
||||
|
||||
if (message) {
|
||||
notification.error({
|
||||
message: t('提示'),
|
||||
description: message,
|
||||
}); //提示消息
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const validateComponent = (list: ComponentOptionModel[]) => {
|
||||
for (const component of list) {
|
||||
//布局组件需要递归子集
|
||||
if (['card', 'tab', 'form', 'grid', 'one-for-one'].includes(component.type)) {
|
||||
//如果是子表单 默认取 children中的组件 其他的 都是取layout中的组件
|
||||
if (component.type === 'form' || component.type === 'one-for-one') {
|
||||
if (!component.bindTable) {
|
||||
return t(`{name}(子表单)未绑定表`, { name: component.label });
|
||||
}
|
||||
|
||||
if (!component.children || component.children.length === 0) {
|
||||
return t(`{name}(子表单)的子组件不能为空`, { name: component.label });
|
||||
}
|
||||
//如果子组件有错误 直接返回顶级
|
||||
const errorMsg = validateComponent(component.children);
|
||||
if (errorMsg) {
|
||||
return errorMsg;
|
||||
}
|
||||
} else {
|
||||
if (!component.layout || component.layout.length === 0) {
|
||||
return t(`{name}组件布局不能为空`, { name: component.label });
|
||||
}
|
||||
|
||||
for (const item of component.layout) {
|
||||
const errorMsg = validateComponent(item.list);
|
||||
if (errorMsg) {
|
||||
return errorMsg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (component.type === 'table-layout') {
|
||||
for (const item of component.layout!) {
|
||||
for (const list of item.list) {
|
||||
if (list.children && list.children.length > 0) {
|
||||
const errorMsg = validateComponent(list.children);
|
||||
if (errorMsg) {
|
||||
return errorMsg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
noHaveTableAndField.includes(component.type) ||
|
||||
component.type === 'form' ||
|
||||
component.type === 'one-for-one'
|
||||
) {
|
||||
//如果是不需要绑定字段的 默认跳过验证
|
||||
continue;
|
||||
}
|
||||
|
||||
//如果是时间区间组件 必须要绑定2个字段 一个开始时间 一个结束时间
|
||||
if (component.type === 'range') {
|
||||
if (!component.bindStartTime) {
|
||||
return t(`{name}未绑定开始时间字段`, { name: component.label });
|
||||
}
|
||||
if (!component.bindEndTime) {
|
||||
return t(`{name}未绑定结束时间字段`, { name: component.label });
|
||||
}
|
||||
}
|
||||
|
||||
//级联组件 必须选择级联配置
|
||||
if (component.type === 'cascader' && !component.options!.apiConfig.apiId) {
|
||||
return t(`{name}未选择级联配置`, { name: component.label });
|
||||
}
|
||||
|
||||
//如果是远程组件并且是 数据源 或者 数据字典 方式
|
||||
if (
|
||||
remoteComponents.includes(component.type) &&
|
||||
component.options!.datasourceType !== 'staticData'
|
||||
) {
|
||||
if (component.options!.datasourceType === 'dic') {
|
||||
if (!component.options!.itemId) {
|
||||
return t(`{name}未选择数据字典`, { name: component.label });
|
||||
}
|
||||
if (!component.options!.dicOptions?.length && component.type === 'associate-popup') {
|
||||
return t(`{name}未进行联想配置`, { name: component.label });
|
||||
}
|
||||
|
||||
if (!component.options!.dicOptions?.length && component.type === 'multiple-popup') {
|
||||
return t(`{name}未进行显示配置`, { name: component.label });
|
||||
}
|
||||
}
|
||||
if (component.options!.datasourceType === 'api') {
|
||||
if (!component.options!.apiConfig.apiId) {
|
||||
return t(`{name}未选择API`, { name: component.label });
|
||||
}
|
||||
if (!component.options!.apiConfig.outputParams && component.type === 'associate-popup') {
|
||||
return t(`{name}未进行联想配置`, { name: component.label });
|
||||
}
|
||||
|
||||
if (!component.options!.apiConfig.outputParams && component.type === 'multiple-popup') {
|
||||
return t(`{name}未进行显示配置`, { name: component.label });
|
||||
}
|
||||
}
|
||||
|
||||
if (component.options!.datasourceType === 'datasource') {
|
||||
if (!component.options!.sourceId) {
|
||||
return t(`{name}未选择数据源`, { name: component.label });
|
||||
}
|
||||
if (!component.options!.labelField) {
|
||||
return t(`{name}未选择数据源显示字段`, { name: component.label });
|
||||
}
|
||||
if (!component.options!.valueField) {
|
||||
return t(`{name}未选择数据源保存字段`, { name: component.label });
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO 这里继续写各组件自己特有的一些验证
|
||||
if (
|
||||
(component.type == 'input' && !component.options!.isSave && !component.bindTable) ||
|
||||
(component.type !== 'input' && !component.bindTable)
|
||||
) {
|
||||
return t(`{name}未绑定表`, { name: component.label });
|
||||
}
|
||||
|
||||
if (!component.bindField && !component.type.includes('range')) {
|
||||
return t(`{name}未绑定字段`, { name: component.label });
|
||||
}
|
||||
|
||||
if (
|
||||
component.type.includes('range') &&
|
||||
(!component.bindStartTime || !component.bindEndTime)
|
||||
) {
|
||||
return t(`{name}未绑定开始字段或结束字段`, { name: component.label });
|
||||
}
|
||||
if (component.type === 'auto-code' && !component.options!.autoCodeRule) {
|
||||
return t(`{name}未选择编码规则`, { name: component.label });
|
||||
}
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
//遍历所有组件 是否有子表单
|
||||
const sumSubFormComponent = (list: ComponentOptionModel[]): number => {
|
||||
let totalSubForm = 0;
|
||||
for (const component of list) {
|
||||
if (
|
||||
(component.type === 'form' || component.type === 'one-for-one') &&
|
||||
!!component.bindTable &&
|
||||
component.children?.length
|
||||
) {
|
||||
totalSubForm++;
|
||||
}
|
||||
|
||||
//布局组件需要递归子集
|
||||
if (['card', 'tab', 'grid'].includes(component.type)) {
|
||||
//如果是子表单 默认取 children中的组件 其他的 都是取layout中的组件
|
||||
if (!component.layout || component.layout.length === 0) {
|
||||
continue;
|
||||
}
|
||||
for (const item of component.layout) {
|
||||
totalSubForm += sumSubFormComponent(item.list);
|
||||
}
|
||||
}
|
||||
if (component.type === 'table-layout') {
|
||||
for (const item of component.layout!) {
|
||||
for (const list of item.list!) {
|
||||
if (list.children && list.children.length > 0) {
|
||||
totalSubForm += sumSubFormComponent(list.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return totalSubForm;
|
||||
};
|
||||
defineExpose({ validateStep });
|
||||
</script>
|
||||
263
src/components/CreateCodeStep/src/FormEventStep.vue
Normal file
263
src/components/CreateCodeStep/src/FormEventStep.vue
Normal file
@ -0,0 +1,263 @@
|
||||
<template>
|
||||
<div style="height: 100%">
|
||||
<div class="event-box">
|
||||
<div>
|
||||
<EventArea
|
||||
:columnList="generatorConfig!.formEventConfig[0]"
|
||||
:index="0"
|
||||
@delete-event="deleteEvent"
|
||||
@click-node="clickNode"
|
||||
/>
|
||||
<AddEvent :lineHeight="`${lineHeight[0]}px`" @click="addEvent(0)" />
|
||||
</div>
|
||||
<div class="box-second">
|
||||
<EventArea
|
||||
:columnList="generatorConfig!.formEventConfig[1]"
|
||||
:index="1"
|
||||
@delete-event="deleteEvent"
|
||||
@click-node="clickNode"
|
||||
/>
|
||||
<AddEvent :lineHeight="`${lineHeight[1]}px`" @click="addEvent(1)" />
|
||||
</div>
|
||||
<div class="box-third">
|
||||
<EventArea
|
||||
:columnList="generatorConfig!.formEventConfig[2]"
|
||||
:index="2"
|
||||
@delete-event="deleteEvent"
|
||||
@click-node="clickNode"
|
||||
/>
|
||||
<AddEvent :lineHeight="`${lineHeight[2]}px`" @click="addEvent(2)" />
|
||||
</div>
|
||||
<div class="box-fourth">
|
||||
<EventArea
|
||||
:columnList="generatorConfig!.formEventConfig[3]"
|
||||
:index="3"
|
||||
@delete-event="deleteEvent"
|
||||
@click-node="clickNode"
|
||||
/>
|
||||
<AddEvent :isLast="true" @click="addEvent(3)" />
|
||||
<EventArea
|
||||
:columnList="generatorConfig!.formEventConfig[4]"
|
||||
:index="4"
|
||||
@click-node="clickNode"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<NodeEvent v-model:columnList="generatorConfig!.formEventConfig" :selectedNode="selectedNode" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, Ref, reactive, inject, watch } from 'vue';
|
||||
import EventArea from './components/EventArea.vue';
|
||||
import NodeEvent from './components/NodeEvent.vue';
|
||||
import AddEvent from './components/AddEvent.vue';
|
||||
import { GeneratorConfig } from '/@/model/generator/generatorConfig';
|
||||
import {
|
||||
FormEventColumnConfig,
|
||||
FormEventStyleConfig,
|
||||
selectedNodeConfig,
|
||||
} from '/@/model/generator/formEventConfig';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { changeCompsApiConfig, changeEventApiConfig, getMainTable } from '/@/utils/event/design';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
const { t } = useI18n();
|
||||
const { notification } = useMessage();
|
||||
const current = inject<Ref<number>>('current');
|
||||
const isCustomForm = inject<boolean>('isCustomForm', false);
|
||||
const designType = inject<string>('designType');
|
||||
const widgetForm = inject<any>('widgetForm');
|
||||
const fromMobile = inject<Boolean>('fromMobile', false);
|
||||
const generatorConfig = inject<GeneratorConfig>('generatorConfig');
|
||||
let lineHeight = reactive<number[]>([]);
|
||||
let column = reactive<FormEventColumnConfig>({
|
||||
0: [
|
||||
{
|
||||
type: 'circle',
|
||||
color: '#2774ff',
|
||||
text: t('开始节点'),
|
||||
icon: '#icon-kaishi',
|
||||
bgcColor: '#D8E5FF',
|
||||
isUserDefined: false,
|
||||
},
|
||||
{
|
||||
color: '#F6AB01',
|
||||
icon: '#icon-chushihua',
|
||||
text: t('初始化表单'),
|
||||
bgcColor: '#f9f5ea',
|
||||
isUserDefined: false,
|
||||
nodeInfo: {
|
||||
processEvent: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
1: [
|
||||
{
|
||||
color: '#B36EDB',
|
||||
icon: '#icon-shujufenxi',
|
||||
text: t('获取表单数据'),
|
||||
detail: t('(新增无此操作)'),
|
||||
bgcColor: '#F8F2FC',
|
||||
isUserDefined: false,
|
||||
nodeInfo: {
|
||||
processEvent: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
2: [
|
||||
{
|
||||
color: '#F8625C',
|
||||
icon: '#icon-jiazai',
|
||||
text: t('加载表单'),
|
||||
bgcColor: '#FFF1F1',
|
||||
isUserDefined: false,
|
||||
nodeInfo: {
|
||||
processEvent: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
3: [
|
||||
{
|
||||
color: '#6C6AE0',
|
||||
icon: '#icon-jsontijiao',
|
||||
text: t('提交表单'),
|
||||
bgcColor: '#F5F4FF',
|
||||
isUserDefined: false,
|
||||
nodeInfo: {
|
||||
processEvent: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
4: [
|
||||
{
|
||||
type: 'circle',
|
||||
color: '#F8625C',
|
||||
text: t('结束节点'),
|
||||
icon: '#icon-jieshuzhiliao',
|
||||
bgcColor: '#FFD6D6',
|
||||
isLast: true,
|
||||
isUserDefined: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
watch(
|
||||
() => current?.value,
|
||||
(val) => {
|
||||
//自定义表单当前步骤为2 或 代码生成器 数据优先当前步骤为2 或 代码生成器 界面优先、简易模板 当前步骤为1
|
||||
if (
|
||||
(isCustomForm && val === 2) ||
|
||||
(!isCustomForm &&
|
||||
((designType === 'data' && val === 2) || (designType !== 'data' && val === 1)))
|
||||
) {
|
||||
if (
|
||||
((isCustomForm && designType == 'code') || (!isCustomForm && designType !== 'data')) &&
|
||||
generatorConfig!.tableStructureConfigs
|
||||
) {
|
||||
let tableFieldConfigs = getMainTable(generatorConfig!.tableStructureConfigs);
|
||||
changeCompsApiConfig(generatorConfig!.formJson.list, designType, tableFieldConfigs);
|
||||
changeEventApiConfig(generatorConfig!.formEventConfig, designType, tableFieldConfigs);
|
||||
widgetForm.value.list = cloneDeep(generatorConfig!.formJson.list);
|
||||
}
|
||||
|
||||
if (
|
||||
generatorConfig?.formEventConfig &&
|
||||
Object.keys(generatorConfig!.formEventConfig)?.length
|
||||
) {
|
||||
column = generatorConfig!.formEventConfig!;
|
||||
lineHeight[0] = 135 + (column[0].length - 2) * 158;
|
||||
lineHeight[1] = 50 + (column[1].length - 1) * 158;
|
||||
lineHeight[2] = 50 + (column[2].length - 1) * 158;
|
||||
} else {
|
||||
generatorConfig!.formEventConfig = column;
|
||||
lineHeight[0] = 135;
|
||||
lineHeight[1] = 50;
|
||||
lineHeight[2] = 50;
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const selectedNode = ref<selectedNodeConfig>();
|
||||
const addEvent = (index) => {
|
||||
if (fromMobile) return;
|
||||
const columnInfo: FormEventStyleConfig = {
|
||||
color: '#2774FF',
|
||||
icon: '#icon-yonghu-xianxing',
|
||||
text: t('用户自定义节点'),
|
||||
bgcColor: '#F5F8FA',
|
||||
isUserDefined: true,
|
||||
nodeInfo: {
|
||||
processEvent: [],
|
||||
},
|
||||
};
|
||||
column[index].push(columnInfo);
|
||||
|
||||
if (index === 3) return;
|
||||
lineHeight[index] += 158;
|
||||
};
|
||||
const deleteEvent = ({ index, columnIndex }) => {
|
||||
if (fromMobile) return;
|
||||
column[columnIndex].splice(index, 1);
|
||||
lineHeight[columnIndex] -= 158;
|
||||
};
|
||||
const clickNode = (selected) => {
|
||||
for (let key in column) {
|
||||
column[key].map((item) => {
|
||||
item.isClick = false;
|
||||
});
|
||||
}
|
||||
column[selected.columnIndex][selected.index]['isClick'] = true;
|
||||
selectedNode.value = selected;
|
||||
};
|
||||
|
||||
//验证当前步骤的数据
|
||||
const validateStep = async (): Promise<boolean> => {
|
||||
const hasNodeName = Object.values(generatorConfig!.formEventConfig).every((config) => {
|
||||
return config.every((item) => {
|
||||
return !!item.text;
|
||||
});
|
||||
});
|
||||
if (!hasNodeName) {
|
||||
notification.error({
|
||||
message: t('提示'),
|
||||
description: t('节点名称不能为空!'),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
defineExpose({ validateStep });
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.event-box {
|
||||
position: relative;
|
||||
padding: 10px 50px;
|
||||
font-size: 13px;
|
||||
width: v-bind("!!selectedNode ? 'calc(100% - 410px)' : '100%'");
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
.box-second {
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
left: 310px;
|
||||
}
|
||||
|
||||
.box-third {
|
||||
position: absolute;
|
||||
top: 120px;
|
||||
left: 570px;
|
||||
}
|
||||
|
||||
.box-fourth {
|
||||
position: absolute;
|
||||
top: 185px;
|
||||
left: 830px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
114
src/components/CreateCodeStep/src/MenuConfigStep.vue
Normal file
114
src/components/CreateCodeStep/src/MenuConfigStep.vue
Normal file
@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div class="step1-form">
|
||||
<BasicForm @register="register" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { inject, onMounted } from 'vue';
|
||||
import { FormSchema, useForm, BasicForm } from '/@/components/Form';
|
||||
import { GeneratorConfig } from '/@/model/generator/generatorConfig';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const generatorConfig = inject<GeneratorConfig>('generatorConfig') as GeneratorConfig;
|
||||
|
||||
const formSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'code',
|
||||
label: t('菜单编号'),
|
||||
required: true,
|
||||
title: t('菜单信息'),
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: t('请输入菜单编号'),
|
||||
},
|
||||
colProps: { span: 12 },
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
label: t('菜单名称'),
|
||||
required: true,
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: t('请输入菜单名称'),
|
||||
},
|
||||
colProps: { span: 12 },
|
||||
},
|
||||
{
|
||||
field: 'parentId',
|
||||
label: t('上级菜单'),
|
||||
component: 'MenuSelect',
|
||||
required: process.env.NODE_ENV === 'production',
|
||||
componentProps: {
|
||||
placeholder: t('请选择上级菜单'),
|
||||
},
|
||||
colProps: { span: 12 },
|
||||
},
|
||||
{
|
||||
field: 'sortCode',
|
||||
label: t('排序'),
|
||||
required: true,
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
placeholder: t('请输入排序号'),
|
||||
min: 0,
|
||||
},
|
||||
colProps: { span: 12 },
|
||||
},
|
||||
{
|
||||
field: 'icon',
|
||||
label: t('图标'),
|
||||
required: true,
|
||||
component: 'IconPicker',
|
||||
componentProps: {
|
||||
placeholder: t('请选择图标'),
|
||||
},
|
||||
colProps: { span: 12 },
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
label: t('备注'),
|
||||
component: 'InputTextArea',
|
||||
componentProps: {
|
||||
placeholder: t('请填写备注'),
|
||||
},
|
||||
colProps: { span: 24 },
|
||||
},
|
||||
];
|
||||
|
||||
const [register, { validate, getFieldsValue, setFieldsValue }] = useForm({
|
||||
labelWidth: 100,
|
||||
schemas: formSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
onMounted(() => {
|
||||
setFieldsValue(generatorConfig.menuConfig);
|
||||
});
|
||||
//验证当前步骤的数据
|
||||
const validateStep = async (): Promise<boolean> => {
|
||||
try {
|
||||
const formData = await validate();
|
||||
setData(formData);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
function getFormData() {
|
||||
const formData = getFieldsValue();
|
||||
setData(formData);
|
||||
}
|
||||
function setData(formData) {
|
||||
generatorConfig.menuConfig!.code = formData.code;
|
||||
generatorConfig.menuConfig!.name = formData.name;
|
||||
generatorConfig.menuConfig!.parentId = formData.parentId;
|
||||
generatorConfig.menuConfig!.remark = formData.remark;
|
||||
generatorConfig.menuConfig!.sortCode = formData.sortCode;
|
||||
generatorConfig.menuConfig!.icon = formData.icon;
|
||||
}
|
||||
defineExpose({ validateStep, getFormData });
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
:deep(.ant-input-number) {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
794
src/components/CreateCodeStep/src/StructureConfigStep.vue
Normal file
794
src/components/CreateCodeStep/src/StructureConfigStep.vue
Normal file
@ -0,0 +1,794 @@
|
||||
<template>
|
||||
<div class="step1">
|
||||
<div class="step1-form" v-if="!isCustomForm">
|
||||
<BasicForm @register="register" />
|
||||
</div>
|
||||
<Divider v-if="designType === 'code' && !isFormGenerator && !isCustomForm" />
|
||||
<template v-if="designType === 'code' && !isFormGenerator">
|
||||
<div v-for="(tableConfig, index) in generatorConfig?.tableStructureConfigs" :key="index">
|
||||
<a-table
|
||||
:columns="tableColumns"
|
||||
:data-source="[tableConfig]"
|
||||
:pagination="false"
|
||||
:defaultExpandAllRows="true"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'tableName'">
|
||||
<a-input
|
||||
v-model:value="record.tableName"
|
||||
:placeholder="t('请填写数据表名称')"
|
||||
@change="changeComponentName(record, 'table')"
|
||||
@blur="handleBlur(record)"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'isMain'">
|
||||
<span>
|
||||
<a-tag :color="record.isMain ? 'blue' : 'orange'">
|
||||
{{ record.isMain ? t('主表') : t('附表') }}
|
||||
</a-tag>
|
||||
</span>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'tableComment'">
|
||||
<a-input v-model:value="record.tableComment" :placeholder="t('请填写备注')" />
|
||||
</template>
|
||||
</template>
|
||||
<template #expandedRowRender>
|
||||
<a-table
|
||||
:columns="fieldColumns"
|
||||
:data-source="tableConfig.tableFieldConfigs"
|
||||
:pagination="false"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'fieldName'">
|
||||
<a-input
|
||||
v-model:value="record.fieldName"
|
||||
:placeholder="t(`请填写{title}`, { title: column.title })"
|
||||
@change="changeComponentName(record, 'field')"
|
||||
:disabled="record.key === 'rule_user_id'"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'fieldLength'">
|
||||
<a-input
|
||||
v-model:value.number="record.fieldLength"
|
||||
:placeholder="t('请填写字段长度')"
|
||||
:disabled="record.fieldType !== 0"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'fieldComment'">
|
||||
<a-input
|
||||
v-model:value.number="record.fieldComment"
|
||||
:placeholder="t('请填写备注')"
|
||||
:disabled="record.key === 'rule_user_id'"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'fieldType'">
|
||||
<a-select
|
||||
v-model:value="record.fieldType"
|
||||
style="width: 100%"
|
||||
:placeholder="t('请选择数据格式')"
|
||||
:disabled="record.key === 'rule_user_id'"
|
||||
allowClear
|
||||
@change="(value) => handleTypeChange(value, record)"
|
||||
>
|
||||
<a-select-option
|
||||
:value="type.value"
|
||||
v-for="(type, idx) in fieldTypeList"
|
||||
:key="idx"
|
||||
>
|
||||
{{ type.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
<TableNameModal @register="registerTableName" @success="handleEditSuccess" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
|
||||
import { Divider } from 'ant-design-vue';
|
||||
import { TableNameModal } from '/@/components/CreateCodeStep';
|
||||
import { onMounted, inject, computed, ref, Ref } from 'vue';
|
||||
import { debounce, cloneDeep, random, snakeCase } from 'lodash-es';
|
||||
import { GeneratorConfig } from '/@/model/generator/generatorConfig';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { validateTableName } from '/@/api/system/generator';
|
||||
import { getAuthList } from '/@/api/system/authorize';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
const { t } = useI18n();
|
||||
const [registerTableName, { openModal, closeModal }] = useModal();
|
||||
const validateTable = ref(false);
|
||||
const emit = defineEmits(['validateTable']);
|
||||
const { notification } = useMessage();
|
||||
const dataAuthPlaceholder = computed(() => {
|
||||
return generatorConfig!.outputConfig!.isDataAuth
|
||||
? t('请选择已有通用数据权限')
|
||||
: t('请先启用数据权限');
|
||||
});
|
||||
const dataAuthHelpMessage = `
|
||||
1.启用数据权限会判断主表是否包含RuleUserlD字段如果存在,则不进行表结构修改,如果不存在,则会对主表进行字段添加。
|
||||
2.RuleUserlD主要用来控制每一条记录的权限所属人新增时,默认将当前登录认作为权限所属人。
|
||||
3.在表单设计中会添加“批量设置权限所属人”功能启用后,拥有该按钮权限的人员可以设置每一条记录的权限所属人。
|
||||
`;
|
||||
const formSchemaCode: FormSchema[] = [
|
||||
{
|
||||
field: 'className',
|
||||
label: t('功能名称'),
|
||||
required: true,
|
||||
component: 'Input',
|
||||
title: t('基本信息'),
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: t('请填写功能名称'),
|
||||
onChange: debounce((val: ChangeEvent) => {
|
||||
generatorConfig!.outputConfig!.className = val.target.value;
|
||||
const { outputConfig, tableStructureConfigs } = generatorConfig!;
|
||||
tableStructureConfigs?.map((item) => {
|
||||
const name = snakeCase(outputConfig!.className!);
|
||||
const tableName = isFieldUpper.value ? name.toUpperCase() : name;
|
||||
item.tableName = item.isMain
|
||||
? tableName
|
||||
: isFieldUpper.value
|
||||
? `${tableName}_CHILD_${random(1000, 9999)}`
|
||||
: `${tableName}_child_${random(1000, 9999)}`;
|
||||
changeComponentName(item, 'table');
|
||||
});
|
||||
}, 200),
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'comment',
|
||||
label: t('功能描述'),
|
||||
required: true,
|
||||
component: 'Input',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: t('请填写功能描述'),
|
||||
onChange: debounce((val: ChangeEvent) => {
|
||||
generatorConfig!.outputConfig!.comment = val.target.value;
|
||||
}, 200),
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'outputArea',
|
||||
label: t('功能模块'),
|
||||
component: 'DicSelect',
|
||||
required: true,
|
||||
componentProps: {
|
||||
placeholder: t('请选择功能模块'),
|
||||
itemId: '1419276800524423333',
|
||||
onChange: debounce((_, obj) => {
|
||||
if (obj) {
|
||||
generatorConfig!.outputConfig!.outputArea = obj.id;
|
||||
generatorConfig!.outputConfig!.outputValue = obj.value;
|
||||
}
|
||||
}, 200),
|
||||
},
|
||||
colProps: { span: 12 },
|
||||
},
|
||||
{
|
||||
field: 'remarks',
|
||||
label: t('备注'),
|
||||
component: 'Input',
|
||||
required: false,
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: t('请填写备注'),
|
||||
onChange: debounce((val) => {
|
||||
generatorConfig!.outputConfig!.remarks = val.target.value;
|
||||
generatorConfig!.tableStructureConfigs?.map(
|
||||
(item) => (item.tableComment = item.isMain ? val.target.value : item.tableComment),
|
||||
);
|
||||
}, 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!.outputConfig!.dataAuthList = [];
|
||||
if (generatorConfig?.tableStructureConfigs?.length) {
|
||||
generatorConfig.tableStructureConfigs[0].tableFieldConfigs =
|
||||
generatorConfig?.tableStructureConfigs[0].tableFieldConfigs.filter(
|
||||
(x) => x.key !== 'rule_user_id',
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const hasRuleUserField =
|
||||
generatorConfig?.tableStructureConfigs?.length &&
|
||||
generatorConfig?.tableStructureConfigs[0].tableFieldConfigs.find(
|
||||
(x) => x.key === 'rule_user_id',
|
||||
);
|
||||
if (hasRuleUserField) return;
|
||||
generatorConfig?.tableStructureConfigs?.length &&
|
||||
generatorConfig?.tableStructureConfigs[0].tableFieldConfigs.push({
|
||||
key: 'rule_user_id',
|
||||
fieldName: 'rule_user_id',
|
||||
fieldLength: 500,
|
||||
fieldType: 7,
|
||||
fieldComment: t('数据权限所属人ID'),
|
||||
});
|
||||
}
|
||||
generatorConfig!.outputConfig!.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!.outputConfig!.dataAuthList = val;
|
||||
},
|
||||
},
|
||||
dynamicDisabled: ({ values }) => {
|
||||
return !values.isDataAuth;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const formSchemaTemplate: FormSchema[] = [
|
||||
{
|
||||
field: 'className',
|
||||
label: t('功能名称'),
|
||||
required: true,
|
||||
component: 'Input',
|
||||
title: t('基本信息'),
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: t('请填写功能名称'),
|
||||
onChange: (val: ChangeEvent) => {
|
||||
generatorConfig!.outputConfig!.className = val.target.value;
|
||||
const { outputConfig, tableStructureConfigs } = generatorConfig!;
|
||||
tableStructureConfigs?.map((item) => {
|
||||
const name = snakeCase(outputConfig!.className!);
|
||||
const tableName = isFieldUpper.value ? name.toUpperCase() : name;
|
||||
item.tableName = item.isMain
|
||||
? tableName
|
||||
: isFieldUpper.value
|
||||
? `${tableName}_CHILD_${random(1000, 9999)}`
|
||||
: `${tableName}_child_${random(1000, 9999)}`;
|
||||
changeComponentName(item, 'table');
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'comment',
|
||||
label: t('功能描述'),
|
||||
required: true,
|
||||
component: 'Input',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: t('请填写功能描述'),
|
||||
onChange: debounce((val: ChangeEvent) => {
|
||||
generatorConfig!.outputConfig!.comment = val.target.value;
|
||||
}, 200),
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'outputArea',
|
||||
label: t('功能模块'),
|
||||
component: 'DicSelect',
|
||||
required: true,
|
||||
componentProps: {
|
||||
placeholder: t('请选择功能模块'),
|
||||
itemId: '1419276800524423333',
|
||||
onChange: debounce((_, obj) => {
|
||||
if (obj) {
|
||||
generatorConfig!.outputConfig!.outputArea = obj.id;
|
||||
generatorConfig!.outputConfig!.outputValue = obj.value;
|
||||
}
|
||||
}, 200),
|
||||
},
|
||||
colProps: { span: 12 },
|
||||
},
|
||||
{
|
||||
field: 'remarks',
|
||||
label: t('备注'),
|
||||
component: 'Input',
|
||||
required: false,
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: t('请填写备注'),
|
||||
onChange: debounce((val) => {
|
||||
generatorConfig!.outputConfig!.remarks = val.target.value;
|
||||
generatorConfig!.tableStructureConfigs?.map(
|
||||
(item) => (item.tableComment = item.isMain ? val.target.value : item.tableComment),
|
||||
);
|
||||
}, 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!.outputConfig!.dataAuthList = [];
|
||||
generatorConfig?.tableStructureConfigs?.length &&
|
||||
generatorConfig?.tableStructureConfigs[0].tableFieldConfigs.map(
|
||||
(x) => x.key !== 'rule_user_id',
|
||||
);
|
||||
} else {
|
||||
const hasRuleUserField =
|
||||
generatorConfig?.tableStructureConfigs?.length &&
|
||||
generatorConfig?.tableStructureConfigs[0].tableFieldConfigs.find(
|
||||
(x) => x.key === 'rule_user_id',
|
||||
);
|
||||
if (hasRuleUserField) return;
|
||||
generatorConfig?.tableStructureConfigs?.length &&
|
||||
generatorConfig?.tableStructureConfigs[0].tableFieldConfigs.push({
|
||||
key: 'rule_user_id',
|
||||
fieldName: 'rule_user_id',
|
||||
fieldLength: 500,
|
||||
fieldType: 7,
|
||||
fieldComment: t('数据权限所属人ID'),
|
||||
});
|
||||
}
|
||||
generatorConfig!.outputConfig!.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!.outputConfig!.dataAuthList = val;
|
||||
},
|
||||
},
|
||||
dynamicDisabled: ({ values }) => {
|
||||
return !values.isDataAuth;
|
||||
},
|
||||
},
|
||||
];
|
||||
const generatorSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'className',
|
||||
label: t('功能名称'),
|
||||
required: true,
|
||||
component: 'Input',
|
||||
title: t('基本信息'),
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: t('请填写功能名称'),
|
||||
onChange: debounce((val: ChangeEvent) => {
|
||||
generatorConfig!.outputConfig!.className = val.target.value;
|
||||
}, 200),
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'comment',
|
||||
label: t('功能描述'),
|
||||
required: true,
|
||||
component: 'Input',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: t('请填写功能描述'),
|
||||
onChange: debounce((val: ChangeEvent) => {
|
||||
generatorConfig!.outputConfig!.comment = val.target.value;
|
||||
}, 200),
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'outputArea',
|
||||
label: t('功能模块'),
|
||||
component: 'DicSelect',
|
||||
required: true,
|
||||
componentProps: {
|
||||
placeholder: t('请选择功能模块'),
|
||||
itemId: '1419276800524423333',
|
||||
onChange: debounce((_, obj) => {
|
||||
if (obj) {
|
||||
generatorConfig!.outputConfig!.outputArea = obj.id;
|
||||
generatorConfig!.outputConfig!.outputValue = obj.value;
|
||||
}
|
||||
}, 200),
|
||||
},
|
||||
colProps: { span: 12 },
|
||||
},
|
||||
{
|
||||
field: 'remarks',
|
||||
label: t('备注'),
|
||||
component: 'Input',
|
||||
required: false,
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: t('请填写备注'),
|
||||
onChange: debounce((val) => {
|
||||
generatorConfig!.outputConfig!.remarks = val.target.value;
|
||||
}, 200),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const tableColumns = [
|
||||
{
|
||||
title: t('数据表名称'),
|
||||
dataIndex: 'tableName',
|
||||
},
|
||||
{
|
||||
title: t('数据表类别'),
|
||||
dataIndex: 'isMain',
|
||||
align: 'center',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: t('备注'),
|
||||
dataIndex: 'tableComment',
|
||||
align: 'center',
|
||||
},
|
||||
];
|
||||
|
||||
const fieldColumns = [
|
||||
{
|
||||
title: t('字段名'),
|
||||
dataIndex: 'fieldName',
|
||||
width: 500,
|
||||
},
|
||||
{
|
||||
title: t('字段长度'),
|
||||
dataIndex: 'fieldLength',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('数据格式'),
|
||||
dataIndex: 'fieldType',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('备注'),
|
||||
dataIndex: 'fieldComment',
|
||||
},
|
||||
];
|
||||
|
||||
const fieldTypeList = [
|
||||
{
|
||||
label: t('短文本'),
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: t('长文本(适用于多行文本等组件)'),
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: t('整数'),
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
label: t('小数'),
|
||||
value: 3,
|
||||
},
|
||||
{
|
||||
label: t('日期'),
|
||||
value: 4,
|
||||
},
|
||||
{
|
||||
label: t('日期时间'),
|
||||
value: 5,
|
||||
},
|
||||
{
|
||||
label: t('外键'),
|
||||
value: 6,
|
||||
},
|
||||
{
|
||||
label: t('长整数'),
|
||||
value: 7,
|
||||
},
|
||||
{
|
||||
label: t('时间'),
|
||||
value: 8,
|
||||
},
|
||||
];
|
||||
|
||||
const generatorConfig = inject<GeneratorConfig>('generatorConfig');
|
||||
const widgetForm = inject<any>('widgetForm');
|
||||
let mainTableName = inject<any>('mainTableName', '');
|
||||
const designType = inject<string>('designType', '');
|
||||
const isCustomForm = inject<boolean>('isCustomForm', false);
|
||||
const isFieldUpper = inject<Ref<boolean>>('isFieldUpper', ref(false));
|
||||
|
||||
const props = defineProps({
|
||||
//是否是自定义表单生成代码
|
||||
isFormGenerator: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
//是否是编辑状态
|
||||
isUpdate: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
//新增时的表名集合
|
||||
beforeTableNames: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
|
||||
const [register, { validate, setFieldsValue, clearValidate }] = useForm({
|
||||
labelWidth: 100,
|
||||
schemas: props.isFormGenerator
|
||||
? generatorSchema
|
||||
: designType === 'code'
|
||||
? formSchemaCode
|
||||
: formSchemaTemplate,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
onMounted(() => {
|
||||
const { outputConfig } = generatorConfig!;
|
||||
if (
|
||||
!isCustomForm &&
|
||||
(outputConfig?.className || outputConfig?.comment || outputConfig?.outputArea)
|
||||
) {
|
||||
setFieldsValue({
|
||||
className: outputConfig?.className,
|
||||
comment: outputConfig?.comment,
|
||||
outputArea: outputConfig?.outputArea,
|
||||
isDataAuth: outputConfig?.isDataAuth,
|
||||
dataAuthList: outputConfig?.dataAuthList,
|
||||
});
|
||||
clearValidate();
|
||||
}
|
||||
});
|
||||
const changeComponentInfo = (list, type, fieldKey, name, tableKey, rangField?) => {
|
||||
const isTable = type === 'table';
|
||||
if (generatorConfig?.formJson.hiddenComponent?.length) {
|
||||
generatorConfig?.formJson.hiddenComponent?.map((component: any) => {
|
||||
if (component.key === fieldKey) {
|
||||
isTable ? (component.bindTable = name) : (component.bindField = name);
|
||||
}
|
||||
});
|
||||
widgetForm!.value.hiddenComponent = cloneDeep(generatorConfig?.formJson.hiddenComponent);
|
||||
}
|
||||
list?.map((component: any) => {
|
||||
if (component.type === 'form' || component.type === 'one-for-one') {
|
||||
if (isTable && component.key === tableKey) {
|
||||
component.bindTable = name;
|
||||
}
|
||||
if (component.children.length) {
|
||||
component.children.map((subComponent: any) => {
|
||||
if (['tab', 'grid', 'card'].includes(subComponent.type)) {
|
||||
subComponent.bindTable = name;
|
||||
for (const subChild of subComponent.layout!) {
|
||||
changeComponentInfo(subChild.list, type, fieldKey, name, tableKey, rangField);
|
||||
}
|
||||
} else if (subComponent.key === fieldKey) {
|
||||
isTable ? (subComponent.bindTable = name) : (subComponent.bindField = name);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (component.key === fieldKey) {
|
||||
//修改主表名
|
||||
mainTableName!.value = name;
|
||||
if (['time-range', 'date-range'].includes(component.type)) {
|
||||
isTable ? (component.bindTable = name) : (component[rangField] = name);
|
||||
} else {
|
||||
isTable ? (component.bindTable = name) : (component.bindField = name);
|
||||
}
|
||||
} else if (['tab', 'grid', 'card'].includes(component.type)) {
|
||||
for (const child of component.layout!) {
|
||||
changeComponentInfo(child.list, type, fieldKey, name, tableKey, rangField);
|
||||
}
|
||||
} else if (component.type == 'table-layout') {
|
||||
for (const child of component.layout!) {
|
||||
for (const el of child.list!) {
|
||||
changeComponentInfo(el.children, type, fieldKey, name, tableKey, rangField);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
widgetForm!.value.list = cloneDeep(generatorConfig!.formJson.list); //表单设计页面绑定表和字段需要同步修改
|
||||
};
|
||||
|
||||
const changeComponentName = (record, type) => {
|
||||
const rangField = record.fieldStartName
|
||||
? 'bindStartTime'
|
||||
: record.fieldEndName
|
||||
? 'bindEndTime'
|
||||
: '';
|
||||
if (type === 'table') {
|
||||
const name = snakeCase(record.tableName);
|
||||
record.tableFieldConfigs.forEach((item) => {
|
||||
changeComponentInfo(
|
||||
generatorConfig?.formJson.list,
|
||||
'table',
|
||||
item.key,
|
||||
isFieldUpper.value ? name.toUpperCase() : name,
|
||||
record.key,
|
||||
);
|
||||
});
|
||||
} else {
|
||||
changeComponentInfo(
|
||||
generatorConfig?.formJson.list,
|
||||
'field',
|
||||
record.key,
|
||||
record.fieldName,
|
||||
'',
|
||||
rangField,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBlur = (record) => {
|
||||
const name = snakeCase(record.tableName);
|
||||
record.tableName = isFieldUpper.value ? name.toUpperCase() : name;
|
||||
};
|
||||
|
||||
const handleTypeChange = (value, record) => {
|
||||
if (value !== 0) {
|
||||
record.fieldLength = null;
|
||||
} else {
|
||||
record.fieldLength = 500;
|
||||
}
|
||||
};
|
||||
|
||||
//验证当前步骤的数据
|
||||
const validateStep = async (): Promise<boolean> => {
|
||||
try {
|
||||
const { tableStructureConfigs, outputConfig } = generatorConfig!;
|
||||
|
||||
if (!isCustomForm) {
|
||||
await validate();
|
||||
|
||||
if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(outputConfig!.className!)) {
|
||||
notification.error({
|
||||
message: t('提示'),
|
||||
description: t('功能名称只能是数字和字母组成,必须以英文字母开头'),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (props.isFormGenerator) return true;
|
||||
const tableNames: string[] = tableStructureConfigs!.map((x) => x.tableName);
|
||||
|
||||
const reg = /^[a-zA-Z0-9_]*$/;
|
||||
//判断表名是否符合要求
|
||||
const isTableNotSuccess = tableStructureConfigs?.some((config: any) => {
|
||||
return !reg.test(config.tableName);
|
||||
});
|
||||
if (isTableNotSuccess) {
|
||||
notification.error({
|
||||
message: t('提示'),
|
||||
description: '表名只能包括字母、数字、下划线',
|
||||
}); //提示消息
|
||||
return false;
|
||||
}
|
||||
//判断字段名是否符合要求
|
||||
const isFieldNotSuccess = tableStructureConfigs?.some((config: any) => {
|
||||
const isNotPassValidation = config.tableFieldConfigs.some((fieldConfig: any) => {
|
||||
return !reg.test(fieldConfig.fieldName) || fieldConfig.fieldName.length > 30;
|
||||
});
|
||||
return isNotPassValidation;
|
||||
});
|
||||
if (isFieldNotSuccess) {
|
||||
notification.error({
|
||||
message: t('提示'),
|
||||
description:
|
||||
'字段名必须以小写字母开头,只能包括小写字母、数字、下划线,并且不能超过30个字符',
|
||||
}); //提示消息
|
||||
return false;
|
||||
}
|
||||
const notHasRepeatName = tableStructureConfigs?.every((config) => {
|
||||
const nameArr = config.tableFieldConfigs.map((x) => x.fieldName);
|
||||
return [...new Set(nameArr)].length === nameArr.length;
|
||||
});
|
||||
if (!notHasRepeatName) {
|
||||
notification.error({
|
||||
message: t('提示'),
|
||||
description: t('同一个表内,不能有相同字段名'),
|
||||
}); //提示消息
|
||||
return false;
|
||||
}
|
||||
const testTableNames = tableNames.filter((x) => {
|
||||
return !props.beforeTableNames.includes(x);
|
||||
});
|
||||
const params = {
|
||||
id: designType === 'code' ? generatorConfig!.databaseId! : 'master',
|
||||
tableNames: props.isUpdate ? testTableNames.toString() : tableNames.toString(),
|
||||
};
|
||||
if (!props.isUpdate || (!props.isUpdate && testTableNames.length)) {
|
||||
await validateTableName(params);
|
||||
}
|
||||
|
||||
if (props.isUpdate) {
|
||||
if (!validateTable.value) {
|
||||
openModal();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
validateTable.value = false;
|
||||
return true;
|
||||
};
|
||||
async function handleEditSuccess() {
|
||||
validateTable.value = true;
|
||||
closeModal();
|
||||
emit('validateTable');
|
||||
}
|
||||
defineExpose({ validateStep });
|
||||
</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-tag-orange),
|
||||
:deep(.ant-tag-blue) {
|
||||
background: #fff;
|
||||
padding: 3px 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
:deep(.ant-spin-nested-loading) {
|
||||
overflow: hidden; //去掉表格嵌套子表滚动条
|
||||
}
|
||||
</style>
|
||||
542
src/components/CreateCodeStep/src/TableConfigStep.vue
Normal file
542
src/components/CreateCodeStep/src/TableConfigStep.vue
Normal file
@ -0,0 +1,542 @@
|
||||
<template>
|
||||
<div class="step1">
|
||||
<div class="step1-form">
|
||||
<BasicForm @register="register" />
|
||||
</div>
|
||||
<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('请选择附表关联主表字段')"
|
||||
show-search
|
||||
>
|
||||
<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('请选择主表字段')"
|
||||
show-search
|
||||
>
|
||||
<SelectOption
|
||||
v-for="(name, idx) in selectOptions[mainTableName]"
|
||||
: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" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
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/SelectDatabase.vue';
|
||||
import { computed, inject, nextTick, onMounted, Ref, ref, toRaw } from 'vue';
|
||||
import { Select } from 'ant-design-vue';
|
||||
import { debounce, uniqBy } from 'lodash-es';
|
||||
import { TableConfig } from '/@/model/generator/tableConfig';
|
||||
import { GeneratorConfig } from '/@/model/generator/generatorConfig';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { TableInfo, FieldInfo } 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 generatorConfig = inject<GeneratorConfig>('generatorConfig');
|
||||
|
||||
const curDataBase = ref();
|
||||
const dataAuthPlaceholder = computed(() => {
|
||||
return generatorConfig!.outputConfig!.isDataAuth
|
||||
? t('请选择已有通用数据权限')
|
||||
: t('请先启用数据权限');
|
||||
});
|
||||
|
||||
const dataAuthHelpMessage = `
|
||||
1.启用数据权限会判断主表是否包含RuleUserlD字段如果存在,则不进行表结构修改,如果不存在,则会对主表进行字段添加。
|
||||
2.RuleUserlD主要用来控制每一条记录的权限所属人新增时,默认将当前登录认作为权限所属人。
|
||||
3.在表单设计中会添加“批量设置权限所属人”功能启用后,拥有该按钮权限的人员可以设置每一条记录的权限所属人。
|
||||
`;
|
||||
|
||||
const formSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'className',
|
||||
label: t('功能名称'),
|
||||
required: true,
|
||||
component: 'Input',
|
||||
title: t('基本信息'),
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: t('请填写功能名称'),
|
||||
onChange: debounce((val: ChangeEvent) => {
|
||||
generatorConfig!.outputConfig!.className = val.target.value;
|
||||
}, 200),
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'comment',
|
||||
label: t('功能描述'),
|
||||
required: true,
|
||||
component: 'Input',
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: t('请填写功能描述'),
|
||||
onChange: debounce((val: ChangeEvent) => {
|
||||
generatorConfig!.outputConfig!.comment = val.target.value;
|
||||
}, 200),
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'outputArea',
|
||||
label: t('功能模块'),
|
||||
component: 'DicSelect',
|
||||
required: true,
|
||||
componentProps: {
|
||||
placeholder: t('请选择功能模块'),
|
||||
itemId: '1419276800524423333',
|
||||
onChange: debounce((_, obj) => {
|
||||
if (obj) {
|
||||
generatorConfig!.outputConfig!.outputArea = obj.id;
|
||||
generatorConfig!.outputConfig!.outputValue = obj.value;
|
||||
}
|
||||
}, 200),
|
||||
},
|
||||
colProps: { span: 12 },
|
||||
},
|
||||
{
|
||||
field: 'remarks',
|
||||
label: t('备注'),
|
||||
component: 'Input',
|
||||
required: false,
|
||||
colProps: { span: 12 },
|
||||
componentProps: {
|
||||
placeholder: t('请填写备注'),
|
||||
onChange: debounce((val) => {
|
||||
generatorConfig!.outputConfig!.remarks = 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!.outputConfig!.dataAuthList = [];
|
||||
}
|
||||
generatorConfig!.outputConfig!.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!.outputConfig!.dataAuthList = val;
|
||||
},
|
||||
},
|
||||
dynamicDisabled: ({ values }) => {
|
||||
return !values.isDataAuth;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'databaseId',
|
||||
label: t('数据库'),
|
||||
component: 'DbSelect',
|
||||
required: true,
|
||||
title: t('数据库信息'),
|
||||
colProps: { span: 24 },
|
||||
componentProps: {
|
||||
placeholder: t('请选择数据库'),
|
||||
onChange: debounce((val) => {
|
||||
generatorConfig!.databaseId = val;
|
||||
if (curDataBase.value && curDataBase.value !== val) {
|
||||
generatorConfig!.tableConfigs = [];
|
||||
}
|
||||
|
||||
curDataBase.value = val;
|
||||
}, 200),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t('序号'),
|
||||
dataIndex: 'order',
|
||||
key: 'order',
|
||||
width: 80,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('数据表类别'),
|
||||
dataIndex: 'isMain',
|
||||
key: 'isMain',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
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 [registerModal, { openModal }] = useModal();
|
||||
|
||||
const [register, { validate, getFieldsValue, setFieldsValue, clearValidate }] = useForm({
|
||||
labelWidth: 100,
|
||||
schemas: formSchema,
|
||||
showActionButtonGroup: false,
|
||||
});
|
||||
const selectOptions = ref({});
|
||||
const selectTableName = computed(() =>
|
||||
generatorConfig!.tableConfigs!.map((item) => item.tableName),
|
||||
);
|
||||
|
||||
const mainTableName = computed(
|
||||
() => generatorConfig!.tableConfigs!.find((item) => item.isMain)!.tableName,
|
||||
);
|
||||
|
||||
defineEmits(['register']);
|
||||
|
||||
const tableInfo = inject<Ref<TableInfo[]>>('tableInfo', ref([]));
|
||||
|
||||
onMounted(() => {
|
||||
const { tableConfigs, databaseId, outputConfig } = generatorConfig!;
|
||||
|
||||
setFieldsValue({
|
||||
className: outputConfig?.className,
|
||||
comment: outputConfig?.comment,
|
||||
outputArea: outputConfig?.outputArea,
|
||||
databaseId: databaseId,
|
||||
isDataAuth: outputConfig?.isDataAuth || false,
|
||||
dataAuthList: outputConfig?.dataAuthList || [],
|
||||
});
|
||||
|
||||
if (tableConfigs && tableConfigs.length > 0) {
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
selectOptions.value[key] = columnInfo.map((x) => x.column);
|
||||
}
|
||||
});
|
||||
}
|
||||
nextTick(() => {
|
||||
clearValidate();
|
||||
});
|
||||
});
|
||||
|
||||
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) {
|
||||
tableInfo.value!.length = 0;
|
||||
generatorConfig!.tableConfigs = [...toRaw(selectRows)];
|
||||
} else {
|
||||
generatorConfig!.tableConfigs = uniqBy(
|
||||
generatorConfig!.tableConfigs!.concat([...selectRows]),
|
||||
'tableName',
|
||||
);
|
||||
}
|
||||
const formData = getFieldsValue();
|
||||
|
||||
getDatabaselinkMultiTableColumns({
|
||||
id: formData.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 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 validateStep = async (): Promise<boolean> => {
|
||||
try {
|
||||
await validate();
|
||||
const { tableConfigs, outputConfig } = generatorConfig as GeneratorConfig;
|
||||
|
||||
//判断tableconfig 是否为空 或者 一条数据都没有
|
||||
if (!tableConfigs || tableConfigs!.length === 0) {
|
||||
notification.error({
|
||||
message: t('提示'),
|
||||
description: t('数据表配置不能为空!'),
|
||||
}); //提示消息
|
||||
return false;
|
||||
}
|
||||
if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(outputConfig.className!)) {
|
||||
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;
|
||||
};
|
||||
|
||||
function editFieldsValue() {
|
||||
const { databaseId, outputConfig } = generatorConfig!;
|
||||
|
||||
setFieldsValue({
|
||||
className: outputConfig?.className,
|
||||
comment: outputConfig?.comment,
|
||||
outputArea: outputConfig?.outputArea,
|
||||
databaseId: databaseId,
|
||||
isDataAuth: outputConfig?.isDataAuth || false,
|
||||
dataAuthList: outputConfig?.dataAuthList || [],
|
||||
});
|
||||
}
|
||||
defineExpose({ validateStep, setFieldsValue, editFieldsValue });
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.step1 {
|
||||
// &-form {
|
||||
// width: 450px;
|
||||
// margin: 0 auto;
|
||||
// }
|
||||
|
||||
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-tag-orange),
|
||||
:deep(.ant-tag-blue) {
|
||||
background: #fff;
|
||||
padding: 3px 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
:deep(.ant-tooltip-inner) {
|
||||
white-space: pre-wrap !important;
|
||||
}
|
||||
|
||||
:deep(.ant-table-tbody > tr > td) {
|
||||
padding: 16px 8px;
|
||||
}
|
||||
</style>
|
||||
1570
src/components/CreateCodeStep/src/ViewDesignStep.vue
Normal file
1570
src/components/CreateCodeStep/src/ViewDesignStep.vue
Normal file
File diff suppressed because it is too large
Load Diff
121
src/components/CreateCodeStep/src/components/AddEvent.vue
Normal file
121
src/components/CreateCodeStep/src/components/AddEvent.vue
Normal file
@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<div class="add-box">
|
||||
<svg class="icon add-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-add" />
|
||||
</svg>
|
||||
<div class="dashed-arrow add-arrow" v-if="isLast"></div>
|
||||
<div class="bottom-arrow" v-else>
|
||||
<div class="top-arrow" :style="{ left: `-${lineHeight}`, width: lineHeight }"> </div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
const props = defineProps({
|
||||
lineHeight: String,
|
||||
isLast: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.add-box {
|
||||
position: relative;
|
||||
left: 40px;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin-top: 65px;
|
||||
background: #e7faf3;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transform: rotate(45deg);
|
||||
cursor: pointer;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
background: #fff;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
position: absolute;
|
||||
top: v-bind("props.isLast ? 'calc(100% - 10px)' : '0'");
|
||||
left: calc(100% - 10px);
|
||||
border: 2px solid #00d37e;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.dashed-arrow {
|
||||
height: 40px;
|
||||
position: absolute;
|
||||
bottom: -40px;
|
||||
left: calc(50% - 1px);
|
||||
border-left: 2px dashed #d9d9d9;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -7px;
|
||||
bottom: -14px;
|
||||
height: 0;
|
||||
width: 0;
|
||||
border: 6px solid #000;
|
||||
border-color: #d9d9d9 transparent transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.add-icon {
|
||||
font-size: 30px;
|
||||
background-color: #fff;
|
||||
padding: 5px;
|
||||
border-radius: 50%;
|
||||
transform: rotate(45deg);
|
||||
fill: #00d37e;
|
||||
}
|
||||
|
||||
.bottom-arrow {
|
||||
height: 58px;
|
||||
width: 45px;
|
||||
position: absolute;
|
||||
left: 64px;
|
||||
top: -64px;
|
||||
transform: rotate(45deg);
|
||||
border-top: 2px dashed #d9d9d9;
|
||||
border-right: 2px dashed #d9d9d9;
|
||||
border-radius: 0 10%;
|
||||
|
||||
.top-arrow {
|
||||
height: 48px;
|
||||
position: absolute;
|
||||
top: -48px;
|
||||
border-bottom: 2px dashed #d9d9d9;
|
||||
border-left: 2px dashed #d9d9d9;
|
||||
border-radius: 0 10%;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -7px;
|
||||
top: -14px;
|
||||
height: 0;
|
||||
width: 0;
|
||||
border: 6px solid #000;
|
||||
border-color: transparent transparent #d9d9d9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.add-arrow {
|
||||
left: 116%;
|
||||
transform: rotate(-45deg);
|
||||
top: 92%;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentcolor;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
183
src/components/CreateCodeStep/src/components/EventArea.vue
Normal file
183
src/components/CreateCodeStep/src/components/EventArea.vue
Normal file
@ -0,0 +1,183 @@
|
||||
<template>
|
||||
<div v-for="(item, idx) in columnList" :key="idx" @click="eventClick(idx)">
|
||||
<div
|
||||
:style="{
|
||||
'--bgcColor': item.bgcColor,
|
||||
'--color': item.color,
|
||||
marginTop: item.isLast ? '68px' : 0,
|
||||
}"
|
||||
:class="['circle-box', { dot: !item.isLast }]"
|
||||
v-if="item.type === 'circle'"
|
||||
>
|
||||
<svg class="icon circle-icon" aria-hidden="true">
|
||||
<use :xlink:href="item.icon" />
|
||||
</svg>
|
||||
<div class="dashed-arrow" v-if="!item.isLast"> </div>
|
||||
</div>
|
||||
<div
|
||||
:style="{ background: item.bgcColor, '--color': item.color }"
|
||||
:class="[{ 'area-outline': item.isClick }, 'area-box']"
|
||||
v-else
|
||||
>
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use :xlink:href="item.icon" />
|
||||
</svg>
|
||||
<p>{{ item.text }}</p>
|
||||
<p v-if="item.detail" style="font-size: 12px">{{ item.detail }}</p>
|
||||
<svg
|
||||
class="icon del-icon"
|
||||
aria-hidden="true"
|
||||
v-if="item.isUserDefined"
|
||||
@click.stop="delEvent(idx)"
|
||||
>
|
||||
<use xlink:href="#icon-shanchu" />
|
||||
</svg>
|
||||
<div class="dashed-arrow"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { FormEventStyleConfig } from '../../../../model/generator/formEventConfig';
|
||||
|
||||
const props = defineProps({
|
||||
index: Number,
|
||||
isLast: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
columnList: {
|
||||
type: Array as PropType<FormEventStyleConfig[]>,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['deleteEvent', 'clickNode']);
|
||||
const eventClick = (index) => {
|
||||
const clickNode = {
|
||||
index,
|
||||
columnIndex: props.index,
|
||||
};
|
||||
emits('clickNode', clickNode);
|
||||
};
|
||||
const delEvent = (index) => {
|
||||
const clickNode = {
|
||||
index,
|
||||
columnIndex: props.index,
|
||||
};
|
||||
emits('deleteEvent', clickNode);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.circle-box {
|
||||
position: relative;
|
||||
left: 50px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: var(--bgcColor);
|
||||
margin-bottom: 54px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
|
||||
&.dot::after {
|
||||
content: '';
|
||||
background: #fff;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
position: absolute;
|
||||
bottom: -5px;
|
||||
left: calc(50% - 5px);
|
||||
border: 2px solid var(--color);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.circle-icon {
|
||||
font-size: 32px;
|
||||
fill: var(--color);
|
||||
}
|
||||
}
|
||||
|
||||
.area-box {
|
||||
position: relative;
|
||||
width: 135px;
|
||||
height: 90px;
|
||||
padding: 10px 0 0 15px;
|
||||
// margin-bottom: 65px;
|
||||
margin-top: 48px;
|
||||
border-radius: 6px 6px 6px 6px;
|
||||
border: 5px solid #fff;
|
||||
box-sizing: content-box;
|
||||
cursor: pointer;
|
||||
// background: v-bind(bgcColor);
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
background: #fff;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
position: absolute;
|
||||
bottom: -5px;
|
||||
left: calc(50% - 5px);
|
||||
border: 2px solid var(--color);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
font-size: 22px;
|
||||
background: #fff;
|
||||
padding: 8px;
|
||||
margin-bottom: 8px;
|
||||
box-sizing: content-box;
|
||||
border-radius: 50%;
|
||||
fill: var(--color);
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.del-icon {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
font-size: 14px;
|
||||
fill: #2774ff;
|
||||
}
|
||||
}
|
||||
|
||||
.area-outline {
|
||||
outline: 2px dashed #2774ff !important;
|
||||
}
|
||||
|
||||
.dashed-arrow {
|
||||
height: 40px;
|
||||
position: absolute;
|
||||
bottom: -40px;
|
||||
left: calc(50% - 1px);
|
||||
border-left: 2px dashed #d9d9d9;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -7px;
|
||||
bottom: -14px;
|
||||
height: 0px;
|
||||
width: 0px;
|
||||
border: 6px solid #000;
|
||||
border-top-color: #d9d9d9;
|
||||
border-bottom-color: transparent;
|
||||
border-left-color: transparent;
|
||||
border-right-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<div>
|
||||
<BasicDrawer
|
||||
v-bind="$attrs"
|
||||
@register="registerDrawer"
|
||||
:isDetail="true"
|
||||
@close="handleCloser"
|
||||
:title="t('表单预览1')"
|
||||
>
|
||||
<div>
|
||||
<BasicForm
|
||||
@register="registerForm"
|
||||
autoFocusFirstItem
|
||||
:labelWidth="100"
|
||||
:schemas="schemas"
|
||||
:actionColOptions="{ span: 24 }"
|
||||
:submitButtonOptions="{ text: t('提交') }"
|
||||
@submit="handleSubmit"
|
||||
@reset="handleReset"
|
||||
/>
|
||||
</div>
|
||||
<template #titleToolbar> {{ t('重置') }} </template>
|
||||
</BasicDrawer>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
|
||||
import { BasicForm, FormProps, FormSchema, useForm } from '/@/components/Form/index';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { buildOption } from '/@/utils/helper/designHelper';
|
||||
// import { buildCode } from '/@/utils/helper/generatorHelper';
|
||||
|
||||
const formProps = ref<FormProps>();
|
||||
|
||||
const schemas: FormSchema[] = [];
|
||||
// [
|
||||
// {
|
||||
// field: 'radio_1527c74b77dd43ceb8daceb35e22d657',
|
||||
// label: '单选框组',
|
||||
// component: 'ApiRadioGroup',
|
||||
// colProps: {
|
||||
// span: 24,
|
||||
// },
|
||||
// rules: [
|
||||
// {
|
||||
// trigger: 'blur',
|
||||
// enum: '',
|
||||
// message: '',
|
||||
// pattern: '',
|
||||
// required: false,
|
||||
// type: 'any',
|
||||
// },
|
||||
// ],
|
||||
// required: false,
|
||||
// defaultValue: '',
|
||||
// componentProps: {
|
||||
// api: getMenuList,
|
||||
// params: {
|
||||
// count: 2,
|
||||
// },
|
||||
// resultField: 'data',
|
||||
// // use name as label
|
||||
// labelField: 'name',
|
||||
// // use id as value
|
||||
// valueField: 'id',
|
||||
// isBtn: false,
|
||||
// },
|
||||
// },
|
||||
// ];
|
||||
|
||||
//使用表单钩子 注册表单 获取到 操作表单的方法
|
||||
const [registerForm, { setProps, resetFields, validate }] = useForm();
|
||||
|
||||
//使用抽屉内部钩子 获取到 操作抽屉的方法
|
||||
const [registerDrawer] = useDrawerInner((option) => {
|
||||
formProps.value = buildOption(option);
|
||||
// const codes = buildCode(generatorConfig, tableInfo, formProps.value.schemas as FormSchema[]);
|
||||
// console.log(JSON.stringify(formProps.value), '@@@@@@@@@@', codes);
|
||||
setProps(formProps.value);
|
||||
});
|
||||
|
||||
//提交方法
|
||||
const handleSubmit = async (formData: Recordable) => {
|
||||
//提交之后将按钮设置为loading 不可操作状态
|
||||
try {
|
||||
const values = await validate();
|
||||
console.log(values);
|
||||
} finally {
|
||||
console.log(formData);
|
||||
}
|
||||
};
|
||||
|
||||
const handleReset = () => {};
|
||||
|
||||
//关闭方法
|
||||
const handleCloser = () => {
|
||||
resetFields(); //重置表单
|
||||
// clearValidate(); //清空验证
|
||||
};
|
||||
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
299
src/components/CreateCodeStep/src/components/NodeEvent.vue
Normal file
299
src/components/CreateCodeStep/src/components/NodeEvent.vue
Normal file
@ -0,0 +1,299 @@
|
||||
<template>
|
||||
<div class="node-box" v-if="node?.nodeInfo">
|
||||
<div class="node-title">{{ node.text }}</div>
|
||||
<a-tabs v-model:activeKey="nodeKey">
|
||||
<a-tab-pane key="1" :tab="t('基本信息')">
|
||||
<a-form :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
|
||||
<a-form-item :label="t('节点名称')" required>
|
||||
<a-input v-model:value="node.text" :disabled="!node.isUserDefined" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" :tab="t('过程处理')" v-if="node.isUserDefined">
|
||||
<div class="process-top">
|
||||
<div class="process-title">{{ t('过程处理事件列表') }}</div>
|
||||
<a-button type="primary" @click="addProcess" :disabled="fromMobile">
|
||||
{{ t('添加') }}
|
||||
</a-button>
|
||||
</div>
|
||||
<a-table :columns="columns" :data-source="node.nodeInfo.processEvent" :pagination="false">
|
||||
<template #headerCell="{ column }">
|
||||
<template v-if="column.key === 'sort'">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-fangxiang1" />
|
||||
</svg>
|
||||
</template>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.key === 'sort'">
|
||||
<svg class="icon draggable-icon" aria-hidden="true" style="cursor: move">
|
||||
<use xlink:href="#icon-paixu" />
|
||||
</svg>
|
||||
</template>
|
||||
<template v-if="column.key === 'operateType'">
|
||||
<a-select v-model:value="record[column.dataIndex]" :disabled="fromMobile">
|
||||
<a-select-option value="api">{{ t('执行API') }}</a-select-option>
|
||||
<a-select-option value="liteflow">{{ t('规则引擎') }}</a-select-option>
|
||||
<a-select-option value="js">JS脚本</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
<template v-if="column.key === 'operateConfig'">
|
||||
<a-input
|
||||
v-if="record.operateType === 'api'"
|
||||
v-model:value="record[column.dataIndex].path"
|
||||
:disabled="fromMobile"
|
||||
@click="showConfig(index)"
|
||||
>
|
||||
<template #suffix>
|
||||
<Icon icon="ant-design:ellipsis-outlined" />
|
||||
</template>
|
||||
</a-input>
|
||||
<a-input
|
||||
v-model:value="record.showValue"
|
||||
v-else-if="record.operateType === 'js'"
|
||||
@click="showJSConfig(index)"
|
||||
>
|
||||
<template #suffix>
|
||||
<Icon icon="ant-design:ellipsis-outlined" />
|
||||
</template>
|
||||
</a-input>
|
||||
<a-select
|
||||
v-else-if="record.operateType === 'liteflow'"
|
||||
v-model:value="record[column.dataIndex]"
|
||||
:options="liteFlowOptions"
|
||||
:disabled="fromMobile"
|
||||
:field-names="{ label: 'chainName', value: 'id' }"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<DeleteTwoTone
|
||||
two-tone-color="#ff8080"
|
||||
@click="deleteEvent(index)"
|
||||
:style="{ cursor: fromMobile ? 'not-allowed' : 'pointer' }"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<ApiConfig
|
||||
v-if="apiConfigDialog"
|
||||
:title="t('API配置')"
|
||||
v-model:apiConfigDialog="apiConfigDialog"
|
||||
v-model:apiConfig="node.nodeInfo.processEvent[configIndex].operateConfig"
|
||||
/>
|
||||
<ScriptConfig
|
||||
@register="registerModal"
|
||||
@success="submitConfig"
|
||||
:disabled="fromMobile ? true : false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, nextTick, inject, Ref } from 'vue';
|
||||
import { DeleteTwoTone } from '@ant-design/icons-vue';
|
||||
import { Icon } from '/@/components/Icon';
|
||||
import { ApiConfig } from '/@/components/ApiConfig';
|
||||
import { getLiteflowList } from '/@/api/liteflow';
|
||||
import { FormEventColumnConfig, selectedNodeConfig } from '/@/model/generator/formEventConfig';
|
||||
import Sortable from 'sortablejs';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import ScriptConfig from '/@/components/Designer/src/components/componentProperty/settings/ScriptConfig.vue';
|
||||
import { noHaveTableAndField } from '/@/components/Designer/src/types';
|
||||
|
||||
const { t } = useI18n();
|
||||
const nodeKey = ref<string>('1');
|
||||
const props = defineProps({
|
||||
columnList: {
|
||||
type: Object as PropType<FormEventColumnConfig>,
|
||||
default: () => {},
|
||||
},
|
||||
selectedNode: {
|
||||
type: Object as PropType<selectedNodeConfig>,
|
||||
},
|
||||
});
|
||||
const node = ref<any>();
|
||||
const configIndex = ref(0);
|
||||
const apiConfigDialog = ref(false);
|
||||
const liteFlowOptions = ref();
|
||||
const widgetForm = inject<Ref>('widgetForm');
|
||||
const fromMobile = inject<Boolean>('fromMobile', false);
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
dataIndex: 'sort',
|
||||
key: 'sort',
|
||||
},
|
||||
{
|
||||
title: t('操作类别'),
|
||||
dataIndex: 'operateType',
|
||||
key: 'operateType',
|
||||
width: '35%',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('操作配置'),
|
||||
dataIndex: 'operateConfig',
|
||||
key: 'operateConfig',
|
||||
width: '50%',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('操作'),
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: '25%',
|
||||
align: 'center',
|
||||
},
|
||||
]);
|
||||
|
||||
watch(
|
||||
() => props.selectedNode,
|
||||
(val) => {
|
||||
if (val) {
|
||||
node.value = props.columnList[val.columnIndex][val.index];
|
||||
|
||||
if (!node.value.isUserDefined) nodeKey.value = '1';
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => node.value,
|
||||
(val) => {
|
||||
if (val.nodeInfo?.processEvent && val.nodeInfo?.processEvent.length) {
|
||||
nextTick(() => {
|
||||
const tbody: any = document.querySelector('.node-box .ant-table-tbody');
|
||||
Sortable.create(tbody, {
|
||||
handle: '.draggable-icon',
|
||||
});
|
||||
});
|
||||
delete val.nodeInfo.processEvent[configIndex.value]?.operateConfig?.script;
|
||||
}
|
||||
if (val.isUserDefined && !Array.isArray(liteFlowOptions.value)) getList();
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
const showConfig = (index) => {
|
||||
configIndex.value = index;
|
||||
apiConfigDialog.value = true;
|
||||
};
|
||||
|
||||
const getMainTableComponents = (list, mainTableComponent) => {
|
||||
const rangeComponents = ['time-range', 'date-range'];
|
||||
list.forEach((item) => {
|
||||
if (['tab', 'grid', 'card'].includes(item.type)) {
|
||||
for (const child of item.layout!) {
|
||||
getMainTableComponents(child.list, mainTableComponent);
|
||||
}
|
||||
} else if (rangeComponents.includes(item.type)) {
|
||||
//时间范围、日期范围需改为两个字段
|
||||
mainTableComponent.push({
|
||||
label: t(`{name}开始时间`, { name: item.label }),
|
||||
bindField: item.bindStartTime,
|
||||
});
|
||||
mainTableComponent.push({
|
||||
label: t(`{name}结束时间`, { name: item.label }),
|
||||
bindField: item.bindEndTime,
|
||||
});
|
||||
} else if (item.type !== 'form' && !noHaveTableAndField.includes(item.type)) {
|
||||
//除去不绑定表和字段的组件 以及表格组件
|
||||
mainTableComponent.push(item);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const showJSConfig = (index) => {
|
||||
console.log('widgetForm', widgetForm?.value, node.value);
|
||||
const mainTableComponent = ref<any[]>([]);
|
||||
getMainTableComponents(widgetForm?.value.list, mainTableComponent.value);
|
||||
|
||||
const operateConfig = node.value?.nodeInfo.processEvent[index].operateConfig;
|
||||
const content = typeof operateConfig !== 'string' ? '' : operateConfig;
|
||||
openModal(true, {
|
||||
content,
|
||||
list: mainTableComponent.value,
|
||||
index,
|
||||
});
|
||||
};
|
||||
|
||||
const submitConfig = (_, script, index) => {
|
||||
node.value.nodeInfo.processEvent[index].operateConfig = script;
|
||||
node.value.nodeInfo.processEvent[index].showValue = script ? '已配置' : '';
|
||||
};
|
||||
|
||||
const addProcess = () => {
|
||||
node.value.nodeInfo.processEvent.push({
|
||||
operateType: 'api',
|
||||
operateConfig: {},
|
||||
});
|
||||
};
|
||||
|
||||
const deleteEvent = (index) => {
|
||||
if (fromMobile) return;
|
||||
node.value.nodeInfo.processEvent.splice(index, 1);
|
||||
};
|
||||
|
||||
const getList = async () => {
|
||||
liteFlowOptions.value = (await getLiteflowList()) || [];
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.node-box {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
box-shadow: -7px -1px 7px #dadcde;
|
||||
padding: 20px 30px 20px 20px;
|
||||
height: 100%;
|
||||
width: 410px;
|
||||
|
||||
.node-title {
|
||||
line-height: 20px;
|
||||
margin-bottom: 10px;
|
||||
padding-left: 6px;
|
||||
border-left: 6px solid #5e95ff;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.process-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.process-title {
|
||||
line-height: 18px;
|
||||
padding-left: 6px;
|
||||
border-left: 6px solid #5e95ff;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-select) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-table-cell) {
|
||||
padding: 10px !important;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentcolor;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
144
src/components/CreateCodeStep/src/components/SelectDatabase.vue
Normal file
144
src/components/CreateCodeStep/src/components/SelectDatabase.vue
Normal file
@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
wrap-class-name="fixedHeight"
|
||||
@register="registerModal"
|
||||
:title="t('选择数据库')"
|
||||
v-bind="$attrs"
|
||||
width="800px"
|
||||
:fixedHeight="true"
|
||||
@ok="handlerClick"
|
||||
>
|
||||
<BasicTable @register="registerTable" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { useTable, FormSchema, BasicColumn, BasicTable } from '/@/components/Table';
|
||||
import { getDatabaselinkTable } from '/@/api/system/databaselink';
|
||||
import { ref } from 'vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { TableConfig } from '/@/model/generator/tableConfig';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
field: 'tableName',
|
||||
label: t('表名'),
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
|
||||
const columns: BasicColumn[] = [
|
||||
{
|
||||
title: t('表名'),
|
||||
dataIndex: 'tableName',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: t('备注'),
|
||||
dataIndex: 'tableComment',
|
||||
width: 200,
|
||||
},
|
||||
];
|
||||
|
||||
const databaseId = ref('');
|
||||
const selectedKeys = ref<string[] | number[]>([]);
|
||||
|
||||
const emit = defineEmits(['success', 'register']);
|
||||
|
||||
const { createMessage } = useMessage();
|
||||
const selectTableName = ref<string[]>([]);
|
||||
const formConfig = {
|
||||
rowProps: {
|
||||
gutter: 16,
|
||||
},
|
||||
schemas: searchFormSchema,
|
||||
showResetButton: false,
|
||||
};
|
||||
const [registerTable, { getSelectRows, setSelectedRowKeys, reload }] = useTable({
|
||||
title: t('数据表列表'),
|
||||
api: getDatabaselinkTable,
|
||||
striped: false,
|
||||
rowKey: 'tableName',
|
||||
columns,
|
||||
formConfig,
|
||||
beforeFetch: (params) => {
|
||||
//发送请求默认新增 左边树结构所选机构id
|
||||
return { ...params, id: databaseId.value };
|
||||
},
|
||||
afterFetch: () => {
|
||||
//搜索完成后 将选中项重新赋值给SelectedRowKeys
|
||||
setSelectedRowKeys(selectedKeys.value);
|
||||
},
|
||||
rowSelection: {
|
||||
type: 'checkbox',
|
||||
getCheckboxProps: (record) => ({
|
||||
disabled: selectTableName.value.includes(record.tableName),
|
||||
}),
|
||||
onChange: (selectedRowKeys) => {
|
||||
//搜索后会把原有选中项清空 所以需要进行存储
|
||||
selectedKeys.value = selectedRowKeys;
|
||||
},
|
||||
},
|
||||
isPaginateByDataSource: true,
|
||||
useSearchForm: true,
|
||||
showTableSetting: true,
|
||||
tableSetting: {
|
||||
size: false,
|
||||
setting: false,
|
||||
},
|
||||
});
|
||||
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
selectTableName.value = data.selectTableName;
|
||||
setModalProps({ confirmLoading: false, canFullscreen: false });
|
||||
setSelectedRowKeys(data.selectTableName);
|
||||
databaseId.value = data.databaseId;
|
||||
|
||||
reload({ searchInfo: { id: databaseId.value } });
|
||||
});
|
||||
|
||||
const handlerClick = () => {
|
||||
const selectRows = getSelectRows() as TableConfig[];
|
||||
if (selectRows.length === 0) {
|
||||
createMessage.error(t('至少需要选择一个数据表!'));
|
||||
return;
|
||||
}
|
||||
selectRows.map((item, index) => {
|
||||
item.order = index + 1;
|
||||
if (index === 0) {
|
||||
item.isMain = true;
|
||||
} else {
|
||||
item.isMain = false;
|
||||
}
|
||||
});
|
||||
emit('success', selectRows);
|
||||
closeModal();
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
:deep(.ant-pagination) {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
:deep(.ant-table-wrapper .ant-table-title) {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
:deep(.ant-table) {
|
||||
height: calc(100% - 44px);
|
||||
}
|
||||
|
||||
:deep(.ant-table-container) {
|
||||
height: calc(100% - 102px);
|
||||
}
|
||||
|
||||
:deep(.ant-modal) {
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
:deep(.ant-table-wrapper) {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
204
src/components/CreateCodeStep/src/components/TableNameModal.vue
Normal file
204
src/components/CreateCodeStep/src/components/TableNameModal.vue
Normal file
@ -0,0 +1,204 @@
|
||||
<template>
|
||||
<BasicModal
|
||||
@register="registerModal"
|
||||
title="数据表处理"
|
||||
:width="1400"
|
||||
:destroyOnClose="true"
|
||||
v-bind="$attrs"
|
||||
@ok="handleSuccess"
|
||||
>
|
||||
<BasicTable @register="registerTable">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'status'">
|
||||
<span v-if="record.status === 1" class="text-blue-500">表结构相同</span>
|
||||
<span v-else-if="record.status === 2" class="text-orange-500">表结构不同</span>
|
||||
<span v-else-if="record.status === 3">数据库不存在该表,按新增处理</span>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'operator'">
|
||||
<span v-if="record.status === 3">无需操作</span>
|
||||
<a-radio-group
|
||||
v-model:value="record.operator"
|
||||
name="radioGroup"
|
||||
@change="handleOperator(record)"
|
||||
v-else
|
||||
>
|
||||
<a-radio :value="1" v-if="record.status !== 2">沿用旧表</a-radio>
|
||||
<a-radio :value="2">创建新表(重新生成表名)</a-radio>
|
||||
<a-radio :value="3">
|
||||
覆盖旧表(
|
||||
<span class="text-red-500" v-if="record.status === 1">此操作会清空旧表数据</span>
|
||||
<span class="text-red-500" v-else>
|
||||
此操作会删除旧表,然后以相同表名按新结构进行生成
|
||||
</span>
|
||||
)
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
</template>
|
||||
<template v-if="column.dataIndex === 'newTableName'">
|
||||
<a-input
|
||||
v-if="record.operator === 2"
|
||||
v-model:value="record.newTableName"
|
||||
placeholder="请输入表名"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { inject } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicTable, useTable, BasicColumn } from '/@/components/Table';
|
||||
import { GeneratorConfig } from '/@/model/generator/generatorConfig';
|
||||
import { validateTable, validateTableName } from '/@/api/system/generator';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
|
||||
const { notification } = useMessage();
|
||||
const { t } = useI18n();
|
||||
|
||||
const emits = defineEmits(['success', 'register']);
|
||||
const generatorConfig = inject<GeneratorConfig>('generatorConfig');
|
||||
const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '数据库名称',
|
||||
dataIndex: 'databaseName',
|
||||
width: 100,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '数据表名',
|
||||
dataIndex: 'tableName',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '表结构配置比对结果',
|
||||
dataIndex: 'status',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operator',
|
||||
width: 400,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '新表表名',
|
||||
dataIndex: 'newTableName',
|
||||
width: 80,
|
||||
align: 'center',
|
||||
},
|
||||
];
|
||||
const [registerModal] = useModalInner();
|
||||
|
||||
const [registerTable, { getDataSource }] = useTable({
|
||||
api: validateTable,
|
||||
beforeFetch: () => {
|
||||
return {
|
||||
databaseId: generatorConfig?.databaseId,
|
||||
tableStructureConfigs: generatorConfig?.tableStructureConfigs,
|
||||
};
|
||||
},
|
||||
afterFetch: (data) => {
|
||||
if (Array.isArray(data) && data.length) {
|
||||
data.map((item) => {
|
||||
const operator = item.status === 3 ? 4 : item.status === 2 ? 2 : 1;
|
||||
item.operator = operator;
|
||||
item.newTableName = '';
|
||||
});
|
||||
}
|
||||
},
|
||||
columns,
|
||||
striped: false,
|
||||
pagination: false,
|
||||
showIndexColumn: false,
|
||||
});
|
||||
|
||||
const handleOperator = (record) => {
|
||||
if (record.operator !== 2) record.newTableName = '';
|
||||
};
|
||||
|
||||
const handleSuccess = async () => {
|
||||
const newTableNameArr: string[] = [];
|
||||
const hasNoTableName = getDataSource().some((info) => {
|
||||
return info.operator === 2 && !info.newTableName;
|
||||
});
|
||||
if (hasNoTableName) {
|
||||
notification.error({
|
||||
message: t('提示'),
|
||||
description: '新表表名不能为空,否则无法创建数据表',
|
||||
});
|
||||
return;
|
||||
}
|
||||
getDataSource().map((item) => {
|
||||
if (item.operator === 2) {
|
||||
newTableNameArr.push(item.newTableName);
|
||||
}
|
||||
});
|
||||
const reg = /^[a-zA-Z0-9_]*$/;
|
||||
//判断表名是否符合要求
|
||||
const isTableNotSuccess = newTableNameArr?.some((tableName) => !reg.test(tableName));
|
||||
if (isTableNotSuccess) {
|
||||
notification.error({
|
||||
message: t('提示'),
|
||||
description: '表名只能包括字母、数字、下划线',
|
||||
});
|
||||
return;
|
||||
}
|
||||
await validateTableName({
|
||||
id: generatorConfig!.databaseId!,
|
||||
tableNames: newTableNameArr.toString(),
|
||||
});
|
||||
generatorConfig?.tableStructureConfigs?.forEach((item) => {
|
||||
getDataSource().forEach((data) => {
|
||||
if (item.tableName === data.tableName) {
|
||||
item.operator = data.operator;
|
||||
if (data.operator === 2) {
|
||||
item.tableName = data.newTableName;
|
||||
item.tableFieldConfigs.forEach((field) => {
|
||||
changeTableName(generatorConfig?.formJson.list, field.key, item.tableName, item.key);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
emits('success');
|
||||
};
|
||||
|
||||
const changeTableName = (list, fieldKey, tableName, tableKey) => {
|
||||
if (generatorConfig?.formJson.hiddenComponent?.length) {
|
||||
generatorConfig?.formJson.hiddenComponent?.map((component: any) => {
|
||||
if (component.key === fieldKey) {
|
||||
component.bindTable = tableName;
|
||||
}
|
||||
});
|
||||
}
|
||||
list?.map((component: any) => {
|
||||
if (component.type === 'form' || component.type === 'one-for-one') {
|
||||
if (component.key === tableKey) {
|
||||
component.bindTable = tableName;
|
||||
}
|
||||
if (component.children.length) {
|
||||
component.children.map((subComponent: any) => {
|
||||
if (subComponent.key === fieldKey) {
|
||||
subComponent.bindTable = tableName;
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (component.key === fieldKey) {
|
||||
component.bindTable = tableName;
|
||||
} else if (['tab', 'grid', 'card'].includes(component.type)) {
|
||||
for (const child of component.layout!) {
|
||||
changeTableName(child.list, fieldKey, tableName, tableKey);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
* {
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user