vue实现v-for循环出来的dom元素拖拽改变位置、排序
初始效果
实现添加路线效果,点击√,完成添加
路线5已经添加成功
实现拖拽效果,更改路线3和5的位置
代码如下:
<template>
<div class="right-wrap">
<!-- 路线展示区 -->
<div class="all-item-wrap">
<div class="item-wrap"
v-for="(item, index) in lineItems"
:key="`${item.name}-${index}`"
@click="choosedItem(index)"
draggable="true"
ondragstart="event.dataTransfer.setData('text/plain',null)"
:class="{'active-item': currentIndex === index}"
>
<div class="item-info">
{{item.name}}-{{item.interval}}
</div>
<Button class="item-opera" type="primary" size="small">跳转</Button>
</div>
</div>
<!-- 添加路线区 -->
<div class="add-item-wrap" v-if="isAddItem">
<Input class="name" v-model="item.name" placeholder="名称" />
<Input class="interval" v-model="item.interval" placeholder="停留时间(s)" />
<Icon
class="confirm-add"
:size="22"
color="#61beff"
type="md-checkmark"
@click="confirmAddItem()"
/>
</div>
<!-- 添加按钮与删除按钮区 -->
<div class="option-wrap">
<div class="add-icon" @click="isAddItem = true">
<img :src="addIcon">
</div>
<div class="delete-icon" @click="deleteItem()">
<img :src="deleteIcon">
</div>
</div>
</div>
</template>
ts部分
<script lang="ts">
import { Component, Vue, Watch, Prop } from "vue-property-decorator"
@Component({})
export default class autoCruise extends Vue {
// 添加和删除的图标
addIcon: string = require('@/assets/add-icon.png')
deleteIcon: string = require('@/assets/delete-icon.png')
// 控制添加的input框是否出现
isAddItem: boolean = false
// 用来存放路线的数组
lineItems = [
{ id: 1, name: '路线1', interval: 3 },
{ id: 2, name: '路线2', interval: 3 },
{ id: 3, name: '路线3', interval: 3 },
{ id: 4, name: '路线4', interval: 3 },
]
// 单个添加路线的对象
item: {name: string, interval: number} = {
name: '',
interval: 0
}
// 当前index
currentIndex: number = -1
//拖拽的目标对象
dragged!: any
mounted() {
let self = this
// this.getPatrolByStation()
/* 拖动目标元素时触发drag事件 */
// document.addEventListener("drag", function( event ) {
// }, false);
// 当拖拽开始的时候,拖拽目标变成半透明
document.addEventListener("dragstart", function( event: any ) {
// 保存拖动元素的引用(ref.)
self.dragged = event.target;
// 使其半透明
(event.target as any).style.opacity = .5;
}, false);
document.addEventListener("dragend", function( event: any ) {
// 重置透明度
event.target.style.opacity = "";
}, false);
/* 放置目标元素时触发事件 */
document.addEventListener("dragover", function( event ) {
// 阻止默认动作以启用drop
event.preventDefault();
}, false);
document.addEventListener("dragenter", function( event: any ) {
// 当可拖动的元素进入可放置的目标时高亮目标节点
if ( event.target.className == "item-wrap" ) {
event.target.style.background = "purple";
}
}, false);
document.addEventListener("dragleave", function( event: any ) {
// 当拖动元素离开可放置目标节点,重置其背景
if ( event.target.className == "item-wrap" ) {
event.target.style.background = "";
}
}, false);
document.addEventListener("drop", function( event: any ) {
// 阻止默认动作(如打开一些元素的链接)
event.preventDefault();
// 将拖动的元素到所选择的放置目标节点中
if ( event.target.className == "item-wrap" ) {
event.target.style.background = "";
// 要被替换元素的索引
let targetIndex = self.getIndex(event.target)
// 拖拽元素的目标索引
let draggedIndex = self.getIndex(self.dragged)
// 拖拽元素对应的数组中的数据
let arrItem = self.lineItems[draggedIndex]
// 删除原位置的拖拽元素对应的数据
self.lineItems.splice(draggedIndex, 1)
// 添加到指定位置
self.lineItems.splice(targetIndex, 0, arrItem)
// 减少对dom的操作,所以下面这边不不推荐,但是这两步也可以实现与上面同样的效果
// self.dragged.parentNode.removeChild(self.dragged);
// event.target.parentNode.insertBefore(self.dragged, event.target)
}
}, false);
}
获取dom元素的index值
getIndex(element) {
return Array.prototype.slice.call(element.parentNode.children).indexOf(element)
}
// 添加巡航路线
confirmAddItem() {
this.isAddItem = false
let temp = {
id: this.lineItems.length + 1,
name: this.item.name,
interval: this.item.interval
}
this.lineItems.push(temp)
this.item.name = ''
this.item.interval = 0
}
// 选中当前item
choosedItem(index) {
this.currentIndex = index
}
// 删除选中的item
deleteItem() {
if (this.currentIndex === -1) {
this.$Message.warning('请选择你要删除的路线')
} else {
this.lineItems.splice(this.currentIndex, 1)
}
}
}
css部分,使用的less
<style lang="less">
.right-wrap {
flex: 2;
position: relative;
.all-item-wrap {
max-height: 220px;
overflow-y: auto;
.item-wrap {
line-height: 40px;
border: 1px solid #eee;
border-radius: 4px;
margin: 5px;
padding: 0 5px;
.item-info {
display: inline-block;
width: 75%;
}
.item-opera {
display: inline-block;
}
}
.active-item {
border: 1px solid red;
}
}
.option-wrap {
position: absolute;
bottom: 0px;
right: 2px;
.add-icon,
.delete-icon {
cursor: pointer;
display: inline-block;
}
}
.add-item-wrap {
position: absolute;
bottom: 2.9rem;
display: flex;
align-items: center;
.name,
.interval {
flex: 3;
}
.confirm-add {
flex: 1;
cursor: pointer;
}
}
}
</style>