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&…...

springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...