likes
comments
collection

Vant-UI之选择器封装(支持多选和单选且可默认多选选中值)

作者站长头像
站长
· 阅读数 13

需求描述

刚接手一个移动端的项目,是利用Vant-ui-2版本做的,由于每次都要自己写一个筛选器,要实现的功能可以搜索也可以选择单选也可多选并且在多选状态下可以有默认选中,于是乎就自己封装了一个,效果是这样的。

实现逻辑

  • 确定组件需要的参数,统一组件的入口和出口
  • 定义组件交互的逻辑(可输入查询、可选中添加、可多选、多选状态可默认选中)
  • 样式以及所用的原生组件

代码部分

<template>
  <van-popup
    v-model="isShow"
    position="bottom"
    :style="{ width: '100%', height: '100%' }"
  >
    <div class="side-box with-fix-top with-fix-bottom">
      <div class="search-panel fix-top">
        <div class="panel-bd">
          <div class="control-search">
            <input
              type="text"
              v-model="searchName"
              @keypress.enter="doSearch"
              :placeholder="placeholder"
            />
            <div class="icon icon-search" @click="doSearch"></div>
          </div>
        </div>
      </div>
      <div class="search-list-panel">
        <div class="panel-bd" v-if="list.length > 0">
          <div
            class="item"
            v-for="(item, index) in list"
            :key="index"
            :class="{ active: selectorType ? activeList.includes(item[valueName]):selectItem ? item[valueName] === selectItem[valueName] : false }"
            @click="select(item)"
          >
            <div class="value">{{ item[labelName] }}</div>
          </div>
        </div>
      </div>
      <div class="action-bar-panel" style="position: absolute">
        <div class="panel-bd">
          <div class="btn btn-main-light" @click="cancel">取消</div>
          <div class="btn btn-main" @click="confirm">确定</div>
        </div>
      </div>
    </div>
  </van-popup>
</template>

<script>
// 组件 - 通用的选择器

export default {
  name: 'SelectComponents',
  props: {
    list: {
      type: Array,
      default: () => []
    },
    // 选择器类型默认单选
    selectorType: {
      type: Boolean,
      default :false
    },
    //默认选中的数据
    defaultSelectList: {
      type: Array,
      default: () => []
    },
    activeItem: {
      type: Object,
      default: () => {}
    },
    valueName: {
      type: String,
      default: 'value'
    },
    labelName: {
      type: String,
      default: 'label'
    },
    isShow: {
      type: Boolean,
      default: false
    },
    placeholder: {
      type: String,
      default: '请输入'
    }
  },
  data() {
    return {
      searchName: '',
      selectItem: null,
      selectList: [],
      activeList: []
    };
  },
  watch: {
    activeItem: {
      handler(newValue) {
        this.selectItem = newValue;
      },
      deep: true
    }
  },
  methods: {
    // 初始化
    doInit() {
      this.activeList = [];
      this.selectList = [];
      //判断是否多选且传入默认选中的值
      if (this.selectorType && this.defaultSelectList.length > 0) {
        defaultSelectList.forEach((e) => {
          this.activeList.push(e[this.valueName]);
          this.selectList.push(e);
        });
      }
    },
    // 搜索
    doSearch() {
      this.$emit('search', this.searchName);
    },
    // 选择
    select(item) {
      //多选
      if (this.selectorType) {
        const index = this.activeList.indexOf(item[this.valueName]);
        if (index > -1) {
          this.activeList.splice(index, 1);
          this.selectList.splice(index, 1);
        } else {
          this.activeList.push(item[this.valueName]);
          this.selectList.push(item);
        }
      }
      //单选 
      else {
        this.selectItem = item;
      }
    },
    // 确认
    confirm() {
      if (this.selectorType) {
        let List = this.selectList;
        if (List.length === 0) {
          this.$toast('请选择至少一条数据');
          return false;
        }
        this.$emit('confirm', List);
        this.cancel();
      } else {
        let item = this.selectItem;
        if (!item) {
          this.$toast('请选择至少一条数据');
          return false;
        }
        this.$emit('confirm', item);
      }
      this.cancel();
    },
    // 返回
    cancel() {
      this.$emit('close');
    }
  }
};
</script>

使用

  • 使用$refs调用组件的doInit方法进入同时控制isShow为true或fasle
  • 多选需要通过传入selectorType为true
  • 多选且有默认选中时需要传入defaultSelectList且此默认选中的数组格式和传入的list一致(暨valueName和labelName要是同样的字段)

写到最后

如有不足之处,还请各位大佬指正,后续持续优化