您现在的位置是:首页 > 技术教程 正文

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

admin 阅读: 2024-04-02
后台-插件-广告管理-内容页头部广告(手机)

这个是在若依框架无意中发现的一个下拉树通用组件。@riophae/vue-treeselect 是一个基于 Vue.js 的树形选择器组件,可以用于选择树形结构的数据。它支持多选、搜索、异步加载等功能,可以自定义选项的样式和模板。该组件易于使用和扩展,适用于各种类型的项目。

npm:https://www.npmjs.com/package/@riophae/vue-treeselect 

首先安装: 

 使用自己习惯使用的包管理器安装就可以了

pnpm add @riophae/vue-treeselect

引入注册: 

  1. import Treeselect from '@riophae/vue-treeselect'
  2. import '@riophae/vue-treeselect/dist/vue-treeselect.css'
  3. export default {
  4. components: { Treeselect }
  5. }

基本使用:

  1. <script>
  2. // import the component
  3. import Treeselect from '@riophae/vue-treeselect'
  4. // import the styles
  5. import '@riophae/vue-treeselect/dist/vue-treeselect.css'
  6. export default {
  7. // register the component
  8. components: { Treeselect },
  9. data() {
  10. return {
  11. // define the default value
  12. value: null,
  13. // define options
  14. options: [ {
  15. id: 'a',
  16. label: 'a',
  17. children: [ {
  18. id: 'aa',
  19. label: 'aa',
  20. }, {
  21. id: 'ab',
  22. label: 'ab',
  23. } ],
  24. }, {
  25. id: 'b',
  26. label: 'b',
  27. }, {
  28. id: 'c',
  29. label: 'c',
  30. } ],
  31. }
  32. },
  33. }
  34. script>

里面可配置的属性很多,下面是在源码中看到的: 

