vue2之element-ui多个穿梭框实现 -Transfer

news2024/11/23 20:30:58

效果图
在这里插入图片描述

组件实现 -目录结构
在这里插入图片描述

Transfer.vue实现

<template>
  <el-card :body-style="{ minHeight: '350px' }">
    <el-input
      v-show="filterable"
      v-model="filterName"
      :placeholder="filterPlaceholder"
      clearable
      @input="handleInput"
    >
      <i slot="prefix" class="el-input__icon el-icon-search"></i>
    </el-input>
    <div class="transfer-title">
      <el-checkbox
        class="transfer-title-check"
        :indeterminate="isIndeterminate"
        :disabled="!data.length"
        v-model="checkAll"
        @change="handleCheckAllChange"
        >{{ checkAllTitle }}</el-checkbox
      >
      <span class="check-num">
        {{ checkList.length + "/" + checkData.length }}</span
      >
    </div>
    <main>
      <el-checkbox-group v-model="checkList">
        <div
          class="transfer-checkbox-item"
          v-for="item in checkData"
          :key="item[props.key]"
        >
          <el-checkbox
            class="transfer-checkbox"
            :disabled="item[props.disabled]"
            :label="item[props.label]"
            @change="handleCheckChange"
          >
          </el-checkbox>
        </div>
        <div class="empty" v-show="!checkData.length">
          {{ beSearched ? "无匹配数据" : "暂无数据" }}
        </div>
      </el-checkbox-group>
    </main>
    <footer>
      <slot name="footer">
        <el-button
          class="footer-button"
          v-for="item in buttonList"
          :key="item.key"
          :disabled="!checkList.length"
          :type="item.type"
          @click="handleConfirm(item.key)"
          >{{ item.title }}</el-button
        >
      </slot>
    </footer>
  </el-card>
</template>

<script>
export default {
  name: "transfer-view",
  props: {
    data: {
      type: Array,
      default: () => [],
    },
    // 可自定义传入的数据结构
    props: {
      type: Object,
      default: () => {
        return { key: "label_name", label: "label_name", disabled: "disabled" };
      },
    },
    filterPlaceholder: {
      type: String,
      default: "请输入搜索名称",
    },
    checkAllTitle: {
      type: String,
      default: "列表名称",
    },
    // 实际对应 列表类型 每个列表一个特有的类型与按钮关联
    buttonType: {
      type: String,
      default: "origin",
    },
    // 按钮Map对象 自定义插槽按钮可不传
    buttonMap: {
      type: Object,
      default: () => {},
    },
    // 是否可搜索
    filterable: {
      type: Boolean,
      default: true,
    },
    // 定义自己的搜索逻辑 同el
    filterMethod: {
      type: Function,
    },
  },
  data() {
    return {
      filterName: "",
      checkList: [],
      checkAll: false,
      isIndeterminate: false,
      beSearched: false,
      searchList: [],
    };
  },
  computed: {
    buttonList() {
      return this.buttonMap[this.buttonType];
    },
    checkData() {
      return this.beSearched ? this.searchList : this.data;
    },
  },
  methods: {
    /**
     * 搜索框搜索
     */
    handleInput(value) {
      if (value === "") {
        this.beSearched = false;
        return;
      }
      this.beSearched = true;
      let label = this.props.label;
      let data = this.data;
      this.searchList = data.filter((item) => {
        if (typeof this.filterMethod === "function") {
          return this.filterMethod(this.filterName, item);
        }
        return item[label].includes(value);
      });
    },
    /**
     * 全选
     */
    handleCheckAllChange(flag) {
      if (flag) {
        let label = this.props.label;
        this.checkList = this.data.map((i) => i[label]);
        this.checkAll = true;
      } else {
        this.checkList = [];
        this.checkAll = false;
      }
      this.isIndeterminate = false;
    },
    /**
     * 单选
     */
    handleCheckChange(flag) {
      if (this.checkList.length === this.data.length && flag) {
        this.isIndeterminate = false;
        this.checkAll = true;
      } else {
        this.checkAll = false;
      }
      if (this.data.length > 1) {
        this.isIndeterminate = true;
      }
    },
    /**
     * 按钮点击
     */
    handleConfirm(key) {
      let checkList = this.checkList;
      let label = this.props.label;
      let data = this.data.filter((item) => checkList.includes(item[label]));
      let newData = this.data.filter(
        (item) => !checkList.includes(item[label])
      );
      this.$emit("confirm", { key, data });
      this.$emit("update:data", newData);
      if (this.filterable) {
        this.resetSearchData();
      }
      this.resetData();
    },
    /**
     * 重置为原始状态
     */
    resetData() {
      this.checkAll = false;
      this.isIndeterminate = false;
      this.checkList = [];
    },
    /**
     * 重置搜索状态
     */
    resetSearchData() {
      if (this.searchList.length) {
        let label = this.props.label;
	    let checkList = this.checkList;
	    let searchList = this.searchList;
        this.searchList = searchList.filter(
          (i) => !checkList.includes(i[label])
        );
      }
    },
  },
};
</script>

