表单是 IMS 系统中用户与数据交互的核心界面,表单设计器让用户能够通过可视化拖拽的方式自定义表单结构,无需编写代码即可创建各类业务表单。本文将详细介绍表单设计器的架构设计、组件系统、属性配置与数据验证机制。
表单设计器整体架构
表单设计器采用模块化设计,主要包含以下核心组件:
- 组件面板 - 展示所有可用的表单组件
- 画布区域 - 拖拽放置组件的可视化区域
- 属性面板 - 配置选中组件的属性
- 数据模型 - 表单的 JSON schema 定义
- 渲染引擎 - 根据配置渲染表单 UI
表单数据结构
表单配置采用 JSON 格式存储,支持完整的组件配置:
form-schema.js
// 表单 Schema 定义 const FormSchema = { id: 'form_001', name: '员工信息登记表', layout: 'pc', // pc | mobile | responsive // 表单布局配置 layoutConfig: { labelCol: 6, wrapperCol: 18, columns: 2, // 多列布局 gutter: 20 }, // 表单字段定义 fields: [ { id: 'field_name', type: 'input', label: '姓名', placeholder: '请输入姓名', required: true, rules: [ { required: true, message: '请输入姓名' }, { min: 2, max: 20, message: '姓名长度为2-20个字符' } ], props: { maxLength: 20, showWordLimit: true } }, { id: 'field_department', type: 'select', label: '部门', required: true, options: [ { label: '技术部', value: 'tech' }, { label: '运营部', value: 'ops' }, { label: '销售部', value: 'sales' } ], remote: { url: '/api/departments', labelField: 'name', valueField: 'id' } }, { id: 'field_avatar', type: 'upload', label: '头像上传', props: { type: 'image', maxCount: 1, maxSize: 2, // MB accept: 'image/*' } }, { id: 'field_bio', type: 'textarea', label: '个人简介', props: { rows: 4, maxLength: 500, showWordLimit: true, autoSize: { minRows: 3, maxRows: 6 } } } ], // 提交按钮配置 submit: { text: '提交', api: '/api/form/submit', successMessage: '提交成功', redirect: '/list' } };
表单组件系统
表单设计器内置丰富的组件,支持业务自定义扩展:
component-registry.js
// 表单组件注册表 class ComponentRegistry { constructor() { this.components = new Map(); this.registerDefaults(); } registerDefaults() { // 基础输入组件 this.register('input', { label: '单行文本', icon: 'text-input', category: 'basic', defaultProps: { placeholder: '' } }); this.register('textarea', { label: '多行文本', icon: 'text-area', category: 'basic' }); this.register('number', { label: '数字输入', icon: 'number', category: 'basic' }); // 选择组件 this.register('select', { label: '下拉选择', icon: 'select', category: 'select' }); this.register('radio', { label: '单选框', icon: 'radio', category: 'select' }); this.register('checkbox', { label: '复选框', icon: 'checkbox', category: 'select' }); // 日期时间组件 this.register('date', { label: '日期', icon: 'calendar', category: 'datetime' }); this.register('datetime', { label: '日期时间', icon: 'clock', category: 'datetime' }); this.register('daterange', { label: '日期范围', icon: 'date-range', category: 'datetime' }); // 文件上传组件 this.register('upload', { label: '文件上传', icon: 'upload', category: 'upload' }); // 布局组件 this.register('grid', { label: '格栅布局', icon: 'grid', category: 'layout', isLayout: true }); this.register('divider', { label: '分割线', icon: 'divider', category: 'layout', isLayout: true }); } register(type, config) { this.components.set(type, config); } getComponent(type) { return this.components.get(type); } getAll() { return Array.from(this.components.values()); } }
拖拽实现
使用 HTML5 Drag and Drop API 实现组件的拖拽放置:
drag-handler.js
class DragHandler { constructor(canvas) { this.canvas = canvas; this.draggedComponent = null; this.init(); } init() { // 组件面板的拖拽源 const sources = document.querySelectorAll('.component-source'); sources.forEach(source => { source.addEventListener('dragstart', (e) => { this.draggedComponent = { type: source.dataset.type, label: source.dataset.label }; e.dataTransfer.setData('text/plain', JSON.stringify(this.draggedComponent)); e.dataTransfer.effectAllowed = 'copy'; }); }); // 画布区域 this.canvas.addEventListener('dragover', (e) => { e.preventDefault(); e.dataTransfer.dropEffect = 'copy'; // 显示放置指示器 const dropPosition = this.getDropPosition(e); this.showDropIndicator(dropPosition); }); this.canvas.addEventListener('dragleave', () => { this.hideDropIndicator(); }); this.canvas.addEventListener('drop', (e) => { e.preventDefault(); const data = JSON.parse(e.dataTransfer.getData('text/plain')); const dropPosition = this.getDropPosition(e); // 添加新组件到画布 this.addComponentToCanvas(data, dropPosition); this.hideDropIndicator(); }); } getDropPosition(e) { const rect = this.canvas.getBoundingClientRect(); const y = e.clientY - rect.top + this.canvas.scrollTop; return { y }; } addComponentToCanvas(component, position) { const field = { id: generateFieldId(), type: component.type, label: component.label, required: false }; // 触发自定义事件,通知画布更新 this.canvas.dispatchEvent(new CustomEvent('component:add', { detail: { field, position } })); } }
属性配置面板
属性面板根据选中的组件动态显示配置项:
property-panel.js
class PropertyPanel { constructor(container) { this.container = container; this.currentField = null; this.render(); } setField(field) { this.currentField = field; this.render(); } render() { if (!this.currentField) { this.container.innerHTML = '请选择一个组件'; return; } const field = this.currentField; const component = ComponentRegistry.getComponent(field.type); this.container.innerHTML = `class="property-group">${this.renderAdvancedProperties(field, component)} `; // 绑定事件 this.bindEvents(); } renderAdvancedProperties(field, component) { // 根据组件类型渲染高级属性 let html = ''; if (field.type === 'select' || field.type === 'radio' || field.type === 'checkbox') { html += `>基本属性
class="property-item"> type="text" value="${field.id}" disabled>class="property-item"> type="text" value="${field.label}" data-prop="label">class="property-item">class="property-item"> type="text" value="${field.placeholder || ''}" data-prop="placeholder">class="property-group">`; } return html; } bindEvents() { const inputs = this.container.querySelectorAll('[data-prop]'); inputs.forEach(input => { input.addEventListener('change', (e) => { const prop = e.target.dataset.prop; let value = e.target.type === 'checkbox' ? e.target.checked : e.target.value; if (prop === 'options') { try { value = JSON.parse(value); } catch {} } this.currentField[prop] = value; emit('field:update', this.currentField); }); }); } }>选项配置
动态数据验证
表单验证支持内置规则和自定义验证器:
form-validator.js
class FormValidator { constructor(schema) { this.schema = schema; this.validators = this.buildValidators(); } buildValidators() { const validators = {}; this.schema.fields.forEach(field => { validators[field.id] = []; // 必填验证 if (field.required) { validators[field.id].push({ validate: (value) => value !== '' && value != null, message: field.errorMessage?.required || `${field.label}不能为空` }); } // 内置规则 if (field.rules) { field.rules.forEach(rule => { if (rule.required) return; if (rule.min !== undefined || rule.max !== undefined) { validators[field.id].push({ validate: (value) => { const len = String(value).length; return (!rule.min || len >= rule.min) && (!rule.max || len <= rule.max); }, message: rule.message || `${field.label}长度应在${rule.min}-${rule.max}之间` }); } if (rule.pattern) { validators[field.id].push({ validate: (value) => new RegExp(rule.pattern).test(value), message: rule.message || `${field.label}格式不正确` }); } }); } // 自定义验证函数 if (field.validator) { validators[field.id].push({ validate: (value) => eval(field.validator)(value), message: field.errorMessage?.custom || `${field.label}验证失败` }); } }); return validators; } validate(values) { const errors = {}; let isValid = true; for (const fieldId in this.validators) { const fieldValidators = this.validators[fieldId]; const value = values[fieldId]; for (const validator of fieldValidators) { if (!validator.validate(value)) { errors[fieldId] = validator.message; isValid = false; break; } } } return { isValid, errors }; } }
总结
表单设计器是 IMS 系统的核心功能之一,良好的设计需要考虑:
- 组件化架构 - 易于扩展新组件
- 可视化拖拽 - 降低使用门槛
- 灵活的属性配置 - 满足业务需求
- 强大的验证机制 - 保证数据质量
- 响应式布局 - 支持多端展示
通过模块化设计和良好的扩展性,表单设计器可以快速响应业务变化,提升开发效率。