功能需求
在一个表格中按顺序显示图层名称,可以通过拖动表格行的形式改变图层的顺序,通过滑块改变图层透明度。
实现方法
ElementUI的表格不支持拖拽,查找之后决定使用Sortablejs这个JavaScript拖拽库实现。
安装
引入
import Sortable from "sortablejs";
问题
在拖拽表格行后,表格立刻又恢复为原来的顺序。因为vue2.0后引入了虚拟DOM,Sortable拖拽后只改变了真实DOM,虚拟DOM没有发生变化,所以更新后又将真实DOM还原成原来的样子。根本原因是真实DOM和VNode不一致,所以可以通过把拖拽移动真实DOM的操作还原,把DOM的操作交还给Vue。也可以尝试用Vue.Draggable实现。
代码
<template>
<div class="layer-manage-container">
<div class="layer-manage-icon" @click="toggleLayerManage"></div>
<div class="layer-manage-content" v-show="isManageShow">
<div class="layer-manage-title">
<span @click="toggleLayerManage"></span>
</div>
<el-table
class="table-content"
:data="layersData"
:show-header="false"
row-key="id"
align="left"
>
<el-table-column
prop="name"
label="图层名称"
align="left"
header-align="center"
width="100"
></el-table-column>
<el-table-column
label="透明度"
align="left"
header-align="center"
width="100"
>
<template slot-scope="scope">
<el-slider
v-model="scope.row.alpha"
:show-tooltip="false"
:max="1"
:step="0.01"
></el-slider>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script>
import Sortable from "sortablejs";
export default {
props: {
layers: {
type: Array,
default: () => []
},
layerChangeFlag: {
type: Number,
default: 0
}
},
data() {
return {
layersData: [],
col: [
{
label: "名称",
prop: "name"
},
{
label: "透明度",
prop: "alpha"
}
],
isManageShow: false
// dropCol: [
// {
// label: "名称",
// prop: "name"
// },
// {
// label: "透明度",
// prop: "alpha"
// }
// ]
};
},
mounted() {
// 初始化行拖拽
this.rowDrop();
// this.columnDrop();
},
watch: {
/**
* 监听图层改变
*/
layerChangeFlag: function() {
this.getAllowDropLayers();
}
},
methods: {
/**
* 获取可以操作的图层
*/
getAllowDropLayers() {
this.layersData = [];
// console.log("this.layers-->", this.layers);
this.layers.map(layer => {
if (layer.show == true && layer.inManage == true) {
this.layersData.unshift(layer);
}
});
// 返回图层管理中的新数据
this.$emit("layersDataChange", this.layersData);
},
formatTooltip(val) {
return val;
},
/**
* 行拖拽
*/
rowDrop() {
const tbody = document.querySelector(".el-table__body-wrapper tbody");
const _this = this;
let a = Sortable.create(tbody, {
onUpdate: function(event) {
let newIndex = event.newIndex;
let oldIndex = event.oldIndex;
/**
* 解决Vue2.0以后,引入Virtual DOM,导致Virtual DOM和真实DOM之间出现了不一致,使得列表显示与layersData不一致的问题
* Vue2.0前: 拖拽移动真实DOM -> 操作数据数组 -> Patch算法再更新真实DOM
* Vue2.0后:拖拽移动真实DOM -> 还原移动操作 -> 操作数据数组 -> Patch算法再更新真实DOM
*/
let $tr = tbody.children[newIndex];
let $oldTr = tbody.children[oldIndex];
// 先删除移动的节点
tbody.removeChild($tr);
// 再插入移动的节点到原有节点,还原了移动的操作
if (newIndex > oldIndex) {
tbody.insertBefore($tr, $oldTr);
} else {
tbody.insertBefore($tr, $oldTr.nextSibling);
}
//----------------------------------------------------------------------------------------------------------------//
// 更新layersData数组
const currRow = _this.layersData.splice(oldIndex, 1);
_this.layersData.splice(newIndex, 0, currRow[0]);
// 翻转数组
let newArray = [];
for (let i = _this.layersData.length - 1; i >= 0; i--) {
newArray.push(_this.layersData[i]);
}
_this.$emit("layerLevelChange", newArray);
}
});
},
//列拖拽
// columnDrop() {
// const wrapperTr = document.querySelector(".el-table__header-wrapper tr");
// this.sortable = Sortable.create(wrapperTr, {
// animation: 180,
// delay: 0,
// onEnd: evt => {
// const oldItem = this.dropCol[evt.oldIndex];
// this.dropCol.splice(evt.oldIndex, 1);
// this.dropCol.splice(evt.newIndex, 0, oldItem);
// }
// });
// }
reverseArray(oldArray) {
return newArray;
},
toggleLayerManage() {
this.isManageShow = !this.isManageShow;
}
}
};
</script>
<style lang="scss" scoped>
.layer-manage-icon {
width: 54px;
height: 54px;
background-image: url("~@/assets/imgs/layerManage/icon-hide.png");
cursor: pointer;
}
.layer-manage-content {
width: 250px;
position: relative;
top: -50px;
padding: 16px 24px;
background-color: #fff;
box-shadow: 0px 0px 6px 0px rgba(102, 102, 102, 0.25);
box-sizing: border-box;
border-radius: 5px;
.layer-manage-title {
height: 13px;
margin-bottom: 19px;
& > span {
display: block;
width: 24px;
height: 24px;
background-image: url("~@/assets/imgs/layerManage/icon-show.png");
cursor: pointer;
float: right;
}
}
::v-deep .table-content {
cursor: pointer;
&::before {
height: 0;
}
& tr.el-table__row {
& td {
padding: 3px 0;
border: none;
}
}
}
::v-deep .el-slider__button-wrapper {
z-index: 3;
}
}
</style>
参考
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!