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

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

  1. 安装第三方包
javascript 复制代码
npm i @riophae/vue-treeselect
  1. 导入并注册
java 复制代码
import Treeselect from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
 
export default {
  components: { Treeselect } 
}
  1. 基本使用
javascript 复制代码
<template>
  <div class="main">
    <div class="tree">
      <Treeselect 
        v-model="value"
        :options="options"
        :placeholder="'请选择人员'"
        :normalizer="tenantIdnormalizer"
        :multiple="true"
        @input="treeSelectInput"
        @select="treeSelectChange"
        @deselect="treeSelectDeselect"
        @search-change="treeSelectSearch"
        @open="treeSelectOpen"
        @close="treeSelectClose"
      />
    </div>
  </div>
</template>
<script>
import Treeselect from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
import treeData from './data/tree'
export default {
  data() {
    return {
      value: null,
      options: []
    }
  },
  components: { Treeselect },
  mounted(){
    // 延迟模拟请求数据
    setTimeout(() => {
      this.options = treeData
      this.value = [111, 113] // 测试回显操作
    }, 300)
  },
  methods:{
    // 选中触发(第一次回显的时候会触发,清除值的时候会触发, value值为undefined) input事件用于v-model双向绑定组件更新父组件值
    treeSelectInput(value, instanceId) {
      console.log(value, 'input事件')
      console.log(this.value, 'this.value -- input') // 这个不需要 延迟
    },
    // 选中触发(清除值的时候不会触发)
    treeSelectChange(raw, instanceId) {
      console.log(raw, '当前的对象')
      setTimeout(() => { // 如果用到this.value 需要setTimeout延迟一下拿到最新的值
        console.log(this.value, 'this.value -- select')
      })
    },
    // 移除选项时触发 当设置multiple为true时生效  raw为当前移除的对象
    treeSelectDeselect(raw, instanceId) {
      console.log(raw, 'deselect-->>')
    },
    // 搜索
    treeSelectSearch(searchQuery, instanceId) {
      console.log(searchQuery, '当前搜索的值')
    },
    // 展开触发
    treeSelectOpen(instanceId) {
      console.log('展开了')
    },
    // 关闭触发
    treeSelectClose(value, instanceId) {
      console.log(value, '当前的value值')
    },
    // 字段默认 id label 用于规范化数据源
    tenantIdnormalizer(node, instanceId) {
      if (node.children && !node.children.length) {
        delete node.children
      }
      return {
        id: node.id,
        label: node.title,
        children: node.children
      }
    }
  }
}
</script>
<style scoped>
.main {
  width: 100%;
  height: 100%;
  padding: 60px 0 0 200px;
}
 
.main .tree {
  width: 240px;
  height: 40px;
}
 
::v-deep .vue-treeselect__label {
  color: #606266;
}
</style>
  1. 比较全面的属性配置参考
