vue实战:构建通用的下拉树组件

记录基于Vue.js的移动端Tree树形组件 Vue 树形选择器( Vue tree select )组件在搭建 Vue 的 app 中特别常用,Vue tree select 除了简单的树形结构外,还有非常多样的功能来配合不同场景的使用。比如搜索过滤,前端添加删除树枝,前端编辑修改子树名,拖拽排序,对用户操作事件记录等。本文记录了我自己使用多年最好用的 2款 Vue tree select 组件,每一款都经过我实际测试,推荐给大家。 阅读详情
AI摘要

博客介绍了在Vue项目中实现下拉树组件的方法。一是使用@riophae/vue-treeselect插件,涵盖安装、引入、基础与进阶使用,还解决了占位符、数据提示英文等常见问题;二是通过自定义组件,介绍了自定义属性和方法、封装及使用文件。

前言

在 vue 中,如果你想实现一个下拉树的组件,可以直接使用 element plus 中的 treeSelect 组件,但是如果你的项目正在用的是 element 2.X 版本,那么它是不包含 treeSelect 组件的,但是我们还是可以基于一些第三方的插件或者自己封装组件实现这个操作。


一、@riophae/vue-treeselect 插件

riophae/vue-treeselect 是一个基于 vue.js 的插件,它提供了一个树形选择器组件,用于选择树形结构的数据。该插件支持多选、搜索、异步加载等功能,并且可以自定义选项的样式和模板。它的易用性和扩展性使其适用于各种类型的项目。

1.1 安装

npm/cnpm 或者 yarn 安装

npm i @riophae/vue-treeselect
yarn add @riophae/vue-treeselect

1.2 引入

入口文件 main.js 全局引入

import Treeselect from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
Vue.component('treeselect', Treeselect)

使用文件局部引入

<script>
import Treeselect from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
export default {
  components: {
    Treeselect
  },
}
</script>

1.3 基础使用

<template>
  <div class="box">
    <treeselect v-model="selectedItems" placeholder="请选择" :options="treeData"></treeselect>
    <el-button @click="getSelectedItems">获取选中的数据</el-button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      selectedItems: null,
      treeData: [
        {
          id: 1,
          label: "水果",
          children: [
            { id: 2, label: "西瓜" },
            { id: 3, label: "香蕉" },
            { id: 4, label: "橙子" },
          ],
        },
        {
          id: 5,
          label: "蔬菜",
          children: [
            { id: 6, label: "西红柿" },
            { id: 7, label: "黄瓜" },
            { id: 8, label: "青菜" },
          ],
        },
        {
          id: 9,
          label: "零食",
          children: [
            { id: 10, label: "薯片" },
            { id: 11, label: "巧克力" },
          ],
        },
      ],
    };
  },
  methods: {
    getSelectedItems() {
      console.log(this.selectedItems);
    },
  },
};
</script>

实现效果

在这里插入图片描述


1.4 进阶使用

  • 常用的属性
属性描述
v-model=“value”使用v-model指令绑定选中的项
:options=“treeData”设置树形数据
:multiple=“true”允许多选
:clearable=“true”显示清除按钮
:searchable=“true”显示搜索框
:disabled=“false”是否禁用
:openOnFocus=“true”获得焦点时自动展开下拉菜单
:openOnClick=“true”点击时自动展开下拉菜单
:auto-load-root-options=“true”自动加载根级选项
:async=“true”异步加载选项
:load-options=“loadOptions”异步加载选项的方法
:noChildrenText=“‘没有子选项’”没有子选项时的文本提示
:noOptionsText=“‘没有可选项’”没有可选项时的文本提示
:noResultsText=“‘没有匹配的结果’”搜索结果为空时的文本提示
:placeholder=“‘请选择’”占位符文本
:appendToBody=“true”弹出框是否附加到body元素
:normalizer=“normalizeOptions”规范化选项数据。通过normalizer属性,可以自定义选项数据的结构,以适应插件的要求
valueFormat=“”能够决定value属性的格式。当设置为"id"时,value属性的格式就是 id 或 id数组。当设置为"object"时,value属性的格式就是 node 或 node数组
  • 常用的方法
方法描述
@open=“handleOpen”下拉菜单打开时触发的事件
@close=“handleClose”下拉菜单关闭时触发的事件
@deselect=“handleRemove”移除选中项时触发的事件
@search-change=“handleSearch”搜索时触发的事件
@select=“handleSelect”选择项时触发的事件(清除值时不会触发)
@input=“handleInput”选中触发(第一次回显会触发,清除值会触发, value值为undefined)多用于v-model双向绑定组件更新父组件
  • 实践代码
