先上一张效果图,然后再上代码(由于只做效果,未做数据相关的处理:实际处理数据时不修改 dom 元素,只是利用 dom 元素传递数据,然后需改数据,靠数据驱动效果)
<div :id="index+'_morning'" style="min-height: 20px;" @drop='drop($event)' @dragover='allowDrop($event)'> <li style="padding:0 0 5px 0"> <span class="type-icon time">上</span>上午(morning) </li> <template v-if="item.morning !== undefined"> <template v-for="(itemSon,idIdx) in item.morning"> <li class="item ui-sortable-handle" :id="index+'_morning_'+idIdx" draggable='true' @dragstart='drag($event)' @drop='drop($event)' @dragover='allowDrop($event)'> <span class="destination-name"> <em class="ball ball-red">{{itemSon.node_sort}}</em> <a href="javascript:;" class="item-name">{{itemSon.name}}</a> <NodeSetMore @click="onEventPoi(itemSon)" :poi="itemSon" v-on:poiOperate="onPoiOperate"></NodeSetMore> <DisplayIcon :poi="itemSon"></DisplayIcon> </span> </li> </template> </template> </div>
methods: { // 拖拽相关 drag:function(event){ console.log('拖动事件', event) this.dom = event.currentTarget }, drop:function(event){ event.preventDefault(); // 组织事件的传播(防止冒泡,节点向节点容器冒泡,因为都监听了此事件) event.cancelBubble = true; console.log('源对比', event.target, event.currentTarget) console.log('目标id', event.currentTarget.id) // 为event.currentTarget(点击事件本身) // 分割id 2_morning格式为容器;2_morning_0为容器下的节点: const idPath = event.currentTarget.id.split('_'); if (idPath.length === 2) { event.currentTarget.appendChild(this.dom); } else if(idPath.length === 3) { // 获取当前排序第几,然后插在这个元素之后 event.currentTarget.parentNode.appendChild(this.dom); // 重新改变顺序data中的数组顺序 this.againSort() } else { console.log("暂不处理", event.target) } //// 数据处理阶段,如果是节点容器,直接插在最前面,如果是节点,则放在这个节点后面 // // 不改变dom节点,直接改变数据 // this.tripList['1']['morning'].splice(0, 1); // this.againSort() }, allowDrop:function(event){ event.preventDefault(); // 组织事件的传播 event.cancelBubble = true; } }
主要关注点在于 event.target 和 event.currentTarget 的理解,一个是事件触发时点击的元素(如:span),一个是事件触发时点击绑定事件的元素(如:li @drop)