feat: 新增 ele infra 表单设计器模块

This commit is contained in:
puhui999
2025-05-14 16:07:03 +08:00
parent c743932c5a
commit ffc7e21d4a
19 changed files with 1623 additions and 0 deletions

View File

@@ -0,0 +1,182 @@
/* eslint-disable no-template-curly-in-string */
const selectRule = [
{
type: 'select',
field: 'selectType',
title: '选择器类型',
value: 'select',
options: [
{ label: '下拉框', value: 'select' },
{ label: '单选框', value: 'radio' },
{ label: '多选框', value: 'checkbox' },
],
// 参考 https://www.form-create.com/v3/guide/control 组件联动,单选框和多选框不需要多选属性
control: [
{
value: 'select',
condition: '==',
method: 'hidden',
rule: [
'multiple',
'clearable',
'collapseTags',
'multipleLimit',
'allowCreate',
'filterable',
'noMatchText',
'remote',
'remoteMethod',
'reserveKeyword',
'defaultFirstOption',
'automaticDropdown',
],
},
],
},
{
type: 'switch',
field: 'filterable',
title: '是否可搜索',
},
{ type: 'switch', field: 'multiple', title: '是否多选' },
{
type: 'switch',
field: 'disabled',
title: '是否禁用',
},
{ type: 'switch', field: 'clearable', title: '是否可以清空选项' },
{
type: 'switch',
field: 'collapseTags',
title: '多选时是否将选中值按文字的形式展示',
},
{
type: 'inputNumber',
field: 'multipleLimit',
title: '多选时用户最多可以选择的项目数,为 0 则不限制',
props: { min: 0 },
},
{
type: 'input',
field: 'autocomplete',
title: 'autocomplete 属性',
},
{ type: 'input', field: 'placeholder', title: '占位符' },
{ type: 'switch', field: 'allowCreate', title: '是否允许用户创建新条目' },
{
type: 'input',
field: 'noMatchText',
title: '搜索条件无匹配时显示的文字',
},
{ type: 'input', field: 'noDataText', title: '选项为空时显示的文字' },
{
type: 'switch',
field: 'reserveKeyword',
title: '多选且可搜索时,是否在选中一个选项后保留当前的搜索关键词',
},
{
type: 'switch',
field: 'defaultFirstOption',
title: '在输入框按下回车,选择第一个匹配项',
},
{
type: 'switch',
field: 'popperAppendToBody',
title: '是否将弹出框插入至 body 元素',
value: true,
},
{
type: 'switch',
field: 'automaticDropdown',
title: '对于不可搜索的 Select是否在输入框获得焦点后自动弹出选项菜单',
},
];
const apiSelectRule = [
{
type: 'input',
field: 'url',
title: 'url 地址',
props: {
placeholder: '/system/user/simple-list',
},
},
{
type: 'select',
field: 'method',
title: '请求类型',
value: 'GET',
options: [
{ label: 'GET', value: 'GET' },
{ label: 'POST', value: 'POST' },
],
control: [
{
value: 'GET',
condition: '!=',
method: 'hidden',
rule: [
{
type: 'input',
field: 'data',
title: '请求参数 JSON 格式',
props: {
autosize: true,
type: 'textarea',
placeholder: '{"type": 1}',
},
},
],
},
],
},
{
type: 'input',
field: 'labelField',
title: 'label 属性',
info: '可以使用 el 表达式:${属性},来实现复杂数据组合。如:${nickname}-${id}',
props: {
placeholder: 'nickname',
},
},
{
type: 'input',
field: 'valueField',
title: 'value 属性',
info: '可以使用 el 表达式:${属性},来实现复杂数据组合。如:${nickname}-${id}',
props: {
placeholder: 'id',
},
},
{
type: 'input',
field: 'parseFunc',
title: '选项解析函数',
info: `data 为接口返回值,需要写一个匿名函数解析返回值为选择器 options 列表
(data: any)=>{ label: string; value: any }[]`,
props: {
autosize: true,
rows: { minRows: 2, maxRows: 6 },
type: 'textarea',
placeholder: `
function (data) {
console.log(data)
return data.list.map(item=> ({label: item.nickname,value: item.id}))
}`,
},
},
{
type: 'switch',
field: 'remote',
info: '是否可搜索',
title: '其中的选项是否从服务器远程加载',
},
{
type: 'input',
field: 'remoteField',
title: '请求参数',
info: '远程请求时请求携带的参数名称name',
},
];
export { apiSelectRule, selectRule };

View File