<template>
  <div>
    <treeselect v-model="selectedItems" :options="treeData" :multiple="true" :clearable="true" :searchable="true" :disabled="false"
      :openOnFocus="true" :openOnClick="true" :auto-load-root-options="true" :async="false" :load-options="loadOptions"
      :noChildrenText="'没有子选项'" :noOptionsText="'没有可选项'" :noResultsText="'没有匹配的结果'" :placeholder="'请选择'" :appendToBody="true"
      @open="handleOpen" @close="handleClose" @input="handleInput" @deselect="handleRemove" @search-change="handleSearch"
      @select="handleSelect"></treeselect>
    <button @click="getSelectedItems">获取选中的数据</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      selectedItems: [], // 选中的项
      treeData: [
        {
          id: 1,
          label: "水果",
          children: [
            { id: 2, label: "西瓜" },
            { id: 3, label: "香蕉" },
            { id: 4, label: "橙子" },
          ],
        },
        {
          id: 5,
          label: "蔬菜",
          children: [
            { id: 6, label: "西红柿" },
            { id: 7, label: "黄瓜" },
            { id: 8, label: "青菜" },
          ],
        },
        {
          id: 9,
          label: "零食",
          children: [
            { id: 10, label: "薯片" },
            { id: 11, label: "巧克力" },
          ],
        },
      ],
    };
  },
  methods: {
    loadOptions({ parentNode, callback }) {
      // 异步加载选项的方法
      // parentNode: 当前父节点
      // callback: 加载完成后的回调函数
      // 在这里根据需要进行异步加载选项的操作,并在加载完成后调用callback方法传递选项数据
    },
    handleOpen() {
      // 下拉菜单打开时触发的事件
      console.log("下拉菜单打开");
    },
    handleClose() {
      // 下拉菜单关闭时触发的事件
      console.log("下拉菜单关闭");
    },
    handleRemove(removedItem) {
      // 移除选中项时触发的事件
      console.log("移除选中项", removedItem);
    },
    handleSearch(searchText) {
      // 搜索时触发的事件
      console.log("搜索", searchText);
    },
    handleSelect(selectedItems) {
      // 选择项时触发的事件
      console.log("选择项select", selectedItems);
    },
    handleInput(selectedItems) {
      // 选择项时触发的事件
      console.log("选择项input", selectedItems);
    },
    getSelectedItems() {
      // 获取选中的数据
      console.log(this.selectedItems);
    },
  },
};
</script>

1.5 常见问题

1.5.1 占位符 unknown

  • 问题截图
    在这里插入图片描述

  • 解决方法

    v-model 不能写成空字符串或者空数组,否则会出现 unknown,可以默认是 null。

1.5.2 数据提示英文

  • 问题截图

在这里插入图片描述

  • 解决方法

    使用 noChildrenText、noOptionsText 和 noResultsText 自定义文本的属性。

    noChildrenText:用于定义当某个选项没有子选项时的文本提示。例如,当一个分类没有子分类时,可以使用 noChildrenText 来显示相应的提示文本。
    noOptionsText:用于定义当没有可选项时的文本提示。例如,当数据为空或没有匹配的选项时,可以使用 noOptionsText 来显示相应的提示文本。
    noResultsText:用于定义当搜索结果为空时的文本提示。例如,当用户进行搜索但没有匹配的结果时,可以使用 noResultsText 来显示相应的提示文本。

    <treeselect v-model="selectedItems" :options="treeData"  
    noChildrenText="没有子选项" noOptionsText="没有可选项" noResultsText="没有匹配的结果"></treeselect>
    

