vue2 element el-transfer穿梭框组件支持拖拽及排序 已封装,随取随用
//先看封装好的组件,此处我们是加了弹框的,如果不需要自行修改去掉即可
<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>