前言
这个 demo 的衍生也是来自于 朋友的问题
需求是类似于 有一个资源池本身是一个组, 另外有 N 个队伍, 然后每一个队伍 有一个领导, 一组成员, 然后 各个元素互斥
可以从 资源池中拖拽元素, 来配置 各个队伍的人员信息
然后 这里也仅仅是一个 简单的需求的实现, 另外增加了一些 特性
- 领导这边的拖拽是替换式的, 一个队伍已经有一个领导的情况下, 从其他地方拖拽过来的元素会替换掉已有的领导
- 队伍的领导, 成员有最小人员限定, 少于这个人员数量, 则不能再拖拽人员出去
此 demo 基于 vue-draggable, 仅仅用于交流学习
包含两个文件, 一个 HelloDraggable, 一个 DraggableItem, 前者是业务组件, 后者是基础组件
HelloDraggable.vue
<template>
<div class="testParent">
<span style="position: absolute; top:250px; left: 430px; color: green;"> resource pool </span>
<span style="position: absolute; top:250px; left: 1380px; color: red;"> leader </span>
<span style="position: absolute; top:520px; left: 1380px; color: blue;"> members </span>
<DraggableItem group-name="role" ref="resourcePool" drag-item-id="resourcePool" :init-item-list="resourcePool" :min-retain-item-count="0"
@onDragItemAdd="onDragItemAdd" @onDragItemUpdate="onDragItemUpdate"
style="position:absolute; top:300px;left:300px;width: 400px;height: 600px; border: green 1px solid;"
></DraggableItem>
<DraggableItem group-name="role" ref="leaderGroup" drag-item-id="leaderGroup" :init-item-list="leaderGroup" :min-retain-item-count="1"
@onDragItemAdd="onDragItemAdd" @onDragItemUpdate="onDragItemUpdate"
style="position:absolute; top:300px;left:1200px;width: 400px;height: 200px; border: red 1px solid;"
></DraggableItem>
<DraggableItem group-name="role" ref="membersGroup" drag-item-id="membersGroup" :init-item-list="membersGroup" :min-retain-item-count="2"
@onDragItemAdd="onDragItemAdd" @onDragItemUpdate="onDragItemUpdate"
style="position:absolute; top:550px;left:1200px;width: 400px;height: 300px; border: blue 1px solid;"
></DraggableItem>
</div>
</template>
<script>
import draggable from 'vuedraggable'
import DraggableItem from './DraggableItem'
export default {
name: "HelloDraggable",
components: {
draggable,
DraggableItem,
},
data() {
return {
resourcePool: [
{id: "01", name: "sheldon"},
{id: "02", name: "leonard"},
{id: "03", name: "howard"},
{id: "04", name: "raj"},
{id: "05", name: "penny"},
],
leaderGroup: [
{id: "06", name: "alpha"}
],
membersGroup: [
{id: "07", name: "beta"},
{id: "08", name: "gamma"},
]
}
},
computed: {},
created() {
},
methods: {
onDragItemAdd(dragContext, itemList) {
console.log("onDragItemAdd - ", dragContext)
let toDragItemId = dragContext.toDragItemId
// 如果是 leaderGroup, 直接替换, 将已有的 item 返回 resourcePool
if (toDragItemId === "leaderGroup") {
let newItemId = itemList[dragContext.toDragIndex].id
let oldItemId = itemList[1-dragContext.toDragIndex].id
let oldItem = this.findItemById(oldItemId)
this.$refs.leaderGroup.removeItem(oldItemId)
this.$refs.resourcePool.addItem(oldItem)
// 如果是 membersGroup, 则限定最多放入 3 个成员, 默认将最后的成员返回 资源池
} else if (toDragItemId === "membersGroup") {
let membersCountMax = 3
if (itemList.length <= membersCountMax) {
return
}
let toEvictItem = itemList[membersCountMax]
this.$refs.membersGroup.removeItem(toEvictItem.id)
this.$refs.resourcePool.addItem(toEvictItem)
}
},
onDragItemUpdate(dragContext, itemList) {
console.log("onDragItemUpdate - ", dragContext)
},
findItemById(itemId) {
let item = this.findItemByIdInList(this.$data.resourcePool, itemId)
if(item == null) {
item = this.findItemByIdInList(this.$data.leaderGroup, itemId)
}
if(item == null) {
item = this.findItemByIdInList(this.$data.membersGroup, itemId)
}
return item
},
findItemByIdInList(list, itemId) {
for(let i in list) {
if(list[i].id === itemId) {
return list[i]
}
}
return null
}
},
}
</script>
<style>
</style>
DraggableItem.vue
<template>
<div class="draggableItem">
<draggable v-model="itemList" :group="groupName" class="dragGroupClass" animation="200"
@add="onAdd" @update="onUpdate" :move="onMove" >
<transition-group :id="dragItemId" style="display: block; height: 100%" >
<div v-for="entity in itemList" :key="entity.id" class="list-element app-container">
<span class="defaultBlock">{{entity.name}}</span><!---->
<p></p>
<div class="xl-tooltip">
<div class="el-tooltip dv-tooltip-more" aria-describedby="el-tooltip-3283" tabindex="0"
style="overflow: hidden;">
<span style="box-shadow: transparent 0px 0px; color:red; ">
<span :aria-label="entity.name">{{entity.name}}</span>
</span>
</div>
</div>
</div>
</transition-group>
</draggable>
</div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
name: "DraggableItem",
components: {
draggable
},
props: {
dragItemId: {
type: String,
},
minRetainItemCount: {
type: Number,
},
initItemList: {
type: Array,
default: function () {
return []
}
},
groupName: {
type: String,
default: function () {
return "defGroup"
}
}
},
data() {
return {
itemList: []
}
},
created() {
},
mounted() {
this.initItemList.forEach(item => this.itemList.push(item))
},
methods: {
onAdd(evt) {
let dragContext = {
fromDragItemId : evt.from.id,
toDragItemId : evt.to.id,
fromDragIndex : evt.oldDraggableIndex,
toDragIndex : evt.newDraggableIndex
}
this.$emit("onDragItemAdd", dragContext, this.itemList)
},
onUpdate(evt) {
let dragContext = {
fromDragItemId : evt.from.id,
toDragItemId : evt.to.id,
fromDragIndex : evt.oldDraggableIndex,
toDragIndex : evt.newDraggableIndex
}
this.$emit("onDragItemUpdate", dragContext, this.itemList)
},
onMove(evt, originalEvt) {
let dragContext = {
fromDragItemId : evt.from.id,
toDragItemId : evt.to.id,
fromDragIndex : evt.oldDraggableIndex,
toDragIndex : evt.newDraggableIndex
}
if(dragContext.fromDragItemId === dragContext.toDragItemId) {
return true
}
if(this.itemList.length <= this.minRetainItemCount) {
return false
}
return true
},
addItem(item) {
this.itemList.push(item)
},
removeItem(itemId) {
for (let i in this.itemList) {
if (this.itemList[i].id === itemId) {
this.itemList.splice(i, 1)
}
}
}
},
}
</script>
<style>
.dragGroupClass {
width: 100%;
height: 100%;
}
.list-element {
display: inline-block;
background: #eee;
/*float:left;*/
width: 128px;
height: 128px;
}
.app-container {
width: 90px;
padding: 17.0666px 8.5334px;
text-align: center;
color: #fff;
font-size: 14.9334px;
height: 90px;
position: relative;
}
.app-container .defaultBlock {
background: rgb(70, 176, 190);
border-radius: 20%;
width: 60px;
height: 60px;
display: block;
line-height: 65px;
font-size: 22.9333px;
margin: 0 auto;
}
.app-container:hover {
background-color: rgba(0, 0, 0, .2);
cursor: pointer
}
.xl-tooltip {
width: 100%;
word-break: break-all
}
.xl-tooltip .ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
outline: none
}
.xl-tooltip .ellipsis .show-more-ellipsis {
display: -webkit-box;
line-clamp: 2;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
white-space: normal;
overflow: hidden
}
</style>
演示效果
完