1.5.3 数据结构不符合

  • 问题描述

    很多时候后台返回回来的数据结构的字段并不是 id、label、children 这些,这个时候就需要我们将其换成符合要求的数据结构。

  • 解决方法

    使用 normalizer 属性,它用于规范化选项数据。通过 normalizer 属性,你可以自定义选项数据的结构,以适应插件的要求。

    <template>
      <div class="box">
        <treeselect v-model="selectedItems" :normalizer="normalizeOptions" :options="treeData"></treeselect>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          selectedItems: null,
          treeData: [
            {
              id: 1,
              name: "水果",
              children: [
                {
                  id: 2,
                  name: "苹果",
                  children: [
                    { id: 21, name: "红苹果" },
                    { id: 22, name: "绿苹果" },
                  ],
                },
                {
                  id: 3,
                  name: "香蕉",
                  children: [
                    { id: 31, name: "大香蕉" },
                    { id: 32, name: "小香蕉" },
                  ],
                },
              ],
            },
            {
              id: 5,
              name: "蔬菜",
              children: [
                {
                  id: 6,
                  name: "叶菜类",
                  children: [
                    { id: 61, name: "菠菜" },
                    { id: 62, name: "生菜" },
                  ],
                },
                {
                  id: 7,
                  name: "根茎类",
                  children: [],
                },
              ],
            },
          ],
        };
      },
      methods: {
        // 规范化选项数据的方法
        normalizeOptions(node) {
          // node: 原始的选项数据
          // 在这里根据需要进行选项数据的规范化操作,并返回规范化后的选项数据
          // 例如,可以将原始的选项数据转换为符合插件要求的结构
          if (node.children && !node.children.length) {
            // 去掉children=[]的children属性
            delete node.children;
          }
          return {
            id: node.id,
            label: node.name,
            children: node.children,
          };
        },
      },
    };
    </script>
    

    当然你也可以手动实现,通过递归的方式。

    <template>
      <div class="box">
        <treeselect v-model="selectedItems" :options="treeData"></treeselect>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          selectedItems: null,
          treeData: [],
        };
      },
      mounted() {
        let list = [
          {
            id: 1,
            name: "层级1",
            children: [
              {
                id: 2,
                name: "层级2",
                children: [
                  {
                    id: 3,
                    name: "层级3",
                    children: [
                      {
                        id: 4,
                        name: "层级4",
                      },
                    ],
                  },
                ],
              },
            ],
          },
        ];
        this.treeData = this.normalizeOptions(list);
      },
      methods: {
        normalizeOptions(options) {
          const normalizedOptions = [];
          if (options) {
            for (const option of options) {
              // 创建一个规范化选项对象,将id和name属性映射到该对象中
              const normalizedOption = {
                id: option.id,
                label: option.name,
              };
              // 检查当前选项是否有子选项
              if (option.children && option.children.length > 0) {
                // 如果有子选项,递归调用normalizeOptions方法对子选项进行规范化
                // 并将规范化后的子选项数组赋值给当前选项的children属性
                normalizedOption.children = this.normalizeOptions(option.children);
              }
              // 将规范化后的选项对象添加到normalizedOptions数组中
              normalizedOptions.push(normalizedOption);
            }
          }
          return normalizedOptions;
        },
      },
    };
    </script>
    

1.5.4 样式调整

/* 组件样式 */
::v-deep .vue-treeselect {
  width: 200px;
  height: 30px;
  line-height: 30px;
  font-size: 18px;
}
/* 内容样式 */
::v-deep .vue-treeselect__control {
  height: 30px;
  color: blue;
}
/* 占位符样式 */
::v-deep .vue-treeselect__placeholder,
::v-deep .vue-treeselect__single-value {
  color: red;
}

1.5.5 获取选中节点对象而不是单一的值

valueFormat 属性能够决定 value 属性的格式。当设置为 id 时,value 属性的格式就是 id 或 id 数组。当设置为 object 时,value 属性的格式就是 node 或 node 数组。

  • 单一的值

  • 节点对象

    <treeselect v-model="selectedItems" valueFormat="object" :options="treeData" @input="handleInput"></treeselect>
    

在这里插入图片描述


二、自定义组件

自定义的属性和方法

属性/方法描述类型
data展示数据array
props配置选项,具体配置可以参照element ui库中el-tree的配置object
show-checkbox节点是否可被选择boolean
check-strictly在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为falseboolean
icon-class自定义树节点的图标string
load加载子树数据的方法,仅当lazy属性为true时生效function(node, resolve)
lazy是否懒加载子节点,需要和load方法结合使用boolean
disabled下拉框是否禁用boolean
getCheckedKeys若节点可被选择(即show-checkbox为true),则返回目前被选中的节点的 key 所组成的数组–
getCurrentNode获取当前被选中节点的data,若没有节点被选中则返回 null–
collapse-tags多选时是否将选中值按文字的形式展示–
select-last-node单选时是否只能选择最后一个节点–
show-count若节点中存在children则在父节点展示所属children的数量,注意但设置插槽时show-count将失效–
clearable单选时是否可以清空选项boolean
filterable启用搜索功能boolean

