同一界面多个设备,需要点击查看详情,且可以同时点开多个div详情框,进行详情数据对比查看。
最初考虑通过el-dialog实现,但是怎么尝试设置属性,都只能一层层弹出,切存在蒙版层难以设置div的层级关系,无法同时弹出多个对话框切换使用。
最后考虑通过v-for控制div的动态渲染来实现。
点击设备按钮和div动态渲染部分
101
102
103
{{ devOne.devCode }}
假设此处有内容
样式设置部分
.just-click {
cursor: pointer;
width: 100px;
height: 40px;
position: fixed;
background: white;
color: black;
line-height: 40px;
text-align: center;
}
.multi-dialog {
position: fixed;
width: 580px;
background: rgba(0,93,172,0.75);
box-shadow: 0px 0px 12px rgba(0,186,255,0.5);
top: 20px;
left: 20px;
z-index: 10;
font-size: 14px;
}
.multi-dialog-title {
padding: 20px;
border: 1px solid rgba(0,93,172,0.75);
border-top: 2px solid rgba(127,255,255);
cursor: move;
font-size: 18px;
}
.multi-dialog-content {
padding: 10px;
}
js动态控制部分
data () {
return {
devDialogs: []
}
}
// 点击设备按钮弹出弹框
clickRect (val) {
// 动态展示设备弹框
let exist = false
this.devDialogs.forEach(element => {
if (val === element.devCode) {
exist = true
}
})
if (!exist) {
let devOne = {
devCode: val,
box: 'box' + val,
title: 'title' + val,
left: '20px',
top: '20px'
}
this.devDialogs.push(devOne)
this.$nextTick(() => {
this.divMove(devOne.title, devOne.box)
})
}
},
// 关闭设备弹框
closeDialog (devOne) {
this.devDialogs.forEach(function (item, index, arr) {
if (item.devCode === devOne.devCode) {
arr.splice(index, 1)
}
})
},
// 移动设备弹框
divMove (titleId, boxId) {
let title = document.getElementById(titleId) // 获取点击元素(可选中拖动的部分)
let box = document.getElementById(boxId) // 获取盒子元素(需要移动的整体)
let divX = 0 // div的x坐标
let divY = 0 // div的y坐标
let isDrop = false // 是否可拖动 按下鼠标置为true 松开鼠标置为false
let self = this
// 将鼠标点击事件绑定在顶部title元素上
title.onmousedown = function (e) {
let el = e || window.event // 获取鼠标位置
divX = el.clientX - box.offsetLeft // 鼠标相对盒子内部的坐标x
divY = el.clientY - box.offsetTop // 鼠标相对盒子顶部的坐标y
isDrop = true // 设为true表示可以移动
document.onmousemove = function (e) {
// 是否为可移动状态
if (isDrop) {
let el = e || window.event
let leftX = el.clientX - divX // 盒子距窗口左边的距离
let leftY = el.clientY - divY // 盒子距窗口顶部的距离
// 盒子不超出窗口的最大移动位置 即拖动置右下角时
let maxX = document.documentElement.clientWidth - box.offsetWidth // 窗口宽度-盒子宽度
let maxY = document.documentElement.clientHeight - box.offsetHeight // 窗口高度-盒子高度
// 当移动到最左最上时,leftX < 0、leftY < 0,盒子左边距、上边距取最大值0
// 当移动到最右最下时,leftX > maxX、leftY > maxY、已超出边界,盒子左边距、上边距取maxX、maxY
leftX = Math.min(maxX, Math.max(0, leftX))
leftY = Math.min(maxY, Math.max(0, leftY))
box.style.left = leftX + 'px'
box.style.top = leftY + 'px'
}
}
document.onmouseup = function () {
// 防止删除上一个div,下一个div挪位到上一个,需要在停止移动时给div赋位置
self.devDialogs.forEach(function (item) {
if (item.box === boxId) {
item.left = box.style.left
item.top = box.style.top
}
})
isDrop = false
document.onmousemove = null
document.onmouseup = null
}
}
}
如果把onmousemove和onmouseup写在onmousedown的外面,会导致后面的div覆盖前面的,isDrop仅仅是最后一个弹框的isDrop标识。
其实不用额外对z-index进行处理就解决了需求,最开始很烦这个z-index咋控制啊很麻烦啊,结果不控制也可以。
.multi-dialog设置position为absolute会局限在上一层底下移动,若要跳出局限到整个窗口,只需设置position: fixed;即可。
有问题的绑定方式:
乍一看似乎功能都很正常,通过数组的index来绑定key确实是唯一的,也不会有报错提示。
但是!!因为我们对devDialogs这个数组进行了增删操作,vue的渲染是根据index来的,这样导致一个现象:
依次点击101、102,关闭101的弹框,再点击101,再移动102发现101会跟着移动,原因是移动102获取到的id竟然是101的,然后两弹框同时进行了移动,问题在于vue.js根据这个:key=index绑定的div,而我们对devDialogs数组进行增删操作时改动了对应div的index……讲不清了,因为我也不知道为什么,只知道就是这个原因,改成如下绑定key值即可。
正确的绑定方式:
结论:
严格根据div的唯一标识绑定key真的很重要!!!