vue仿iview穿梭框组件
微信公众号:前端程序猿之路
关注可了解更多的前端知识,反馈问题或建议,请公众号留言。
如果你觉得公众号内容对你有帮助,欢迎关注并转载
穿梭框中左侧为源数据
,右侧为目标数据
,其中左侧包含右侧全部数据,右侧可增加或减少,也可单选或者多选,也可选择数据然后上下移动展示顺序。
之前参考了iview穿梭框
,但是其现有的功能不能满足需求,所以自己重新写了一个穿梭框,在原有的基础上功能上新增了做了一些修改和调整,还有很多不足之处,仅供参考
功能:
1. 左侧数据穿梭到右侧,左侧数据不减少,右侧数据增加
2. 右侧数据穿梭到左侧,右侧数据减少,左侧数据不变,因为左侧已经全部包含右侧的数据了
3. 右侧数据可上下移动,调整展示顺序
4. 左侧和右侧都可以有搜索框进行筛选
效果图:
穿梭框组件代码:
template:
<template>
<div>
<Modal
v-model="modal1"
title="功能列表"
@on-ok="ok"
@on-cancel="cancel" class="content">
<div class="transfer-content">
<div class="selection-container">
<div class="select-box left">
<div class="select-box-title" ref="originalCheckAll">
<input type="checkbox" class="checkbox-all" id="checkbox-all1" @click="originalChooseAll"/>
<label for="checkbox-all1" class="lable-all"></label>
<span class="title">源列表</span>
<span class="totalCount">{{count.originalCount}}</span>
</div>
<div class="select-content">
<Input search clearable placeholder="请输入搜索内容" v-model="originalSearchVal"/>
<ul class="unselect-ul" ref="originalSelect">
<li v-for="item in originalSearch" :key="item.key">
<input type="checkbox" class="checkboxs" :id="item.key" @click="originalchooseItem(item,item.key)"/>
<label :for="item.key"></label>
<span>{{item.label}}</span>
</li>
</ul>
</div>
</div>
<div class="arrows-box">
<div class="arrow-btns">
<button id="leftBtn" class="arrow-btn right" @click="leftBtn">
<span class="leftArrow"></span>
</button>
<button id="rightBtn" class="arrow-btn left" @click="rightBtn">
<span class="rightArrow"></span>
</button>
</div>
</div>
<div class="select-box right">
<div class="select-box-title" ref="targetCheckAll">
<input type="checkbox" class="checkbox-all" id="checkbox-all2" @click="targetChooseAll"/>
<label for="checkbox-all2" class="lable-all"></label>
<span class="title">目标列表</span>
<span class="totalCount">{{count.targetCount}}</span>
</div>
<div class="select-content">
<Input search clearable placeholder="请输入搜索内容" v-model="targetSearchVal"/>
<ul class="selected-ul" ref="targetSelect">
<li v-for="item in targetSearch" track-by="$index" :key="item.id">
<input type="checkbox" class="checkboxs" :id="item.id" @click="targetchooseItem(item,item.key,item.id)"/>
<label :for="item.id"></label>
<span>{{item.label}}</span>
</li>
</ul>
</div>
</div>
<div class="arrows-box">
<div class="arrow-btns">
<button disabled="disabled" id="leftBtn" class="arrow-btn top">
<span class="topArrow"></span>
</button>
<button disabled="disabled" id="rightBtn" class="arrow-btn bottom">
<span class="downArrow"></span>
</button>
</div>
</div>
</div>
</div>
</Modal>
</div>
</template>
script:
<script>
import {randomID} from '../../va/va-biz/utils'
export default {
name: 'v-transfer',
props: {
originalData: {},
targetDataPar: {},
modify: Boolean
},
data () {
return {
modal1: true,
targetData: [],
originalCheckedData: [],
targetCheckedData: [],
originalCheck: false,
originalSearchVal: '',
targetSearchVal: '',
targetCheck: false,
originalCount: '',
targetCount: '',
randomKey: [],
activateUp: false,
activateDown: false,
clearable: true
}
},
computed: {
originalSearch () {
this.copy()
var originalData = this.originalData.filter(ele => {
if (ele.label.match(this.originalSearchVal)) {
return ele
}
})
return originalData
},
targetSearch () {
var targetData = this.targetData.filter(ele => {
if (ele.title.match(this.targetSearchVal)) {
return ele
}
})
return targetData
},
count () {
return {
originalCount: this.originalData.length,
targetCount: this.targetData.length
}
}
},
watch: {
targetData: function (val, val1) {
if (this.modify) {
this.originalCheck = true
this.targetCheck = true
this.originalChooseAll()
this.targetChooseAll()
this.btnColor()
this.$refs.originalCheckAll.children[0].checked = false
this.$refs.targetCheckAll.children[0].checked = false
this.activateUp = this.activateDown = false
this.originalSearchVal = ''
this.targetSearchVal = ''
}
},
originalSearchVal: function () {
this.originalCheck = true
this.originalChooseAll()
this.$refs.originalCheckAll.children[0].checked = false
setTimeout(function () {
let len = document.getElementsByClassName('unselect-ul')[0].children.length
if (len === 0) {
document.getElementsByClassName('unemptyTip')[0].style.display = 'block'
} else {
document.getElementsByClassName('unemptyTip')[0].style.display = 'none'
}
})
},
targetSearchVal: function () {
this.targetCheck = true
this.targetChooseAll()
this.$refs.targetCheckAll.children[0].checked = false
this.activateUp = this.activateDown = false
setTimeout(function () {
let len = document.getElementsByClassName('selected-ul')[0].children.length
if (len === 0) {
document.getElementsByClassName('emptyTip')[0].style.display = 'block'
} else {
document.getElementsByClassName('emptyTip')[0].style.display = 'none'
}
})
},
deep: true,
immediate: true
},
methods: {
copy () {
this.targetData = JSON.parse(JSON.stringify(this.targetDataPar))
},
// 左边数据选择
originalchooseItem (item, key) {
if (JSON.stringify(this.originalCheckedData).indexOf(JSON.stringify(item)) === -1) {
this.originalCheckedData.push(item)
} else {
this.originalCheckedData = this.originalCheckedData.filter(item => {
return item.key !== key
})
}
if (this.originalCheckedData.length === this.originalData.length) {
this.$refs.originalCheckAll.children[0].checked = true
this.originalCheck = true
} else {
this.$refs.originalCheckAll.children[0].checked = false
this.originalCheck = false
}
this.btnColor()
this.modify = false
},
// 右边数据选择
targetchooseItem (item, key, id) {
if (JSON.stringify(this.targetCheckedData).indexOf(JSON.stringify(item)) === -1) {
this.targetCheckedData.push(item)
} else {
this.targetCheckedData = this.targetCheckedData.filter(item => {
return item.id !== id
})
}
if (this.targetCheckedData.length === this.targetData.length) {
this.$refs.targetCheckAll.children[0].checked = true
this.targetCheck = true
} else {
this.$refs.targetCheckAll.children[0].checked = false
this.targetCheck = false
}
this.btnColor()
this.handleSelctedChange(this.targetCheckedData)
this.modify = false
},
// 左边全选中
originalChooseAll () {
this.originalCheck = !this.originalCheck
if (this.originalCheck) {
// 全选中
for (let i = 0; i < this.$refs.originalSelect.children.length; i++) {
this.$refs.originalSelect.children[i].children[0].checked = true
}
// 源数据全部选择
this.originalCheckedData = this.originalData.map(function (element) {
return element
})
} else {
// 清空选中
for (let i = 0; i < this.$refs.originalSelect.children.length; i++) {
this.$refs.originalSelect.children[i].children[0].checked = false
}
// 清空源数据选择
this.originalCheckedData = []
}
this.btnColor()
this.modify = false
},
// 右边全选中
targetChooseAll () {
this.targetCheck = !this.targetCheck
if (this.targetCheck) {
// 全选中
for (let i = 0; i < this.$refs.targetSelect.children.length; i++) {
this.$refs.targetSelect.children[i].children[0].checked = true
}
// 源数据全部选择
this.targetCheckedData = this.targetData.map(function (element) {
return element
})
} else {
// 清空选中
for (let i = 0; i < this.$refs.targetSelect.children.length; i++) {
this.$refs.targetSelect.children[i].children[0].checked = false
}
// 清空源数据选择
this.targetCheckedData = []
}
this.btnColor()
this.modify = false
},
// 数据右移
rightBtn () {
if (this.originalCheckedData.length > 0) {
for (let index in this.originalCheckedData) {
let obj = {
action: {
type: this.originalCheckedData[index].key
},
// id: this.randomArr(500, 0, 1000),
id: randomID(),
type: 'v-tool-item',
title: this.originalCheckedData[index].label
}
this.targetData.unshift(obj)
}
this.originalCheck = true
this.originalChooseAll()
this.$refs.originalCheckAll.children[0].checked = false
this.btnColor()
this.$refs.targetCheckAll.children[0].checked = false
this.$emit('changeData', this.targetData)
this.activateUp = this.activateDown = false
this.modify = false
}
},
// 数据左移
leftBtn () {
if (this.targetCheckedData.length > 0) {
for (let index in this.targetCheckedData) {
this.targetData = this.targetData.filter(item => {
return item.id !== this.targetCheckedData[index].id
})
}
this.targetCheck = true
this.targetChooseAll()
this.$refs.targetCheckAll.children[0].checked = false
this.btnColor()
this.$emit('changeData', this.targetData)
this.modify = false
}
this.handleSelctedChange(this.targetCheckedData)
},
// 左右移按钮颜色修改
btnColor () {
// 右移按钮
if (this.originalCheckedData.length > 0) {
document.getElementById('rightBtn').style.backgroundColor = '#03a4ad'
} else {
document.getElementById('rightBtn').style.backgroundColor = '#f5f7fa'
}
// 左移按钮
if (this.targetCheckedData.length > 0) {
document.getElementById('leftBtn').style.backgroundColor = '#03a4ad'
} else {
document.getElementById('leftBtn').style.backgroundColor = '#f5f7fa'
}
this.modify = false
},
// len生成数组的长度,min生成数最小值,max生成数的最大值
randomArr (len, min, max) {
// 可生成数的范围小于数组长度
if ((max - min) < len) {
return null
}
while (this.randomKey.length < len) {
var num = this.randomNum(min, max)
if (this.randomKey.indexOf(num) === -1) {
this.randomKey.push(num)
return num
}
}
},
randomNum (min, max) {
let S4 = () => (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1)
return S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4()
// return Math.floor(Math.random() * (max - min) + min)
},
moveUpOrDown (actionType) {
const tempKey = this.targetData[this.selectedKeyIndex]
if (actionType === 'up') {
this.targetData[this.selectedKeyIndex--] = this.targetData[this.selectedKeyIndex]
} else {
this.targetData[this.selectedKeyIndex++] = this.targetData[this.selectedKeyIndex]
}
this.targetData[this.selectedKeyIndex] = tempKey
this.targetData = [...this.targetData]
this.activateUpOrDown()
this.modify = false
},
handleSelctedChange (targetSelectedKeys) {
if (targetSelectedKeys.length !== 1) {
this.activateUp = this.activateDown = false
return
}
this.selectedKeyIndex = this.targetData.indexOf(targetSelectedKeys[0])
this.activateUpOrDown()
this.modify = false
},
activateUpOrDown () {
this.activateUp = this.activateDown = true
this.selectedKeyIndex === 0 && (this.activateUp = false)
this.selectedKeyIndex === this.targetData.length - 1 && (this.activateDown = false)
this.$emit('changeData', this.targetData)
this.modify = false
}
}
}
</script>
style
<style scope>
*{
padding:0;
margin:0;
}
.transfer-content{
display: inline-block;
position: relative;
line-height: 1.5;
}
li{
list-style:none;
cursor:pointer;
position: relative;
}
.selection-container{
height: 282px;
}
.select-box{
width: 180px;
border: 1px solid #ebeef5;
border-radius: 6px;
display: inline-block;
font-size: 12px;
left: 15px;
height: 283px;
position: relative;
float: left;
}
.arrows-box{
float: left;
display: inline-block;
position: relative;
line-height: 1.5;
top: 35%;
}
.select-content{
width: 100%;
overflow: hidden;
height: 86%;
}
.select-box-title{
height:40px;
line-height:40px;
font-size:16px;
font-family:"微软雅黑";
padding:0 6%;
display: flex;
align-items: center;
position: relative;
width: 100%;
background: #f9fafc;
border-bottom: 1px solid #ebeef5;
box-sizing: border-box;
color: #000;
}
/*.checkbox-all{
float:left;
}*/
.select-box-title label:before {
left: -14px!important;
top: 4px!important;
}
.select-box-title label:after {
left: -10px!important;
top: 5px!important;
}
.checkboxs{
vertical-align:middle;
}
.unselect-ul{
padding:8px 0 10px 0;
overflow: auto;
height: 87%;
}
.selected-ul{
padding:8px 0 10px 0;
overflow: auto;
height: 87%;
}
.select-content li{
padding:5px 15px;
font-size:12px;
font-family:"微软雅黑";
overflow: hidden;
text-overflow: ellipsis;
}
/* .select-content .ivu-input{ */
/* border-radius: 0% !important; */
/* border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px; */
/* } */
.select-content .ivu-input-icon{
line-height: 29px;
right: 2px;
}
.select-content .ivu-input-wrapper{
padding: 5px 8px 0px;
}
.select-content .ivu-input{
height: 29px;
}
.select-box input[type='checkbox'] {
position: absolute;
left: 13px;
top: 9px;
width: 20px;
height: 20px;
opacity: 0;
}
.select-box label {
position: absolute;
left: 30px;
top: 12px;
height: 20px;
line-height: 20px;
}
.select-box span{
padding-left: 34px;
white-space: nowrap;
}
.select-box label:before {
content: '';
position: absolute;
left: -14px;
top: -3px;
width: 16px;
height: 16px;
border: 1px solid #ddd;
border-radius: 2px;
transition: all 0.3s ease;
-webkit-transition: all 0.3s ease;
-moz-transition: all 0.3s ease;
background-color: #fff;
}
.select-box label:after {
content: '';
position: absolute;
left: -8px;
top: 0px;
width: 4px;
height: 8px;
border: 2px solid #fff;
background: #fff;
border-top: 0;
border-left: 0;
transform: rotate(45deg) scale(1);
transition: all .2s ease-in-out;
cursor: pointer;
}
.select-box .select-box-title label:after {
content: '';
position: absolute;
left: -8px !important;
top: 7px !important;
width: 4px;
height: 8px;
border: 0;
border: 2px solid #fff;
border-top: 0;
border-left: 0;
background: #fff;
transform: rotate(45deg);
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transition: all 0.3s ease;
-webkit-transition: all 0.3s ease;
-moz-transition: all 0.3s ease;
}
.select-box input[type='checkbox']:checked + label:before {
background: #03a4ad;
border-color: #03a4ad;
cursor: pointer;
}
.select-box input[type='checkbox']:checked + label:after {
background: #03a4ad;
cursor: pointer;
}
.arrow-btns{
display: inline-block;
margin: 0px 3px 0px 40px;
min-width: 24px;
}
.btn-cursor{
background-color: #409eff !important;
border:1px solid #409EFF !important;
transition: all 0.3s ease;
-webkit-transition: all 0.3s ease;
-moz-transition: all 0.3s ease;
}
.btn-cursor svg{
fill:#fff !important;
transition: all 0.3s ease;
-webkit-transition: all 0.3s ease;
-moz-transition: all 0.3s ease;
}
/*.btn-cursor{
cursor: not-allowed !important;
}*/
.arrow-btn{
display: block;
position: relative;
width:40px;
height:31px;
background:#eee;
margin:0 auto 5px;
cursor:pointer;
border: 1px solid #dcdfe6;
outline: none;
border-radius: 3px;
color: #c5c8ce;
background-color: #f7f7f7;
border-color: #dcdee2;
}
.arrow-btn svg{
padding: 11px;
width:17px;
height: 17px;
color: white;
fill:#C0C4CC;
}
.leftArrow:before {
content: "\F115";
font-size: 14px;
color: #d9dde7;
display: inline-block;
font-family: Ionicons;
speak: none;
font-style: normal;
font-weight: 400;
font-variant: normal;
text-transform: none;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
vertical-align: middle;
}
.rightArrow:before {
content: "\F11F";
font-size: 14px;
color: #d9dde7;
display: inline-block;
font-family: Ionicons;
speak: none;
font-style: normal;
font-weight: 400;
font-variant: normal;
text-transform: none;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
vertical-align: middle;
}
.topArrow:before {
content: "\F124";
font-size: 14px;
color: #ccd0d8;
display: inline-block;
font-family: Ionicons;
speak: none;
font-style: normal;
font-weight: 400;
font-variant: normal;
text-transform: none;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
vertical-align: middle;
cursor: not-allowed;
}
.downArrow:before {
content: "\F116";
font-size: 14px;
color: #ccd0d8;
display: inline-block;
font-family: Ionicons;
speak: none;
font-style: normal;
font-weight: 400;
font-variant: normal;
text-transform: none;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
vertical-align: middle;
}
.top{
cursor: not-allowed !important;
}
.bottom{
cursor: not-allowed !important;
}
#leftBtn{
margin-bottom: 12px;
}
.lable-all{
top:7px !important;
}
.title{
padding-left: 39px !important;
}
.totalCount{
max-width: 16px;
padding: 0 !important;
margin-left: 36px;
}
.emptyTip{
text-align: center;
color: #c5c8ce;
font-size: 14px !important;
display: none;
}
.unemptyTip{
text-align: center;
color: #c5c8ce;
font-size: 14px !important;
display: none;
}
</style>