表单设计器是 IMS 系统的核心功能之一,允许用户通过可视化配置创建各种业务表单。本文详细介绍表单设计器的架构与实现。
表单组件体系
设计器支持丰富的表单组件:
/* 表单组件类型定义 */
type ComponentType =
| 'input' // 单行文本
| 'textarea' // 多行文本
| 'number' // 数字输入
| 'select' // 下拉选择
| 'radio' // 单选
| 'checkbox' // 多选
| 'date' // 日期
| 'datetime' // 日期时间
| 'time' // 时间
| 'file' // 文件上传
| 'image' // 图片上传
| 'switch' // 开关
| 'slider' // 滑块
| 'rating' // 评分
| 'table' // 子表
| 'user' // 用户选择
| 'dept' // 部门选择;
/* 组件配置 */
interface ComponentConfig {
type: ComponentType;
key: string;
label: string;
placeholder?: string;
required?: boolean;
disabled?: boolean;
visible?: boolean;
defaultValue?: any;
options?: Array<{ label: string; value: any }>;
validation?: ValidationRule[];
props?: Record<string, any>;
}
拖拽设计实现
使用拖拽 API 实现组件拖放:
/* 拖拽设计器核心 */
class FormDesigner {
constructor(container) {
this.container = container;
this.components = [];
this.draggedComponent = null;
this.init();
}
init() {
/* 初始化组件面板可拖拽 */
const toolboxItems = this.container.querySelectorAll('.toolbox-item');
toolboxItems.forEach(item => {
item.addEventListener('dragstart', (e) => this.onDragStart(e));
});
/* 初始化画布区域 */
const canvas = this.container.querySelector('.canvas');
canvas.addEventListener('dragover', (e) => this.onDragOver(e));
canvas.addEventListener('drop', (e) => this.onDrop(e));
}
onDragStart(event) {
const componentType = event.target.dataset.type;
event.dataTransfer.setData('componentType', componentType);
event.dataTransfer.effectAllowed = 'copy';
}
onDragOver(event) {
event.preventDefault();
event.dataTransfer.dropEffect = 'copy';
/* 显示插入指示器 */
this.showInsertIndicator(event.target);
}
onDrop(event) {
event.preventDefault();
const componentType = event.dataTransfer.getData('componentType');
/* 创建新组件 */
const newComponent = this.createComponent(componentType);
this.components.push(newComponent);
/* 渲染组件 */
this.renderComponent(newComponent);
}
}
表单校验规则
实现多层次的表单校验:
/* 校验规则定义 */
interface ValidationRule {
type: 'required' | 'pattern' | 'min' | 'max'
| 'minLength' | 'maxLength' | 'custom';
message: string;
value?: any;
validator?: (value: any) => boolean;
}
/* 校验器 */
class FormValidator {
static validate(value: any, rules: ValidationRule[]): string[] {
const errors: string[] = [];
for (const rule of rules) {
switch (rule.type) {
case 'required':
if (!value && value !== 0) {
errors.push(rule.message);
}
break;
case 'pattern':
if (rule.value && !new RegExp(rule.value).test(value)) {
errors.push(rule.message);
}
break;
case 'minLength':
if (typeof value === 'string' && value.length < rule.value) {
errors.push(rule.message);
}
break;
case 'custom':
if (rule.validator && !rule.validator(value)) {
errors.push(rule.message);
}
break;
}
}
return errors;
}
}
表单数据渲染
将配置渲染为实际表单:
/* 表单渲染器 */
class FormRenderer {
render(formConfig: FormConfig, data: Record<string, any>): HTMLElement {
const form = document.createElement('form');
form.className = 'ims-form';
for (const component of formConfig.components) {
/* 检查组件是否可见 */
if (!isComponentVisible(component, data)) {
continue;
}
/* 创建表单项 */
const field = this.renderField(component, data);
form.appendChild(field);
}
return form;
}
renderField(component: ComponentConfig, data: Record<string, any>): HTMLElement {
const field = document.createElement('div');
field.className = 'form-field';
/* 标签 */
if (component.label) {
const label = document.createElement('label');
label.textContent = component.label;
if (component.required) {
label.innerHTML += ' *';
}
field.appendChild(label);
}
/* 输入控件 */
const input = this.createInput(component, data[component.key]);
input.name = component.key;
input.value = data[component.key] ?? component.defaultValue ?? '';
field.appendChild(input);
return field;
}
}
数据关联与计算
支持表单项之间的数据关联和计算:
/* 数据关联配置 */
interface DataLink {
source: string; // 源字段
target: string; // 目标字段
expression: string; // 计算表达式
trigger: string[]; // 触发条件
}
/* 表达式计算器 */
class ExpressionEvaluator {
evaluate(expression: string, context: Record<string, any>): any {
/* 支持的变量格式:{{fieldName}} */
let parsed = expression.replace(
/\{\{(\w+)\}\}/g,
'(context["$1"] ?? 0)'
);
/* 安全计算 */
try {
return new Function('context', `return ${parsed}`)(context);
} catch (e) {
console.error('Expression error:', e);
return null;
}
}
}
表单设计建议
- 合理分组相关字段,提升填写效率
- 必填项尽量靠前,减少用户等待时间
- 复杂校验规则配合友好提示
- 充分利用数据关联简化填写
总结
- 组件化设计,支持灵活扩展
- 拖拽式配置,所见即所得
- 多层次校验,保障数据质量
- 数据关联,实现智能计算