el-select组装树形结构,支持多选,回显

1,651 阅读1分钟

image.png

超级无敌的奇葩且复杂的需求,在朋友和我的努力下终于解决了

ps: 朋友给我写的demo,我负责cv github.com/OBQun/vue-p…

  1. 后端返回了部门数据,人员列表需根据最后一级获取
  2. 我需要把对应的人员跟部门组装成tree格式
  3. 支持多选,回显,人员搜索
  4. el-select下拉框不展示
  5. 回显的时候后端只返回了人员id
  6. 已经忘记还有什么了,太难了记不住,找朋友售后了N多次才能实现这么完美的需求

人员组件 UserPanel

<el-select
  class="filter-select"
  :value="value"
  remote
  :remote-method="getPersonList"
  filterable
  :loading="loading"
  @change="handleSelectChange"
  v-bind="$attrs"
  v-on="$listeners"
  :multiple="multiple"
  @blur="handleSelectBlur"
  @focus="popoverShow = false"
  :popper-class="selectQuery ? '' : 'hide-select-popper'"
    >
  <el-option
    v-for="person in personList"
    :key="person.userId"
    :value="person.userId"
    :label="person.nickName"
  ></el-option>
</el-select>
<el-popover
  v-model="popoverShow"
  trigger="click"
  placement="bottom"
  popper-class="person-select-popover"
>
  <el-cascader-panel
    :value="multiple ? value : null"
    :props="props"
    @change="handleCascaderChange"
    @expand-change="syncCascaderCheckState"
    ref="cascader"
  ></el-cascader-panel>
  <template #reference>
    <i class="el-icon-user-solid person-select-suffix"></i>
  </template>
</el-popover>
export default {
  model: {
    event: "change",
    prop: "value",
  },
  props: {
    value: {
      type: [String, Array, Number],
    },
    multiple: Boolean,
  },
  data() {
    return {
      selectQuery: "",
      popoverShow: false,
      loading: false,
      personList: [],
      props: {
        lazy: true,
        value: "id",
        emitPath: false,
        lazyLoad: ({ root, data }, resolve) => {  //组装tree结构
          if (root) { // 判断是否是一级,如果是直接返回
            treeselect().then(response => { // treeselect部门接口
              resolve(response.data);
            })
          } else if (data.children) {
            resolve(null);
          } else { // 不是一级并且没有children,请求人员列表
            listUser({ deptId: data.id }).then((res) => {
              resolve(
                res.rows.map(item => ({
                label: item.nickName,
                id: item.userId,
                leaf: true
              })) // 替换成自己想要的key,value
              )
              this.syncCascaderCheckState();
            })
          }
        },
      },
    };
  },
  methods: {
    syncCascaderCheckState() {
      if (this.multiple) {
        this.$refs.cascader.syncMultiCheckState();
      }
    },
    handleSelectBlur() {
      if (this.multiple) {
        this.personList = this.personList.filter(({ userId }) =>
          this.value.includes(userId)
        );
      }
    },
    handleSelectChange(value) {
      this.$emit("change", value);
      this.selectQuery = "";
      this.personList = this.getSelectedOptionsByValue(value);
    },
    getSelectedOptionsByValue(value) {
      return this.multiple
        ? this.personList.filter(({ userId }) => value.includes(userId))
        : [this.personList.find(({ userId }) => userId === value)].filter(
            Boolean
          );
    },
    handleCascaderChange(value) {
      const { cascader } = this.$refs;
      if (this.multiple) {
        cascader.getCheckedNodes(true).forEach(({ data: { id, label } }) => {
          if (this.personList.findIndex(({ userId }) => userId === id) === -1) {
            this.personList.push({ nickName: label, userId: id });
          }
        });
        this.$emit("change", [...new Set(value.concat(this.value))]);
      } else {
        this.popoverShow = false;
        if (value) {
          this.$emit("change", value);

          const [
            {
              data: { id, label },
            },
          ] = cascader.getCheckedNodes();
          this.personList = [{ nickName: label, userId: id }];
        }
        cascader.clearCheckedNodes();
        cascader.activePath = [];
        cascader.menus = cascader.menus.slice(0, 1);
      }
    },
    getPersonList(query) {
      this.selectQuery = query;
      if(!query) return
      this.loading = true;
      listUser({ nickName: query })
        .then(({rows}) => {
          this.personList = this.multiple
            ? rows.concat(
                this.personList.filter(
                  ({ userId }) =>
                    this.value.includes(userId) &&
                    !~rows.findIndex(({ userId }) => this.value.includes(userId))
                )
              )
            : rows;
        })
        .finally(() => {
          this.loading = false;
        });
    },
    initPersonList() {
      if (this.value) {
        this.loading = true;
        listUser()
          .then(({rows}) => {
            this.personList = this.multiple
              ? rows.filter(({ userId }) => this.value.includes(userId))
              : [rows.find(({ userId }) => userId === this.value)];
          })
          .finally(() => {
            this.loading = false;
          });
      }
    },
  },
  watch: {
    multiple: {
      handler(val) {
        this.props.multiple = val;
      },
      immediate: true,
    },
  },
  created() {
    this.initPersonList();
  },
};
// 使用,去掉multiple属性只能单选
user-panel-component v-model="form.bpId" clearable :multiple="isCss"></user-panel-component>