基于虚拟列表封装穿梭组件
element-plus的穿梭组件列表不支持虚拟列表,数据量大时会出现卡顿
因此封装了基于虚拟列表的穿梭框组件
虚拟列表组件:juejin.cn/post/739031…
组件参数:
- data: 全量列表数据
- values:穿梭框右侧的数据
- toLeftText:向左穿梭按钮的文本
- toRightText:向右穿梭按钮的文本
<template>
<div class="flex-container">
<VList
:data="leftList"
:listHeight="200"
:itemHeight="30"
>
<template #header>
<div class="header">
<el-checkbox
v-model="checkAllLeft"
@change="handleCheckAllChangeLeft"
>全选</el-checkbox>
<div>{{checkedLeft}}/{{leftList.length}}</div>
</div>
</template>
<template #default="{item}">
<div class="item">
<el-checkbox
v-model="item.checked"
:label="item.label" size="large"
@change="handleChangeChecked(item)"
:disabled="item.disabled"
/>
</div>
</template>
</VList>
<div class="flex-button">
<el-button type="primary" :disabled="disabledToLeft" @click="handleToLeft">{{toLeftText}}</el-button>
<el-button type="primary" :disabled="disabledToRight" @click="handleToRight">{{toRightText}}</el-button>
</div>
<VList
:data="rightList"
:listHeight="200"
:itemHeight="30"
>
<template #header>
<div class="header">
<el-checkbox
v-model="checkAllRight"
@change="handleCheckAllChangeRight"
>全选</el-checkbox>
<div>{{checkedRight}}/{{rightList.length}}</div>
</div>
</template>
<template #default="{item}">
<div class="item">
<el-checkbox
v-model="item.checked"
:label="item.label" size="large"
@change="handleChangeChecked(item)"
:disabled="item.disabled"
/>
</div>
</template>
</VList>
</div>
</template>
<script setup lang='ts'>
import { ref, computed } from 'vue'
import VList from './list-v.vue'
interface item{
key: number|string,
label: string,
disabled: boolean,
checked: boolean,
isRight: boolean
}
interface PropsType{
data:item[],
values: (number|string)[],
toLeftText?: string,
toRightText?: string
}
const props = withDefaults(defineProps<PropsType>(),{
data: ()=>[],
values: ()=>[],
toLeftText:'<',
toRightText:'>'
})
const emits = defineEmits(['update:values'])
props.data.forEach(i=>{ i.checked = false }) // 全量数据,初始化checked为false
const dataList = computed(()=> props.data.map(item=>{ // 全量数据,根据values设置isRight属性
return {
...item,
isRight:props.values.includes(item.key)
}
}));
const leftList = computed(() => dataList.value.filter(i => !i.isRight))
const rightList = computed(() => dataList.value.filter(i => i.isRight));
const disabledToLeft = computed(() => rightList.value.filter(i => i.checked).length === 0)
const disabledToRight = computed(() => leftList.value.filter(i => i.checked).length === 0)
const checkAllLeft = computed(() => leftList.value.filter(i=>!i.checked && !i.disabled && !props.values.includes(i.key)).length === 0)
const checkAllRight = computed(() => rightList.value.filter(i=>!i.checked && !i.disabled && props.values.includes(i.key)).length === 0)
const checkedLeft = computed(() => leftList.value.filter(i=>i.checked && !props.values.includes(i.key)).length)
const checkedRight = computed(() => rightList.value.filter(i=>i.checked && props.values.includes(i.key)).length)
// 勾选
const handleChangeChecked = (item)=>{
props.data.forEach(i=>{
if(i.key===item.key){
i.checked = item.checked
}
})
}
// 穿梭
const handleToLeft = ()=>{
props.data.forEach(i=>{
if(i.checked && props.values.includes(i.key)){
props.values.splice(props.values.indexOf(i.key),1)
i.checked = false
}
})
}
const handleToRight = ()=>{
props.data.forEach(i=>{
if(i.checked && !props.values.includes(i.key)){
props.values.push(i.key)
i.checked = false
}
})
}
// 全选
const handleCheckAllChangeLeft = (val)=>{
console.log(val)
props.data.forEach(item=>{
if(!item.disabled && !props.values.includes(item.key)){
item.checked = val
}
})
}
const handleCheckAllChangeRight = (val)=>{
console.log(val)
props.data.forEach(item=>{
if(!item.disabled && props.values.includes(item.key)){
item.checked = val
}
})
}
</script>
<style lang="scss" scoped>
.flex-container{
display: flex;
justify-content: space-around;
align-items: center;
margin: 0 10px;
.header{
display: flex;
justify-content: space-between;
color: #303133;
}
.item{
.el-checkbox{
padding: 0 10px;
width: 100%;
&:hover{
background: rgba(40, 147, 248, 0.2);
}
}
}
}
.flex-button{
display: flex;
justify-content: center;
align-items: center;
padding: 0 10px;
}
</style>
使用穿梭组件
<transferV v-model:values="values" :data="data" />
const values = ref([1, 4])
const data = ref([
{
key: 1,
label: '备选项1',
disabled: false,
},
{
key: 2,
label: '备选项2',
disabled: true,
},
{
key: 3,
label: '备选项3',
disabled: false,
},
{
key: 4,
label: '备选项4',
disabled: false,
},
{
key: 5,
label: '备选项5',
disabled: false,
},
{
key: 6,
label: '备选项6',
disabled: false,
},
{
key: 7,
label: '备选项7',
disabled: false,
},
{
key: 8,
label: '备选项8',
disabled: false,
},
])
组件参数配置
| 参数 | 说明 | 类型 | 默认值 | 是否必传 |
|---|---|---|---|---|
| values | 选中值 | Array | [] | 是 |
| data | 数据 | Array | [] | 是 |
| toLeftText | 向左穿梭按钮文本 | String | '>' | 否 |
| toRightText | 向右穿梭按钮文本 | String | '<' | 否 |