Element UI-Select选择器结合树形控件终极版

Element UI Select选择器控件结合树形控件实现单选和多选,并且通过v-model的方式实现节点的双向绑定,封装成vue组件,文件名为electricity-meter-tree.vue,其代码如下:

<template>
  <div>
    <el-select
        :value="selectedId"
        :multiple="multiple"
        placeholder="请选择"
        ref="selectTree"
        clearable
        @change="handleChange"
        @clear="handleClear">
      <el-option v-for="form in hiddenForm" :key="form.id" :value="form.id" :label="form.label" hidden/>
      <div style="padding: 10px 14px">
        <el-input placeholder="输入关键字进行过滤" v-model="filterText" clearable>
          <template #suffix>
            <el-button type="text" icon="el-icon-search" style="max-width: 22px">
              <i class="el-icon-search" style="display: none;"></i>
            </el-button>
          </template>
        </el-input>
        <el-tree
            v-loading="loading"
            :data="treeData"
            ref="tree"
            :props="defaultProps"
            node-key="id"
            accordion
            default-expand-all
            :filter-node-method="filterNode"
            :show-checkbox="multiple"
            :check-strictly="checkStrictly"
            :highlight-current="highLightCurrent"
            @check-change="handleCheckChange"
            @node-click="handleNodeClick">
            <!-- 来源于省的电表以蓝色底标识。-->
             <template v-slot="{ node, data }">
               <span v-if="data.sources === 'province'" style="background: #1890ff;color: #FFFFFF;padding: 2px;">
                 {{ node.label}}
               </span>
               <span v-else>{{ node.label}}</span>
            </template>
        </el-tree>
      </div>
    </el-select>
  </div>
</template>

<script>

export default {
  model: {
    prop: 'value',
    event: 'change'
  },
  props: {
    value: {
      type: [Object, Array], // 根据实际情况选择类型
      default: () => {} // 格式:{id: 'xx', label: 'xx'}或者[{id: 'xx', label: 'xx'}]
    },
    multiple: { // 是否多选
      type: Boolean,
      default: false
    },
    checkStrictly: { // 父子是否不互相关联
      type: Boolean,
      default: false
    },
  },
  watch: {
    value: {
      handler (val) {
        if (this.multiple) {
          if (val && val.length > 0) {
            this.hiddenForm = val;
            this.selected = val;
          }else {
            this.hiddenForm = [{id: '', label: ''}];
            this.selected = [];
          }
        } else {
          if (val) {
            this.selected = val;
            this.hiddenForm = [val];
          }else {
            this.hiddenForm = [{id: '', label: ''}];
            this.selected = null;
          }
        }
      },
      immediate: true,
      deep: true
    },
    filterText(val) {
      this.$refs.tree.filter(val);
    }
  },
  computed: {
    selectedId () {
      if (this.multiple) {
        return this.selected && this.selected.length > 0 ? this.selected.map(item => item.id) : [];
      }
      return this.selected? this.selected.id : '';
    },
  },
  data () {
    return {
      filterText: '',
      treeData: [],
      defaultProps: {
        children: 'children',
        label: 'label',
        id: 'id',
        disabled: 'disabled'
      },
      selected: [],// 格式:{} 或者 []
      hiddenForm: [
        {
          id: '',
          label: ''
        },
      ],
      highLightCurrent: true,
      loading: false,
    }
  },
  methods: {
    filterNode(value, data) {
      if (!value) return true;
      return data.label.indexOf(value) !== -1;
    },
    handleCheckChange (data, checked) {
      if (this.multiple) { // 多选
        const nodes = this.$refs.tree.getCheckedNodes();
        this.hiddenForm = nodes.length > 0 ? nodes : [{id: '', label: ''}];
        // 保证重现加载数据时,已选中的数据不被清除
        if (checked) {
          if (!this.selected || this.selected.length === 0) {
            this.selected = nodes;
          }else {
            // 定义一个函数来去重
            function removeDuplicates(array, uniqueKey) {
              let seen = new Map(); // 使用 Map 来存储已见过的 id 和对应对象
              // 遍历数组中的每个对象
              for (let obj of array) {
                // 如果 Map 中还没有这个 id,就添加进去
                if (!seen.has(obj[uniqueKey])) {
                  seen.set(obj[uniqueKey], obj); // 使用 id 作为 key,对象作为 value
                }
              }
              // 从 Map 中提取所有对象组成新的数组返回
              return Array.from(seen.values());
            }
            this.selected = removeDuplicates([...this.selected, ...nodes], 'id');
          }
        }else {
          this.selected = this.selected?.filter(item => item.id !== data.id);
        }
        this.$emit('change', this.selected);
      } else {}
    },
    handleNodeClick (data, node) {
      if (data.disabled) return;
      if (this.multiple) {} else { // 单选
        this.hiddenForm = [
          {
            id: data.id,
            label: data.label
          }
        ]
        this.selected = data;
        this.$emit('change', data);
        // 使 input 失去焦点,并隐藏下拉框
        // node.isLeaf && this.$refs.selectTree.blur();
      }
    },
    handleChange (value) {
      if (!value) return;
      this.selected = this.selected?.filter(item => value.includes(item.id));
      this.$emit('change', this.selected);
      this.multiple && this.$refs.tree.setCheckedKeys(this.selectedId);
    },
    handleClear () {
      this.selected = [];
      this.multiple && this.$refs.tree.setCheckedKeys([]);
      this.hiddenForm = [
        {
          id: '',
          label: ''
        }
      ];
      this.highLightCurrent = false;
      this.$emit('change', this.multiple ? [] : null);
    },
    getTreeDataAsync() {
      // 这里模拟调用后端接口返回的数据
      this.treeData = [
        {
          id: '1',
          label: 'XX市智能电表',
          children: [
            {
              id: '1-1',
              label: '通用智能电表A',
              children: [
                {
                  id: '1-1-1',
                  label: '通用智能电表B',
                  sources: 'province',
                },
                {
                  id: '1-1-2',
                  label: '通用智能电表C',
                },
              ]
            },
            {
              id: '1-2',
              label: '智能电表01',
              children: [
                {
                  id: '1-2-1',
                  label: '智能电表02',
                  sources: 'province',
                }
              ]
            },
          ]
        }
      ];
    }
  },
  created() {
    this.getTreeDataAsync();
  },
}
</script>

