android系统定制开发vue递归组件—开发树形组件Tree--(构建树形菜单)

在 Vue 中,android系统定制开发组件可以递归的调用本身,android系统定制开发但是有一些条件:

  • android系统定制开发该组件一定要有 name 属性
  • android系统定制开发要确保递归的调用有终止条件,android系统定制开发防止内存溢出

不知道大家有没遇到过这样的场景:渲染列表数据的时候,列表的子项还是列表。如果层级少尚且可以用几个for循环搞定,但是层级多或者层级不确定就有点无从下手了。

其实这就是数据,像常见的例如导航、空间或逻辑组织、页面定位、级联选择等,其结构可展开或折叠,都属于这种结构。

效果展示

以上就是使用组件递归,并加入简单交互的展示效果。点击节点会在控制台输出节点对应的数据,如果有子节点,则会展开或收起子节点。接下来我们就看看如何实现以上效果吧! 

渲染完整数据

渲染数据这一步非常简单,首先是把树形结构封装成一个列表组件,其次判断每一项有没有子节点,如果有子节点,再使用自身组件去渲染就可以了。

递归组件:src/components/tree-folder.vue

  1. //项目用到vant-ui库
  2. <template>
  3. <div class="tree-item">
  4. <div v-for="item in treeData" :key="item.id">
  5. <div class="item-title">
  6. <span v-text="item.name"></span>
  7. <span v-if="item.children && item.children.length">
  8. //vant组件库图标 看个人需求换成自己需要的
  9. <van-iconname="arrow-down"/>
  10. </span>
  11. </div>
  12. <div v-if="item.children && item.children.length" class="item-childen">
  13. <tree-folder :treeData="item.children"></tree-folder>
  14. </div>
  15. </div>
  16. </div>
  17. </template>
  18. <script>
  19. export default {
  20. name: 'TreeFolder',
  21. props: {
  22. treeData: {
  23. type: Array,
  24. default: () => []
  25. }
  26. }
  27. }
  28. </script>
  29. <style lang="stylus">
  30. .tree-item
  31. .item-title
  32. padding: 4px 8px
  33. .name
  34. color: #000
  35. .negative-rotate
  36. transform: rotate(-180deg)
  37. .item-childen
  38. padding-left: 20px
  39. </style>

父组件: src/home.vue

  1. <template>
  2. //引用递归组件,并传递数据
  3. <TreeFolder :treeData="treeData" />
  4. </template>
  5. <script>
  6. const treeData = [
  7. { id: 1, name: '一级1' },
  8. {
  9. id: 2,
  10. name: '一级2',
  11. children: [
  12. { id: 3, name: '二级2-1' },
  13. { id: 4, name: '二级2-2' }
  14. ]
  15. },
  16. {
  17. id: 5,
  18. name: '一级3',
  19. children: [
  20. {
  21. id: 6,
  22. name: '二级3-1',
  23. children: [
  24. { id: 7, name: '三级3-1-1' },
  25. { id: 8, name: '三级3-1-2' }
  26. ]
  27. },
  28. { id: 9, name: '二级3-2' },
  29. { id: 10, name: '二级3-3' }
  30. ]
  31. }
  32. ]
  33. import TreeFolder from '@/components/tree-folder.vue'
  34. export default {
  35. components: {
  36. TreeFolder
  37. },
  38. data() {
  39. return {
  40. treeData: treeData
  41. }
  42. }
  43. }
  44. </script>

效果如下

获取节点数据

接下来我们要做的是,点击节点时在控制台输出对应的数据。首先我们使用 $emit,将一级节点的 item 传递出去,也就是的方法,相信大家都会。

其次是将内层节点的数据传递出去,同样使用子传父的方法,只是我们需要给组件里面的 tree-folder绑定@tree-node-click="$emit('tree-node-click', $event)",这样每次子级每次都可以调用父级的 tree-node-click 方法,父级又调用它的父级 tree-node-click 方法,最终调的都是最外层的 tree-node-click 方法,我们只需要在这个过程中,把数据传递过去就可以了。这块有点绕,相信大家多看几遍应该可以看懂。修改如下:

递归组件:src/components/tree-folder.vue

  1. //方法名可以自取 不一定非是'tree-node-click'
  2. <div class="item-title">
  3. <span v-text="item.name"></span>
  4. <span v-if="item.children && item.children.length">
  5. //vant组件库图标 看个人需求换成自己需要的
  6. <van-icon name="arrow-down"/>
  7. </span>
  8. </div>
  9. <div v-if="item.children && item.children.length" class="item-childen">
  10. <tree-folder
  11. :treeData="item.children"
  12. @tree-node-click="$emit('tree-node-click', $event)"
  13. ></tree-folder>
  14. </div>
  15. ...
  16. methods: {
  17. itemNodeClick(item) {
  18. this.$emit('tree-node-click', item)
  19. }
  20. }