部分注释百度翻译成中文了,但太多了,懒得挨个翻译了,直接看也大概知道啥意思 

  1. props: {
  2. /**
  3. * 即使有禁用的选定节点,是否允许重置值
  4. */
  5. allowClearingDisabled: {
  6. type: Boolean,
  7. default: false,
  8. },
  9. /**
  10. * 选择/取消选择祖先节点时,是否应选择/取消选中其禁用的后代
  11. * 和 allowClearingDisabled 一起使用
  12. */
  13. allowSelectingDisabledDescendants: {
  14. type: Boolean,
  15. default: false,
  16. },
  17. /**
  18. * 菜单是否应始终打开
  19. */
  20. alwaysOpen: {
  21. type: Boolean,
  22. default: false,
  23. },
  24. /**
  25. * 是否将菜单加到body上
  26. */
  27. appendToBody: {
  28. type: Boolean,
  29. default: false,
  30. },
  31. /**
  32. * 是否启用异步搜索模式
  33. */
  34. async: {
  35. type: Boolean,
  36. default: false,
  37. },
  38. /**
  39. * 是否自动将组件集中在装载上?
  40. */
  41. autoFocus: {
  42. type: Boolean,
  43. default: false,
  44. },
  45. /**
  46. * 装载时自动加载根选项。当设置为“false”时,打开菜单时将加载根选项。
  47. */
  48. autoLoadRootOptions: {
  49. type: Boolean,
  50. default: true,
  51. },
  52. /**
  53. * 当用户取消选择一个节点时,会自动取消选择其祖先。仅适用于平面模式。
  54. */
  55. autoDeselectAncestors: {
  56. type: Boolean,
  57. default: false,
  58. },
  59. /**
  60. * 当用户取消选择节点时,会自动取消选择其子节点。仅适用于平面模式。
  61. */
  62. autoDeselectDescendants: {
  63. type: Boolean,
  64. default: false,
  65. },
  66. /**
  67. * 当用户选择一个节点时,会自动选择其祖先。仅适用于平面模式。
  68. */
  69. autoSelectAncestors: {
  70. type: Boolean,
  71. default: false,
  72. },
  73. /**
  74. * 当用户选择一个节点时,会自动选择其子节点。仅适用于平面模式。
  75. */
  76. autoSelectDescendants: {
  77. type: Boolean,
  78. default: false,
  79. },
  80. /**
  81. * 如果没有文本输入,按退格键是否删除最后一项。
  82. */
  83. backspaceRemoves: {
  84. type: Boolean,
  85. default: true,
  86. },
  87. /**
  88. * 在清除所有输入字段之前进行处理的函数。
  89. * 返回“false”以防止清除值
  90. * @type {function(): (boolean|Promise)}
  91. */
  92. beforeClearAll: {
  93. type: Function,
  94. default: constant(true),
  95. },
  96. /**
  97. * 在叶节点之前显示分支节点?
  98. */
  99. branchNodesFirst: {
  100. type: Boolean,
  101. default: false,
  102. },
  103. /**
  104. * 是否应该缓存每个搜索请求的结果?
  105. */
  106. cacheOptions: {
  107. type: Boolean,
  108. default: true,
  109. },
  110. /**
  111. * 是否显示重置值的“×”按钮?
  112. */
  113. clearable: {
  114. type: Boolean,
  115. default: true,
  116. },
  117. /**
  118. * 清楚文本,multiple为true时
  119. */
  120. clearAllText: {
  121. type: String,
  122. default: 'Clear all',
  123. },
  124. /**
  125. * 选择后是否清除搜索输入。
  126. * 仅当“multiple”为“true”时使用。
  127. * 对于单选模式,无论道具值如何,它总是**在选择一个选项后清除输入。
  128. */
  129. clearOnSelect: {
  130. type: Boolean,
  131. default: false,
  132. },
  133. /**
  134. * “×”按钮的标题。
  135. */
  136. clearValueText: {
  137. type: String,
  138. default: 'Clear value',
  139. },
  140. /**
  141. * 选择选项后是否关闭菜单?
  142. * 仅当“multiple”为“true”时使用。
  143. */
  144. closeOnSelect: {
  145. type: Boolean,
  146. default: true,
  147. },
  148. /**
  149. * 加载时应自动展开多少级别的分支节点。
  150. * 设置Infinity以使所有分支节点在默认情况下展开。
  151. */
  152. defaultExpandLevel: {
  153. type: Number,
  154. default: 0,
  155. },
  156. /**
  157. * 在用户开始搜索之前要显示的默认选项集。用于异步搜索模式。
  158. * 当设置为“true”时,将自动加载作为空字符串的搜索查询结果。
  159. * @type {boolean|node[]}
  160. */
  161. defaultOptions: {
  162. default: false,
  163. },
  164. /**
  165. * 如果没有文本输入,按delete键是否删除最后一项。
  166. */
  167. deleteRemoves: {
  168. type: Boolean,
  169. default: true,
  170. },
  171. /**
  172. * 用于连接隐藏字段值的多个值的分隔符。
  173. */
  174. delimiter: {
  175. type: String,
  176. default: ',',
  177. },
  178. /**
  179. * 仅显示与搜索值直接匹配的节点,不包括其祖先。
  180. *
  181. * @type {Object}
  182. */
  183. flattenSearchResults: {
  184. type: Boolean,
  185. default: false,
  186. },
  187. /**
  188. * 是否阻止选择分支节点?
  189. */
  190. disableBranchNodes: {
  191. type: Boolean,
  192. default: false,
  193. },
  194. /**
  195. * 禁用控制?
  196. */
  197. disabled: {
  198. type: Boolean,
  199. default: false,
  200. },
  201. /**
  202. * 是否禁用模糊匹配功能?
  203. */
  204. disableFuzzyMatching: {
  205. type: Boolean,
  206. default: false,
  207. },
  208. /**
  209. *是否启用平面模式。非平面模式(默认)是指:
  210. * - 每当检查分支节点时,它的所有子节点也将被检查
  211. * - 每当一个分支节点检查了所有子节点时,该分支节点本身也会被检查
  212. * 设置“true”以禁用此机制
  213. */
  214. flat: {
  215. type: Boolean,
  216. default: false,
  217. },
  218. /**
  219. * 将以所有事件作为最后一个参数进行传递。
  220. * 有助于识别事件的起源。
  221. */
  222. instanceId: {
  223. // Add two trailing "$" to distinguish from explictly specified ids.
  224. default: () => `${instanceId++}$$`,
  225. type: [String, Number],
  226. },
  227. /**
  228. * Joins multiple values into a single form field with the `delimiter` (legacy mode).
  229. * 使用“分隔符”将多个值合并到一个表单字段中(传统模式)。
  230. */
  231. joinValues: {
  232. type: Boolean,
  233. default: false,
  234. },
  235. /**
  236. * 限制所选选项的显示。
  237. * 其余部分将隐藏在limitText字符串中。
  238. */
  239. limit: {
  240. type: Number,
  241. default: Infinity,
  242. },
  243. /**
  244. * Function that processes the message shown when selected elements pass the defined limit.
  245. * @type {function(number): string}
  246. */
  247. limitText: {
  248. type: Function,
  249. default: function limitTextDefault(count) { // eslint-disable-line func-name-matching
  250. return `and ${count} more`
  251. },
  252. },
  253. /**
  254. * Text displayed when loading options.
  255. */
  256. loadingText: {
  257. type: String,
  258. default: 'Loading...',
  259. },
  260. /**
  261. * Used for dynamically loading options.
  262. * @type {function({action: string, callback: (function((Error|string)=): void), parentNode: node=, instanceId}): void}
  263. */
  264. loadOptions: {
  265. type: Function,
  266. },
  267. /**
  268. * Which node properties to filter on.
  269. */
  270. matchKeys: {
  271. type: Array,
  272. default: constant(['label']),
  273. },
  274. /**
  275. * Sets `maxHeight` style value of the menu.
  276. */
  277. maxHeight: {
  278. type: Number,
  279. default: 300,
  280. },
  281. /**
  282. * Set `true` to allow selecting multiple options (a.k.a., multi-select mode).
  283. */
  284. multiple: {
  285. type: Boolean,
  286. default: false,
  287. },
  288. /**
  289. * Generates a hidden tag with this field name for html forms.
  290. */
  291. name: {
  292. type: String,
  293. },
  294. /**
  295. * Text displayed when a branch node has no children.
  296. */
  297. noChildrenText: {
  298. type: String,
  299. default: 'No sub-options.',
  300. },
  301. /**
  302. * Text displayed when there are no available options.
  303. */
  304. noOptionsText: {
  305. type: String,
  306. default: 'No options available.',
  307. },
  308. /**
  309. * Text displayed when there are no matching search results.
  310. */
  311. noResultsText: {
  312. type: String,
  313. default: 'No results found...',
  314. },
  315. /**
  316. * Used for normalizing source data.
  317. * @type {function(node, instanceId): node}
  318. */
  319. normalizer: {
  320. type: Function,
  321. default: identity,
  322. },
  323. /**
  324. * By default (`auto`), the menu will open below the control. If there is not
  325. * enough space, vue-treeselect will automatically flip the menu.
  326. * You can use one of other four options to force the menu to be always opened
  327. * to specified direction.
  328. * Acceptable values:
  329. * - `"auto"`
  330. * - `"below"`
  331. * - `"bottom"`
  332. * - `"above"`
  333. * - `"top"`
  334. */
  335. openDirection: {
  336. type: String,
  337. default: 'auto',
  338. validator(value) {
  339. const acceptableValues = ['auto', 'top', 'bottom', 'above', 'below']
  340. return includes(acceptableValues, value)
  341. },
  342. },
  343. /**
  344. * Whether to automatically open the menu when the control is clicked.
  345. */
  346. openOnClick: {
  347. type: Boolean,
  348. default: true,
  349. },
  350. /**
  351. * Whether to automatically open the menu when the control is focused.
  352. */
  353. openOnFocus: {
  354. type: Boolean,
  355. default: false,
  356. },
  357. /**
  358. * Array of available options.
  359. * @type {node[]}
  360. */
  361. options: {
  362. type: Array,
  363. },
  364. /**
  365. * Field placeholder, displayed when there's no value.
  366. */
  367. placeholder: {
  368. type: String,
  369. default: 'Select...',
  370. },
  371. /**
  372. * Applies HTML5 required attribute when needed.
  373. */
  374. required: {
  375. type: Boolean,
  376. default: false,
  377. },
  378. /**
  379. * Text displayed asking user whether to retry loading children options.
  380. */
  381. retryText: {
  382. type: String,
  383. default: 'Retry?',
  384. },
  385. /**
  386. * Title for the retry button.
  387. */
  388. retryTitle: {
  389. type: String,
  390. default: 'Click to retry',
  391. },
  392. /**
  393. * Enable searching feature?
  394. */
  395. searchable: {
  396. type: Boolean,
  397. default: true,
  398. },
  399. /**
  400. * Search in ancestor nodes too.
  401. */
  402. searchNested: {
  403. type: Boolean,
  404. default: false,
  405. },
  406. /**
  407. * Text tip to prompt for async search.
  408. */
  409. searchPromptText: {
  410. type: String,
  411. default: 'Type to search...',
  412. },
  413. /**
  414. * Whether to show a children count next to the label of each branch node.
  415. */
  416. showCount: {
  417. type: Boolean,
  418. default: false,
  419. },
  420. /**
  421. * Used in conjunction with `showCount` to specify which type of count number should be displayed.
  422. * Acceptable values:
  423. * - "ALL_CHILDREN"
  424. * - "ALL_DESCENDANTS"
  425. * - "LEAF_CHILDREN"
  426. * - "LEAF_DESCENDANTS"
  427. */
  428. showCountOf: {
  429. type: String,
  430. default: ALL_CHILDREN,
  431. validator(value) {
  432. const acceptableValues = [ALL_CHILDREN, ALL_DESCENDANTS, LEAF_CHILDREN, LEAF_DESCENDANTS]
  433. return includes(acceptableValues, value)
  434. },
  435. },
  436. /**
  437. * Whether to show children count when searching.
  438. * Fallbacks to the value of `showCount` when not specified.
  439. * @type {boolean}
  440. */
  441. showCountOnSearch: null,
  442. /**
  443. * In which order the selected options should be displayed in trigger & sorted in `value` array.
  444. * Used for multi-select mode only.
  445. * Acceptable values:
  446. * - "ORDER_SELECTED"
  447. * - "LEVEL"
  448. * - "INDEX"
  449. */
  450. sortValueBy: {
  451. type: String,
  452. default: ORDER_SELECTED,
  453. validator(value) {
  454. const acceptableValues = [ORDER_SELECTED, LEVEL, INDEX]
  455. return includes(acceptableValues, value)
  456. },
  457. },
  458. /**
  459. * Tab index of the control.
  460. */
  461. tabIndex: {
  462. type: Number,
  463. default: 0,
  464. },
  465. /**
  466. * The value of the control.
  467. * Should be `id` or `node` object for single-select mode, or an array of `id` or `node` object for multi-select mode.
  468. * Its format depends on the `valueFormat` prop.
  469. * For most cases, just use `v-model` instead.
  470. * @type {?Array}
  471. */
  472. value: null,
  473. /**
  474. * Which kind of nodes should be included in the `value` array in multi-select mode.
  475. * Acceptable values:
  476. * - "ALL" - Any node that is checked will be included in the `value` array
  477. * - "BRANCH_PRIORITY" (default) - If a branch node is checked, all its descendants will be excluded in the `value` array
  478. * - "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
  479. * - "ALL_WITH_INDETERMINATE" - Any node that is checked will be included in the `value` array, plus indeterminate nodes
  480. */
  481. valueConsistsOf: {
  482. type: String,
  483. default: BRANCH_PRIORITY,
  484. validator(value) {
  485. const acceptableValues = [ALL, BRANCH_PRIORITY, LEAF_PRIORITY, ALL_WITH_INDETERMINATE]
  486. return includes(acceptableValues, value)
  487. },
  488. },
  489. /**
  490. * Format of `value` prop.
  491. * Note that, when set to `"object"`, only `id` & `label` properties are required in each `node` object in `value` prop.
  492. * Acceptable values:
  493. * - "id"
  494. * - "object"
  495. */
  496. valueFormat: {
  497. type: String,
  498. default: 'id',
  499. },
  500. /**
  501. * z-index of the menu.
  502. */
  503. zIndex: {
  504. type: [Number, String],
  505. default: 999,
  506. }
  507. }

