这个是在若依框架无意中发现的一个下拉树通用组件。@riophae/vue-treeselect 是一个基于 Vue.js 的树形选择器组件,可以用于选择树形结构的数据。它支持多选、搜索、异步加载等功能,可以自定义选项的样式和模板。该组件易于使用和扩展,适用于各种类型的项目。
npm: www.npmjs.com/package/@ri…
首先安装:
使用自己习惯使用的包管理器安装就可以了
pnpm add @riophae/vue-treeselect 引入注册:
import Treeselect from '@riophae/vue-treeselect' import '@riophae/vue-treeselect/dist/vue-treeselect.css'
export default { components: { Treeselect } }
基本使用:
里面可配置的属性很多,下面是在源码中看到的:
部分注释百度翻译成中文了,但太多了,懒得挨个翻译了,直接看也大概知道啥意思
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)} */ 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 "{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
maxHeightstyle value of the menu. */ maxHeight: { type: Number, default: 300, },
/**
- Set
trueto allow selecting multiple options (a.k.a., multi-select mode). */ multiple: { type: Boolean, default: false, },
/**
- Generates a hidden 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
showCountto 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
showCountwhen not specified. - @type {boolean} */ showCountOnSearch: null,
/**
- In which order the selected options should be displayed in trigger & sorted in
valuearray. - 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
idornodeobject for single-select mode, or an array ofidornodeobject for multi-select mode. - Its format depends on the
valueFormatprop. - For most cases, just use
v-modelinstead. - @type {?Array} */ value: null,
/**
- Which kind of nodes should be included in the
valuearray in multi-select mode. - Acceptable values:
-
- "ALL" - Any node that is checked will be included in the
valuearray
- "ALL" - Any node that is checked will be included in the
-
- "BRANCH_PRIORITY" (default) - If a branch node is checked, all its descendants will be excluded in the
valuearray
- "BRANCH_PRIORITY" (default) - If a branch node is checked, all its descendants will be excluded in the
-
- "LEAF_PRIORITY" - If a branch node is checked, this node itself and its branch descendants will be excluded from the
valuearray but its leaf descendants will be included
- "LEAF_PRIORITY" - If a branch node is checked, this node itself and its branch descendants will be excluded from the
-
- "ALL_WITH_INDETERMINATE" - Any node that is checked will be included in the
valuearray, 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) }, },
- "ALL_WITH_INDETERMINATE" - Any node that is checked will be included in the
/**
- Format of
valueprop. - Note that, when set to
"object", onlyid&labelproperties are required in eachnodeobject invalueprop. - Acceptable values:
-
- "id"
-
- "object" */ valueFormat: { type: String, default: 'id', },
/**
- z-index of the menu. */ zIndex: { type: [Number, String], default: 999, } } 然后我简单看了一下好像一共向外暴露了6个方法如下:
@input // // 选中触发(第一次回显的时候会触发,清除值的时候会触发, value值为undefined) input事件用于v-model双向绑定组件更新父组件值 @select // 选中触发(清除值的时候不会触发) @deselect // 移除选项时触发 当设置multiple为true时生效 raw为当前移除的对象 @search-change // 搜索触发(输入框输入 值改变时) @open // 展开时触发 @close // 关闭时触发
下面是我测试的一个例子,一般的需求应该足够了
字体样式简单调了一下
.main { width: 100%; height: 100%; padding: 60px 0 0 200px; } .main .tree { width: 240px; height: 40px; }::v-deep .vue-treeselect__label { color: #606266; } 测试数据:
export default [ { "title": "系统管理", "parentId": 0, "id": 1, "children": [ { "title": "菜单管理", "parentId": 1, "id": 11, "children": [ { "title": "菜单新增", "parentId": 11, "id": 111 }, { "title": "菜单编辑", "parentId": 11, "id": 112 }, { "title": "菜单删除", "parentId": 11, "id": 113 } ] }, { "title": "角色管理", "parentId": 1, "id": 22, "children": [ { "title": "角色编辑", "parentId": 22, "id": 222 }, { "title": "角色新增", "parentId": 22, "id": 221 }, { "title": "角色删除", "parentId": 22, "id": 223 } ] } ] }, { "title": "用户管理", "parentId": 0, "id": 33, "children": [ { "title": "用户新增", "parentId": 33, "id": 331 }, { "title": "用户编辑", "parentId": 33, "id": 332 }, { "title": "用户删除", "parentId": 33, "id": 333 } ] } ]