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>
相关推荐
27669582921 分钟前
京东e卡滑块 分析
java·javascript·python·node.js·go·滑块·京东
golitter.6 分钟前
Ajax和axios简单用法
前端·ajax·okhttp
PleaSure乐事16 分钟前
【Node.js】内置模块FileSystem的保姆级入门讲解
javascript·node.js·es6·filesystem
雷特IT26 分钟前
Uncaught TypeError: 0 is not a function的解决方法
前端·javascript
长路 ㅤ   1 小时前
vite学习教程02、vite+vue2配置环境变量
前端·vite·环境变量·跨环境配置
亚里士多没有德7751 小时前
强制删除了windows自带的edge浏览器,重装不了怎么办【已解决】
前端·edge
micro2010141 小时前
Microsoft Edge 离线安装包制作或获取方法和下载地址分享
前端·edge
.生产的驴1 小时前
Electron Vue框架环境搭建 Vue3环境搭建
java·前端·vue.js·spring boot·后端·electron·ecmascript
awonw1 小时前
[前端][easyui]easyui select 默认值
前端·javascript·easyui
老齐谈电商1 小时前
Electron桌面应用打包现有的vue项目
javascript·vue.js·electron