然后我简单看了一下好像一共向外暴露了6个方法如下:

  1. @input // // 选中触发(第一次回显的时候会触发,清除值的时候会触发, value值为undefined) input事件用于v-model双向绑定组件更新父组件值
  2. @select // 选中触发(清除值的时候不会触发)
  3. @deselect // 移除选项时触发 当设置multiple为true时生效 raw为当前移除的对象
  4. @search-change // 搜索触发(输入框输入 值改变时)
  5. @open // 展开时触发
  6. @close // 关闭时触发

下面是我测试的一个例子,一般的需求应该足够了

字体样式简单调了一下 

  1. <script>
  2. import Treeselect from '@riophae/vue-treeselect'
  3. import '@riophae/vue-treeselect/dist/vue-treeselect.css'
  4. import treeData from './data/tree'
  5. export default {
  6. data() {
  7. return {
  8. value: null,
  9. options: []
  10. }
  11. },
  12. components: { Treeselect },
  13. mounted(){
  14. // 延迟模拟请求数据
  15. setTimeout(() => {
  16. this.options = treeData
  17. this.value = [111, 113] // 测试回显操作
  18. }, 300)
  19. },
  20. methods:{
  21. // 选中触发(第一次回显的时候会触发,清除值的时候会触发, value值为undefined) input事件用于v-model双向绑定组件更新父组件值
  22. treeSelectInput(value, instanceId) {
  23. console.log(value, 'input事件')
  24. console.log(this.value, 'this.value -- input') // 这个不需要 延迟
  25. },
  26. // 选中触发(清除值的时候不会触发)
  27. treeSelectChange(raw, instanceId) {
  28. console.log(raw, '当前的对象')
  29. setTimeout(() => { // 如果用到this.value 需要setTimeout延迟一下拿到最新的值
  30. console.log(this.value, 'this.value -- select')
  31. })
  32. },
  33. // 移除选项时触发 当设置multiple为true时生效 raw为当前移除的对象
  34. treeSelectDeselect(raw, instanceId) {
  35. console.log(raw, 'deselect-->>')
  36. },
  37. // 搜索
  38. treeSelectSearch(searchQuery, instanceId) {
  39. console.log(searchQuery, '当前搜索的值')
  40. },
  41. // 展开触发
  42. treeSelectOpen(instanceId) {
  43. console.log('展开了')
  44. },
  45. // 关闭触发
  46. treeSelectClose(value, instanceId) {
  47. console.log(value, '当前的value值')
  48. },
  49. // 字段默认 id label 用于规范化数据源
  50. tenantIdnormalizer(node, instanceId) {
  51. if (node.children && !node.children.length) {
  52. delete node.children
  53. }
  54. return {
  55. id: node.id,
  56. label: node.title,
  57. children: node.children
  58. }
  59. }
  60. }
  61. }
  62. script>
  63. <style scoped>
  64. .main {
  65. width: 100%;
  66. height: 100%;
  67. padding: 60px 0 0 200px;
  68. }
  69. .main .tree {
  70. width: 240px;
  71. height: 40px;
  72. }
  73. ::v-deep .vue-treeselect__label {
  74. color: #606266;
  75. }
  76. style>