@@ -0,0 +1,6 @@
export { useDictSelectRule } from './use-dict-select';
export { useEditorRule } from './use-editor-rule';
export { useSelectRule } from './use-select-rule';
export { useUploadFileRule } from './use-upload-file-rule';
export { useUploadImageRule } from './use-upload-image-rule';
export { useUploadImagesRule } from './use-upload-images-rule';

View File

@@ -0,0 +1,69 @@
import { onMounted, ref } from 'vue';
import { buildUUID, cloneDeep } from '@vben/utils';
import * as DictDataApi from '#/api/system/dict/type';
import {
localeProps,
makeRequiredRule,
} from '#/components/form-create/helpers';
import { selectRule } from '#/components/form-create/rules/data';
/**
* 字典选择器规则,如果规则使用到动态数据则需要单独配置不能使用 useSelectRule
*/
export const useDictSelectRule = () => {
const label = '字典选择器';
const name = 'DictSelect';
const rules = cloneDeep(selectRule);
const dictOptions = ref<{ label: string; value: string }[]>([]); // 字典类型下拉数据
onMounted(async () => {
const data = await DictDataApi.getSimpleDictTypeList();
if (!data || data.length === 0) {
return;
}
dictOptions.value =
data?.map((item: DictDataApi.SystemDictTypeApi.DictType) => ({
label: item.name,
value: item.type,
})) ?? [];
});
return {
icon: 'icon-descriptions',
label,
name,
rule() {
return {
type: name,
field: buildUUID(),
title: label,
info: '',
$required: false,
};
},
props(_: any, { t }: any) {
return localeProps(t, `${name}.props`, [
makeRequiredRule(),
{
type: 'select',
field: 'dictType',
title: '字典类型',
value: '',
options: dictOptions.value,
},
{
type: 'select',
field: 'valueType',
title: '字典值类型',
value: 'str',
options: [
{ label: '数字', value: 'int' },
{ label: '字符串', value: 'str' },
{ label: '布尔值', value: 'bool' },
],
},
...rules,
]);
},
};
};

View File

@@ -0,0 +1,36 @@
import { buildUUID } from '@vben/utils';
import {
localeProps,
makeRequiredRule,
} from '#/components/form-create/helpers';
export const useEditorRule = () => {
const label = '富文本';
const name = 'Tinymce';
return {
icon: 'icon-editor',
label,
name,
rule() {
return {
type: name,
field: buildUUID(),
title: label,
info: '',
$required: false,
};
},
props(_: any, { t }: any) {
return localeProps(t, `${name}.props`, [
makeRequiredRule(),
{
type: 'input',
field: 'height',
title: '高度',
},
{ type: 'switch', field: 'readonly', title: '是否只读' },
]);
},
};
};

View File

@@ -0,0 +1,45 @@
import type { SelectRuleOption } from '#/components/form-create/typing';
import { buildUUID, cloneDeep } from '@vben/utils';
import {
localeProps,
makeRequiredRule,
} from '#/components/form-create/helpers';
import { selectRule } from '#/components/form-create/rules/data';
/**
* 通用选择器规则 hook
*
* @param option 规则配置
*/
export const useSelectRule = (option: SelectRuleOption) => {
const label = option.label;
const name = option.name;
const rules = cloneDeep(selectRule);
return {
icon: option.icon,
label,
name,
event: option.event,
rule() {
return {
type: name,
field: buildUUID(),
title: label,
info: '',
$required: false,
};
},
props(_: any, { t }: any) {
if (!option.props) {
option.props = [];
}
return localeProps(t, `${name}.props`, [
makeRequiredRule(),
...option.props,
...rules,
]);
},
};
};

View File

@@ -0,0 +1,84 @@
import { buildUUID } from '@vben/utils';
import {
localeProps,
makeRequiredRule,
} from '#/components/form-create/helpers';
export const useUploadFileRule = () => {
const label = '文件上传';
const name = 'FileUpload';
return {
icon: 'icon-upload',
label,
name,
rule() {
return {
type: name,
field: buildUUID(),
title: label,
info: '',
$required: false,
};
},
props(_: any, { t }: any) {
return localeProps(t, `${name}.props`, [
makeRequiredRule(),
{
type: 'select',
field: 'fileType',
title: '文件类型',
value: ['doc', 'xls', 'ppt', 'txt', 'pdf'],
options: [
{ label: 'doc', value: 'doc' },
{ label: 'xls', value: 'xls' },
{ label: 'ppt', value: 'ppt' },
{ label: 'txt', value: 'txt' },
{ label: 'pdf', value: 'pdf' },
],
props: {
multiple: true,
},
},
{
type: 'switch',
field: 'autoUpload',
title: '是否在选取文件后立即进行上传',
value: true,
},
{
type: 'switch',
field: 'drag',
title: '拖拽上传',
value: false,
},
{
type: 'switch',
field: 'isShowTip',
title: '是否显示提示',
value: true,
},
{
type: 'inputNumber',
field: 'fileSize',
title: '大小限制(MB)',
value: 5,
props: { min: 0 },
},
{
type: 'inputNumber',
field: 'limit',
title: '数量限制',
value: 5,
props: { min: 0 },
},
{
type: 'switch',
field: 'disabled',
title: '是否禁用',
value: false,
},
]);
},
};
};

