动态表单设计器开发问题记录
拖拽控件必须深拷贝、fieldId 稳定性、删除选中项后清空状态、vuedraggable 下手动收集 ref——表单设计器的实现细节。
本文是「bylcloud-web 开发问题记录」系列之一。bylcloud-web 是基于 Vue 3 + Vite + Element Plus 的企业后台系统,笔记按「现象、排查、处理、下次注意」记录开发中真实遇到的问题。
记录 1:拖出来的控件必须深拷贝
表单设计器左侧是组件库,中间是画布。左侧控件只是模板,拖到画布以后应该变成一份独立配置。
一开始如果直接把左侧对象放进画布,会出现很隐蔽的问题:拖两个文本框,改第一个的 placeholder,第二个也跟着变。原因是两个控件共享了同一个 props 引用。
后来在拖拽 clone 时统一 cloneDeep(e)。这个细节必须保留,尤其是控件配置越来越复杂以后,浅拷贝很容易漏掉嵌套属性。
记录 2:fieldId 不是可有可无
画布上的每个控件都需要唯一 fieldId。删除、选中、排序、右侧属性配置都依赖它。
当时我遇到过删除控件不稳定的问题。表面看是删除按钮偶发失效,实际是多个控件没有稳定 id,删除时找不到正确 index。后来在从左侧拖到右侧后检查:
if (!item.fieldId) item.fieldId = `field-${new Date().getTime()}`
这个实现能用,但也有隐患:如果极短时间内批量生成,时间戳可能重复。后续如果继续扩展,最好换成更稳的 id 生成方式,比如带随机串或自增计数。
记录 3:当前选中项删除后要清空
设计器右侧配置面板绑定的是当前选中控件。删除控件时,如果删的是当前选中项,但没有清空 currentComp,右侧还会显示旧配置。用户再改配置,实际上改的是一个已经不在画布里的对象。
后来的处理是:删除时判断 currentComp.value.fieldId === e.fieldId,如果相同就设为 null。
这个问题属于 UI 状态问题,不会报错,但会让用户感觉“配置没生效”。
记录 4:vuedraggable 下 ref 需要手动收集
表单保存前要校验每个控件配置是否完整,比如题目名称、必填项、选项等。普通 v-for 下可以比较自然地拿到子组件 ref,但在 vuedraggable 包一层后,ref 收集不太稳定。
项目里用了 refHandler(e, index) 手动把每个 FormItemRenderer 存到数组里。校验时先过滤空值,再执行每个子组件的 validate()。
当时这个问题排查得比较久,因为页面上控件都显示正常,只有点保存时校验不完整。以后遇到类似拖拽组件,不能默认 ref 行为和普通列表完全一样。
记录 5:重复题名要在整体层面校验
单个控件可以校验自己的字段名不能为空,但“题目名称重复”必须在整个表单层面判断。否则每个控件单独看都是合法的,合在一起却会影响提交数据识别。
项目里先收集所有控件校验结果,再用数组检查重复项。如果有重复,返回统一错误:
{ fieldName: duplicates.duplicateItems.join(','), validateMsg: ['题目名称重复'] }
这个处理不复杂,但很实用。动态表单最怕保存一份看似合法、实际后续无法统计的数据。
