Vue 仿Transfer 穿梭框功能实现人员选择,左侧备选列表,右侧已选列表,带搜索带备注项

老规矩,先上效果图

在这里插入图片描述
设计出来的东西,非说element组件库穿梭框就有 , 扯犊子, 人名下面的手机号怎么加md…

还得靠自己实现 , 真操蛋啊…

  1. 左右侧数据带搜索,前端本地实现搜索功能
  2. 后台接口对接,代码中还未对接
  3. 如有帮助到您,还望一键三连

代码

<template>
  <el-drawer
    class="associated-member"
    title="关联成员"
    :visible.sync="drawer"
    :direction="direction"
    :append-to-body="modalToBody"
    :modal-append-to-body="modalToBody"
    :size="685"
  >
    <el-col :span="12" class="section-left">
      <div class="header">
        <el-checkbox
          :indeterminate="isIndeterminate"
          v-model="checkAll"
          @change="handleCheckAllChange"
        >
          全选
        </el-checkbox>
      </div>
      <el-input
        class="search"
        size="mini"
        placeholder="请输入姓名搜索"
        suffix-icon="el-icon-search"
        v-model="searchTextLeft"
        clearable
        @input="getSearch"
      />
      <el-checkbox-group
        class="checkbox-group"
        v-model="checkedRoles"
        @change="handleCheckedCitiesChange"
      >
        <el-checkbox
          v-for="(role, index) in leftData"
          :label="role.id"
          :key="role.id"
        >
          {{ role.name }}
          <p class="mt-10">{{ role.phone }}</p>
        </el-checkbox>
      </el-checkbox-group>
    </el-col>
    <el-col :span="12" class="section-right">
      <div class="header">管理员</div>
      <el-input
        class="search"
        size="mini"
        placeholder="请输入姓名搜索"
        suffix-icon="el-icon-search"
        v-model="searchTextRight"
        clearable
        @input="getSearch"
      />
      <div class="checked-group">
        <el-col
          class="li"
          v-for="(role, index) in selectedRoles"
          :key="role.name + role.id"
        >
          {{ role.name }}
          <p class="mt-10">{{ role.phone }}</p>
          <zn-icon :iconName="'icon-close'" @click="delRole(role, index)" />
        </el-col>
      </div>
    </el-col>
    <div class="drawer-footer">
      <el-button class="el-cancel" @click="drawer = false">取消</el-button>
      <el-button type="primary" @click="save">确定</el-button>
    </div>
  </el-drawer>
