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

效果图

 代码

<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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/80146.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【从0开始学架构笔记】01 基础架构

文章目录 一、架构的定义1. 系统与子系统2. 模块与组件3. 框架与架构4. 重新定义架构 二、架构设计的目的三、复杂度来源&#xff1a;高性能1. 单机复杂度2. 集群复杂度2.1 任务分配2.2 任务分解&#xff08;微服务&#xff09; 四、复杂度来源&#xff1a;高可用1. 计算高可用…

深度剖析数据在内存中的存储

目录 一、数据类型介绍 类型的基本归类 1.整形家族 2.浮点数家族 3.构造类型 &#xff08;自定义类型&#xff09; 4.指针类型 5.空类型 二、整形在内存中的存储 1.原码、反码、补码 1.1原码 1.2反码 1.3补码 1.4计算规则 2 .大小端介绍 三、浮点型在内存中的存…

如何保证数据库的数据和Redis的数据一致性

实际项目中有可能会使用Redis缓存数据&#xff0c;那么在更新数据的时候如何保证数据库中的数据和Redis缓存的数据一致&#xff0c;缓存同步策略的选择是一个很重要的问题。网上有各种说法&#xff0c;大概总结有以下几种&#xff0c;看看每种方案是否可行以及存在的问题和适用…

解决git reset --soft HEAD^撤销commit时报错

今天在使用git回退功能的时候&#xff0c;遇到以下错误&#xff1a; 解决git reset --soft HEAD^撤销commit时报错 问题&#xff1a; 在进行完commit后&#xff0c;想要撤销该commit&#xff0c;于是使用了git reset --soft HEAD^命令&#xff0c;但是出现如下报错&#xff1…

科大讯飞星火模型申请与chatgpt 3.5模型以及new bing的对比

科大讯飞星火模型 申请科大讯飞星火认知大模型账号科大讯飞星火认知大模型使用1.界面介绍2. 在编程能力上与chatgpt 3.5对比科大讯飞星火模型chatgpt 3.5模型 3. 在图片生成能力上与new bing对比 总结 申请科大讯飞星火认知大模型账号 注册网址&#xff1a; 科大讯飞星火认知大…

ansible入门

ansible入门 一.ansible 背景介绍 Ansible 是一个广受欢迎的 IT 自动化系统。可以用来处理配置管理、应用自动化部署、云资源配给、网络 自动化和多借点部署等任务。其也可以使得复杂的变更如带负载均衡的零停机滚动更新更加容易。Ansible.com 1.1 自动化运维概念 1.1.1 运维…

python+django+mysql项目实践五(信息搜索)

python项目实践 环境说明: Pycharm 开发环境 Django 前端 MySQL 数据库 Navicat 数据库管理 信息搜素 输入内容进行搜索,内容有文本类和时间类 文本类需要模糊搜索,包含即检索 时间类需要选取时间范围内的内容 views 利用Q完成对指定内容的检索 检索后按检索内容更新…

RabbitMq-发布确认高级(避坑指南版)

在初学rabbitMq的时候&#xff0c;伙伴们肯定已经接触到了“发布确认”的概念&#xff0c;但是到了后期学习中&#xff0c;会接触到“springboot”中使用“发布确认”高级的概念。后者主要是解决什么问题呢&#xff1f;或者是什么样的场景引出这样的概念呢&#xff1f; 在生产环…

postgresql 分组

postgresql 数据汇总 分组汇总聚合函数注意 总结 分组统计总结 高级分组总结 分组汇总 聚合函数 聚合函数&#xff08;aggregate function&#xff09;针对一组数据行进行运算&#xff0c;并且返回单个结果。PostgreSQL 支持以下常见的聚合函数&#xff1a; • AVG - 计算一…

SpringCloud实用篇7——深入elasticsearch

目录 1 数据聚合1.1 聚合的种类1.2 DSL实现聚合1.2.1 Bucket聚合语法1.2.2 聚合结果排序1.2.3 限定聚合范围1.2.4 Metric聚合语法1.2.5.小结 1.3 RestAPI实现聚合1.3.1 API语法1.3.2 业务需求1.3.3 业务实现 2 自动补全2.1 拼音分词器2.2 自定义分词器2.3 自动补全查询2.4 实现…

C++ STL关联式容器(详解)

STL关联式容器 C STL关联式容器是什么&#xff1f; 在《C STL容器》一节中讲到&#xff0c;C 容器大致分为 2 类&#xff0c;即序列式容器和关联式容器。其中&#xff0c;序列式容器&#xff08;包括 array、vector、list、deque 和 forward_list&#xff09;已经在前面章节中…

Golang使用MinIO

最近在使用Golang做了一个网盘项目&#xff08;学习&#xff09;&#xff0c;文件存储一直保存在本地&#xff08;各厂商提供的oss贵&#xff09;&#xff0c;所以就在思考怎么来处理这些文件&#xff0c;类似的方案很对hdfs、fastdfs&#xff0c;但这其中MinIO是最近几年比较火…

【Django】Task1安装python环境及运行项目

【Django】Task1安装python环境及运行项目 写在最前 8月份Datawhale组队学习&#xff0c;在这个群除我佬的时代&#xff0c;写一下blog记录学习过程。 参考资源&#xff1a; 学习项目github&#xff1a;https://github.com/Joe-2002/sweettalk-django4.2 队长博客&#xff1a…

windows权限维持—SSPHOOKDSRMSIDhistorySkeletonKey

windows权限维持—SSP&HOOK&DSRM&SIDhistory&SkeletonKey 1. 权限维持介绍1.1. 其他 2. 基于验证DLL加载—SPP2.1. 操作演示—临时生效2.1.1. 执行命令2.1.2. 切换用户 2.2. 操作演示—永久生效2.2.1. 上传文件2.2.2. 执行命令2.2.3. 重启生效 2.3. 总结 3. 基…

k8s的pv和pvc创建

//NFS使用PV和PVC 1、配置nfs存储 2、定义PV 实现 下图的pv和pvc测试 pv的定义 这里定义5个PV&#xff0c;并且定义挂载的路径以及访问模式&#xff0c;还有PV划分的大小 vim /pv.yamlapiVersion: v1 kind: PersistentVolume metadata:name: pv001 spec:capacity:storage: …

私密数据采集:隧道爬虫IP技术的保密性能力探究

作为一名专业的爬虫程序员&#xff0c;今天要和大家分享一个关键的技术&#xff0c;它能够为私密数据采集提供保密性能力——隧道爬虫IP技术。如果你在进行敏感数据采集任务时需要保护数据的私密性&#xff0c;那么这项技术将是你的守护神。 在进行私密数据采集任务时&#xff…

【制作npm包4】api-extractor 学习

制作npm包目录 本文是系列文章&#xff0c; 作者一个橙子pro&#xff0c;本系列文章大纲如下。转载或者商业修改必须注明文章出处 一、申请npm账号、个人包和组织包区别 二、了解 package.json 相关配置 三、 了解 tsconfig.json 相关配置 四、 api-extractor 学习 五、npm包…

Fluent-MyBatis

Fluent-MyBatis Fluent-MyBatis 简介 何为 Fluent Mybatis&#xff1f; Fluent Mybatis, 是一款 Mybatis 语法增强框架, 综合了 Mybatis Plus, Dynamic SQL, JPA 等框架特性和优点 Fluent-MyBatis 开源地址 Fluent-MyBatis 原理 Fluent-MyBatis 原理是利用 annotation pro…

【IMX6ULL驱动开发学习】08.马达驱动实战:驱动编写、手动注册平台设备和设备树添加节点信息

目录 一、使用设备树 1.1 修改设备树流程 二、手动创建平台设备 三、总结&#xff08;附驱动程序&#xff09; 前情提要&#xff1a;​​​​​​​【IMX6ULL驱动开发学习】07.驱动程序分离的思想之平台总线设备驱动模型和设备树_阿龙还在写代码的博客-CSDN博客 手动注册…

爬虫逆向实战(五)--猿人学第三题

一、数据接口分析 主页地址&#xff1a;猿人学第三题 1、抓包 通过抓包可以发现数据接口是api/match/3 2、判断是否有加密参数 请求参数是否加密&#xff1f; 无请求头是否加密&#xff1f; 无响应是否加密&#xff1f; 无cookie是否加密&#xff1f; 无 二、发送请求 …