vue2 element el-transfer穿梭框组件支持拖拽及排序 已封装,随取随用

时间:2025-01-19 16:25:56
//先看封装好的组件,此处我们是加了弹框的,如果不需要自行修改去掉即可 <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="!" @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,此处为了避免修改父组件传过来的数据 export default { name: "visible-item-table-dialog", props: { //此处是可以随意定义弹框的文字,且有默认值 dialogTitle: { type: String, default: '显示列设置' }, //此处是可以定义transfer组件的props transferProps: { type: Object, default: { key: 'key', label: 'label', } }, //此处是可以定义transfer组件的title transferTitle: { 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 为了保证顺利取到refs this.$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 } = evt const temp = this.visibleValue[oldIndex] if (!temp || temp === 'undefined') { return } // 解决右边最后一项从右边拖左边,有undefined的问题 // this.$set(, oldIndex, [newIndex]) //这种赋值方法会导致数据更新视图未更新,排序后顺序展示错乱,强制更新也无效,原博主是这么赋值的,仅供参考 // this.$set(, 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 } = evt const 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>