vue 基于eleui的自定义穿梭框Transfer

最近在写公司业务的时候,有需要用到transfer的业务场景,但是ele给的穿梭框组件并不是很符合期望要求,故自己基于eleui手写了一个穿梭框;

效果如下:

 

具体代码如下,给大家一个参考!

<template>
  <div class="transfer">
    <!-- 左侧列表 -->
    <div class="card">
      <div class="card-header">
        <el-checkbox
          v-model="achecked"
          :disabled="alist.length > 0 ? false : true"
          @change="aCheckBoxChange"
        ></el-checkbox>
        <span>a列表</span>
        <span class="number-box"
          >{{
            getCheckedList({
              arr: alist,
              type: "length",
              checkedStr: "achecked",
            })
          }}
          / {{ alist.length }}</span
        >
      </div>
      <ul>
        <template v-if="alist.length > 0">
          <li
            v-for="item in alist"
            :key="item.id"
            @click="item.checked = !item.checked"
            :class="{ 'select-option': item.checked }"
          >
            {{ item.title }}
          </li>
        </template>
        <el-empty v-else></el-empty>
      </ul>
    </div>

    <!-- 穿梭操作按钮 -->
    <div class="handler-button">
      <el-button
        type="primary"
        icon="el-icon-arrow-left"
        :disabled="getBlistSlected"
        @click="transferToLeft"
      ></el-button>
      <el-button
        type="primary"
        icon="el-icon-arrow-right"
        :disabled="getAlistSlected"
        @click="transferToRight"
      ></el-button>
    </div>

    <!-- 右侧列表 -->
    <div class="card">
      <div class="card-header">
        <el-checkbox
          v-model="bchecked"
          :disabled="blist.length > 0 ? false : true"
          @change="bCheckBoxChange"
        ></el-checkbox>
        <span> b列表</span>
        <span class="number-box"
          >{{
            getCheckedList({
              arr: blist,
              type: "length",
              checkedStr: "bchecked",
            })
          }}
          / {{ blist.length }}</span
        >
      </div>
      <ul>
        <template v-if="blist.length > 0">
          <li
            v-for="item in blist"
            :key="item.id"
            @click="item.checked = !item.checked"
            :class="{ 'select-option': item.checked }"
          >
            {{ item.title }}
          </li>
        </template>
        <el-empty v-else></el-empty>
      </ul>
    </div>
  </div>
</template>
 
<script>
export default {
  data() {
    return {
      achecked: false,
      bchecked: false,
      alist: [
        {
          id: 11153551,
          title: "a1",
          checked: false,
        },
        {
          id: 24546515,
          title: "a2",
          checked: false,
        },
        {
          id: 347845231,
          title: "a3",
          checked: false,
        },
        {
          id: 421321332,
          title: "a4",
          checked: false,
        },
        {
          id: 545546645,
          title: "a5",
          checked: false,
        },
        {
          id: 6242153,
          title: "a6",
          checked: false,
        },
        {
          id: 77898846,
          title: "a7",
          checked: false,
        },
      ],
      blist: [
        {
          id: 15745,
          title: "b1",
          checked: false,
        },
        {
          id: 23232,
          title: "b2",
          checked: false,
        },
        {
          id: 35353,
          title: "b3",
          checked: false,
        },
        {
          id: 45645465,
          title: "b4",
          checked: false,
        },
        {
          id: 523123,
          title: "b5",
          checked: false,
        },
        {
          id: 689788,
          title: "b6",
          checked: false,
        },
        {
          id: 746565,
          title: "b7",
          checked: false,
        },
      ],
    };
  },
  computed: {
    getAlistSlected() {
      if (
        this.getCheckedList({
          arr: this.alist,
          type: "length",
          checkedStr: "achecked",
        }) > 0
      ) {
        return false;
      }
      return true;
    },
    getBlistSlected() {
      if (
        this.getCheckedList({
          arr: this.blist,
          type: "length",
          checkedStr: "bchecked",
        }) > 0
      ) {
        return false;
      }
      return true;
    },
  },
  methods: {
    getCheckedList({ arr, type = "data", checkedStr }) {
      let list = arr.filter((item) => item.checked);
      if (list.length === arr.length) this[checkedStr] = true;
      else this[checkedStr] = false;
      if (type === "data") return list;
      if (type === "length") return list.length;
    },
    getNoCheckedList(arr, type = "data") {
      let list = arr.filter((item) => !item.checked);
      if (type === "data") return list;
      if (type === "length") return list.length;
    },
    aCheckBoxChange(e) {
      if (e) {
        this.alist.forEach((e) => (e.checked = true));
      } else {
        this.alist.forEach((e) => (e.checked = false));
      }
    },
    bCheckBoxChange(e) {
      if (e) {
        this.blist.forEach((e) => (e.checked = true));
      } else {
        this.blist.forEach((e) => (e.checked = false));
      }
    },
    //深拷贝
    deepClone(obj) {
      let newObj = null;
      if (typeof obj === "object" && obj !== null) {
        newObj = obj instanceof Array ? [] : {};
        for (let i in obj) {
          newObj[i] =
            typeof obj[i] === "object" ? this.deepClone(obj[i]) : obj[i];
        }
      } else {
        newObj = obj;
      }
      return newObj;
    },
    transferToRight() {
      let alist = this.deepClone(this.alist);
      let selectedList = this.getCheckedList({
        arr: alist,
        checkedStr: "achecked",
      });
      selectedList.forEach((item) => (item.checked = false));
      this.blist = [...this.blist, ...selectedList];
      this.alist = this.getNoCheckedList(this.alist);
    },
    transferToLeft() {
      let blist = this.deepClone(this.blist);
      let selectedList = this.getCheckedList({
        arr: blist,
        checkedStr: "bchecked",
      });
      selectedList.forEach((item) => (item.checked = false));
      this.alist = [...this.alist, ...selectedList];
      this.blist = this.getNoCheckedList(this.blist);
    },
  },
};
</script>
 
<style scoped lang="scss">
$card-width: 500px; //左右两侧card宽高
$ul-height: 500px; //ul高度
$li-height: 36px; //每个选项的高度

.transfer {
  display: flex;
  align-items: center;
  justify-content: center;

  .handler-button {
    margin: 0 20px;
    white-space: nowrap;
  }
}

ul,
li {
  padding: 0;
  margin: 0;
  list-style: none;
  display: block;
}

.select-option {
  background: #409eff !important;
  color: #ffffff;
}

.card {
  width: $card-width;
  border: 1px solid #ebeef5;
  border-radius: 10px;

  .card-header {
    background: #f5f7fa;
    height: 40px;
    line-height: 40px;
    padding: 0 16px;
    font-weight: bold;
    display: flex;
    align-items: center;
    justify-content: space-between;

    .number-box {
      font-weight: normal;
      font-size: 14px;
      color: #666;
    }
  }
  ul {
    padding: 10px;
    height: $ul-height;
    overflow-y: auto;

    li {
      height: $li-height;
      line-height: $li-height;
      padding: 0 20px;
      border-radius: 5px;
      font-size: 14px;
      background: #f5f5f5;
      margin: 10px 0;
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;

      &:hover {
        cursor: pointer;
        background: #ececec;
      }
    }
  }
}
</style>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jay丶萧邦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值