项目开发中需要用到 拖拽组件,因为 前端技术框架 是 Vue2 ,
这里就使用了 Vue 的一款 拖拽插件 vue.draggable ,
一般基本的需求都能满足 ,这里使用了多个 draggable 嵌套,达到两级之间相互拖拽的功能。
以下是类似 teambition 的效果图片,最外层为一个 draggable ,可以左右拖动,
然后每个块里面还有小块,小块组成一个 draggable ,
可以上下拖动,也可以在大的块之间拖动。
这里说一下实现原理。
首先,要搞清楚 draggable 的 Api ,
Vue.Draggable 是一款基于 Sortable.js 实现的 Vue 拖拽插件。
支持 移动设备、拖拽 和 选择文本、智能滚动 ,
可以在 不同列表间 拖拽、不依赖 jQuery 为基础、Vue2 过渡动画兼容、支持撤销操作 ,
使用起来特别简单,对被拖拽元素也没有 CSS 样式的特殊要求。
中文文档 :
里面 基本属性 和 方法都有讲到 ,有时间的可以去学习一下,
要查看详细的说明可以去 git 看官方文档说明,
官方文档 :
GitHub - SortableJS/Vue.Draggable: Vue drag-and-drop component based on Sortable.js
这里用到了以下几个属性和事件,着重说明下:
1、group :拖拽分组 ,多组之间相互拖拽 ,可以实现不同数组之间相互拖拽
比如 group 都为 itxst 的组之间才可以相互拖动,
本文例子中分别给两个 draggable 设置了不同的 group 属性,是防止大的被拖到小的块里面去
示例 :group="viewer" 或 group : { name: "viewer", pull: "clone", put: false }
2、sort : 是否开启内部排序( true / false )
3、disable : 通过 disabled 属性实现 开启 或 禁用 vue.draggable 的 拖拽效果
4、animation :通过 animation 属性设置 vue.draggable 过渡效果
这样拖动时 过渡位置 就不会显的太生硬 。
5、handle : 设置(限制)可拖拽区域
6、ghostClass :目标位置占位符的样式及需要停靠位置的样式
7、chosenClass :被选中目标的样式
8、dragClass :拖动元素的样式
9、list :作为 值属性 的 替代 ,list 是一个与 拖放 同步 的 数组
( 比如说, :list = "data" , 就是说,拖拽组件里面的内容数据绑定了 这个 data 值,
一开始这个 data 可能为 [ ] 空数组,但后续通过绑定 @add="addCommand" ,
即可通过拖拽生成事件来将所拖拽内容本身的数据内容添加至 data 数组中去了,
也就是通过拖拽新生成的数据内容会实时更新至 data 里面了,因此 data 也就有了数据 。)
这里在 二级 draggable 使用了该属性 ,因为 两个 draggable 用到了同一数据源 ,
二级 draggable 中用 list 替代 v-model
10、move : 自定义控制那些元素可以拖拽或不允许拖拽并控制是否允许停靠
11、start() : 开始拖动时触发的事件
12、add() :从一个数组拖拽到另外一个数组时触发的事件
11、end() :拖拽完成时的事件
如果想和后端实现动态更新,可以在这个方法里面和后端进行数据交互
tag : 自定义 导航编译生成后的 HTML 标签
然后就是示例 demo 了,这里一些代码段,样式什么的大家可以自由发挥
1、第一步 ,安装 vue.draggable 插件,yarn 或者 npm 都可以
yarn add vuedraggable
npm i -S vuedraggable"vuedraggable": "^2.43.3"
2、第二步 ,使用插件
import draggable from 'vuedraggable'3、注册组件
components: { draggable }
<script>
import draggable from "vuedraggable";
export default {
components: { draggable },
data() {
return {
disabled: false,
// 定义要被拖拽对象的数组
arr1: [
{ id: 1, name: "www.itxst.com(不允许停靠)" },
{ id: 2, name: "www.jd.com" },
{ id: 3, name: "www.baidu.com" },
{ id: 5, name: "www.google.com" },
{ id: 4, name: "www.taobao.com(不允许拖拽)" },
],
};
},
methods: {
// move回调方法
moveCommand(e) {
// 目标是容器不允许停靠
if (
e.relatedContext.element &&
e.relatedContext.element.type === "container"
)
return false;
// 布局容器不允许容器拖拽
if (
e.relatedContext.element &&
e.draggedContext.element.type === "container"
)
return false;
return true;
},
// 开始拖动时触发的事件
startCommand(e) {
console.log("移动中...", e);
},
// 从一个数组拖拽到另外一个数组时触发的事件
addCommand(e) {},
// 拖拽完成时的事件
endCommand(e) {
console.log("结束移动...", e);
},
},
};
</script>
<div>
<draggable class="list-group" group="task" v-model="projectList" @end="draggerEnd()">
<div class="card-list-span" v-for="(process, index) in projectList" :key="process.opListId">
<div class="span-header">
<div class="header-title">
{{ process.opListName }}
</div>
</div>
<div class="span-body">
<div class="body-list">
<draggable
group="project"
:list="process.taskVoList"
@end="taskDraggerEnd()"
>
<div
class="body-list-span"
v-for="task in process.taskVoList"
:key="task.opTaskId"
>
<div>
{{ task.opTaskTitle }}
</div>
<div>{{ task.opTaskHandler }}</div>
</div>
</draggable>
<div slot="footer">
<Author author="yunygl-yylcgl-cjrw">
<div class="add-task" @click="addTask(index)">
<i class="el-icon-plus" style="margin-right: 5px;"></i>
添加任务
</div>
</Author>
</div>
</div>
</div>
</div>
</draggable>
</div>
data() {
return {
projectList: [
{
id: "001",
name: "流程一",
taskList: [
{id: "001001",taskTitle: "任务一"},
{id: "001002",taskTitle: "任务二"}
]
},
{
id: "002",
name: "流程二",
taskList: [
{id: "002001",taskTitle: "任务一"}
]
}
]
}
method: {
// 添加任务
addTask(index) {
this.projectList[index].taskList.push( {id: "00200232",taskTitle: "任务二"})
},
}
项目使用 :