javascript 复制代码
props: {
  /**
   * 即使有禁用的选定节点,是否允许重置值
   */
  allowClearingDisabled: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 选择/取消选择祖先节点时,是否应选择/取消选中其禁用的后代
   * 和 allowClearingDisabled 一起使用
   */
  allowSelectingDisabledDescendants: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 菜单是否应始终打开
   */
  alwaysOpen: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 是否将菜单加到body上
   */
  appendToBody: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 是否启用异步搜索模式
   */
  async: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 是否自动将组件集中在装载上?
   */
  autoFocus: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 装载时自动加载根选项。当设置为"false"时,打开菜单时将加载根选项。
   */
  autoLoadRootOptions: {
    type: Boolean,
    default: true,
  },
 
  /**
   * 当用户取消选择一个节点时,会自动取消选择其祖先。仅适用于平面模式。
   */
  autoDeselectAncestors: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 当用户取消选择节点时,会自动取消选择其子节点。仅适用于平面模式。
   */
  autoDeselectDescendants: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 当用户选择一个节点时,会自动选择其祖先。仅适用于平面模式。
   */
  autoSelectAncestors: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 当用户选择一个节点时,会自动选择其子节点。仅适用于平面模式。
   */
  autoSelectDescendants: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 如果没有文本输入,按退格键是否删除最后一项。
   */
  backspaceRemoves: {
    type: Boolean,
    default: true,
  },
 
  /**
   * 在清除所有输入字段之前进行处理的函数。
   * 返回"false"以防止清除值
   * @type {function(): (boolean|Promise<boolean>)}
   */
  beforeClearAll: {
    type: Function,
    default: constant(true),
  },
 
  /**
   * 在叶节点之前显示分支节点?
   */
  branchNodesFirst: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 是否应该缓存每个搜索请求的结果?
   */
  cacheOptions: {
    type: Boolean,
    default: true,
  },
 
  /**
   * 是否显示重置值的"×"按钮?
   */
  clearable: {
    type: Boolean,
    default: true,
  },
 
  /**
   * 清楚文本,multiple为true时
   */
  clearAllText: {
    type: String,
    default: 'Clear all',
  },
 
  /**
   * 选择后是否清除搜索输入。
   * 仅当"multiple"为"true"时使用。
   * 对于单选模式,无论道具值如何,它总是**在选择一个选项后清除输入。
   */
  clearOnSelect: {
    type: Boolean,
    default: false,
  },
 
  /**
   * "×"按钮的标题。
   */
  clearValueText: {
    type: String,
    default: 'Clear value',
  },
 
  /**
   * 选择选项后是否关闭菜单?
   * 仅当"multiple"为"true"时使用。
   */
  closeOnSelect: {
    type: Boolean,
    default: true,
  },
 
  /**
   * 加载时应自动展开多少级别的分支节点。
   * 设置Infinity以使所有分支节点在默认情况下展开。
   */
  defaultExpandLevel: {
    type: Number,
    default: 0,
  },
 
  /**
   * 在用户开始搜索之前要显示的默认选项集。用于异步搜索模式。
   * 当设置为"true"时,将自动加载作为空字符串的搜索查询结果。
   * @type {boolean|node[]}
   */
  defaultOptions: {
    default: false,
  },
 
  /**
   * 如果没有文本输入,按delete键是否删除最后一项。
   */
  deleteRemoves: {
    type: Boolean,
    default: true,
  },
 
  /**
   * 用于连接隐藏字段值的多个值的分隔符。
   */
  delimiter: {
    type: String,
    default: ',',
  },
 
  /**
   * 仅显示与搜索值直接匹配的节点,不包括其祖先。
   *
   * @type {Object}
   */
  flattenSearchResults: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 是否阻止选择分支节点?
   */
  disableBranchNodes: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 禁用控制?
   */
  disabled: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 是否禁用模糊匹配功能?
   */
  disableFuzzyMatching: {
    type: Boolean,
    default: false,
  },
 
  /**
   *是否启用平面模式。非平面模式(默认)是指: 
   *   - 每当检查分支节点时,它的所有子节点也将被检查
   *   - 每当一个分支节点检查了所有子节点时,该分支节点本身也会被检查
   * 设置"true"以禁用此机制
   */
  flat: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 将以所有事件作为最后一个参数进行传递。
   * 有助于识别事件的起源。
  */
  instanceId: {
    // Add two trailing "$" to distinguish from explictly specified ids.
    default: () => `${instanceId++}$$`,
    type: [String, Number],
  },
 
  /**
   * Joins multiple values into a single form field with the `delimiter` (legacy mode).
   * 使用"分隔符"将多个值合并到一个表单字段中(传统模式)。
  */
  joinValues: {
    type: Boolean,
    default: false,
  },
 
  /**
   * 限制所选选项的显示。
   * 其余部分将隐藏在limitText字符串中。
   */
  limit: {
    type: Number,
    default: Infinity,
  },
 
  /**
   * Function that processes the message shown when selected elements pass the defined limit.
   * @type {function(number): string}
   */
  limitText: {
    type: Function,
    default: function limitTextDefault(count) { // eslint-disable-line func-name-matching
      return `and ${count} more`
    },
  },
 
  /**
   * Text displayed when loading options.
   */
  loadingText: {
    type: String,
    default: 'Loading...',
  },
 
  /**
   * Used for dynamically loading options.
   * @type {function({action: string, callback: (function((Error|string)=): void), parentNode: node=, instanceId}): void}
   */
  loadOptions: {
    type: Function,
  },
 
  /**
   * Which node properties to filter on.
   */
  matchKeys: {
    type: Array,
    default: constant(['label']),
  },
 
  /**
   * Sets `maxHeight` style value of the menu.
   */
  maxHeight: {
    type: Number,
    default: 300,
  },
 
  /**
   * Set `true` to allow selecting multiple options (a.k.a., multi-select mode).
   */
  multiple: {
    type: Boolean,
    default: false,
  },
 
  /**
   * Generates a hidden <input /> tag with this field name for html forms.
   */
  name: {
    type: String,
  },
 
  /**
   * Text displayed when a branch node has no children.
   */
  noChildrenText: {
    type: String,
    default: 'No sub-options.',
  },
 
  /**
   * Text displayed when there are no available options.
   */
  noOptionsText: {
    type: String,
    default: 'No options available.',
  },
 
  /**
   * Text displayed when there are no matching search results.
   */
  noResultsText: {
    type: String,
    default: 'No results found...',
  },
 
  /**
   * Used for normalizing source data.
   * @type {function(node, instanceId): node}
   */
  normalizer: {
    type: Function,
    default: identity,
  },
 
  /**
   * By default (`auto`), the menu will open below the control. If there is not
   * enough space, vue-treeselect will automatically flip the menu.
   * You can use one of other four options to force the menu to be always opened
   * to specified direction.
   * Acceptable values:
   *   - `"auto"`
   *   - `"below"`
   *   - `"bottom"`
   *   - `"above"`
   *   - `"top"`
   */
  openDirection: {
    type: String,
    default: 'auto',
    validator(value) {
      const acceptableValues = ['auto', 'top', 'bottom', 'above', 'below']
      return includes(acceptableValues, value)
    },
  },
 
  /**
   * Whether to automatically open the menu when the control is clicked.
   */
  openOnClick: {
    type: Boolean,
    default: true,
  },
 
  /**
   * Whether to automatically open the menu when the control is focused.
   */
  openOnFocus: {
    type: Boolean,
    default: false,
  },
 
  /**
   * Array of available options.
   * @type {node[]}
   */
  options: {
    type: Array,
  },
 
  /**
   * Field placeholder, displayed when there's no value.
   */
  placeholder: {
    type: String,
    default: 'Select...',
  },
 
  /**
   * Applies HTML5 required attribute when needed.
   */
  required: {
    type: Boolean,
    default: false,
  },
 
  /**
   * Text displayed asking user whether to retry loading children options.
   */
  retryText: {
    type: String,
    default: 'Retry?',
  },
 
  /**
   * Title for the retry button.
   */
  retryTitle: {
    type: String,
    default: 'Click to retry',
  },
 
  /**
   * Enable searching feature?
   */
  searchable: {
    type: Boolean,
    default: true,
  },
 
  /**
   * Search in ancestor nodes too.
   */
  searchNested: {
    type: Boolean,
    default: false,
  },
 
  /**
   * Text tip to prompt for async search.
   */
  searchPromptText: {
    type: String,
    default: 'Type to search...',
  },
 
  /**
   * Whether to show a children count next to the label of each branch node.
   */
  showCount: {
    type: Boolean,
    default: false,
  },
 
  /**
   * Used in conjunction with `showCount` to specify which type of count number should be displayed.
   * Acceptable values:
   *   - "ALL_CHILDREN"
   *   - "ALL_DESCENDANTS"
   *   - "LEAF_CHILDREN"
   *   - "LEAF_DESCENDANTS"
   */
  showCountOf: {
    type: String,
    default: ALL_CHILDREN,
    validator(value) {
      const acceptableValues = [ALL_CHILDREN, ALL_DESCENDANTS, LEAF_CHILDREN, LEAF_DESCENDANTS]
      return includes(acceptableValues, value)
    },
  },
 
  /**
   * Whether to show children count when searching.
   * Fallbacks to the value of `showCount` when not specified.
   * @type {boolean}
   */
  showCountOnSearch: null,
 
  /**
   * In which order the selected options should be displayed in trigger & sorted in `value` array.
   * Used for multi-select mode only.
   * Acceptable values:
   *   - "ORDER_SELECTED"
   *   - "LEVEL"
   *   - "INDEX"
   */
  sortValueBy: {
    type: String,
    default: ORDER_SELECTED,
    validator(value) {
      const acceptableValues = [ORDER_SELECTED, LEVEL, INDEX]
      return includes(acceptableValues, value)
    },
  },
 
  /**
   * Tab index of the control.
   */
  tabIndex: {
    type: Number,
    default: 0,
  },
 
  /**
   * The value of the control.
   * Should be `id` or `node` object for single-select mode, or an array of `id` or `node` object for multi-select mode.
   * Its format depends on the `valueFormat` prop.
   * For most cases, just use `v-model` instead.
   * @type {?Array}
   */
  value: null,
 
  /**
   * Which kind of nodes should be included in the `value` array in multi-select mode.
   * Acceptable values:
   *   - "ALL" - Any node that is checked will be included in the `value` array
   *   - "BRANCH_PRIORITY" (default) - If a branch node is checked, all its descendants will be excluded in the `value` array
   *   - "LEAF_PRIORITY" - If a branch node is checked, this node itself and its branch descendants will be excluded from the `value` array but its leaf descendants will be included
   *   - "ALL_WITH_INDETERMINATE" - Any node that is checked will be included in the `value` array, plus indeterminate nodes
   */
  valueConsistsOf: {
    type: String,
    default: BRANCH_PRIORITY,
    validator(value) {
      const acceptableValues = [ALL, BRANCH_PRIORITY, LEAF_PRIORITY, ALL_WITH_INDETERMINATE]
      return includes(acceptableValues, value)
    },
  },
 
  /**
   * Format of `value` prop.
   * Note that, when set to `"object"`, only `id` & `label` properties are required in each `node` object in `value` prop.
   * Acceptable values:
   *   - "id"
   *   - "object"
   */
  valueFormat: {
    type: String,
    default: 'id',
  },
 
  /**
   * z-index of the menu.
   */
  zIndex: {
    type: [Number, String],
    default: 999,
  }
}
  1. 插件的的方法配置
