feat(form): 新增动态表单设计与数据收集功能
- src/api/form: 新增表单定义、渲染、数据收集、菜单发布接口与类型 - src/views/dynamic-form: 新增表单列表、设计器、预览、内嵌填写、公开填写、数据页 - src/plugins/form-create: 注册 form-create 渲染器、设计器与业务组件拖拽规则 - src/router: 新增公开表单分享路由 /f/:formKey,并加入登录守卫白名单 - src/enums/business: 新增 FormStatus 表单状态枚举 - src/layouts/components/LayoutSidebarItem: 支持渲染菜单角标,取自 sys_menu.params.badge - src/main.ts: 应用启动时调用 setupFormCreate - package.json: 新增 @form-create/element-ui 与 @form-create/designer 依赖
This commit is contained in:
157
src/plugins/form-create.ts
Normal file
157
src/plugins/form-create.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* FormCreate 动态表单全局注册
|
||||
*
|
||||
* @description 注册 form-create 渲染器(@form-create/element-ui 3.x)、可视化设计器
|
||||
* (@form-create/designer 3.x)与业务组件拖拽规则
|
||||
*
|
||||
* <p>ElementPlus 只能全量注册:两个包的产物在运行时用 resolveComponent 解析 el-xxx
|
||||
* 标签,unplugin-vue-components 只转换项目源码、不处理 node_modules 里的预编译产物,
|
||||
* 按需导入会让设计器内部组件解析失败并抛出 vnode 为 null 的报错</p>
|
||||
*
|
||||
* @see https://www.form-create.com/v3/guide/
|
||||
*/
|
||||
|
||||
import type { App } from "vue";
|
||||
|
||||
import ElementPlus from "element-plus";
|
||||
import formCreate from "@form-create/element-ui";
|
||||
import FcDesigner from "@form-create/designer";
|
||||
import type { DragRule } from "@form-create/designer";
|
||||
|
||||
import DictSelect from "@/components/DictSelect/index.vue";
|
||||
import FileUpload from "@/components/Upload/FileUpload.vue";
|
||||
|
||||
import "element-plus/dist/index.css";
|
||||
|
||||
/**
|
||||
* 注册 ElementPlus 全量组件、form-create 渲染器、设计器与业务组件
|
||||
*
|
||||
* @param app Vue 应用实例
|
||||
*/
|
||||
export function setupFormCreate(app: App): void {
|
||||
app.use(ElementPlus);
|
||||
app.use(formCreate);
|
||||
app.use(FcDesigner);
|
||||
// 业务组件(字典/文件上传)注册必须先于设计器实例挂载
|
||||
setupFormCreateComponents();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 业务组件拖拽规则
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** 字段名自增序号(拖入画布时生成唯一 field,语义前缀便于数据侧识别) */
|
||||
let fieldSeed = 0;
|
||||
|
||||
/**
|
||||
* 生成唯一字段名
|
||||
*
|
||||
* @param prefix 字段前缀(如 dict/user/dept/file)
|
||||
*/
|
||||
function uniqueField(prefix: string): string {
|
||||
return `${prefix}_${++fieldSeed}`;
|
||||
}
|
||||
|
||||
/** 字典选择:选项由字典编码在运行时从字典中心加载,设计时无需配置选项 */
|
||||
const dictSelectRule: DragRule = {
|
||||
name: "DictSelect",
|
||||
label: "字典选择",
|
||||
icon: "icon-select",
|
||||
menu: "biz",
|
||||
input: true,
|
||||
event: ["change"],
|
||||
languageKey: [],
|
||||
validate: ["string", "number", "array"],
|
||||
rule: () => ({
|
||||
type: "DictSelect",
|
||||
field: uniqueField("dict"),
|
||||
title: "字典选择",
|
||||
info: "",
|
||||
$required: false,
|
||||
props: {
|
||||
code: "",
|
||||
type: "select",
|
||||
placeholder: "请选择",
|
||||
},
|
||||
}),
|
||||
props: () => [
|
||||
{ type: "input", field: "code", title: "字典编码" },
|
||||
{
|
||||
type: "select",
|
||||
field: "type",
|
||||
title: "展示形态",
|
||||
value: "select",
|
||||
options: [
|
||||
{ label: "下拉", value: "select" },
|
||||
{ label: "单选", value: "radio" },
|
||||
{ label: "多选", value: "checkbox" },
|
||||
],
|
||||
},
|
||||
{ type: "input", field: "placeholder", title: "提示文字" },
|
||||
],
|
||||
};
|
||||
|
||||
/** 文件上传:直连项目对象存储,值存文件信息数组 [{name, url}] */
|
||||
const fileUploadRule: DragRule = {
|
||||
name: "FileUpload",
|
||||
label: "文件上传",
|
||||
icon: "icon-import-file",
|
||||
menu: "biz",
|
||||
input: true,
|
||||
event: ["change"],
|
||||
languageKey: [],
|
||||
validate: ["array"],
|
||||
rule: () => ({
|
||||
type: "FileUpload",
|
||||
field: uniqueField("file"),
|
||||
title: "附件上传",
|
||||
info: "",
|
||||
$required: false,
|
||||
props: {
|
||||
limit: 5,
|
||||
maxFileSize: 10,
|
||||
accept: "*",
|
||||
uploadBtnText: "上传文件",
|
||||
},
|
||||
}),
|
||||
props: () => [
|
||||
{ type: "inputNumber", field: "limit", title: "数量上限", props: { min: 1 } },
|
||||
{
|
||||
type: "inputNumber",
|
||||
field: "maxFileSize",
|
||||
title: "单文件大小上限(MB)",
|
||||
props: { min: 1 },
|
||||
},
|
||||
{ type: "input", field: "accept", title: "文件类型" },
|
||||
{ type: "input", field: "uploadBtnText", title: "按钮文本" },
|
||||
],
|
||||
};
|
||||
|
||||
/**
|
||||
* 注册业务组件到设计器与运行态渲染器
|
||||
*
|
||||
* <p>拖入画布的组件在运行态渲染(render/preview/share/data 页)同样可解析:
|
||||
* FcDesigner.component() 会同时挂载到设计态渲染器(designerForm)与
|
||||
* 运行态渲染器(@form-create/element-ui 默认实例,即全局 <form-create>)</p>
|
||||
*
|
||||
* <p>注意:业务组件选项数据来自需登录的管理端接口,仅适用于系统内嵌表单;
|
||||
* 公开表单(匿名访问)请使用设计器自带基础组件,否则选项接口会 401</p>
|
||||
*
|
||||
* 幂等保护:HMR 或多次调用时跳过重复注册
|
||||
*/
|
||||
let registered = false;
|
||||
|
||||
function setupFormCreateComponents(): void {
|
||||
if (registered) return;
|
||||
registered = true;
|
||||
|
||||
// 新增左侧菜单分组,业务组件与设计器基础组件区分开
|
||||
FcDesigner.addMenu({ name: "biz", title: "业务组件", list: [] });
|
||||
|
||||
// 组件本体:同时挂载到设计态渲染器与运行态渲染器
|
||||
FcDesigner.component("DictSelect", DictSelect);
|
||||
FcDesigner.component("FileUpload", FileUpload);
|
||||
|
||||
// 拖拽规则:定义左侧菜单项、拖入画布的默认规则与右侧属性面板
|
||||
FcDesigner.addDragRule([dictSelectRule, fileUploadRule]);
|
||||
}
|
||||
Reference in New Issue
Block a user