4. 字段级自定义扩展:动态表单设计、自定义字段类型与校验规则配置
好,咱们今天聊点真正能落地的干货。
做MES系统这么多年,我最大的体会就是:没有两个工厂的工艺是完全一样的。你想想看,同样是做电子组装,有的厂要测温度曲线,有的厂要记录锡膏批号,有的厂还要拍X光照片。如果每个需求都改代码,那项目永远交付不了。
所以,字段级自定义扩展就成了MES系统的核心能力。说白了,就是让用户自己定义表单长什么样,要填什么内容,怎么校验。我管这个叫「给系统装上乐高积木」。
核心思想: 把字段的定义权交给业务人员,而不是开发人员。开发人员只负责搭建「字段工厂」,业务人员用这个工厂生产自己需要的「字段产品」。
4.1 动态表单设计:从静态到动态的跨越
传统的MES表单,字段是写死在代码里的。比如一个「生产报工」页面,工单号、工序、数量、操作员,这几个字段是固定的。一旦客户说「我还要加一个备注字段」,开发就得改代码、重新部署。
动态表单的思路完全不同。我们把表单的定义存到数据库里,运行时动态渲染。我习惯用一张「字段定义表」来管理所有可配置字段。
先看核心的表结构设计:
-- 字段定义主表
CREATE TABLE sys_field_definition (
id INT PRIMARY KEY AUTO_INCREMENT,
form_code VARCHAR(50) NOT NULL COMMENT '表单编码,如:PROD_REPORT',
field_key VARCHAR(50) NOT NULL COMMENT '字段唯一标识',
field_label VARCHAR(100) NOT NULL COMMENT '字段显示名称',
field_type VARCHAR(20) NOT NULL COMMENT '字段类型:text/number/select/date',
is_required TINYINT DEFAULT 0 COMMENT '是否必填',
sort_order INT DEFAULT 0 COMMENT '排序号',
default_value VARCHAR(500) COMMENT '默认值',
options_json TEXT COMMENT '下拉选项JSON,仅select类型使用',
validation_rules TEXT COMMENT '校验规则JSON',
ui_config TEXT COMMENT 'UI配置JSON,如宽度、占位符等',
status TINYINT DEFAULT 1 COMMENT '状态:1启用 0禁用',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
嗯,这里要注意:form_code 是关键。一个表单编码对应一个业务场景。比如 PROD_REPORT 是生产报工,QC_CHECK 是质检记录。每个表单编码下可以挂N个自定义字段。
我的经验: 字段定义表里一定要留一个 ui_config 字段。我在项目中遇到过,客户要求某些字段在移动端显示为「大按钮」,在PC端显示为「小输入框」。没有这个字段,你后面会改得很痛苦。
4.2 自定义字段类型:五种核心类型详解
字段类型是动态表单的基石。我一般会预置五种核心类型,覆盖90%以上的业务场景。
| 字段类型 | 类型标识 | 适用场景 | 存储方式 |
|---|---|---|---|
| 文本 | text | 备注、批号、序列号 | VARCHAR |
| 数字 | number | 数量、温度、重量 | DECIMAL |
| 下拉选择 | select | 工位、班组、物料编码 | VARCHAR(存选中值) |
| 日期时间 | date | 生产日期、检验时间 | DATETIME |
| 布尔 | boolean | 是否合格、是否返工 | TINYINT |
每种类型的处理逻辑都不一样。我举个例子,下拉选择类型的 options_json 字段,我习惯这样存:
[
{"label": "SMT车间", "value": "SMT"},
{"label": "DIP车间", "value": "DIP"},
{"label": "组装车间", "value": "ASSEMBLY"}
]
前端拿到这个JSON,直接渲染成下拉框。后端存的时候只存 value 值。这样既灵活又干净。
避坑指南: 我曾经在项目里把下拉选项直接硬编码在代码里,结果客户每换一个物料就要改一次代码。后来我改成从数据库读取,客户自己维护选项列表,再也没找过我。记住:凡是可能变化的数据,都别写死在代码里。
4.3 字段校验规则配置:让数据自己「守规矩」
光有字段还不够,还得有校验。没有校验的动态表单,就像没有红绿灯的十字路口——数据乱得一塌糊涂。
我设计了一套「校验规则引擎」,用JSON配置来描述校验逻辑。来看一个完整的校验规则示例:
{
"required": true,
"minLength": 2,
"maxLength": 50,
"pattern": "^[A-Za-z0-9]+$",
"customValidator": "checkBatchNoUnique",
"errorMessage": {
"required": "批号不能为空",
"minLength": "批号至少2个字符",
"maxLength": "批号最多50个字符",
"pattern": "批号只能包含字母和数字",
"customValidator": "批号已存在,请重新输入"
}
}
这个JSON里,required、minLength 这些是内置校验规则,前端和后端都能解析。customValidator 是自定义校验函数名,需要后端注册对应的校验逻辑。
为什么要有自定义校验?因为有些规则太特殊了。比如「批号不能重复」,这个没法用正则表达式搞定,必须查数据库。我一般会在后端维护一个校验函数注册表:
// 伪代码:校验函数注册表
Map<String, ValidatorFunction> validators = new HashMap<>();
validators.put("checkBatchNoUnique", (value, context) -> {
// 查询数据库,检查批号是否唯一
int count = batchNoMapper.countByBatchNo(value);
return count == 0 ? ValidationResult.success() : ValidationResult.fail("批号已存在");
});
前端渲染时,根据 validation_rules 生成对应的校验逻辑。用户输入时实时反馈,提交时后端再校验一遍。双重保险,数据质量就有保障了。
4.4 动态表单渲染引擎:前端怎么玩?
后端把字段定义和校验规则准备好了,前端怎么渲染呢?我一般用「组件工厂模式」。来看核心逻辑:
// 伪代码:动态表单渲染组件
function DynamicFieldRenderer({ fieldDefinition, value, onChange }) {
const { fieldType, fieldLabel, validationRules, uiConfig } = fieldDefinition;
// 根据字段类型选择组件
switch (fieldType) {
case 'text':
return <TextField
label={fieldLabel}
value={value}
onChange={onChange}
validation={validationRules}
uiConfig={uiConfig}
/>;
case 'number':
return <NumberField
label={fieldLabel}
value={value}
onChange={onChange}
validation={validationRules}
uiConfig={uiConfig}
/>;
case 'select':
return <SelectField
label={fieldLabel}
options={fieldDefinition.optionsJson}
value={value}
onChange={onChange}
validation={validationRules}
uiConfig={uiConfig}
/>;
case 'date':
return <DateField
label={fieldLabel}
value={value}
onChange={onChange}
validation={validationRules}
uiConfig={uiConfig}
/>;
default:
return <div>未知字段类型: {fieldType}</div>;
}
}
这个组件的好处是:新增一种字段类型,只需要加一个 case 分支。业务人员配置好字段定义,前端自动渲染,完全不需要改代码。
我建议: 在开发阶段,给每个字段类型都做一个「预览模式」。我在项目中遇到过,业务人员配置完字段后,非要等部署到测试环境才能看到效果。后来我加了一个实时预览面板,配置完直接看效果,效率提升了好几倍。
4.5 整体架构:一张图看懂字段级自定义扩展
说了这么多,咱们用一张图把整个体系串起来。这张图是我自己画的,涵盖了从配置到渲染的完整链路。
这张图展示了完整的四层架构:配置层 → 存储层 → 服务层 → 展示层。业务人员在配置层定义字段,数据存到数据库,后端引擎负责解析和校验,前端组件工厂负责渲染。每一层各司其职,互不干扰。
4.6 实战中的几个关键决策
最后,分享几个我在实战中踩过的坑和总结的经验:
- 字段缓存一定要做:每次渲染都查数据库,性能扛不住。我习惯用Redis缓存字段定义,变更时刷新缓存。
- 版本管理不能忘:字段定义改错了怎么办?加一个
version字段,支持回滚。我曾经因为没有版本管理,客户误操作导致所有表单乱掉,恢复数据花了一整天。 - 前后端校验要一致:前端校验是为了用户体验,后端校验是为了数据安全。缺一不可。我见过太多只做前端校验的项目,被直接调接口灌了一堆脏数据。
- 预留扩展点:字段类型、校验规则、UI组件,这三个维度都要预留扩展接口。你不知道客户明天会提出什么奇怪的需求。
总结一句话: 字段级自定义扩展,本质上是把「写死」变成「配活」。你给业务人员一个配置界面,他们就能自己长出想要的功能。这才是MES系统真正能落地的关键。
公众号:蓝海资料掘金营,微信deep3321