vue实现穿梭框,ctrl多选,shift多选

效果图

代码

javascript 复制代码
<template>
    <div class="container">
        <!--左侧-->
        <div>
            <div class="title">{{ titles[0] }}</div>
            <div class="layerContainer">
                <div v-for="item in leftLayerArray"
                     :key="getKey(item)"
                     :ref="getRefKey(item)"
                     @click="e =>layerClicked(e,item,true)"
                >
                    <el-tooltip effect="light" :content="item.key" placement="top" v-if="item.key.length > 10">
                        <div>{{ item.key }}</div>
                    </el-tooltip>
                    <div v-else>{{ item.key }}</div>
                </div>
            </div>
        </div>
        <!--中间按钮-->
        <div class="centerButton">
            <div @click="transferToRight">&gt;</div>
            <div @click="transferAllToRight">&gt;&gt;</div>
            <div @click="transferToLeft">&lt;</div>
            <div @click="transferAllToLeft">&lt;&lt;</div>
        </div>
        <!--右侧-->
        <div>
            <div class="title">{{ titles[1] }}</div>
            <div class="layerContainer">
                <div v-for="item in rightLayerArray"
                     :key="getKetRight(item)"
                     :ref="getRefKeyRight(item)"
                     @click="e =>layerClicked(e,item, false)"
                >
                    <el-tooltip effect="light" :content="item.key" placement="top" v-if="item.key.length > 10">
                        <div>{{ item.key }}</div>
                    </el-tooltip>
                    <div v-else>{{ item.key }}</div>
                </div>
            </div>
        </div>
        <!--  上下移动的按钮  -->
        <div class="transfer-right-buttons">
            <div :class="upClass" @click="moveMoreStep('up')">
                <img src="图片地址"
                     alt="">
            </div>
            <div :class="upClass" @click="moveOneStep('up')">
                <img src="图片地址"
                     alt="">
            </div>
            <div :class="downClass" @click="moveOneStep('down')">
                <img src="图片地址"
                     alt="">
            </div>
            <div :class="downClass" @click="moveMoreStep('down')">
                <img src="图片地址"
                     alt="">
            </div>
        </div>
    </div>
</template>
<script>

export default {
    name: "ha-transfer",
    props: {
        titles: {
            type: Array,
            default: () => ['地图图层', '图例项']
        },
        originData: {
            type: Array,
            default: () => [
                {
                    key: '点图层',
                    id: 1
                },
                {
                    key: '线图层',
                    id: 2
                },
                {
                    key: '面图层',
                    id: 3
                },
                {
                    key: '多点图层',
                    id: 4
                },
                {
                    key: '多线图层',
                    id: 5
                },
                {
                    key: '多面图层',
                    id: 6
                }
            ]
        },
        selectedData: {
            type: Array,
            default: () => [
                {
                    key: '图例项1',
                    id: 7
                },
                {
                    key: '图例项2',
                    id: 8
                },
                {
                    key: '图例项3',
                    id: 9
                },
                {
                    key: '图例项4',
                    id: 10
                },
                {
                    key: '图例项5',
                    id: 11
                },
                {
                    key: '图例项6',
                    id: 12
                }
            ]
        }
    },
    data() {
        return {
            leftLayerArray: [...this.originData],
            leftCurrentSelectedLayer: [],
            rightLayerArray: [...this.selectedData],
            rightCurrentSelectedLayer: []
        }
    },
    computed: {
        upClass() {
            if (this.rightCurrentSelectedLayer.length === 0) {
                return 'disabled'
            }
            for (let item of this.rightCurrentSelectedLayer) {
                if (item.id === this.rightLayerArray[0].id) {
                    return 'disabled'
                }
            }
            return ''
        },

        downClass() {
            if (this.rightCurrentSelectedLayer.length === 0) {
                return 'disabled'
            }
            for (let item of this.rightCurrentSelectedLayer) {
                if (item.id === this.rightLayerArray[this.rightLayerArray.length - 1].id) {
                    return 'disabled'
                }
            }
            return ''
        },
    },
    methods: {
        getRefKey(item) {
            return `layer-${item.id}`
        },
        getKey(item) {
            return `layer-${item.id}`
        },
        getRefKeyRight(item) {
            return `layer-right-${item.id}`
        },
        getKetRight(item) {
            return `layer-right-${item.id}`
        },

        /**
         * 单击穿梭框列表项,选中或取消选中
         * @param e 事件对象
         * @param item  当前项
         * @param isLeft  是否是左侧
         */
        layerClicked(e, item, isLeft) {
            let currentLayer, layerArray, refFunction
            if (isLeft) {
                currentLayer = [...this.leftCurrentSelectedLayer]
                layerArray = [...this.leftLayerArray]
                refFunction = this.getRefKey
            } else {
                currentLayer = [...this.rightCurrentSelectedLayer]
                layerArray = [...this.rightLayerArray]
                refFunction = this.getRefKeyRight
            }

            const refElement = this.$refs[refFunction(item)][0];

            if (e.ctrlKey || e.metaKey) {
                const isSelected = currentLayer.includes(item);
                if (isSelected) {
                    refElement.classList.remove('active');
                    currentLayer.splice(currentLayer.indexOf(item), 1);
                } else {
                    refElement.classList.add('active');
                    currentLayer.push(item);
                }
            } else if (e.shiftKey) {
                const firstIndex = layerArray.indexOf(currentLayer[0]);
                const lastIndex = layerArray.indexOf(item);
                const [startIndex, endIndex] = [firstIndex, lastIndex].sort();

                currentLayer = layerArray.slice(startIndex, endIndex + 1);

                layerArray.forEach((item, index) => {
                    const refElement = this.$refs[refFunction(item)][0];
                    if (index >= startIndex && index <= endIndex) {
                        refElement.classList.add('active');
                    } else {
                        refElement.classList.remove('active');
                    }
                })
            } else {
                currentLayer = [item];
                layerArray.forEach(item => {
                    this.$refs[refFunction(item)][0].classList.remove('active');
                })
                refElement.classList.add('active');
            }
            if (isLeft) {
                this.leftCurrentSelectedLayer = [...currentLayer];
                this.leftLayerArray = [...layerArray];
            } else {
                this.rightCurrentSelectedLayer = [...currentLayer];
                this.rightLayerArray = [...layerArray];
            }
        },

        /**
         * 把选中的图层移动到右侧
         */
        transferToRight() {
            this.rightLayerArray.push(...this.leftCurrentSelectedLayer)
            this.leftLayerArray = this.leftLayerArray.filter(item => {
                return this.leftCurrentSelectedLayer.indexOf(item) === -1
            })
            this.leftCurrentSelectedLayer = []
        },
        /**
         * 把所有的图层移动到右侧
         */
        transferAllToRight() {
            this.rightLayerArray.push(...this.leftLayerArray)
            this.leftCurrentSelectedLayer = []
            this.leftLayerArray = []
        },

        /**
         * 把选中的图层移动到左侧
         */
        transferToLeft() {
            this.leftLayerArray.push(...this.rightCurrentSelectedLayer)
            this.rightLayerArray = this.rightLayerArray.filter(item => {
                return this.rightCurrentSelectedLayer.indexOf(item) === -1
            })
            this.rightCurrentSelectedLayer = []
        },
        /**
         * 把所有的图层移动到左侧
         */
        transferAllToLeft() {
            this.leftLayerArray.push(...this.rightLayerArray)
            this.rightCurrentSelectedLayer = []
            this.rightLayerArray = []
        },

        /**
         * 向上或向下移动一步
         * @param status
         */
        moveOneStep(status) {
            if (status === 'up' && this.upClass === 'disabled') return
            if (status === 'down' && this.downClass === 'disabled') return

            let temp = []
            for (let item of this.rightLayerArray) {
                if (this.rightCurrentSelectedLayer.indexOf(item) === -1) {
                    temp.push(item)
                }
            }

            this.rightCurrentSelectedLayer.sort((a, b) => {
                return this.rightLayerArray.indexOf(a) - this.rightLayerArray.indexOf(b)
            })

            let index = this.rightLayerArray.indexOf(this.rightCurrentSelectedLayer[0])
            status === 'up' ? index-- : index++
            this.rightLayerArray = [...temp.slice(0, index), ...this.rightCurrentSelectedLayer, ...temp.slice(index)]
        },

        /**
         * 向上或向下移动多步到顶或者到底
         * @param status
         */
        moveMoreStep(status) {
            if (status === 'up' && this.upClass === 'disabled') return
            if (status === 'down' && this.downClass === 'disabled') return
            let temp = []
            for (let item of this.rightLayerArray) {
                if (this.rightCurrentSelectedLayer.indexOf(item) === -1) {
                    temp.push(item)
                }
            }
            this.rightLayerArray = status === 'up' ?
                [...this.rightCurrentSelectedLayer, ...temp] :
                [...temp, ...this.rightCurrentSelectedLayer]
        },
    }
}
</script>

