拖拽排序 —— 提升表单编辑器体验的关键一步
大家好,我是涛哥。在「智枢矩阵」表单编辑器中,我们实现了一个非常实用的功能:用户可以通过拖拽调整表单字段的顺序。这个功能看起来简单,但背后涉及 Vue 响应式更新、DOM 操作与数据同步等细节。
今天我就把这块核心代码拆解出来,分享给大家。无论你是开发表单设计器、看板工具,还是任何需要拖拽排序的场景,都能直接复用。
一、功能需求
用户打开表单编辑器,中间预览区显示所有已添加的字段(如姓名、手机号、邮箱等)。每个字段左侧有一个“拖拽把手”,用户可以按住把手上下拖动,松开后字段顺序立即改变,同时表单的底层数据也随之更新。
效果示意:
智枢矩阵表单生成
二、技术选型
Vue 3 生态中,最成熟的拖拽排序库是 vue-draggable-plus(基于 SortableJS,对 Vue 3 响应式有良好支持)。安装:
npm install vue-draggable-plus
它本质上是一个组件,包裹需要排序的列表项,当用户拖拽结束时自动触发 update 事件,并给出新的顺序。
三、数据结构
预览区的字段用一个响应式数组 fields 存储。每个字段至少包含 id(稳定唯一标识)、label、type 等属性。
import { ref } from 'vue';
const fields = ref([
{ id: 1, label: '姓名', type: 'text', required: true },
{ id: 2, label: '手机号', type: 'tel', required: true },
{ id: 3, label: '邮箱', type: 'email', required: false }
]);
关键点:id 不能使用数组索引,否则拖拽后 Vue 无法正确识别每个字段。
四、组件使用
在预览区模板中,用 VueDraggable 包裹字段列表,设置 item-key 为 id,同时监听 @end 事件(拖拽结束回调,可选)或直接使用 v-model 双向绑定。
<template>
<div class="preview-area">
<VueDraggable
v-model="fields"
item-key="id"
handle=".drag-handle"
@end="onDragEnd"
>
<template #item="{ element }">
<div class="field-item">
<span class="drag-handle">⋮⋮</span>
<div class="field-content">
<label>{{ element.label }}</label>
<input :type="element.type" :placeholder="element.label" />
</div>
</div>
</template>
</VueDraggable>
</div>
</template>
<script setup>
import { VueDraggable } from 'vue-draggable-plus';
import { ref } from 'vue';
// 字段数据
const fields = ref([...]);
const onDragEnd = () => {
// 拖拽结束后,fields 已经被 VueDraggable 内部更新
console.log('新顺序:', fields.value.map(f => f.label));
// 这里可以调用 API 保存新顺序到后端
};
</script>
重要参数说明:
-
v-model:双向绑定数组,拖拽后自动更新顺序。 -
item-key:必填,相当于:key,用于 DIFF 算法。 -
handle:指定拖拽的“把手”元素的 CSS 选择器,只有按住该元素才能拖拽,避免误触输入框。 -
@end:拖拽结束后的回调,可用于保存数据。
五、支持的拖拽范围
vue-draggable-plus 除了支持同列表内排序,还支持多列表之间拖拽、拖拽克隆等高级功能。但在「智枢矩阵」中,我们只需要单列表内排序,因此以上配置就足够。
如果将来需要实现“从左侧字段库拖拽添加字段”,也可以基于同一个库扩展:左侧字段库作为另一个 VueDraggable 组件,设置 group 属性共享同一组,即可跨列表拖拽。
六、踩坑记录
6.1 拖拽把手与输入框冲突
如果不设置 handle,用户拖拽时可能点到输入框,导致拖拽失效。解决办法:给拖拽图标单独设置一个 DOM 元素,并在 VueDraggable 中指定 handle=".drag-handle"。
6.2 动态字段的 id 稳定性
如果字段的 id 在每次渲染时都重新生成(例如使用 Date.now()),则拖拽后 Vue 无法复用 DOM 元素,会出现渲染闪烁。建议使用后端分配的 ID(编辑时)或使用稳定的 uuid 库生成。
6.3 移动端适配
移动端拖拽需要使用 touch 事件,vue-draggable-plus 默认支持,但需要添加 touch-action: manipulation 样式,避免浏览器滚动冲突。
.drag-handle {
touch-action: manipulation;
cursor: grab;
}
七、性能优化
对于字段数量较多的表单(例如超过 50 个字段),拖拽后频繁渲染可能造成卡顿。可以:
-
防抖保存:拖拽结束后再调用接口,而不是每次
@change都请求。 -
虚拟滚动:如果字段超过 200,可结合虚拟滚动库(如
vue-virtual-scroller)优化渲染,但拖拽库需要额外的集成,复杂度较高。
在「智枢矩阵」的实际使用中,单个表单的字段很少超过 30 个,因此性能完全没问题。
八、总结
拖拽排序是一个“锦上添花”的功能,但实现得当能极大提升用户体验。借助 vue-draggable-plus,我们仅用不到 100 行代码就完成了稳定、流畅的拖拽排序,且与 Vue 3 响应式完美结合。
如果你正在开发类似的后台管理工具、低代码平台,不妨试试这个方案。
演示站:https://zhishujuzhen.com
开源仓库:https://gitee.com/zhang-dongtao/zhishu-matrix-open
如果你对拖拽排序或表单编辑器有其他疑问,欢迎在评论区留言交流。

200

被折叠的 条评论
为什么被折叠?



