用VueDraggable轻松实现拖拽表单和布局搭建:新手也能搞定的低代码方案
如果你做过网页开发,肯定遇到过这样的需求:让用户能自己拖拽组件,拼出一个表单或者页面布局。比如常见的问卷调查系统、网站后台的页面编辑器等。如果用原生JavaScript实现拖拽功能,需要处理大量事件监听、坐标计算,代码复杂容易出错。而 VueDraggable 是一个现成的Vue组件,它帮我们封装好了拖拽的核心逻辑,只需要简单配置
一、为什么要用VueDraggable?
如果你做过网页开发,肯定遇到过这样的需求:让用户能自己拖拽组件,拼出一个表单或者页面布局。比如常见的问卷调查系统、网站后台的页面编辑器等。
如果用原生JavaScript实现拖拽功能,需要处理大量事件监听、坐标计算,代码复杂容易出错。而 VueDraggable 是一个现成的Vue组件,它帮我们封装好了拖拽的核心逻辑,只需要简单配置就能实现以下功能:
- 拖拽排序:列表项自由调整顺序
- 跨容器拖拽:把元素从一个区域拖到另一个区域
- 自动同步数据:拖拽后的顺序自动更新到数据中
二、30分钟快速上手
2.1 安装组件
在项目中运行以下命令安装:
# npm管理器
npm install vuedraggable
# yarn管理器
yarn add vuedraggable
注:不同版本可能有不同的回调方法及回调参数,如非最新版本,需参考官方文档使用,官方文档链接于本文文末。
2.2 基础示例:拖拽列表
<template>
<!-- 一个可以拖拽的列表 -->
<draggable
v-model="myList"
item-key="id"
>
<template #item="{element}">
<div class="item">
{{ element.name }}
</div>
</template>
</draggable>
</template>
<script>
import draggable from 'vuedraggable'
export default {
components: { draggable },
data() {
return {
// 初始列表数据
myList: [
{ id: 1, name: '苹果' },
{ id: 2, name: '香蕉' },
{ id: 3, name: '橘子' }
]
}
}
}
</script>
效果:列表中的水果可以自由拖拽排序,且 myList 数据会自动更新!
三、实战案例:拖拽生成表单
3.1 场景说明
我们要做一个表单设计器:左侧是组件列表(输入框、下拉框等),用户拖拽到右侧画布后,自动在数据库中创建对应字段,同时于右侧生成对应的组件。
[左侧组件列表] [右侧画布]
▼ 输入框 +-------------------+
▼ 下拉框 | 已拖入的组件会 |
▼ 单选按钮 | 自动出现在这里 |
| 并保存到数据库! |
+-------------------+
3.2 实现步骤
3.2.1:准备组件库
<template>
<!-- 左侧组件库 -->
<div class="component-panel">
<h3>表单组件库</h3>
<draggable
:list="componentList"
:group="{ name: 'form', pull: 'clone' }"
:sort="false"
>
<template #item="{ element }">
<div class="component-item">
<i class="icon">{{ element.icon }}</i>
{{ element.label }}
</div>
</template>
</draggable>
</div>
</template>
<script>
export default {
data() {
return {
// 预定义可拖拽的组件
componentList: [
{
type: 'input',
label: '文本输入框',
icon: '📝',
fieldName: '' // 等待用户填写字段名
},
{
type: 'select',
label: '下拉选择框',
icon: '🔽',
options: ['选项1', '选项2']
}
]
}
}
}
</script>
说明:group 的 pull: 'clone' 表示拖拽时复制组件,而不是移动
3.2.2:接收拖拽区域
<template>
<!-- 右侧画布 -->
<div class="canvas">
<h3>表单设计区</h3>
<draggable
v-model="droppedComponents"
group="form"
@end="handleDrop"
>
<template #item="{ element }">
<!-- 动态渲染不同组件 -->
<div class="form-item">
<label>{{ element.label }}</label>
<input
v-if="element.type === 'input'"
type="text"
:placeholder="element.placeholder"
>
<select v-else-if="element.type === 'select'">
<option v-for="(opt, i) in element.options" :key="i">
{{ opt }}
</option>
</select>
</div>
</template>
</draggable>
</div>
</template>
<script>
export default {
data() {
return {
// 存放用户拖入的组件
droppedComponents: []
}
},
methods: {
// 拖拽结束时触发
async handleDrop(evt) {
const newComponent = this.droppedComponents[evt.newIndex];
// 调用接口保存到数据库
const res = await axios.post('/api/save-field', {
type: newComponent.type,
fieldName: newComponent.fieldName,
options: newComponent.options
});
// 给组件绑定数据库生成的ID
newComponent.dbId = res.data.id;
}
}
}
</script>
3.3 常见问题
Q1:如何避免重复字段名?
在拖拽结束后检查字段名是否已存在:
handleDrop(evt) {
const newField = this.droppedComponents[evt.newIndex];
// 检查重复
const isExist = this.droppedComponents.some(
item => item.fieldName === newField.fieldName
);
if(isExist) {
alert('字段名不能重复!');
this.droppedComponents.splice(evt.newIndex, 1); // 移除重复项
return;
}
// ...保存到数据库
}
Q2:如何修改组件属性?
添加双击编辑功能:
<template #item="{ element }">
<div
class="form-item"
@dblclick="showEditPanel(element)"
>
<!-- 组件内容 -->
</div>
</template>
<script>
methods: {
showEditPanel(component) {
this.$prompt('请输入字段名', '编辑', {
inputValue: component.fieldName
}).then(name => {
component.fieldName = name;
});
}
}
</script>
注:本文示例仅使用部分原生组件,如需使用自定义组件或更高级的组件,可于VueDraggable的item插槽中添加更多判断条件即可,使用方式类似。
四、进阶案例:栅格化布局
4.1 目标效果
用户可以通过拖拽调整布局区块的位置和大小,类似下图:
+-----------+-----------+
| 区块1 | 区块2 |
+-----------+-----------+
| 区块3 |
+----------------------+
4.2 实现代码
<draggable
v-model="gridLayout"
class="grid-container"
:options="{ animation: 300 }"
>
<template #item="{element}">
<div
class="grid-item"
:style="{
width: element.width + 'px',
height: element.height + 'px'
}"
>
{{ element.content }}
</div>
</template>
</draggable>
<script>
export default {
data() {
return {
gridLayout: [
{ id: 1, width: 200, height: 100, content: '区块1' },
{ id: 2, width: 200, height: 100, content: '区块2' },
{ id: 3, width: 400, height: 200, content: '区块3' }
]
}
}
}
</script>
<style>
.grid-container {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.grid-item {
border: 1px solid #ccc;
padding: 20px;
cursor: move;
}
</style>
五、常见问题解答
Q1:拖拽时样式错乱怎么办?
尝试添加以下CSS:
/* 拖拽时的预览效果 */
.sortable-chosen {
opacity: 0.5;
}
/* 占位符样式 */
.sortable-ghost {
background: #f0f0f0;
}
Q2:如何限制只能横向拖拽?
设置容器为横向排列:
.draggable-container {
display: flex;
overflow-x: auto;
}
Q3:移动端不生效怎么办?
在组件上添加 forceFallback: true 属性:
<draggable :options="{ forceFallback: true }">
VueDraggable常用属性如下:

六、小结
本文内容均为本人闲时编写,内容不涉及公司内部项目代码,仅提供简单操作思路,无复杂实现流程,可为各位提供VueDraggable使用参考以及快速入门,更多功能可参阅官方文档,本文仅演示实现以上两个功能过程中用到的部分。
进一步学习:
官方文档中给出了大量回调函数及组件参数,具体使用可参考:

如果有其他问题欢迎留言讨论,如果本文对你有帮助,请点赞收藏支持作者哦! 😊
更多推荐

所有评论(0)