項目場景:
項目中有個功能用到穿梭框組件,新版本需要支持穿梭框組件排序,由于element2版本中的穿梭框組件本身不支持排序功能
在此不僅需要支持隨意更換順序,還支持從一側拖拽至另一側,具體功能效果圖如下:
啊啊啊對不住哈哈,GIF過于模糊,自行腦補吧,臨時沒找到免費的視頻轉GIF
穿梭框組件封裝代碼如下
提示:關于拖拽部分代碼是基于其他大佬的基礎上稍作修改,原博文地址:[https://blog.csdn.net/juvenile_Li/article/details/126886010?spm=1001.2014.3001.5506]
Tips1: 此功能使用到了sortablejs插件,請務必先下載本插件再引用下面的代碼,sortablejs安裝命令為:npm install sortablejs --save
Tips2: 下面的代碼中引入的其他方法請自行根據自己的項目進行修改,比如:錯誤提示Messagebox
//先看封裝好的組件,此處我們是加了彈框的,如果不需要自行修改去掉即可
<template><div class="delete-info-dialog"><el-dialog:title="dialogTitle":visible.sync="tableItemDialogVisible":close-on-click-modal="false":close-on-press-escape="false"width="700px":before-close="handleClose"><div class="info-body"><el-transfer ref="transfer"v-model="visibleValue":data="transferData":titles="transferTitle"target-order="push":props="transferProps"@left-check-change="leftCheckChange"@right-check-change="rightCheckChange"><span slot-scope="{ option }"draggable="!option.disabled"@dragstart="drag($event, option)">{{ option.label }}</span></el-transfer></div><span slot="footer" class="dialog-footer"><el-button type="primary" @click="handleConfirm">確 定</el-button></span></el-dialog></div>
</template><script>
import Sortable from 'sortablejs' //記得安裝拖拽組件哦
import cloneDeep from "lodash.clonedeep"; //這個是深拷貝方法,也可以換成自己的,也可以下載lodash,此處為了避免修改父組件傳過來的數據
export default {name: "visible-item-table-dialog",props: {//此處是可以隨意定義彈框的文字,且有默認值dialogTitle: {type: String,default: '顯示列設置'},//此處是可以定義transfer組件的propstransferProps: {type: Object,default: {key: 'key',label: 'label',}},//此處是可以定義transfer組件的titletransferTitle: {type: Array,default: ()=> ['隱藏列', '顯示列']},// 穿梭框總數據transferDataList: {type: Array,default: ()=> []},// 右側顯示列,用戶已經選好保存過的數據,從父組件傳過來回顯visibleTableItemList: {type: Array,default: ()=> []},// 默認顯示列,如果用戶還沒設置過則取這個defaultTableItemList: {type: Array,default: ()=> []},// 控制彈框顯隱tableItemDialogVisible: {type: Boolean,default: false},},data() {return {visibleValue: [],//顯示列表頭數據transferData: [], // 穿梭框數據transferLeftCheckData: [], // 左側選中數據transferRightCheckData: [], // 右側選中數據draggingKey: '', // 當前拖拽項}},mounted() {this.transferData = this.transferDataList;this.visibleValue = this.visibleTableItemList.length>0 ?cloneDeep(this.visibleTableItemList) : cloneDeep(this.defaultTableItemList);// 此處加nextTick 為了保證順利取到refsthis.$nextTick(()=>{const transfer = this.$refs.transfer.$el;const leftPanel = transfer.getElementsByClassName('el-transfer-panel')[0].getElementsByClassName('el-transfer-panel__body')[0]const rightPanel = transfer.getElementsByClassName('el-transfer-panel')[1].getElementsByClassName('el-transfer-panel__body')[0]const rightEl = rightPanel.getElementsByClassName('el-transfer-panel__list')[0]Sortable.create(rightEl, {onEnd: evt => {const { oldIndex, newIndex } = evtconst temp = this.visibleValue[oldIndex]if (!temp || temp === 'undefined') {return} // 解決右邊最后一項從右邊拖左邊,有undefined的問題// this.$set(this.visibleValue, oldIndex, this.visibleValue[newIndex]) //這種賦值方法會導致數據更新視圖未更新,排序后順序展示錯亂,強制更新也無效,原博主是這么賦值的,僅供參考// this.$set(this.visibleValue, newIndex, temp)let _arr = this.visibleValue.splice(oldIndex, 1)this.visibleValue.splice(newIndex, 0, _arr[0])}})// 目前只讓右側支持拖拽順序即可,左側暫時注釋const leftEl = leftPanel.getElementsByClassName('el-transfer-panel__list')[0]Sortable.create(leftEl, {onEnd: evt => {const { oldIndex, newIndex } = evtconst temp = this.transferData[oldIndex]if (!temp || temp === 'undefined') {return} // 解決右邊最后一項從左邊拖右邊,有undefined的問題 let _arr = this.transferData.splice(oldIndex, 1)this.transferData.splice(newIndex, 0, _arr[0])}})// 關于左側拖拽至右側的功能,在本項目中暫時無法實現,經檢測drag事件未觸發(除了本項目外都可),后續再研究^_^leftPanel.ondragover = ev => ev.preventDefault()leftPanel.ondrop = ev => {ev.preventDefault()// 往左拉const index = this.visibleValue.indexOf(this.draggingKey)if (index !== -1) {// 如果當前拉取的是選中數據就將所有選中的數據拉到左邊選中框內if (this.transferRightCheckData.indexOf(this.draggingKey) !== -1) {// 此處為多選執行this.transferRightCheckData.reduce((arr, item) => {if (arr.indexOf(item) !== -1) {// 每次計算將相同的刪掉arr.splice(arr.indexOf(item), 1)}return arr}, this.visibleValue)this.transferRightCheckData = []// 清除右側選中的 不然下次向左拉取時會有緩存// 否則就只拉取當前一個} else {this.visibleValue.splice(index, 1)}}}rightPanel.ondragover = ev => ev.preventDefault()rightPanel.ondrop = ev => {ev.preventDefault()if (!this.draggingKey || this.draggingKey === 'undefined') {return} // 解決右邊最后一項從左邊拖右邊,有undefined的問題// 右邊框里沒有當前key值的時候 向右拉if (this.visibleValue.indexOf(this.draggingKey) === -1) {// 此處為多選執行// 如果當前拉取的是選中數據就將所有選中的數據拉到右邊選中框內if (this.transferLeftCheckData.indexOf(this.draggingKey) !== -1) {this.visibleValue =this.visibleValue.concat(this.transferLeftCheckData)this.transferLeftCheckData = [] // 清除左側選中的 不然下次向右拉取時會有緩存} else {// 否則就只拉取當前一個this.visibleValue.push(this.draggingKey)}}}})},methods: {//關閉彈框handleClose(){this.$emit("handleCloseTableItem");},//點擊確定按鈕handleConfirm(){if(this.visibleValue.length<=0){this.$message({message: '未選中任何需要顯示的數據',type: 'warning'});return;}this.$emit("handleConfirmVisible",this.visibleValue)},drag(ev, option) {// 賦值當前拖拽的唯一標識this.draggingKey = option[this.transferProps.key]},leftCheckChange(val) {// 穿梭框左側多選選中this.transferLeftCheckData = [...val]},rightCheckChange(val) {// 穿梭框右側多選選中this.transferRightCheckData = [...val]},},
}
</script><style lang="scss" scoped>
.delete-info-dialog{/deep/ .el-dialog__body{max-height: 400px;overflow-y: auto;}.info-body{color: #909399;padding-top: 20px;padding-bottom: 20px;.success-info{height: 20px;line-height: 20px;}.info-list{.info{padding: 0 10px;line-height: 20px;word-break: break-all;}}}
}
</style>
封裝組件的應用:
提示:上面的代碼是封裝好的支持拖拽的穿梭框,且在彈框內哦,不在彈框內的自己修改下吧O(∩_∩)O哈哈~:
第一步:在需要的頁面引入封裝好的組件
//此處用Button觸發彈框打開,不需要彈框的省略
<el-button @click="tableItemDialogVisible= true">打開封裝好的穿梭框組件彈框</el-button><visible-dialog:default-table-item-list="defaultTableItemList":table-item-dialog-visible="tableItemDialogVisible"v-if="tableItemDialogVisible":visible-table-item-list="visibleTableItemList":transfer-data-list="transferDataList":transfer-props="{key: 'property',label: 'label',}"@handleCloseTableItem="handleCloseTableItem"@handleConfirmVisible="handleConfirmVisible"></visible-dialog><script>
import VisibleDialog from "../../../components/visible-dialog/index.vue"; //記得修改地址哦,這里引入的就是上面那個封裝好的組件,地址務必改為自己的哈,文件名也記得改為自己的哈
export default { components: {VisibleDialog },data(){return { tableItemDialogVisible: true,//顯隱彈框defaultTableItemList: ['date', 'invoiceClass', 'invoiceCode', 'invoiceNum', 'isTravel'],//如果visibleTableItemList為空,直接取defaultTableItemList的值,這個邏輯可根據自己需要自行修改transferDataList: [{label: '銷售方名稱', property: 'salename', align:'left', minWidth: 180},{label: '銷售方稅號', property: 'saleaxpayerid', align:'left', minWidth: 180},{label: '購買方稅號', property: 'buytaxnumber', align:'left', minWidth: 180},{label: '發票日期', property: 'date', minWidth: 120,},{label: '發票類型', property: 'invoiceClass', minWidth: 180,},{label: '發票代碼', property: 'invoiceCode', minWidth: 180,},{label: '發票號碼', property: 'invoiceNum', minWidth: 180,},{label: '是否差旅', property: 'isTravel', minWidth: 120},],visibleTableItemList: [],//這個數組的數據是從后來請求回來的,用戶自己設置好順序的數據,如果沒有設置就會去取defaultTableItemList里面的數據,這個demo中咱們就默認為空,假裝用戶還沒設置過哈}},methods: {// 關閉顯示列彈框handleCloseTableItem(){this.tableItemDialogVisible = false;},// 點擊彈框的確認按鈕事件,array就是用戶自己放在右側排好序的數組,handleConfirmVisible(array){console.log("用戶選擇好的右側的數據",array)// 這里就可以向后端發送請求,保存用戶排好序的數據了},}}
</script>
結語:
提示:按照上述代碼復制到自己項目中即可實現頂部GIF中的功能了,如有問題,歡迎留言交流,歡迎大佬指點。