效果展示
方法说明
使用Vue.directive(‘draggable’, draggable)插入自定义指令
<div>
<div class="divA" v-draggable="{edit: false, drag: true, drop: false, clone: true}">
A--拖拽元素
</div>
<div class="divB" v-draggable="{edit: true, drag: true, drop: true, clone: false}">
B--拖入元素
</div>
</div>
import handle from "./handle"
export default {
inserted(el, binding, vnode) {
const { value } = binding
// 添加 contenteditable 属性并设置为 true
if (value && value.edit) {
el.setAttribute('contenteditable', 'true')
}
// 添加 draggable 属性并设置为 true
if (value && value.clone && value.drag) {
el.setAttribute('draggable', 'true')
// 添加克隆事件监听器
el.addEventListener('dragstart', handle.onDragClone);
el.addEventListener('drag', handle.onDrag);
el.addEventListener('dragend', handle.onDragend);
} else if (value && value.drag) {
el.setAttribute('draggable', 'true')
// 添加事件监听器
el.addEventListener('dragstart', handle.onDragStart);
el.addEventListener('drag', handle.onDrag);
el.addEventListener('dragend', handle.onDragend);
}
// 添加可移入事件监听器
if (value && value.drop) {
el.addEventListener('dragover', handle.onDragover);
el.addEventListener('dragenter', handle.onDragenter);
el.addEventListener('dragleave', handle.onDragleave);
el.addEventListener('drop', handle.onDrop);
}
},
unbind(el, binding, vnode) {
console.log('unbind: 只调用一次,指令与元素解绑时调用');
const { value } = binding
// 移除拖拽事件监听器
}
}
方法对比
vue.draggable | v-drag |
---|---|
vue.draggable插件 | 自定义封装 |
![]() | ![]() |
原理是在外层囊括了一层可供操作的div盒子,对部分组件样式有影响,影响到了一些布局调整 | 功能方法不完善,目前只实现封装了拖拽、放下、拷贝、数据删除等逻辑,但是每次拖拽会排布到目标空间的最末尾,如果要实现拖拽排序还需要计算移动略过元素来进行计算(较复杂) |
部分代码
/**
* 被拖拽元素 v-drag="{ data: { name: 'A' }, edit: false, clone: true, drag: true, drop: false, onStart: onStart, onDrag: onDrag, onEnd: onEnd }"
* 容器元素 v-drag="{ data: { name: 'B' }, edit: true, clone: false, drag: true, drop: true, onEnter: onEnter, onOver: onOver, onLeave: onLeave, onDrop: onDrop }"
* 拖动中
* onDrag(e, option=null) { console.log("拖动中 :", e, option) },
* 结束拖动
* onEnd(e, option=null) { console.log("结束拖动:", e, option) },
* 移入容器
* onEnter(e, option=null) { console.log("移入容器:", e, option) },
* 容器内部移动
* onOver(e, option=null) { console.log("内部移动:", e, option) },
* 移出容器
* onLeave(e, option=null) { console.log("移出容器:", e, option) },
* 放下元素
* onDrop(e, option=null) { console.log("放下元素:", e, option) },
*
* 参数说明:
*
* edit 可编辑 true|false
* onChange 变化回调 function()
*
* drag 可拖拽 true|false
* onStart 开始拖拽 function()
* onDrag 拖拽中 function()
* onEnd 结束拖拽 function()
*
* drop 可拖入 true|false
* onEnter 进入容器 function()
* onOver 内部移动 function()
* onLeave 离开容器 function()
* onDrop 放置 function()
*
* clone 可拷贝 true|false
* onClone 发生拷贝 function()
*/
import fun from "./script"
export default {
bind(el, binding) {
const { value } = binding
if (value.hideClidk) {
// 原组件设置属性
el.style.position = 'relative';
// 创建隐藏按钮
const hideDom = document.createElement('div');
hideDom.className = 'system-radius-10'
hideDom.id = 'drag-hide-select-button'
hideDom.style.position = 'absolute';
hideDom.style.right = '0';
hideDom.style.top = '0';
hideDom.style.display = 'none';
hideDom.style.zIndex = '9999'
// 移除原有监听
hideDom.removeEventListener('click', e=>{fun.hideClidk(e, value)})
hideDom.removeEventListener('contextmenu', e=>{fun.hideClidk(e, value)})
// 插入新监听
hideDom.addEventListener('click', e=>{fun.hideClidk(e, value)})
hideDom.addEventListener('contextmenu', e=>{fun.hideClidk(e, value)})
// 原组件插入移入监听
el.addEventListener('mouseenter', e => {
e.stopPropagation()
fun.hideDom('drag-hide-select-button')
hideDom.style.display = 'inline-block'
})
// 原组件插入移出监听
el.addEventListener('mouseleave', e => {
e.stopPropagation()
hideDom.style.display = 'none'
})
el.appendChild(hideDom);
}
},
// 插入元素时触发
inserted(el, binding, vnode) {
// 获取自定义指令的参数
const { value } = binding
// 可编辑
if (value.edit) {
el.setAttribute('contenteditable', 'true')
}
// 可调整大小
if (value.resize) {
// el.setAttribute('resize', 'true')
// resize的属性值为both、horizontal、vertical或none之一
// 不生效
// el.className = el.className?el.className + ' resizable':'resizable'
}
// 可克隆 | 可拖拽
if (value.clone || value.drag) {
el.setAttribute('draggable', 'true')
el.addEventListener('dragstart', e=>{fun.onStart(e, value)})
el.addEventListener('drag', e=>{fun.onDrag(e, value)})
el.addEventListener('dragend', e=>{fun.onEnd(e, value)})
}
if (value.onRight) {
el.addEventListener('contextmenu', e=>{fun.onRight(e, value)})
}
// 添加可移入事件监听器
if (value.drop) {
el.addEventListener('dragenter', e=>{fun.onEnter(e, value)})
el.addEventListener('dragover', e=>{fun.onOver(e, value)})
el.addEventListener('dragleave', e=>{fun.onLeave(e, value)})
el.addEventListener('drop', e=>{fun.onDrop(e, value)})
}
},
// 销毁元素时触发
unbind(el, binding, vnode) {
const { value } = binding
// 移除拖拽事件监听器
if (value.clone || value.drag) {
el.removeEventListener('dragstart', e=>{fun.onStart(e, value)})
el.removeEventListener('drag', e=>{fun.onDrag(e, value)})
el.removeEventListener('dragend', e=>{fun.onEnd(e, value)})
}
if (value.onRight) {
el.removeEventListener('contextmenu', e=>{fun.onRight(e, value)})
}
// 移除可移入事件监听器
if (value.drop) {
el.removeEventListener('dragenter', e=>{fun.onEnter(e, value)})
el.removeEventListener('dragover', e=>{fun.onOver(e, value)})
el.removeEventListener('dragleave', e=>{fun.onLeave(e, value)})
el.removeEventListener('drop', e=>{fun.onDrop(e, value)})
}
}
}