<style lang="scss" scoped>
.transfer-title {
  background: #f2f6fc;
  width: 100%;
  height: 50px;
  padding: 16px;
  box-sizing: border-box;
  margin-top: 12px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  .transfer-title-check {
    display: flex;
  }
  .check-num {
    color: #909399;
    font-size: 12px;
  }
}

main {
  height: 250px;
  overflow-y: auto;
  position: relative;
  border-bottom: 1px solid #e4e7ed;
  .transfer-checkbox-item {
    .transfer-checkbox {
      width: 100%;
      box-sizing: border-box;
      padding: 12px;
      display: flex;
      justify-content: flex-start;
      align-items: center;
      border-top: 1px solid #e4e7ed;
    }
    &:nth-child(1) {
      .transfer-checkbox {
        border: none;
      }
    }
  }

  .empty {
    width: 100%;
    height: 20px;
    text-align: center;

    color: #ccc;
    font-size: 13px;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    z-index: 2;
  }
}
footer {
  .footer-button {
    margin-top: 12px;
  }
}
</style>

config.js


export const buttonDefaultMap = {
  origin: [
    { key: "must", type: "primary", title: "设为列表1" },
    { key: "choose", type: "success", title: "设为列表2" },
  ],
  must: [
    { key: "origin", type: "warning", title: "恢复初始" },
    { key: "choose", type: "success", title: "设为列表2" },
  ],
  choose: [
    { key: "origin", type: "warning", title: "恢复初始" },
    { key: "must", type: "success", title: "设为列表1" },
  ],
};

export const buttonMap = {
  origin: [
    { key: "must", type: "primary", title: "设为列表1" },
    { key: "choose", type: "success", title: "设为列表2" },
    { key: "replace", type: "info", title: "设为列表3" },
  ],
  must: [
    { key: "origin", type: "warning", title: "恢复初始" },
    { key: "choose", type: "success", title: "设为列表2" },
    { key: "replace", type: "info", title: "设为列表3" },
  ],
  choose: [
    { key: "origin", type: "warning", title: "恢复初始" },
    { key: "must", type: "success", title: "设为列表1" },
    { key: "replace", type: "info", title: "设为列表3" },
  ],
  replace: [
    { key: "origin", type: "warning", title: "恢复初始" },
    { key: "must", type: "success", title: "设为列表1" },
    { key: "choose", type: "info", title: "设为列表2" },
  ],
};

feeTemplate.vue

<template>
  <div>
    <el-row :gutter="20" type="flex" justify="space-around">
      <el-col>
        <Transfer
          ref="originRef"
          :data.sync="originTable"
          :buttonMap="buttonList"
          @confirm="handleConfirm"
          :filterMethod="filterMethod"
        />
      </el-col>
      <el-col>
        <Transfer
          ref="mustRef"
          :data.sync="mustTable"
          :buttonMap="buttonList"
          checkAllTitle="列表1"
          buttonType="must"
          @confirm="handleConfirm"
        />
      </el-col>
      <el-col>
        <Transfer
          ref="chooseRef"
          :data.sync="chooseTable"
          :buttonMap="buttonList"
          checkAllTitle="列表2"
          buttonType="choose"
          @confirm="handleConfirm"
        />
      </el-col>
      <el-col>
        <Transfer
          ref="replaceRef"
          :data.sync="replaceTable"
          :buttonMap="buttonList"
          checkAllTitle="列表3"
          buttonType="replace"
          @confirm="handleConfirm"
        />
      </el-col>
    </el-row>
  </div>
