vue+elememt-ui el-select组件封装

最终效果图:
在这里插入图片描述
用的是vue2写法,喜欢用vue3的同学可以自行修改下。

需求要求

  1. 实现el-select功能复用;
  2. 支持单选或者多选功能;
  3. 支持全拼或者简拼搜索功能;

直接上代码:

pinyin.js文件

import PinYin from "pinyin";
/**
 *
 * @param { String } q - 翻译的全拼key值
 * @param { String } j - 翻译的简拼key值
 * @return { Object }
 */
export default function(str, q, j) {
  q = q || "q";
  j = j || "j";
  let obj = {
    [q]: "",
    [j]: ""
  };
  if (str) {
    obj[q] =
      PinYin(str, {
        style: PinYin.STYLE_NORMAL
      }).join("") || "";
    obj[j] =
      PinYin(str, {
        style: PinYin.STYLE_FIRST_LETTER
      }).join("") || "";
  }
  return obj;
}
// 注册全局汉字转拼音方法
import pinyin from "@/utils/pinyin";
Vue.prototype.$pinyin = pinyin;
父组件
<select-multiple
  :id.sync="form.shop"
  :list="initData.shops"
  name="适用门店"
  placeholder="请选择适用门店"
  type="Array"
></select-multiple>
子组件
<template>
  <el-select
    v-model="data_id"
    :placeholder="placeholder || `请选择${name}`"
    @change="changeData"
    clearable
    filterable
    collapse-tags
    :no-data-text="`未搜索到${name}`"
    loading-text="搜索中"
    :filter-method="remoteMethod"
    :loading="loading"
    :multiple="multiple"
    @visible-change="visibleChange"
    :default-first-option="true"
    :disabled="disabled"
  >
    <div style="text-align: center; margin-bottom: 10px" v-show="multiple && isShowCheck">
      <el-checkbox v-model="checked" @change="dataSelectAll">全选</el-checkbox>
    </div>
    <el-option
      v-for="item in options"
      :key="item[value] + Math.random()"
      :label="item[label]"
      :value="item[value]"
    >
    </el-option>
  </el-select>
</template>

<script>
export default {
  props: {
    // 搜索placeholder
    placeholder: {
      type: String,
      default: ""
    },
    // 搜索名称
    name: {
      type: String,
      default: "门店"
    },
    // option选项的value键名
    value: {
      type: String,
      default: "id"
    },
    // option选项的label键名
    label: {
      type: String,
      default: "name"
    },
    // 传进来的选中数据
    id: [String, Number, Array],
    // 筛选项数据
    list: Array,
    // 多选数据分隔符
    separator: {
      type: String,
      default: "_"
    },
    // 是否多选
    multiple: {
      type: Boolean,
      default: true
    },
    // 传值类型 string、array
    type: {
      type: String,
      default: "String"
    },
    // 是否禁用
    disabled: {
      type: Boolean,
      default: false
    },
    // 全拼的键名,默认为q
    q: {
      type: String,
      default: "q"
    },
    // 简拼的键名,默认为j
    j: {
      type: String,
      default: "j"
    }
  },
  data() {
    return {
      // 选中数据ID
      data_id: [],
      // 搜索数据加载状态
      loading: false,
      // 简拼搜索添加防抖
      methodTimer: null,
      // 获取到全部的数据
      allOptions: [],
      // 展示的数据
      options: [],
      // 全选、反选
      checked: false,
      // 是否显示全选按钮
      isShowCheck: true
    };
  },
  methods: {
    // 监听选择值变化
    changeData(val) {
      if (this.multiple) {
        if (val.length === this.allOptions.length) {
          this.checked = true;
        } else {
          this.checked = false;
        }
        if (this.type === "String") {
          this.$emit("update:id", val.join(this.separator));
          this.$emit("change", val.join(this.separator));
        } else if (this.type === "Array") {
          this.$emit("update:id", val);
          this.$emit("change", val);
        }
      } else {
        this.$emit("update:id", val);
        this.$emit("change", val);
      }
    },
    // 搜索输入字符监控
    remoteMethod(query) {
      if (this.methodTimer) {
        clearTimeout(this.methodTimer);
      }
      if (query !== "") {
        this.isShowCheck = false;
        this.loading = true;
        if (this.methodTimer) {
          clearTimeout(this.methodTimer);
        }
        query = query.toLowerCase();
        this.methodTimer = setTimeout(() => {
          this.options = this.allOptions.filter(i => {
            return (
              (i[this.q] && i[this.q].indexOf(query) !== -1) ||
              (i[this.j] && i[this.j].indexOf(query) !== -1) ||
              (i[this.label] && i[this.label].indexOf(query) !== -1)
            );
          });
          this.loading = false;
          clearTimeout(this.methodTimer);
        }, 500);
      } else {
        this.loading = false;
        this.isShowCheck = true;
        this.computedOptions();
      }
    },
    // 给可选项赋值全部数据
    computedOptions() {
      this.options = JSON.parse(JSON.stringify(this.allOptions));
    },
    // 下拉框显示隐藏事件
    visibleChange(val) {
      if (val) {
        this.computedOptions();
      }
    },
    // 选择门店全选&&反选
    dataSelectAll() {
      this.data_id = [];
      if (this.checked) {
        this.data_id = this.allOptions.map(item => {
          return item[this.value];
        });
      }
      this.changeData(this.data_id);
    }
  },
  watch: {
    // 监听门店数据变化
    list: {
      handler(val) {
        if (val.length) {
          const options = JSON.parse(JSON.stringify(val));
          this.allOptions = options.map(i => {
            return Object.assign(i, this.$pinyin(i[this.label], this.q, this.j));
          });
          this.computedOptions();
        } else {
          this.allOptions = [];
          this.options = [];
        }
      },
      deep: true,
      immediate: true
    },
    id: {
      handler(val) {
        if (this.multiple) {
          let isTrue = false;
          if (this.type === "String") {
            isTrue = this.data_id.join(this.separator) != val;
          } else {
            isTrue = this.data_id.join(this.separator) != val.join(this.separator);
          }
          if (isTrue) {
            if (!val) {
              this.checked = false;
              this.data_id = [];
            } else {
              if (this.type === "String") {
                this.data_id = val.split(this.separator);
              } else {
                this.data_id = val;
              }
              this.computedOptions();
            }
          }
        } else {
          this.data_id = val;
          this.computedOptions();
        }
      },
      deep: true,
      immediate: true
    }
  }
};
</script>

<style lang="scss" scoped>
::v-deep.el-select {
  width: 100%;
}
::v-deep.el-select .el-select__tags {
  .el-tag {
    max-width: 100px;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}
</style>

希望对大家有帮助哟!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值