父组件: src/home.vue

  1. <TreeFolder:tree-data="treeData" @tree-node-click="nodeClick" />
  2. ...
  3. methods: {
  4. nodeClick(val) {
  5. console.log(val)
  6. }
  7. }

效果如下

动态展开收起并给点击项添加激活样式和设置图标的样式

动态展开收起的思路是给组件设置一个数组,数组中存放的是当前列表中需要展开的节点的id,当点击节点的时候添加或删除节点id,然后判断每个节点的id在不在这个数组,在则显示子节点,不在则隐藏子节点。 

点击项添加激活样式的思路是给组件绑定一个变量,变量存放的值是点击当前列表中节点的id,当点击节点的时候通过id添加相对应的样式。

图标样式的思路是根据数组中存放的的节点的id,判断节点的id如果在这个数组,则旋转 -180deg,不在则回到最初状态。 

 递归组件:src/components/tree-folder.vue

  1. //项目用到vant-ui库
  2. <template>
  3. <div class="tree-item">
  4. <div v-for="item in treeData" :key="item.id">
  5. <div class="item-title">
  6. <span
  7. :class="{ 'fc-theme': item.id == curNameId }" // fc-theme激活样式的类名
  8. v-text="item.name">
  9. </span>
  10. <span v-if="item.children && item.children.length">
  11. //vant组件库图标 看个人需求换成自己需要的
  12. <van-icon
  13. name="arrow-down"
  14. style="transition: transform 0.3s" // 加一个延迟动画效果
  15. :class="{ 'negative-rotate': isOpen(item.id), 'fc-theme': item.id ==
  16. curNameId }"/> // 根据id判断是否旋转图标和添加激活样式
  17. </span>
  18. </div>
  19. <div v-if="item.children && item.children.length" v-show="isOpen(item.id)"
  20. class="item-childen">
  21. //这里是重点:current="curNameId",否则无法实现动态添加激活样式
  22. <tree-folder :treeData="item.children" :current="curNameId" @tree-node-
  23. click="$emit('tree-node-click', $event)">
  24. </tree-folder>
  25. </div>
  26. </div>
  27. </div>
  28. </template>
  29. <script>
  30. export default {
  31. name: 'TreeFolder',
  32. props: {
  33. treeData: {
  34. type: Array,
  35. default: () => []
  36. },
  37. current: Number //保存当前点击节点的id
  38. },
  39. data() {
  40. return {
  41. expandedKeys: [], // 当前列表需要展开的节点id组成的数组
  42. curNameId: 0 //保存current的值
  43. }
  44. },
  45. computed: {
  46. isOpen() {
  47. return function (id) {
  48. // 判断节点id在不在数组中,在则显示,不在则隐藏
  49. return this.expandedKeys.includes(id)
  50. }
  51. }
  52. },
  53. watch: {
  54. //监听当前点击节点的id
  55. current: {
  56. handler(num) {
  57. this.curNameId = num
  58. },
  59. deep: true,
  60. immediate: true
  61. }
  62. },
  63. methods: {
  64. itemNodeClick(item) {
  65. this.$emit('tree-node-click', item)
  66. if (item.children && item.children.length) {
  67. let index = this.expandedKeys.indexOf(item.id)
  68. if (index > -1) {
  69. // 如果当前节点id存在数组中,则删除
  70. this.expandedKeys.splice(index, 1)
  71. } else {
  72. // 如果当前节点id不存在数组中,则添加
  73. this.expandedKeys.push(item.id)
  74. }
  75. }
  76. }
  77. }
  78. }
  79. </script>
  80. <style lang="stylus">
  81. .tree-item
  82. .item-title
  83. padding: 4px 8px
  84. .name
  85. color: #000
  86. .negative-rotate
  87. transform: rotate(-180deg)
  88. .item-childen
  89. padding-left: 20px
  90. </style>

父组件: src/home.vue

  1. <TreeFolder:tree-data="treeData" :current="curNameId" @tree-node-click="nodeClick" />
  2. ...
  3. data() {
  4. return {
  5. curNameId: 0
  6. }
  7. },
  8. methods: {
  9. nodeClick(val) {
  10. this.curNameId = val.id
  11. this.$set(val, 'curNameId', this.curNameId)
  12. }
  13. }

最终效果

 

附上一个疑问的解答

组件调用组件自己的时候,$emit了tree-node-click事件,这个功能主要是做什么的,还有为什么$emit('tree-node-click', $event)第二个参数是$event而不是一个可变数据

 这是为了实现内层的子传父功能,使用了@tree-node-click="$emit('tree-node-click', $event)",在内层执行this.$emit("tree-node-click", item) 时候,其实就是执行父组件的 $emit('tree-node-click', $event),然后又会执行爷爷组件的 $emit('tree-node-click', $event),最终执行的是父组件: src/home.vue 的 nodeClick 事件,其实参数一直是item。

 以上就是今天的分享!是有点绕,请耐心看完思考并且自己动手试一试。有兴趣的小伙伴可以动手试一哈,把组件进一步封装,或修改成自己想要的样式和效果。

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发