</template>
<script>
export default {
  name: 'AssociatedMember',
  props: {
    modalToBody: {
      type: Boolean,
      default: () => true,
    },
  },
  data() {
    return {
      drawer: false,
      direction: 'rtl',
      checkAll: false,
      checkedRoles: [1, 3],
      roles: [
        { name: '上海', id: 1, phone: '18989898998' },
        { name: '广州', id: 2, phone: '18989898998' },
        { name: '北京', id: 3, phone: '18989898998' },
        { name: '深圳', id: 4, phone: '18989898998' },
        { name: '石家庄', id: 5, phone: '18989898998' },
        { name: '唐山', id: 6, phone: '18989898998' },
        { name: '秦皇岛', id: 7, phone: '18989898998' },
        { name: '邯郸', id: 8, phone: '18989898998' },
        { name: '邢台', id: 9, phone: '18989898998' },
        { name: '保定', id: 10, phone: '18989898998' },
        { name: '张家口', id: 11, phone: '18989898998' },
        { name: '承德', id: 12, phone: '18989898998' },
        { name: '沧州', id: 13, phone: '18989898998' },
        { name: '廊坊', id: 14, phone: '18989898998' },
      ],
      isIndeterminate: true,
      searchTextLeft: '',
      searchTextRight: '',
    }
  },
  computed: {
    // 右侧已选数据
    selectedRoles() {
      let arr = []
      if (this.checkedRoles.length > 0) {
        arr = this.roles.filter((item) => this.checkedRoles.includes(item.id))
        if (this.searchTextRight) {
          return this.compare(
            arr.filter((i) => i.name.includes(this.searchTextRight))
          )
        } else {
          return this.compare(arr) //根据人名排序
        }
      }
    },
    leftData() {
      if (this.searchTextLeft) {
        return this.roles.filter((i) => i.name.includes(this.searchTextLeft))
      } else {
        return this.roles
      }
    },
  },
  methods: {
    showEdit(row) {
      this.drawer = true
    },
    // 处理搜索
    getSearch() {
      if (this.searchTextLeft) {
        return this.leftData
      }
      if (this.searchTextRight) {
        return this.selectedRoles
      }
    },
    handleCheckAllChange(val) {
      this.checkedRoles = val
        ? this.roles.map((i) => {
            return i.id
          })
        : [] //深拷贝解决全选之后单个删除右侧数据会触发左侧数据源的删除
      this.isIndeterminate = false
    },
    handleCheckedCitiesChange(value) {
      let checkedCount = value.length
      this.checkAll = checkedCount === this.roles.length
      this.isIndeterminate =
        checkedCount > 0 && checkedCount < this.roles.length
    },
    // 删除某一个
    delRole(role, index) {
      let idx = this.checkedRoles.findIndex((itm) => itm == role.id)
      if (idx != -1) {
        this.checkedRoles.splice(idx, 1)
      }
    },
    // 提交
    save() {
      console.log('this.selectedRoles', this.selectedRoles)
    },
  },
}
</script>
<style lang="scss" scoped>
.associated-member {
  position: relative;
  .el-select {
    width: 100%;
  }
  .el-drawer__body {
    padding: 16px 24px;
    .section-left,
    .section-right {
      height: calc(100% - 68px);
      overflow: hidden;
      border: 1px solid $base-line-2;
      border-collapse: collapse;
      .header {
        height: $base-input-height;
        padding-left: 16px;
        line-height: $base-input-height;
        background: $base-fill-2;
      }
      .search {
        width: 100%;
        padding: 8px 12px;

        border-radius: 2px;
        .el-input__inner {
          background: $base-fill-3;
        }
        ::v-deep .el-input__suffix {
          margin-right: 10px !important;
          top: 2px;
          color: $base-fill-6;
        }
      }
      ::v-deep.checkbox-group {
        height: calc(100% - 84px);
        overflow-y: auto;

        .el-checkbox {
          width: 100%;
          height: 62px;
          padding: 7px 16px 0 16px;
          margin-right: 0;
          //   padding: 10px 0;
          .el-checkbox__input {
            margin-top: 3px;
            vertical-align: top;
          }
          p {
            color: $base-text-3;
          }
          &:hover {
            background: $base-fill-2;
          }
        }
      }
    }
    .section-left {
      border-top-left-radius: $base-border-radius-4;
      border-bottom-left-radius: $base-border-radius-4;
    }
    .section-right {
      border-left: none;
      border-top-right-radius: $base-border-radius-4;
      border-bottom-right-radius: $base-border-radius-4;
      .checked-group {
        height: calc(100% - 84px);
        overflow-y: auto;
        .li {
          height: 62px;
          padding: 7px 16px 0 16px;
          position: relative;
          p {
            line-height: 21px;
            color: $base-text-3;
          }
          .zn-icon {
            position: absolute;
            right: 28px;
            top: 23px;
            color: $base-fill-5;
          }
          &:hover {
            background: $base-fill-2;
          }
        }
      }
    }
  }
  // drawer footer
  .drawer-footer {
    justify-content: flex-end;
    background: #fff;
    position: absolute;
    left: 0;
    bottom: 0;
    right: 0;
    padding: 16px 24px;
    box-shadow: 0px -4px 10px 0px #0000001a;
    .el-button {
      padding: 0;
      min-width: 83px;
      line-height: 36px;
      border-radius: 4px;
      font-size: 14px;
      font-weight: bold;
      border: none;
    }
    .el-cancel {
      background: $base-fill-3;
    }
  }
}
</style>

补充方法

this.compare(arr)方法

补充样式

scss变量

$base-line-2:#E5E6EB;
$base-input-height: 40px;
$base-fill-3:#F2F3F5;
$base-fill-6:#4E5969;
$base-border-radius-4:4px;
$base-border-radius-6:6px;

滚动条样式

@mixin base-scrollbar {
  &::-webkit-scrollbar {
    width: 4px;
    height: 4px;
  }

  &::-webkit-scrollbar-thumb {
    background-color: rgba($base-menu-background, 0.1);
    border: 3px solid transparent;
    border-radius: 7px;
  }

  &::-webkit-scrollbar-thumb:hover {
    background-color: rgba($base-menu-background, 0.2);
  }
}
  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

李泽举

如对你有帮助,那我就没白写

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

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

打赏作者

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

抵扣说明:

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

余额充值