测试数据: 

  1. export default [
  2. {
  3. "title": "系统管理",
  4. "parentId": 0,
  5. "id": 1,
  6. "children": [
  7. {
  8. "title": "菜单管理",
  9. "parentId": 1,
  10. "id": 11,
  11. "children": [
  12. {
  13. "title": "菜单新增",
  14. "parentId": 11,
  15. "id": 111
  16. },
  17. {
  18. "title": "菜单编辑",
  19. "parentId": 11,
  20. "id": 112
  21. },
  22. {
  23. "title": "菜单删除",
  24. "parentId": 11,
  25. "id": 113
  26. }
  27. ]
  28. },
  29. {
  30. "title": "角色管理",
  31. "parentId": 1,
  32. "id": 22,
  33. "children": [
  34. {
  35. "title": "角色编辑",
  36. "parentId": 22,
  37. "id": 222
  38. },
  39. {
  40. "title": "角色新增",
  41. "parentId": 22,
  42. "id": 221
  43. },
  44. {
  45. "title": "角色删除",
  46. "parentId": 22,
  47. "id": 223
  48. }
  49. ]
  50. }
  51. ]
  52. },
  53. {
  54. "title": "用户管理",
  55. "parentId": 0,
  56. "id": 33,
  57. "children": [
  58. {
  59. "title": "用户新增",
  60. "parentId": 33,
  61. "id": 331
  62. },
  63. {
  64. "title": "用户编辑",
  65. "parentId": 33,
  66. "id": 332
  67. },
  68. {
  69. "title": "用户删除",
  70. "parentId": 33,
  71. "id": 333
  72. }
  73. ]
  74. }
  75. ]

效果如下: 

 

标签:
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

在线投稿:投稿 站长QQ:1888636

后台-插件-广告管理-内容页尾部广告(手机)
关注我们

扫一扫关注我们,了解最新精彩内容

搜索
排行榜