封装文件

<template>
  <el-select :value="valueFilter(value)" :placeholder="$attrs['placeholder']" :multiple="$attrs['show-checkbox']"
    :disabled="$attrs['disabled']" :filterable="$attrs['filterable']" :clearable="$attrs['clearable']"
    :collapse-tags="$attrs['collapse-tags']" @change="selectChange" @clear="selectClear" ref="mySelect" :filter-method="remoteMethod">
    <template slot="empty">
      <div class="selecTree">
        <el-tree :data="data" :props="props" @node-click="handleNodeClick" :show-checkbox="$attrs['show-checkbox']"
          :check-strictly="$attrs['check-strictly']" :icon-class="$attrs['icon-class']" :lazy="$attrs['lazy']" :load="$attrs['load']"
          :node-key="props.value" :filter-node-method="filterNode" @check-change="handleCheckChange"
          :default-expanded-keys="defaultExpandedKeys" ref="myTree">
          <template slot-scope="{ node, data }">
            <slot :node="node" :data="data">
              <span class="slotSpan">
                <span>
                  {{ data[props.label] }}
                  <b v-if="$attrs['show-count'] != undefined && data[props.children]">({{ data[props.children].length }})</b>
                </span>
              </span>
            </slot>
          </template>
        </el-tree>
      </div>
    </template>
  </el-select>
</template>
<script>
export default {
  props: {
    value: {
      type: undefined,
      default: null,
    },
    data: {
      type: Array,
      default: null,
    },
    props: {
      type: Object,
      default: null,
    },
  },
  data() {
    return {
      defaultExpandedKeys: [],
    };
  },
  created() {
    this.propsInit();
  },
  mounted() {
    setTimeout(this.initData, 10);
  },
  beforeUpdate() {
    this.propsInit();
    this.initData();
  },

  methods: {
    initData() {
      if (this.$attrs["show-checkbox"] === undefined) {
        let newItem = this.recurrenceQuery(
          this.data,
          this.props.value,
          this.value
        );
        if (newItem.length) {
          if (this.props.value && newItem[0][this.props.value]) {
            this.defaultExpandedKeys = [newItem[0][this.props.value]];
          }
          this.$nextTick(() => {
            this.$refs.myTree.setCurrentNode(newItem[0]);
          });
        }
      } else {
        let newValue = JSON.parse(JSON.stringify(this.value));
        if (!(newValue instanceof Array)) {
          newValue = [newValue];
        }
        if (newValue?.length) {
          let checkList = newValue.map((key) => {
            if (key) {
              let newItem = this.recurrenceQuery(
                this.data,
                this.props.value,
                key
              );
              return newItem[0] || "";
            }
          });
          if (checkList?.length) {
            let defaultExpandedKeys = checkList.map(
              (item) => item?.[this.props.value || ""]
            );
            if (defaultExpandedKeys.length)
              this.defaultExpandedKeys = defaultExpandedKeys;
            this.$nextTick(() => {
              this.$refs.myTree.setCheckedNodes(checkList);
            });
          }
        }
      }
      this.$forceUpdate();
    },
    // 多选
    handleCheckChange(data, e, ev) {
      let checkList = this.$refs.myTree.getCheckedNodes();
      let setList = null;
      if (checkList.length) {
        setList = checkList.map((item) => item[this.props.value]);
      }
      this.$emit("input", setList);
      // 共三个参数,依次为:传递给 data 属性的数组中该节点所对应的对象、节点本身是否被选中、节点的子树中是否有被选中的节点
      this.$emit("change", data, e, ev);
    },
    // 单选事件
    handleNodeClick(data, e) {
      if (!(this.$attrs["select-last-node"] === undefined)) {
        if (data[this.props.children] && data[this.props.children]?.length) {
          return false;
        }
      }
      if (this.$attrs["show-checkbox"] === undefined) {
        this.$emit("input", data[this.props.value]);
        this.$refs.mySelect.blur();
      }
      this.$emit("change", data, e);
    },
    //   递归查找通用方法
    recurrenceQuery(list, key, value) {
      if (!list || !key || !value) return [];
      let queryData = [];
      list.map((item) => {
        if (item[this.props.children] && item[this.props.children].length) {
          queryData.push(
            ...this.recurrenceQuery(item[this.props.children], key, value)
          );
        }
        if (item[key] == value) {
          queryData.push(item);
        }
        return item;
      });
      return queryData;
    },
    selectChange(e) {
      if (this.$attrs["show-checkbox"] !== undefined) {
        let checkList = e.map((key) => {
          let newItem = this.recurrenceQuery(this.data, this.props.label, key);
          return newItem[0] || "";
        });
        this.$refs.myTree.setCheckedNodes(checkList);
        this.$emit("input", e);
      }
    },
    selectClear(flag) {
      if (this.$attrs["show-checkbox"] === undefined) {
        if (!flag) this.$emit("input", "");
        this.$refs.myTree.setCurrentKey(null);
      } else {
        if (!flag) this.$emit("input", []);
        this.$refs.myTree.setCheckedKeys([]);
      }
      this.remoteMethod("");
    },
    getCheckedNodes() {
      if (
        this.value !== null &&
        this.value !== undefined &&
        this.value !== ""
      ) {
        return this.$refs.myTree.getCheckedNodes();
      }
      return [];
    },
    getCurrentNode() {
      if (
        this.value !== null &&
        this.value !== undefined &&
        this.value !== ""
      ) {
        return this.$refs.myTree.getCurrentNode();
      }
      return null;
    },
    valueFilter(val) {
      if (this.$attrs["show-checkbox"] === undefined) {
        let res = "";
        [res] = this.recurrenceQuery(this.data, this.props.value, val);
        return res?.[this.props.label] || "";
      } else {
        if (!val?.length) return [];
        let res = val.map((item) => {
          let [newItem] = this.recurrenceQuery(
            this.data,
            this.props.value,
            item
          );
          return newItem?.[this.props.label] || "";
        });
        if (!res?.length) return [];
        res = res.filter((item) => item);
        return res;
      }
    },
    propsInit() {
      this.props.label = this.props.label || "label";
      this.props.value = this.props.value || "value";
      this.props.children = this.props.children || "children";
      if (
        this.$attrs["select-last-node"] !== undefined &&
        !this.props.disabled
      ) {
        this.props.disabled = (data) => data?.[this.props.children]?.length;
        this.$attrs["check-strictly"] = true;
      }
    },

    remoteMethod(query) {
      this.$refs.myTree.filter(query);
    },
    filterNode(value, data) {
      if (!value) return true;
      return data[this.props.label].indexOf(value) !== -1;
    },
  },

  watch: {
    value: {
      deep: true,
      handler(val) {
        if (!val || !val?.length) {
          this.selectClear(true);
        }
      },
    },
  },
};
</script>

