項目中涉及到三種類型的拖拽:
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