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&…...
超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...
第八部分:阶段项目 6:构建 React 前端应用
现在,是时候将你学到的 React 基础知识付诸实践,构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段,你可以先使用模拟数据,或者如果你的后端 API(阶段项目 5)已经搭建好,可以直接连…...
图解JavaScript原型:原型链及其分析 | JavaScript图解
忽略该图的细节(如内存地址值没有用二进制) 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么:保存在堆中一块区域,同时在栈中有一块区域保存其在堆中的地址(也就是我们通常说的该变量指向谁&…...
Ray框架:分布式AI训练与调参实践
Ray框架:分布式AI训练与调参实践 系统化学习人工智能网站(收藏):https://www.captainbed.cn/flu 文章目录 Ray框架:分布式AI训练与调参实践摘要引言框架架构解析1. 核心组件设计2. 关键技术实现2.1 动态资源调度2.2 …...
深入浅出JavaScript中的ArrayBuffer:二进制数据的“瑞士军刀”
深入浅出JavaScript中的ArrayBuffer:二进制数据的“瑞士军刀” 在JavaScript中,我们经常需要处理文本、数组、对象等数据类型。但当我们需要处理文件上传、图像处理、网络通信等场景时,单纯依赖字符串或数组就显得力不从心了。这时ÿ…...