使用文件

<template>
  <div class="box">
    <tree-select @change="sendSelectedValue" v-model="value" :data="treeData" :props="treeProps" filterable clearable></tree-select>
  </div>
</template>

<script>
import TreeSelect from "@/components/treeSelect";
export default {
  components: {
    TreeSelect,
  },
  data() {
    return {
      value: "",
      treeData: [
        {
          id: 1,
          name: "水果",
          children: [
            {
              id: 2,
              name: "苹果",
              children: [
                { id: 21, name: "红苹果" },
                { id: 22, name: "绿苹果" },
              ],
            },
            {
              id: 3,
              name: "香蕉",
              children: [
                { id: 31, name: "大香蕉" },
                { id: 32, name: "小香蕉" },
              ],
            },
          ],
        },
        {
          id: 5,
          name: "蔬菜",
          children: [
            {
              id: 6,
              name: "叶菜类",
              children: [
                { id: 61, name: "菠菜" },
                { id: 62, name: "生菜" },
              ],
            },
            {
              id: 7,
              name: "根茎类",
              children: [],
            },
          ],
        },
      ],
      // 配置项
      treeProps: {
        label: "name", // 树节点的文本字段
        value: "id", // 树节点的值字段
        children: "children", // 树节点的子节点字段
        disabled: (data) => data.disabled, // 禁用节点的条件函数,接收一个参数 data,返回一个布尔值
        iconClass: "custom-icon", // 自定义树节点的图标样式
        checkStrictly: true, // 在显示复选框的情况下,是否严格遵循父子节点不互相关联的做法
        load: (node, resolve) => {}, // 加载子树数据的方法,仅当 lazy 属性为 true 时生效
        lazy: true, // 是否懒加载子节点,需与 load 方法结合使用
        collapseTags: true, // 多选时是否将选中值按文字的形式展示
        selectLastNode: true, // 单选时是否只能选择最后一个节点
        showCount: true, // 若节点中存在 children,则在父节点展示所属 children 的数量
      },
    };
  },
  methods: {
    sendSelectedValue(e) {
      console.log(e);
    },
  },
};
</script>

