给dragover事件的target盒子都绑定一个id或groupName属性,便于判断元素是否可以拖拽到那个位置
<UxCard class="ux-page-modelSet__container flex-column" inner>
<div :class="['model-set-container-box', isView ? 'model-set-container-box-view' : '']">
<template v-for="(item, index) in modelData">
<div
:key="index"
class="group-title"
@click="handleChangeIndex(index)"
:class="{'different': index === activeIndex && !isView }"
>
<span>{{item.groupName + `(${item.attr.length})`}}</span>
<div class="opt" v-if="!isView">
<Tooltip
placement="top"
content="编辑"
transfer
>
<i class="simo icon-edit" :class="[item.edit ? '' : 'disable']" @click="handleEditTemplate('edit', index, $event, item)"></i>
</Tooltip>
<Tooltip
placement="top"
content="删除"
transfer
>
<i class="simo icon-del" :class="[item.cutable && item.edit ? '' : 'disable']" @click="handleDelTemplate(index, item, $event)"></i>
</Tooltip>
<Tooltip
placement="top"
content="上移"
transfer
>
<i class="simo icon-move-up" :class="[index === 0 || !item.edit ? 'disable' : '']" @click="handleUpTemplate(index, $event, index === 0 || !item.edit)"></i>
</Tooltip>
<Tooltip
placement="top"
content="下移"
transfer
>
<i class="simo icon-move-down" :class="[index === modelData.length - 1 || !item.edit ? 'disable' : '']" @click="handleDownTemplate(index, $event, index === modelData.length - 1 || !item.edit)"></i>
</Tooltip>
</div>
</div>
<div class="card-box"
@dragover="handleDragEnd"
:groupName="modelData[index].groupName"
>
<div class="card-content" v-if="!item.attr.length" :groupName="modelData[index].groupName">
<div class="card-empty" :groupName="modelData[index].groupName">
<div class="name-empty" :groupName="modelData[index].groupName">
新分组至少添加一个属性!
</div>
</div>
</div>
<div class="card-content"
v-else
v-for="(attr, i) in item.attr"
:key="attr.id"
:draggable="!!attr.edit"
@mousedown="handleMouseDown(attr)"
:attrId="attr.id"
>
<div class="card" :attrId="attr.id" :class="{'curser' : !attr.edit}">
<div :attrId="attr.id" class="opt" v-if="attr.edit && !isView" @click="handleDelAttribute(index, i, attr)">×</div>
<div :attrId="attr.id" class="name">
<span :attrId="attr.id" v-if="attr.fieldDefinition.attr==='REQUIRED'">*</span>
{{ attr.fieldName }}
</div>
<div :attrId="attr.id" class="type" v-if="typeMap[attr.fieldType]">{{ typeMap[attr.fieldType][attr.fieldDefinition.format] }}</div>
</div>
</div>
</div>
</template>
<span class="add" @click="handleEditTemplate('add', null, $event)" v-if="!isView">+添加新分组</span>
</div>
<template #bottom v-if="!isView">
<Button @click="handleSave">确认</Button>
<Button cancel @click="handleCancle">取消</Button>
</template>
</UxCard>
用mousedown事件来获取要拖拽的盒子信息:
handleMouseDown(w) {
this.dragItem = w
},
处理拖拽结束事件,注意需阻止浏览器的默认事件:
handleDragEnd(evt) {
if (this.isView){
return
}
if (evt.target.attributes.attrid) {
let fultureId = evt.target.attributes.attrid.nodeValue
if (this.dragItem.id === fultureId) {
return
}
let futureMsg = []
this.modelData.forEach((item, i) => {
item.attr.forEach((attr, index) => {
if (attr.id === fultureId && attr.edit) {
futureMsg = [i, index]
}
})
})
if (!futureMsg.length) {
return
}
let judgeIndex = this.judgeIndex(this.dragItem.id, fultureId)
if (judgeIndex === 'notCommon') {
this.modelData[futureMsg[0]].attr.splice(futureMsg[1], 0,this.dragItem)
this.modelData.forEach((item, index) => {
item.attr.forEach((attr, i) => {
if(attr.id === this.dragItem.id && index !== futureMsg[0]) {
this.modelData[index].attr.splice(i, 1)
}
})
})
} else {
this.modelData.forEach((item, index) => {
item.attr.forEach((attr, i) => {
// 先把原来的删除
if(attr.id === this.dragItem.id) {
this.modelData[index].attr.splice(i, 1)
}
})
})
// 判断一下要删的在前面还是后面,吧要加的加进去
if(judgeIndex[0] > judgeIndex[1]){
this.modelData[futureMsg[0]].attr.splice(futureMsg[1], 0,this.dragItem)
} else {
this.modelData[futureMsg[0]].attr.splice(futureMsg[1] - 1, 0,this.dragItem)
}
}
}
if (evt.target.attributes.groupname) {
let groupName = evt.target.attributes.groupname.nodeValue
this.modelData.forEach((item, index) => {
item.attr.forEach((attr, i) => {
if(attr.id === this.dragItem.id) {
this.modelData[index].attr.splice(i, 1)
}
})
})
this.modelData.forEach((item, index) => {
if(item.groupName === groupName) {
this.modelData[index].attr.push(this.dragItem)
}
})
}
evt.preventDefault();
},
判断要移动的属性和将要移到的那个位置的属性是否是同一组:
// 判断两个属性是否在同一组,并且如果在同一组,哪个后面
judgeIndex(sourceId, targetId) {
let sourceTemplateIndex = -1
let targetTemplateIndex = -1
let sourceAttrIndex = -1
let targetAttrIndex = -1
this.modelData.forEach((item, i) => {
item.attr.forEach((attr, index) => {
if (attr.id === sourceId) {
sourceTemplateIndex = i
sourceAttrIndex = index
}
if (attr.id === targetId) {
targetTemplateIndex = i
targetAttrIndex = index
}
})
})
// 如果是在同一个组,返回同一个组
if (sourceTemplateIndex === targetTemplateIndex) {
return [sourceAttrIndex, targetAttrIndex]
} else {
return 'notCommon'
}
},
modelData:
"groupDef": [
{
"groupName": "基础属性",
"edit": false,
"cutable": false,
"attr": [
{
"id": "202008250000000000000000000000634",
"fieldKey": "name",
"location": 0,
"edit": false,
"cutable": false,
"fieldDefinition": {
"default": "",
"min": "",
"max": "20",
"format": "single",
"attr": "REQUIRED"
},
"fieldType": "Text",
"fieldName": "配置项名称2"
},
{
"id": "202008250000000000000000000000636",
"fieldKey": "name",
"location": 0,
"edit": false,
"cutable": false,
"fieldDefinition": {
"default": "",
"min": "",
"max": "20",
"format": "single",
"attr": "REQUIRED"
},
"fieldType": "Text",
"fieldName": "配置项名称1"
}
]
},
{
"groupName": "基础属性qqqq",
"edit": false,
"cutable": false,
"attr": [
{
"id": "20200825000000000000000000000063qq",
"fieldKey": "name",
"location": 0,
"edit": false,
"cutable": false,
"fieldDefinition": {
"default": "",
"min": "",
"max": "20",
"format": "single",
"attr": "REQUIRED"
},
"fieldType": "Text",
"fieldName": "配置项名称4"
},
{
"id": "2020082500000000000000000000006333",
"fieldKey": "name",
"location": 0,
"edit": false,
"cutable": false,
"fieldDefinition": {
"default": "",
"min": "",
"max": "20",
"format": "single",
"attr": "REQUIRED"
},
"fieldType": "Text",
"fieldName": "配置项名称5"
}
]
}
]
效果图: