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">></div><div @click="transferAllToRight">>></div><div @click="transferToLeft"><</div><div @click="transferAllToLeft"><<</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, refFunctionif (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') returnif (status === 'down' && this.downClass === 'disabled') returnlet 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') returnif (status === 'down' && this.downClass === 'disabled') returnlet 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>
相关文章:

vue实现穿梭框,ctrl多选,shift多选
效果图 代码 <template><div class"container"><!--左侧--><div><div class"title">{{ titles[0] }}</div><div class"layerContainer"><div v-for"item in leftLayerArray":key"…...

Win11中zookeeper的下载与安装
下载步骤 打开浏览器,前往 Apache ZooKeeper 的官方网站:zookeeper官方。在主页上点击"Project"选项,并点击"Release" 点击Download按钮,跳转到下载目录 在下载页面中,选择版本号,并点…...
ubuntu22.04 找不到串口,串口ttyusb时断时续的问题(拔插以后能检测到,过会儿就检测不到了)
1. 问题描述 ubuntu22.04的PC,在连接USB串口的时候,有时能找到ttyUSB0,有时找不到,如下: base) airsairs-Precision-3630-Tower:~$ ls -l /dev/ttyUSB* crwxrwxrwx 1 root dialout 188, 0 Aug 17 16:36 /dev/ttyUSB0 (base) air…...
Pinia基础教程
Pinia wiki Pinia 起始于 2019 年 11 月左右的一次实验,其目的是设计一个拥有组合式 API 的 Vue 状态管理库。从那时起,我们就倾向于同时支持 Vue 2 和 Vue 3,并且不强制要求开发者使用组合式 API,我们的初心至今没有改变。除了安…...

【NOIP】标题统计
author:&Carlton tags:模拟,字符串 topic:【NOIP】标题统计 language:C website:P5015 [NOIP2018 普及组] 标题统计 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) date:2023年8月20日…...

BOXTRADE-天启量化分析平台 系统功能预览
BOXTRADE-天启量化分析平台 系统功能预览 系统功能预览 1.登录 首页 参考登录文档 2. A股 行情与策略分析 2.1 A股股票列表 可以筛选和搜索 2.2 A股行情及策略回测 2.2.1 行情数据提供除权和前复权,后复权数据;外链公司信息 2.2.2 内置策略执行结果…...

解决Kibana(OpenSearch)某些字段无法搜索问题
背景 最近在OpenSearch查看线上日志的时候,发现某个索引下有些字段无法直接在界面上筛选,搜索到也不高亮,非常的不方便,就像下面这样 字段左侧两个筛选按钮禁用了无法点击,提示 Unindexed fields can not be searched…...
代码随想录训练营day15|102.层序遍历 226.翻转二叉树 101.对称二叉树
TOC 前言 代码随想录算法训练营day15 一、Leetcode 102.层序遍历 1.题目 给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。 示例 1: 输入:root [3,9,20,null,null,15,7] 输出:…...
Nginx 配置https以及wss
一、申请https证书 可以在阿里云申请免费ssl证书,一年更换一次 二、Nginx配置ssl upstream tomcat_web{server 127.0.0.1:8080; }server {listen 443 ssl;server_name www.xxx.com;## 配置日志文件access_log /var/log/nginx/web/xxx-ssl-access.log main;er…...

Log4net在.Net Winform项目中的使用
引言: Log4net是一个流行的日志记录工具,可以帮助开发人员在应用程序中实现高效的日志记录。本文将提供一个详细的分步骤示例,来帮助您在.Net Winform项目中使用Log4net。 目录 一、安装Log4net二、配置Log4net三、在项目中使用Log4net四、初…...
从零到一制作扫雷游戏——C语言
什么是扫雷游戏? 扫雷游戏作为一种老少咸宜的益智游戏, 它的游戏目标十分简单,就是要求玩家在最短的时间内, 根据点击格子之后所出现的数字来找出所有没有炸弹的格子, 同时在找的时候要避免点到炸弹,一…...
Python 数据挖掘与机器学习教程
详情点击链接:Python 数据挖掘与机器学习教程 模块一:Python编程 Python编程入门 1、Python环境搭建( 下载、安装与版本选择)。 2、如何选择Python编辑器?(IDLE、Notepad、PyCharm、Jupyter…ÿ…...
排序小白必读:掌握插入排序的基本原理
一、插入排序是什么? 它是一种简单直观的排序算法。类似于整理扑克牌,想象你手上有一堆未排序的牌,你将它们逐个插入已排序的牌堆中的正确位置。拿起一张牌,与已排序的牌进行比较,将它插入到合适的位置。重复这个过程…...
html常见兼容性问题
1. png24位的图片在iE6浏览器上出现背景 解决方案:做成PNG8,也可以引用一段脚本处理. 2. 浏览器默认的margin和padding不同 解决方案:加一个全局的 *{margin:0;padding:0;} 来统一。 3. IE6双边距bug:在IE6下,如果对…...
Docker实战:docker compose 搭建Redis
1、配置文件准备 redis 配置文件:https://pan.baidu.com/s/1YreI9_1BMh8XRyyV9BH08g2、创建目录并赋权 mkdir -p /home/docker/redis/data /home/redis/logs /home/redis/conf chmod -R 777 /home/docker/redis/data* chmod -R 777 /home/docker/redis/logs*3、re…...

Debian11 Crontab
Crontab用户命令 可执行文件 crontab命令的可执行文件在哪儿? $ which -a crontab /usr/bin/crontab /bin/crontabcrontab命令的可执行文件有2个:/usr/bin/crontab 和 /bin/crontab $ diff /usr/bin/crontab /bin/crontab $diff 发现这两个文件并无区…...

css 文字排版-平铺
序: 1、表格的宽度要有!!!!! 2、容器不能是display:inline 3、扩展---》node全栈框架 代码 text-align-last: justify; width: 70px; display: inline-block; 主要是用于表单左侧文字排序!...

把握潮流:服装定制小程序的发展与趋势
随着互联网的快速发展,小程序成为了人们生活中不可或缺的一部分。尤其在服装行业,定制化已经成为了一种趋势。为了满足消费者个性化的需求,服装定制小程序应运而生。 为了方便开发者的设计和制作,我们可以使用第三方的制作平台来创…...

Go 安装配置
介绍Ubuntu20.04 安装和配置Go 可以参考官网的这个为 Go 开发配置Visual Studio Code - Go on Azure | Microsoft Learn 1.安装Go 去这个地方下载Go https://go.dev/doc/install 如果之前安装过,可以参考这个(没有可以忽略) 下载完成后执…...

镜像底层原理详解和基于Docker file创建镜像
目录 一、镜像底层原理 1.联合文件系统(UnionFS) 2.镜像加载原理 3.为什么Docker里的centos的大小才200M? 二、Dockerfile 1.简介 2.Dockerfile操作常用命令 (1)FORM 镜像 (2)MAINTAINER 维护人信息 (3&…...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...

基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...

visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...