</template>
<script>
import Transfer from "@/components/Transfer";
import { buttonMap } from "./config.js";
export default {
  components: {
    Transfer,
  },
  props: {
    openType: {
      type: String,
      default: "",
    },
  },
  data() {
    return {
      originTable: [
        {
          label_name: "备选1",
        },
        {
          label_name: "备选2",
        },
        {
          label_name: "备选3",
        },
        {
          label_name: "备选4",
        },
        {
          label_name: "备选5",
        },
        {
          label_name: "备选6",
        },
        {
          label_name: "备选7",
        },
        {
          label_name: "备选8",
        },
      ],
      mustTable: [],
      chooseTable: [],
      replaceTable: [],
      buttonList: buttonMap,
      //buttonList:buttonMap,
      filterMethod(query,item){
        return item.label_name.includes(query);
      }
    };
  },
  methods: {
    /**
     * 按钮点击
     */
    handleConfirm({ key, data }) {
      switch (key) {
        case "origin":
          this.originTable = [...this.originTable, ...data];
          break;
        case "must":
          this.mustTable = [...this.mustTable, ...data];
          break;
        case "choose":
          this.chooseTable = [...this.chooseTable, ...data];
          break;
        case "replace":
          this.replaceTable = [...this.replaceTable, ...data];
          break;
      }
    },
  },
};
</script>
<style scoped></style>

  • 高度自定义,主要看配置 config.js 对应的操作 支持多个穿梭框 ,部分api同elementui ,降低心智负担
    vue3也是同样的原理 ,只不过 部分api改下 就行

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Flutter】Flutter 如何切换页面

文章目录 一、简介二、Navigator 的使用三、实际示例&#xff1a;电影应用的页面切换四、完整代码五、 总结 一、简介 什么是页面切换呢&#xff1f;简单来说&#xff0c;页面切换就是在应用的不同界面之间进行跳转。例如&#xff0c;在一个电影应用中&#xff0c;从电影列表页…

java基础(多线程)-共享模型之管程