View File

@@ -0,0 +1,93 @@
import { buildUUID } from '@vben/utils';
import {
localeProps,
makeRequiredRule,
} from '#/components/form-create/helpers';
export const useUploadImageRule = () => {
const label = '单图上传';
const name = 'ImageUpload';
return {
icon: 'icon-image',
label,
name,
rule() {
return {
type: name,
field: buildUUID(),
title: label,
info: '',
$required: false,
};
},
props(_: any, { t }: any) {
return localeProps(t, `${name}.props`, [
makeRequiredRule(),
{
type: 'switch',
field: 'drag',
title: '拖拽上传',
value: false,
},
{
type: 'select',
field: 'fileType',
title: '图片类型限制',
value: ['image/jpeg', 'image/png', 'image/gif'],
options: [
{ label: 'image/apng', value: 'image/apng' },
{ label: 'image/bmp', value: 'image/bmp' },
{ label: 'image/gif', value: 'image/gif' },
{ label: 'image/jpeg', value: 'image/jpeg' },
{ label: 'image/pjpeg', value: 'image/pjpeg' },
{ label: 'image/svg+xml', value: 'image/svg+xml' },
{ label: 'image/tiff', value: 'image/tiff' },
{ label: 'image/webp', value: 'image/webp' },
{ label: 'image/x-icon', value: 'image/x-icon' },
],
props: {
multiple: false,
},
},
{
type: 'inputNumber',
field: 'fileSize',
title: '大小限制(MB)',
value: 5,
props: { min: 0 },
},
{
type: 'input',
field: 'height',
title: '组件高度',
value: '150px',
},
{
type: 'input',
field: 'width',
title: '组件宽度',
value: '150px',
},
{
type: 'input',
field: 'borderradius',
title: '组件边框圆角',
value: '8px',
},
{
type: 'switch',
field: 'disabled',
title: '是否显示删除按钮',
value: true,
},
{
type: 'switch',
field: 'showBtnText',
title: '是否显示按钮文字',
value: true,
},
]);
},
};
};

View File

@@ -0,0 +1,89 @@
import { buildUUID } from '@vben/utils';
import {
localeProps,
makeRequiredRule,
} from '#/components/form-create/helpers';
export const useUploadImagesRule = () => {
const label = '多图上传';
const name = 'ImagesUpload';
return {
icon: 'icon-image',
label,
name,
rule() {
return {
type: name,
field: buildUUID(),
title: label,
info: '',
$required: false,
};
},
props(_: any, { t }: any) {
return localeProps(t, `${name}.props`, [
makeRequiredRule(),
{
type: 'switch',
field: 'drag',
title: '拖拽上传',
value: false,
},
{
type: 'select',
field: 'fileType',
title: '图片类型限制',
value: ['image/jpeg', 'image/png', 'image/gif'],
options: [
{ label: 'image/apng', value: 'image/apng' },
{ label: 'image/bmp', value: 'image/bmp' },
{ label: 'image/gif', value: 'image/gif' },
{ label: 'image/jpeg', value: 'image/jpeg' },
{ label: 'image/pjpeg', value: 'image/pjpeg' },
{ label: 'image/svg+xml', value: 'image/svg+xml' },
{ label: 'image/tiff', value: 'image/tiff' },
{ label: 'image/webp', value: 'image/webp' },
{ label: 'image/x-icon', value: 'image/x-icon' },
],
props: {
multiple: true,
maxNumber: 5,
},
},
{
type: 'inputNumber',
field: 'fileSize',
title: '大小限制(MB)',
value: 5,
props: { min: 0 },
},
{
type: 'inputNumber',
field: 'limit',
title: '数量限制',
value: 5,
props: { min: 0 },
},
{
type: 'input',
field: 'height',
title: '组件高度',
value: '150px',
},
{
type: 'input',
field: 'width',
title: '组件宽度',
value: '150px',
},
{
type: 'input',
field: 'borderradius',
title: '组件边框圆角',
value: '8px',
},
]);
},
};
};