实现效果

在这里插入图片描述

vue-treeselect绑值、回显等常见问题总结 最近vue-treeselect使用的比较多,分享一波 可以用在表单里,也可以用在可编辑的表格内 这里以表单里的举例, 在main.js中引入 import ElTreeSelect from ‘el-tree-select’ import Treeselect from ‘@riophae/vue-treeselect’ import ‘@riophae/vue-treeselect/dist/vue-treeselect.css’ Vue.use(ElTreeSelect) Vue.component( 阅读详情

相关推荐

Vue2 el-select 进阶:打造高效下拉树组件与数据回显实战

本文详细介绍了如何在Vue2项目中基于Element UI的el-select和el-tree组件,封装一个支持复杂层级数据选择与数据回显功能的下拉树组件。文章从实际需求出发,深入剖析了组件结构嵌套、样式穿透、事件联动等核心实现思路,并提供了完整的代码示例和实战技巧,帮助开发者高效解决树形数据选择与回显的难题。

ii567的博客 479

VueTreeselect

Vue.jstreeselecte是一个具有嵌套选项的多选择组件,支持Vue.js。Single & multiple select with nested options support(支持嵌套选项的单个和多个选项)Fuzzy matching(模糊匹配)Async searching(异步搜索)Delayed loading (load data of deep level options only when needed)(支持嵌套选项的单个和多个选择)

pickinguptrash的博客 2万+

vue+element 二次组件封装:表单、列表、下拉树、弹出选择、折叠卡片等(持续更新npm依赖)

基于vue+element开发前端时,封装的一些组件 安装 npm i lzg-ctrl 组件介绍 lzg-button 按钮控件 # Attributes(属性) 属性 | 说明 | 类型 | 默认值 code | 编码 | String | "" label | 名称 | String | "" type | 类型 | String | "primary" icon | 图标 | String | "" loading | 加载状态 | Boolean | false upload |

⑧o年崋日常记录 5372

Vue Treeselect

<el-col :span="24"> <el-form-item label="归属部门" prop="deptIds"> <treeselect :show-count="true" :disable-branch-nodes="true" :limit="1" :fl...

NMZLduiduidui的博客 7359

Vue2项目实战:如何用el-select快速实现带搜索功能的下拉树组件(附完整代码)

本文详细介绍了如何在Vue2项目中基于el-select组件实现带搜索功能的下拉树组件,包括多级树形数据展示、高效节点搜索过滤和精确数据回显等核心功能。通过完整的代码示例和性能优化技巧,帮助开发者快速集成到实际项目中,提升复杂表单场景下的用户体验。

weixin_29018335的博客 171

Vue+ElementUI封装下拉树组件:实现树形数据选择与模糊搜索

在前端开发中,表单组件是构建用户界面的基础元素,而选择器(Select)作为表单的核心组件之一,负责处理用户从预定义选项中进行选择的任务。其工作原理是通过数据绑定和事件监听,将用户选择的值与应用程序状态同步。随着业务复杂度的提升,一维列表选择器难以清晰展示具有层级关系的数据(如地区、组织架构),这时需要引入树形结构(Tree)来直观呈现父子节点关系。结合两者优势的下拉树组件(TreeSelect)应运而生,它融合了选择器的便捷交互和树形结构的层级展示能力,在保持表单统一性的同时,显著提升了复杂数据选择的用户

weixin_33871366的博客 423

vue实现下拉树——vue-treeselect

vue实现下拉树——vue-treeselect 官网入口:vue-treeselect 里面提供了各种api,本章只简单介绍如何引用此组件以及一些简单功能。 npm下载安装插件指令 npm install --save @riophae/vue-treeselect 下面是 <template> <TreeSelect v-model="treeValue" :...

qq_38908588的博客 7548

Vue+ElementUI实现下拉树组件:解决树形数据选择与模糊搜索难题

在前端开发中,处理层级数据(如组织架构、分类目录)的交互选择是一个常见需求。传统下拉框(Select)无法直观展示树形结构,而树形控件(Tree)在表单集成时又缺乏紧凑的输入体验。通过组件组合与插槽技术,可以构建一种融合两者优势的“下拉树”组件。其技术价值在于,它既保留了下拉框的便捷表单集成能力,又提供了树形结构的清晰层级展示,并能支持高效的模糊搜索与节点高亮。这种模式特别适用于后台管理系统中的部门选择、权限分配等应用场景。本文以Vue.js和Element UI为例,详细解析如何将el-select与el

weixin_30740581的博客 185

vue-treeselect下拉树

普通加载 延迟加载(规范函数转化字段为label/children,特别关键,不转化时只认label/children,转化之后只认转化后的字段,比如将将name转化为label,那么只会认name,即使数据中有label但没有name,则子选项不会显示,因为规范函数只会去找转化字段name而不会管字段label) 看文档时一定要细心,英文也得翻译过来看,特别是demo,不然会踩跟多坑 ...

欢乐时光已经开始---博客 3446

elementUI,实现el-select + el-tree组合成下拉树。

elementUI 官网API elementUI 中有Select 选择器,效果是这样的。 Tree 树形控件的效果是这样的。 我们在实际开发中需要用到下拉树,elementUI 是没有这个组件的,我们就要自己去写了。下面是我开发中用到的,记录一下el-select + el-tree组合,直接贴代码。 <el-select v-model="baseTableName" class="select-unitName select-option-father" popper-class="se

weixin_38395200的博客 5450

Vue2项目实战:如何用el-select快速实现带搜索功能的下拉树(附完整代码)

本文详细介绍了如何在Vue2项目中利用el-select组件快速实现带搜索功能的下拉树,包括数据回显等高级功能。通过组件嵌套组合和数据流控制,开发者可以轻松构建轻量级且功能强大的树形选择器,适用于权限管理、地区选择等场景。

weixin_29013129的博客 176

vue2用elementUI做单选下拉树

摘要 本文介绍了一个基于Element UI的自定义树形选择组件实现方案。该组件通过组合el-input、el-popover和el-tree组件,实现了类似下拉树的功能,避免了使用级联选择器。主要特点包括:支持点击输入框或图标展开/收起树形菜单、可配置节点过滤功能、自定义节点渲染样式、点击外部区域关闭菜单等。组件通过v-model进行双向绑定,并提供了丰富的配置选项如禁用状态、占位文本、节点键名等。核心逻辑包括处理节点点击事件、设置当前选中值、同步显示标签等,为表单中需要树形选择但不想使用级联选择的场景提

wzp20092009的博客 1113

Vue通用下拉树组件@riophae/vue-treeselect的使用

这个是在若依框架无意中发现的一个下拉树通用组件。@riophae/vue-treeselect 是一个基于 Vue.js 的树形选择器组件,可以用于选择树形结构的数据。它支持多选、搜索、异步加载等功能,可以自定义选项的样式和模板。该组件易于使用和扩展,适用于各种类型的项目。部分注释百度翻译成中文了,但太多了,懒得挨个翻译了,直接看也大概知道啥意思。下面是我测试的一个例子,一般的需求应该足够了。使用自己习惯使用的包管理器安装就可以了。字体样式简单调了一下。

专注于技术,分享实战经验,探索技术边界,用代码构建更美的数字世界。 3万+

vue+iview下拉树组件vue-treeselect的使用

iview下拉树组件vue-treeselect demo示例 下载组件 npm install --save @riophae/vue-treeselect 2.引入组件及样式 import Treeselect from '@riophae/vue-treeselect' import '@riophae/vue-treeselect/dist/vue-treeselect.css' 3.页面使用 <Col span="12"> <FormItem la

zcbmwasd的博客 2464

2023-ICCV-隐式神经网络实现低光照图像增强(NeRCo)论文解读+代码解析

NeRCo()提出了一种名为NeRCo的隐式神经表示方法,用于合作式低光图像增强。现有的低光图像增强方法存在以下三个限制:不可预测的亮度降低和噪声、度量友好版本和视觉友好版本之间的固有差距,以及有限的配对训练数据。为了解决这些限制,论文提出了NeRCo方法,它以无监督的方式稳健地恢复感知友好的结果。具体而言,NeRCo通过一个可控的拟合函数统一了现实场景中多样的退化因素,从而提高了鲁棒性。对于输出结果,论文引入了来自预训练视觉-语言模型的先验信息,采用语义导向的监督方法。

weixin_45595504的博客 3182

matlab液体湿润模拟,【水文模型】01 三水源新安江模型

目录一、概述1.1 流域概况1.2 产流方式论证1.3 设计基本任务1.4 设计原始资料二、新安江模型2.1 流域划分2.2 产汇流计算2.2.1 蒸散发计算2.2.2 产流计算2.2.3 水源划分2.2.4 汇流计算2.3 模型参数2.3.1 参数物理意义2.3.2 参数率定结果2.4 模拟结果2.4.1 精度统计表2.4.2 计算与实测流量过程线比较三、精度统计与误差分析3.1 精度统计3.2...

weixin_34173656的博客 5326

vue: 使用下拉树组件@riophae/vue-treeselect

是一个基于 Vue.js 的树形选择器组件,可以用于选择树形结构的数据。它支持多选、搜索、异步加载等功能,可以自定义选项的样式和模板。该组件易于使用和扩展,适用于各种类型的项目。前言: 在vue中, 因为element-ui 2.X是没有tree-select组件的,到了element-plus就有了。4. 比较全面的属性配置参考。5. 插件的的方法配置。

qq_43340606的博客 8682

2.4G板载PCB IFA天线仿真设计

本文简单介绍下2.4GHz的板载PCB IFA天线的HFSS仿真与设计。

射频问问 (RFASK.NET) 是在"微波射频网(MWRF.NET)”系列原创技术专栏基础上升级打造的技术问答学习平台。 6009

Vue下拉树组件

<template> <el-select :value="valueTitle" :clearable="clearable" @clear="clearHandle"> <el-input class="selectInput" :placeholder="placeho...

qq_29039105的博客 835

Java面试题大全(2025版)

== 对于基本类型来说是值比较,对于引用类型来说是比较的是引用;而 equals 默认情况下是引用比较,只是很多类重新了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。

2401_89221704的博客 2442

RK3399跨楼层机器人:SLAM技术与商业应用解析

SLAM(即时定位与地图构建)技术是移动机器人自主导航的核心,通过激光雷达、视觉传感器等多源数据融合实现厘米级定位。RK3399作为高性能嵌入式平台,其六核架构和Mali GPU能高效处理SLAM算法与传感器数据。在商业场景中,这种技术组合可支撑机器人实现跨楼层物资配送、仓库盘点等复杂任务。以SLAMTEC Hermes平台为例,其模块化设计支持电梯控制集成,配合双深度相机和360°激光雷达,在医疗、物流等领域已实现12分钟平均配送效率和99.7%的识别准确率。

weixin_30376083的博客 617

vue+elementUI+下拉树(select+tree),不用vue的treeselect组件,解决下拉树组件无法校验,支持下拉搜索

经过本人使用vue的treeselect组件,功能都不错,但是有俩个问题,导致我抛弃使用这组件: 1,样式问题,用了elementui的card标签后,区域限制了高度,而treeselect组件的z-index不管怎么调大,都无法浮动在card上,导致过长的list列表大部分因此被遮挡,随可滚动选择,但是很难受。 2.第二点是校验问题,也是最大原因。组件由于是install下来的,改了源码,别人拉代码后用还是要去改下源码,不符合实际。 于是,整合百度的一些代码,自己封装了SelectTree组件。

qq_24241631的博客 3205

Vue3 - 详细实现安装西瓜视频 xgplayer 插件详细配置及使用教程,利用西瓜视频播放器播放 mp4、flv、hls 格式视频文件及视频推流(直播或监控摄像头画面),支持移动端H5及PC网站!

vue3,nuxt3,西瓜视频,xgplayer,插件组件,xgplayer-hls,xgplayer-flv,xgplayer-mp4,播放视频点播和直播视频流,监控实时播放推流,倍速播放,在线flv在线推流地址,HLS播放,vue3西瓜播放器,vue如何使用西瓜视频插件做直播推流回流,视频点播,flv在线视频流播放,截图功能,自定义播放器样式,截屏,监控视频,vue3使用西瓜播放器播放flv、hls、mp4视频,截屏按钮,直播流,报错,黑屏,加载失败,苹果ios不兼容,重连,怎么改样式,很卡,详细教程,

🏆 前端领域优质创作者 1万+
上一篇: 微信小程序自定义数据实现级联省市区组件
下一篇: 深入理解qs库:简化你的工作流程
水星记_
水星记_ 优质创作者: 编程框架技术领域 优质创作者: 编程框架技术领域
博客等级 码龄6年 13万+粉丝 212原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水星记_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值