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:
Ray.Hao committed 2026-09-01 00:17:11 +08:00
1 parent 17ca4c1b7b
commit d36ccf54d5
22 files changed
+7991 -3822

No files matched your search

@@ -0,0 +1,92 @@
/**
* 表单字段解析
*
* @description 从 form-create 规则提取字段元数据、解析提交数据并格式化展示值
*/
/** 表单字段元数据 */
export interface FormFieldMeta {
/** 字段名 */
field: string;
/** 字段标题 */
title: string;
/** 选项映射(value -> label):选项类字段提交的是 value,列表/导出需翻译为 label */
optionMap: Map<string, string>;
}
/**
* 递归提取表单字段元数据
*
* 布局容器(row/col)自身无 field,其子节点递归收集;
* 参数为 JSON 解析产物,结构未经校验,按 unknown 逐层探测
*
* @param rules form-create 规则(JSON 解析值)
*/
export function extractFields(rules: unknown): FormFieldMeta[] {
const result: FormFieldMeta[] = [];
const walk = (nodes: unknown[]): void => {
nodes.forEach((node) => {
if (!node || typeof node !== "object") return;
const item = node as Record<string, unknown>;
if (typeof item.field === "string" && item.field) {
result.push({
field: item.field,
title: String(item.title ?? item.field),
optionMap: extractOptionMap(item.options),
});
}
if (Array.isArray(item.children)) {
walk(item.children);
}
});
};
walk(Array.isArray(rules) ? rules : []);
return result;
}
/**
* 提取字段选项映射(value -> label)
*
* 非对象或缺失 value/label 的脏项跳过
*
* @param options 规则里的 options 数组(JSON 解析值)
*/
export function extractOptionMap(options: unknown): Map<string, string> {
const optionMap = new Map<string, string>();
if (!Array.isArray(options)) return optionMap;
options.forEach((option) => {
if (!option || typeof option !== "object") return;
const { value, label } = option as Record<string, unknown>;
if (value !== undefined && label !== undefined) {
optionMap.set(String(value), String(label));
}
});
return optionMap;
}
/**
* 解析提交数据(field -> value 映射,解析失败按空数据兜底)
*
* @param dataJson 数据 JSON 字符串
*/
export function parseDataJson(dataJson?: string): Record<string, unknown> {
try {
return dataJson ? (JSON.parse(dataJson) as Record<string, unknown>) : {};
} catch {
return {};
}
}
/**
* 格式化单元格展示值(选项翻译、数组拼接、对象序列化、空值占位)
*
* @param value 字段值
* @param field 字段元数据(选项类字段 value 翻译为 label,未命中映射时原样展示)
*/
export function formatCellValue(value: unknown, field?: FormFieldMeta): string {
if (value === null || value === undefined || value === "") return "-";
if (Array.isArray(value)) return value.map((item) => formatCellValue(item, field)).join(", ");
if (typeof value === "object") return JSON.stringify(value);
const raw = String(value);
return field?.optionMap.get(raw) ?? raw;
}