<style scoped lang="less">
.disabled() {
    cursor: not-allowed !important;
    background-color: #999 !important;
    border: #333 solid 1px !important;
}

.hover() {
    background-color: #eee;
    border: #409eff solid 1px;
    cursor: default;
}

.buttonContainer() {
    height: 200px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    align-items: center;
}

.active() {
    background-color: #409eff;
    color: #fff;
}

.container {
    display: flex;
    justify-content: space-between;
    align-items: center;
    user-select: none;

    .layerContainer {
        width: 200px;
        height: 250px;
        border: #999 solid 1px;
        box-sizing: border-box;
        padding: 10px;
        white-space: nowrap;
        overflow-x: hidden;
        overflow-y: auto;

        div {
            &:hover {
                cursor: default;
            }

        }

        .active {
            .active()
        }
    }

    .centerButton {
        .buttonContainer();

        div {
            width: 30px;
            height: 30px;
            border: #666 solid 1px;
            background-color: #ddd;
            text-align: center;
            line-height: 30px;

            &:hover {
                .hover()
            }
        }
    }

    .transfer-right-buttons {

        .buttonContainer();

        div {
            width: 30px;
            height: 30px;
            border: #666 solid 1px;
            background-color: #ddd;

            img {
                width: 100%;
                height: 100%;
            }

            &:hover {
                .hover()
            }
        }

        .disabled {
            .disabled()
        }
    }
}
</style>
相关推荐
四喜花露水2 分钟前
Vue 自定义icon组件封装SVG图标
前端·javascript·vue.js
前端Hardy12 分钟前
HTML&CSS: 实现可爱的冰墩墩
前端·javascript·css·html·css3
web Rookie42 分钟前
JS类型检测大全:从零基础到高级应用
开发语言·前端·javascript
Au_ust1 小时前
css:基础
前端·css
帅帅哥的兜兜1 小时前
css基础:底部固定,导航栏浮动在顶部
前端·css·css3
工业甲酰苯胺1 小时前
C# 单例模式的多种实现
javascript·单例模式·c#
yi碗汤园1 小时前
【一文了解】C#基础-集合
开发语言·前端·unity·c#
就是个名称1 小时前
购物车-多元素组合动画css
前端·css
编程一生1 小时前
回调数据丢了?
运维·服务器·前端
丶21362 小时前
【鉴权】深入了解 Cookie:Web 开发中的客户端存储小数据
前端·安全·web