组件之间的拖拽
项目中涉及到三种类型的拖拽:
1.el-tree拖拽字段至div,表格;
2.多个div之间的拖拽;
3.表格之间列的拖拽;
实现代码:
1.el-tree拖拽字段至div,表格;

1.el-tree设置字段可拖拽至外部:
<el-tree
ref="tree"
@node-drag-start="handleDragStart"
@node-drag-enter="handleDragEnter"
@node-drag-leave="handleDragLeave"
@node-drag-over="handleDragOver"
@node-drag-end="handleDragEnd"
@node-drop="handleDrop"
draggable
:allow-drop="allowDrop"
:allow-drag="allowDrag"
>
handleDragStart(node, ev) {
let dt = ev.dataTransfer;
ev.dataTransfer.effectAllowed = "copy";
dt.setData("text/plain", JSON.stringify(node.data));
},
handleDragEnter(draggingNode, dropNode, ev) {},
handleDragLeave(draggingNode, dropNode, ev) {},
handleDragOver(draggingNode, dropNode, ev) {},
handleDragEnd(draggingNode, dropNode, dropType, ev) {},
handleDrop(draggingNode, dropNode, dropType, ev) {},
allowDrop(draggingNode, dropNode, type) {
return false;
},
allowDrag(draggingNode) {
return true;
},
2.div及表格设置可接收el-tree拖拽过来的字段信息:
@drop="(e) => handleTargetDrop(e, index)"
@dragover.prevent
// 字段列表拖拽字段至报表列
handleTargetDrop(e, index) {
let data = e.dataTransfer;
if (!this.isJsonString(data.getData("text/plain"))) return;
let content = JSON.parse(data.getData("text/plain"));
let newList = JSON.parse(JSON.stringify(this.listCheckedFields));
newList.splice(index + 1, 0, content);
this.dragRowColumnReportColumns(newList);
e.preventDefault();
// 通常不需要阻止冒泡,但是当出现容器嵌套时最好这么做
// 它可以防止节点被添加到数组中两次
e.stopPropagation();
},
// 判断是否可以转换为json数据
isJsonString(str) {
try {
if (typeof JSON.parse(str) == "object") {
return true;
}
} catch (e) {}
return false;
},
若是用在表格中则drop方法需要添加到表头标签上,事件中可以获取到拖拽过来的字段信息,若是想要拖拽到固定位置,可以在循环的表头或div中传入index来动态插入。
dragRowColumnReportColumns方法用来在获取到拖拽之后的数据集合时,通过请求接口来渲染页面数据。
2.多个div之间的拖拽;

1.组行、组列、报表列这三个div之间可以互相拖拽;可以将这三个div进行分组;
<draggable
v-model="form.choosedColumnFiledsData"
@update="dragRowColumnReportColumns"
@add="dragRowColumnReportColumns"
v-bind="dragOptions"
>
</draggable>
computed: {
dragOptions() {
return {
animation: 300,
group: "description",
ghostClass: "ghost",
chosenClass: "chosen",
};
},
},
// 组行、组列、报表列拖拽
dragRowColumnReportColumns() {
// 数组去重
this.form.chooseReportColumnsData = this.unique(
this.form.chooseReportColumnsData
);
let e = {
chooseRowFiledsData: this.form.chooseRowFiledsData,
choosedColumnFiledsData: this.form.choosedColumnFiledsData,
chooseReportColumnsData: this.form.chooseReportColumnsData,
};
this.$bus.$emit("dragRowColumnReportColumnsBus", e);
},
通过在dragOptions中设置相同的group,可以实现对应div之间的互相拖拽,各自内部也可以拖拽,然后通过v-model绑定拖拽之后的数据,在update和add方法中将拖拽后的数据请求接口,渲染页面。
3.表格之间列的拖拽;

表格的列之间可以互相拖拽;
1.给表格的父元素设置class,给el-table设置v-if:
<div class="draggable">
<el-table
:data="tableData"
border
:fit="true"
:cell-style="cellClass"
v-if="showTable"
>
</el-table>
</div>
因为使用Sortable.create进行拖拽后,表格会直接修改为拖拽之后的,尽管表格绑定的列数据并未改变,所以需要使用v-if来控制表格的重新渲染。
2.表格列进行拖拽时,获取到拖拽之后的数据,请求接口:
// 表格列的拖拽
columnDrop() {
const wrapperTr = document.querySelector(
".draggable .el-table__header-wrapper tr"
);
if (!wrapperTr) return;
this.sortable = Sortable.create(wrapperTr, {
animation: 180,
delay: 0,
dragClass: "chosen",
onEnd: (evt) => {
let newList = JSON.parse(JSON.stringify(this.listCheckedFields));
const oldItem = newList[evt.oldIndex];
newList.splice(evt.oldIndex, 1);
newList.splice(evt.newIndex, 0, oldItem);
// 拖拽之后会改变表格列顺序,目前没办法控制,但数据并未改变,可以重新渲染表格来维持原数据
this.showTable = false;
this.$nextTick(() => {
this.showTable = true;
});
this.dragRowColumnReportColumns(newList);
},
});
},
获取到拖拽之后的数据之后,请求接口,渲染页面。
3.当表头数据改变后,拖拽方法需要重新调用,不然拖拽会不生效:
watch: {
listCheckedFields: {
handler(newVal, oldVal) {
this.$nextTick(() => {
this.columnDrop();
});
},
deep: true,
immediate: true,
},
},
注意:
1.三种拖拽中,第一种拖拽,即从el-tree中将字段拖拽至外部区域这种,仅能获取到拖拽字段的信息,无法获取到拖拽后的数据集合,所以需要根据drop绑定的事件再结合拖拽到目标位置的下标来组合拖拽后的数据;
2.后两种拖拽,即多个div之间的拖拽、el-table之间的拖拽,这两种均可以获取到拖拽之后的数据,这样就可以免去组装数据,直接将拖拽后的数据作为入参请求接口即可。
3.参考:
https://blog.csdn.net/qq_41694291/article/details/101384209
https://www.jianshu.com/p/34f44f2eb668
https://github.com/David-Desmaisons/draggable-example
https://blog.csdn.net/weixin_41192489/article/details/114086578
https://www.cnblogs.com/wisewrong/p/8820508.html

浙公网安备 33010602011771号