一、共享资源带来的问题 class ThreadProblem{static int counter 0;public static void testThread(){Thread t1 new Thread(()-> {for (int i 0; i < 5000; i) {counter;}},"t1");Thread t2 new Thread(()-> {for (int i 0; i < 5000; i) {count…

基于html+css的图展示126

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

苹果手机之间如何互传照片?批量传输操作指南

很多时候&#xff0c;我们用手机拍摄了好看的照片或者收藏了一些有趣的图片&#xff0c;想要分享给朋友&#xff0c;却不知道苹果手机之间如何互传照片&#xff1f;在分享大量照片的时候不清楚如何批量操作&#xff1f;别担心&#xff0c;下面小编就来分享一下苹果手机照片传输…

LeetCode279. 完全平方数 DP完全背包

https://leetcode.cn/problems/perfect-squares/ 题目描述 给你一个整数 n &#xff0c;返回 和为 n 的完全平方数的最少数量。 完全平方数 是一个整数&#xff0c;其值等于另一个整数的平方&#xff1b;换句话说&#xff0c;其值等于一个整数自乘的积。例如&#xff0c;1、4…

python类中常用的魔术方法

文章目录 构造方法__init__对象转字符__str__对象自定义大小比较First__lt__Second__le__Third__eq__ 构造方法__init__ 构造方法也是魔术方法的一种&#xff0c;此方法我在python对象与类中已经展示过了 注意&#xff1a;在方法中引用类成员变量一定要记得使用self关键字引用…

Elasticsearch:倒数排序融合 - Reciprocal rank fusion

警告&#xff1a;此功能处于技术预览阶段&#xff0c;可能会在未来版本中更改或删除。 Elastic 将尽最大努力修复任何问题&#xff0c;但技术预览中的功能不受官方 GA 功能的支持 SLA 约束。 倒数排序融合&#xff08;RRF&#xff09;是一种将具有不同相关性指标的多个结果集组…

蓝牙资讯|Canaly发布2023Q1全球可穿戴腕带设备报告

根据市场调查机构 Canalys 公布的最新报告&#xff0c;2023 年第 1 季度全球可穿戴腕带设备出货量为 4100 万台&#xff0c;同比下降 1%。 其中&#xff0c;本季度全球基础手环市场受到厂商和消费者更关注大屏设备影响&#xff0c;出货量为 750 万台&#xff0c;同比下降 24%…

Vue中如何进行拖拽与排序功能实现

Vue中如何进行拖拽与排序功能实现 在Web应用程序中&#xff0c;拖拽和排序功能是非常常见的。在Vue中&#xff0c;我们可以使用一些组件库来实现这个功能&#xff0c;例如sortablejs和vuedraggable。本文将介绍如何使用vuedraggable组件来实现Vue中的拖拽和排序功能。 什么是v…

Selenium Python教程第7章:Selenium编程其它功能

7. Selenium其它功能 7.1 Action Chains 动作链功能 WebDriver只能模拟针对特定元素的click, send_keys 操作&#xff0c;无法执行鼠标移动、悬浮、按键&#xff0c;选择菜单等操作&#xff0c;需要通过 Action Chains 类来操作 如下面操作&#xff0c;打开主菜单项后&#x…

实战:用dockerfile创建镜像实现springboot项目容器化

文章目录 前言技术积累docker基本操作命令dockerfile简介dockerfile指令说明 实战演示创建dockerfile创建挂载目录构建dockerfile启动容器完成验证 写在最后 前言 docker容器化方案是当下流行的服务部署方式&#xff0c;在软件领域举足轻重。我公司的测试、线上环境都采用dock…

选择最适合你的云服务器:腾讯云、华为云、阿里云对比

云服务器是一种基于云计算技术的服务器&#xff0c;它可以为企业提供高效、灵活、安全的运行环境。目前市场上有很多云服务器的选择&#xff0c;其中腾讯云、华为云和阿里云是最受欢迎的三个品牌&#xff0c;下面我们来看看它们各自的优势。 腾讯云的优势在于其强大的技术支持…

谷粒商城p45-自动装配-stream流-lambda表达式

软件&#xff1a;idea、postman、virtual box 服务&#xff1a;gulimall-product 请求路径&#xff1a;http://localhost:10000/product/category/list/tree 启动&#xff1a;启动idea product模块&#xff0c;启动vm&#xff0c;启动docker mysql controller代码 自动装配C…

LeetCode494. 目标和 0-1背包DP

https://leetcode.cn/problems/target-sum/ 题目描述 给你一个整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 ‘’ 或 ‘-’ &#xff0c;然后串联起所有整数&#xff0c;可以构造一个 表达式 &#xff1a; 例如&#xff0c;nums [2, 1] &#xff0c;可以在…

网络传输中的那些编码之-UTF8编码漫谈

编码为什么是一个重要的话题&#xff0c;因为我们和计算机交互的主要方式目前还是文字字符。作为一个程序员&#xff0c;相信大部分都都被字符和编码的问题折磨过&#xff0c;从键盘输入文字字符到编辑器 中&#xff0c;编辑器存储字符到硬盘&#xff0c;再到具体一个编程语言如…

中国电子学会2023年05月份青少年软件编程Scratch图形化等级考试试卷二级真题(含答案)

2023-05 Scratch二级真题 题数&#xff1a;37 分数&#xff1a;100 测试时长&#xff1a;60min 一、单选题(共25题&#xff0c;共50分) 1.运行下列哪段程序&#xff0c;可以让狗狗走到木屋门口&#xff1f;&#xff08;C&#xff09;(2分) A. B. C. D. 答案解析&a…

我用AI提高我的代码质量,周边同事对我的代码赞不绝口,速来围观

文章目录 前言功能演示1.使用Stream API来简化集合操作2.使用switch语句来替代多个if-else语句3.使用try-with-resources语句来自动关闭资源4. Lambda 表达式来简化代码,并提高代码的可读性和可维护性5.查找代码中的bug并优化6.python 使用sort方法来对列表进行排序7.javaScrpi…

一文看懂MySQL是什么

你可以前往我的博客查看更多关于云服务器和数据库以及域名注册、建站等相关文章。 MySQL是一种开源关系型数据库管理系统&#xff0c;它是最受欢迎的数据库之一。MySQL是由瑞典公司MySQL AB创建的&#xff0c;后来被Sun Microsystems收购&#xff0c;现在是Oracle Corporation…

Flink 系列二 Flink 状态化流处理概述

本篇作为Flink系列的第二篇&#xff0c;第一篇是环境准备&#xff0c;需要的同学可以看&#xff1a;https://blog.csdn.net/lly576403061/article/details/130358449?spm1001.2014.3001.5501。希望可以通过系统的学习巩固该方面的知识&#xff0c;丰富自己的技能树。废话不多说…

解决 org.eclipse.jface.text.Document class file version 61.0 报错

问题描述 运行好好的项目&#xff0c;没有做任何改动&#xff0c;最近在编译时报以下错误 java.lang.UnsupportedClassVersionError: org/eclipse/jface/text/Document has been compiled by a more recent version of the Java Runtime (class file version 61.0), this vers…