前言

vue2 el-transfer 穿梭框实现多类别


一、效果图

在这里插入图片描述

二、具体实现

1.重写组件

创建一个组件extends至element-ui的Transfer

template

复制Transfer源码中的template,并拓展我们需要的列表2和button

    <div class="el-transfer">
        <transfer-panel v-bind="$props" ref="leftPanel" :data="sourceData" :title="titles[0] || t('el.transfer.titles.0')"
            :default-checked="leftDefaultChecked" :placeholder="filterPlaceholder || t('el.transfer.filterPlaceholder')"
            @checked-change="onSourceCheckedChange">
            <slot name="left-footer"></slot>
        </transfer-panel>
        <div class="el-transfer__buttons">
            <div>
                <el-button type="primary" :class="['el-transfer__button', hasButtonTexts ? 'is-with-texts' : '']"
                    @click.native="addToLeft" :disabled="listTwoChecked.length === 0 && rightChecked.length === 0">
                    <i class="el-icon-arrow-left"></i>
                    <span v-if="buttonTexts[0] !== undefined">{{ buttonTexts[0] }}</span>
                </el-button>
            </div>
            <div>
                <el-button type="primary" :class="['el-transfer__button', hasButtonTexts ? 'is-with-texts' : '']"
                    @click.native="addToRight" :disabled="leftChecked.length === 0">
                    <span v-if="buttonTexts[1] !== undefined">{{ buttonTexts[1] }}</span>
                    <i class="el-icon-arrow-right"></i>
                </el-button>
            </div>
            <div>
            <!-- 新增的button -->
                <el-button type="primary" :class="['el-transfer__button', hasButtonTexts ? 'is-with-texts' : '']"
                    @click.native="addToListTwo" :disabled="leftChecked.length === 0">
                    <span v-if="buttonTexts[1] !== undefined">{{ buttonTexts[2] }}</span>
                    <i class="el-icon-arrow-right"></i>
                </el-button>
            </div>
            <slot name="buttons"></slot>
        </div>
        <transfer-panel v-bind="$props" ref="rightPanel" :data="targetData" :title="titles[1] || t('el.transfer.titles.1')"
            :default-checked="rightDefaultChecked" :placeholder="filterPlaceholder || t('el.transfer.filterPlaceholder')"
            @checked-change="onTargetCheckedChange">
            <slot name="right-footer"></slot>
        </transfer-panel>
        <!-- 新增的list -->
        <transfer-panel class="m-l-20" v-bind="$props" ref="rightPanel" :data="listTwoData"
            :title="titles[2] || t('el.transfer.titles.1')" :default-checked="listTwoDefaultChecked"
            :placeholder="filterPlaceholder || t('el.transfer.filterPlaceholder')"
            @checked-change="onlistTwoCheckedChange">
            <slot name="right-footer"></slot>
        </transfer-panel>
    </div>

script

这里重写了Transfer的addToLeft方法,按着element-ui的逻辑写出第二个列表的逻辑即可。源码就不做解释了,这个组件比较简单,感兴趣的朋友可以去看看

import { Transfer } from 'element-ui'

export default {
    extends: Transfer,
    props: {
        listTwo: {
            type: Array,
            default: () => []
        },
        listTwoDefaultChecked: {
            type: Array,
            default: () => []
        },
    },
    data() {
        return {
            // 列表2选中的数据
            listTwoChecked: []
        };
    },
    computed: {
        listTwoData() {
            if (this.targetOrder === 'original') {
                return this.data.filter(item => this.listTwo.indexOf(item[this.props.key]) > -1);
            } else {
                return this.listTwo.reduce((arr, cur) => {
                    const val = this.dataObj[cur];
                    if (val) {
                        arr.push(val);
                    }
                    return arr;
                }, []);
            }
        },
        sourceData() {
            return this.data.filter(item => this.value.indexOf(item[this.props.key]) === -1
                && this.listTwo.indexOf(item[this.props.key]) === -1);
        },
    },
    methods: {
        addToListTwo() {
            let currentValue = this.listTwo.slice();
            const itemsToBeMoved = [];
            const key = this.props.key;
            this.data.forEach(item => {
                const itemKey = item[key];
                if (
                    this.leftChecked.indexOf(itemKey) > -1 &&
                    this.listTwo.indexOf(itemKey) === -1
                ) {
                    itemsToBeMoved.push(itemKey);
                }
            });
            currentValue = this.targetOrder === 'unshift'
                ? itemsToBeMoved.concat(currentValue)
                : currentValue.concat(itemsToBeMoved);
            console.debug(currentValue);
            // 更新列表2
            this.$emit('update:listTwo', currentValue);
        },
        // 列表2设备选择
        onlistTwoCheckedChange(val, movedKeys) {
            this.listTwoChecked = val;
            if (movedKeys === undefined) return;
            this.$emit('right-check-change', val, movedKeys);
        },
        addToLeft() {
            // 列表1
            let currentValue = this.value.slice();
            // 列表2
            let listTwo = this.listTwo.slice();
            this.rightChecked.forEach(item => {
                const index = currentValue.indexOf(item);
                if (index > -1) {
                    currentValue.splice(index, 1);
                }
            });
            this.listTwoChecked.forEach(item => {
                const index = listTwo.indexOf(item);
                if (index > -1) {
                    listTwo.splice(index, 1);
                }
            });
            // 更新列表1
            this.$emit('input', currentValue);
            // 更新列表2
            this.$emit('update:listTwo', listTwo);
            this.$emit('change', currentValue, 'left', this.rightChecked);
        },
    },
    watch: {
        listTwo(val) {
            this.dispatch('ElFormItem', 'el.form.change', val);
        },
    }
}

style

.m-l-20{
    margin-left: 20px;
}

GitHub地址


总结

还有些细节没有处理,比如list2的事件。
Logo

前往低代码交流专区

更多推荐