<style scoped>
</style>

该组件的用法如下:

 <electricity-meter-tree v-model="electricityMeter" multiple checkStrictly @change="electricityMeterChange"/>
  1. 引入组件:electricity-meter-tree
  2. 如果multiple为true多选时,那么electricityMeter的结构为[{id: ‘xx’, label: ‘xx’}],如果 mutilple为false单选时,那么electricityMeter的结构为{id: ‘xx’, label: ‘xx’}
  3. checkStrictly是否父子不互相关联,前提是multiple为true
  4. change事件也可以获取选中的节点

效果如下:
在这里插入图片描述

### Element-UI Select 选择器使用教程 #### 基本概念 Element-UI 提供了一个强大的 `el-select` 组件用于创建下拉列表,允许用户从中选择选项。此组件支持单选和多选模式,并提供了丰富的配置项以满足不同的需求。 #### 属性说明 - **v-model**: 双向数据绑定到所选值上,在单选情况下应为具体值;而在多选场景则需指定数组形式的数据源。 - **multiple**: 设置该属性可开启多项选择功能,默认关闭。 - **placeholder**: 定义未做任何选择前显示的文字提示信息。 - **disabled**: 是否禁用整个控件,默认不启用。 #### 配置选项 对于每一个可供选取的对象而言,则通过嵌套于 `<el-select>` 内部的多个 `<el-option>` 来定义它们各自的特性: - **:key**: 每一项唯一标识符,推荐采用 ID 字段作为键名。 - **:label**: 显示给用户的名称标签。 - **:value**: 实际存储并提交至服务器端的关键字或数值[^2]。 ```html <template> <div class="select-demo"> <!-- 单选 --> <el-select v-model="selectedUser" placeholder="请选择"> <el-option v-for="item in users" :key="item.id" :label="item.name" :value="item.id"> </el-option> </el-select> <!-- 多选 --> <el-select v-model="selectedUsers" multiple placeholder="请选择"> <el-option v-for="item in users" :key="item.id" :label="item.name" :value="item.id"> </el-option> </el-select> </div> </template> <script> export default { data() { return { selectedUser: '', selectedUsers: [], users: [ { id: '001', name: '张三' }, { id: '002', name: '李四' } ] }; } }; </script> ``` #### 动态响应事件处理 为了增强交互体验,开发者还可以监听特定时刻触发的动作,比如当选择了新条目或是移除了已有的标记时。这可以通过添加相应的钩子函数来完成,如 `@change` 或者 `@remove-tag` 等[^3]。 ```javascript // 监听 change 事件 methods: { handleChange(value) { console.log('Selected value:', value); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值