vue3 组件之穿梭框2.0

如图所示,没有传统穿梭框中间的箭头,操作更加方便,左侧选择后进入到右侧,并且可以对右侧所选项进行删除,从而左侧勾选也会对应取消。并且带搜索框。

代码如下:

(涉及到深拷贝的一些知识)

 

<template>
  <div style="display: inline-block">
    <!-- 下拉选择 -->
    <el-select
      class="search-input"
      v-model="peopleVal"
      placeholder="请选择"
      ref="selectPeople"
      @focus="openDialog"
    >
    </el-select>
    <!--弹窗 -->
    <el-dialog v-model="dialog" :title="title" @open="openFn" width="630px">
      <el-form label-width="70px">
        <div class="transfer-view">
          <div class="item-label">选择人员</div>
          <div class="checked-box">
            <div class="search-box">
              <el-input
                v-model.trim="searchName"
                placeholder="请输入关键词"
                :prefix-icon="Search"
                @input="searchFn"
                clearable
              ></el-input>
            </div>

            <el-checkbox
              v-show="!searchName"
              :indeterminate="isIndeterminate"
              v-model="checkAll"
              @change="handleCheckAllChange"
              >全选</el-checkbox
            >
            <el-checkbox-group @change="handleChecked" v-model="checkList">
              <el-checkbox
                class="checkbox-item"
                v-for="v in personList"
                :label="v.id"
                :key="v.id"
              >
                {{ v.realName }}
              </el-checkbox>
            </el-checkbox-group>
          </div>
          <div class="select-checked-box">
            <div class="search-box">
              <span class="selected">已选:{{ checkList.length }}人</span>
            </div>
            <div class="select-perosn" v-for="v in checkPerson" :key="v.id">
              <span>
                {{ v.realName }}
              </span>
              <i
                class="iconfont icon-a-zujian18719 icon-error"
                @click="handleRemove(v)"
              ></i>
            </div>
          </div>
        </div>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="dialog = false">取消</el-button>
          <el-button type="primary" @click="submitFn">提交</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>

 

 

<script>
import { reactive, toRefs, watch } from "vue";
import { Close, Search } from "@element-plus/icons";
import { sysUserApi } from "@/http/projectManagement/underMaintenance.js"; //获取人员列表的接口,自行替换
export default {
  emits: ["handleCancel", "handleSure"],
  components: { Close },
  props: {
    dialogFormVisible: {
      type: Boolean,
      default: false,
    },
    title: {
      type: String,
      require,
    },
  },
  setup(props, { emit }) {
    const data = reactive({
      dialog: false, // 弹窗
      checkList: [], // 左侧已选数据Id
      checkPerson: [], // 右侧已选的数据
      searchName: "", // 搜索数据
      checkAll: false, // 全选
      isIndeterminate: false,
      personList: [],
      allPersonList: [], // 所有人员
    });
    watch(
      () => data.personList,
      (newVal, oldVal) => {
        if (data.checkAll) {
          if (newVal.length > oldVal.length) {
            data.checkAll = false;
            data.isIndeterminate = true;
          }
        }
        console.log(newVal, oldVal);
      }
    );
    //打开弹窗
    const openFn = () => {
      sysUserApi({}).then((res) => {
        if (res.data.code === 200) {
          data.personList = res.data.data;
          data.allPersonList = res.data.data;
        }
      });
    };
    // 选择复选框
    const handleChecked = (val) => {
      handleCheck(data.checkList);
      let checkedCount = data.checkList.length;
      data.checkAll = checkedCount === data.personList.length;
      data.isIndeterminate =
        checkedCount > 0 && checkedCount < data.personList.length;
    };
    // 深拷贝
    const handleCheck = (val) => {
      let arr = [];
      val.forEach((v) => {
        let a = data.allPersonList.filter((item) => item.id === v);
        if (a.length) {
          arr.push(...a);
        }
      });
      data.checkPerson = arr;
    };
    // 全选
    const handleCheckAllChange = (val) => {
      let arr = data.personList.map((v) => v.id);
      data.checkList = val ? arr : [];
      val ? handleCheck(arr) : handleCheck([]);
      data.isIndeterminate = false;
    };
    // 删除
    const handleRemove = (val) => {
      data.checkPerson.forEach((item, index) => {
        if (item.id === val.id) {
          data.checkPerson.splice(index, 1);
          data.checkList.splice(index, 1);
        }
      });
      if (!data.checkList.length) {
        data.checkAll = false;
        data.isIndeterminate = false;
      }
    };
    // 搜索
    const searchFn = () => {
      let list = [];
      data.allPersonList.map((item) => {
        if (item.realName.indexOf(data.searchName) > -1) {
          list.push(item);
        }
      });
      data.personList = list;
    };
    // 下拉框信息
    const selectInfo = reactive({
      selectPeople: null, // 选人dom
      peopleVal: "",
      openDialog() {
        selectInfo.selectPeople.blur();
        data.dialog = true;
      },
    });
    // 弹窗确定
    const submitFn = () => {
        data.dialog = false;
        let box = [];
        data.checkPerson.map((item) => {
          box.push(item.realName);
        });
        selectInfo.peopleVal = box.join(",");
    };
    return {
      ...toRefs(data),
      ...toRefs(selectInfo), // 下拉框信息
      openFn, //打开弹窗
      submitFn, // 确定
      handleCheck,
      handleCheckAllChange, // 全选事件
      handleChecked, // 选择复选框
      handleRemove, // 删除
      searchFn, // 搜索
      Search, // 搜索图标
    };
  },
};
</script>

 

<style lang="scss" scoped>
/deep/ .el-radio__label {
  color: #888ea2;
}
.transfer-view {
  width: 100%;
  height: 25rem;
  display: flex;
  margin-top: 1rem;
  .checked-box,
  .select-checked-box {
    width: 15rem;
    height: 25rem;
    border: 1px solid #eeeeee;
    border-radius: 8px;
    padding: 1.31rem;
    box-sizing: border-box;
    overflow-y: auto;
    overflow-x: hidden;
  }
  .search-box {
    font-family: Source Han Sans CN;
    font-weight: 400;
    display: flex;
    height: 2.4rem;
    justify-content: space-between;
    align-items: center;
    color: #fff;
    margin-bottom: 0.8rem;
    .selected {
      color: #888ea2;
    }
  }
  .checked-box {
    margin-right: 1.25rem;
  }
  .item-label {
    width: 60px;
    margin-right: 12px;
    height: 100%;
    text-align: right;
    font-weight: 400;
  }
  .el-input {
    width: 12.5rem;
    height: 2.38rem;
    /deep/ .el-input__inner {
      width: 100%;
      height: 100%;
    }
  }
  .checkbox-item {
    display: block;
    margin-top: -5px;
    line-height: 40px;
    margin-left: 15px;
  }
  .select-perosn {
    color: #888ea2;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-right: 1rem;
    box-sizing: border-box;
    > span {
      display: inline-block;
      max-width: 14rem;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .icon-error {
      font-size: 14px;
      line-height: 14px;
      cursor: pointer;
    }
  }
}

// /*滚动条里面轨道*/
::-webkit-scrollbar-track {
  border-radius: 3px;
  background: #3d609100;
}
</style>

 

posted @ 2022-04-20 15:16  如意酱  阅读(2014)  评论(1编辑  收藏  举报