IMS系统表单设计器完全指南

表单设计器是 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;
        }
    }
}

表单设计建议

  • 合理分组相关字段,提升填写效率
  • 必填项尽量靠前,减少用户等待时间
  • 复杂校验规则配合友好提示
  • 充分利用数据关联简化填写

总结

  • 组件化设计,支持灵活扩展
  • 拖拽式配置,所见即所得
  • 多层次校验,保障数据质量
  • 数据关联,实现智能计算