javascript 复制代码
@input // // 选中触发(第一次回显的时候会触发,清除值的时候会触发, value值为undefined) input事件用于v-model双向绑定组件更新父组件值
 
@select // 选中触发(清除值的时候不会触发)
 
@deselect // 移除选项时触发 当设置multiple为true时生效  raw为当前移除的对象
 
@search-change // 搜索触发(输入框输入 值改变时)
 
@open // 展开时触发
 
@close // 关闭时触发
 
  1. 最终的效果
相关推荐
xing251622 分钟前
pytest-html
前端·html·pytest
茂茂在长安32 分钟前
Linux 命令大全完整版(11)
java·linux·运维·服务器·前端·centos
Violet51533 分钟前
ECMAScript规范解读——this的判定
javascript
知识分享小能手1 小时前
Html5学习教程,从入门到精通,HTML5 简介语法知识点及案例代码(1)
开发语言·前端·javascript·学习·前端框架·html·html5
IT、木易1 小时前
大白话React第二章深入理解阶段
前端·javascript·react.js
晚安7201 小时前
Ajax相关
前端·javascript·ajax
图书馆钉子户1 小时前
怎么使用ajax实现局部刷新
前端·ajax·okhttp
bin91532 小时前
DeepSeek 助力 Vue 开发:打造丝滑的单选按钮(Radio Button)
前端·javascript·vue.js·ecmascript·deepseek
qianmoQ2 小时前
第五章:工程化实践 - 第五节 - Tailwind CSS 常见问题解决方案
前端·css
那就可爱多一点点2 小时前
超高清大图渲染性能优化实战:从页面卡死到流畅加载
前端·javascript·性能优化