uniapp 如何实现自定义树形结构并点击加载子集

在uniapp中如何实现自定义树形结构,并且点击节点时可以动态加载对应的子集数据?目前尝试用uview的tree组件但无法满足需求,希望实现类似递归组件的方式,能够根据点击事件异步获取子节点数据并渲染。请问应该如何设计组件结构和处理数据加载逻辑?

2 回复

在uniapp中实现自定义树形结构,可通过递归组件实现。每个节点包含点击事件,点击时请求子节点数据并动态渲染。使用v-for循环渲染子节点,结合v-if控制子节点显示。数据格式建议为嵌套数组,包含children字段。


在 UniApp 中实现自定义树形结构并支持点击加载子集,可以通过以下步骤实现:

1. 数据结构设计

使用嵌套数组或对象表示树形数据,每个节点包含:

  • id:唯一标识
  • label:显示文本
  • children:子节点数组
  • isOpen:控制展开状态(可选)
  • hasChildren:标记是否有子节点(用于懒加载)

示例数据:

data() {
  return {
    treeData: [
      {
        id: 1,
        label: '节点1',
        children: [
          { id: 11, label: '子节点1-1', children: [] },
          { id: 12, label: '子节点1-2', children: [] }
        ],
        isOpen: false
      },
      {
        id: 2,
        label: '节点2',
        children: [],
        hasChildren: true, // 标记需要懒加载
        isOpen: false
      }
    ]
  }
}

2. 递归组件实现

创建递归组件 TreeItem

<!-- components/TreeItem.vue -->
<template>
  <view>
    <view class="node" @click="toggleNode">
      <text>{{ node.label }}</text>
      <text v-if="node.hasChildren && !node.children.length">▶</text>
      <text v-else-if="node.children.length">▼</text>
    </view>
    <view v-if="node.isOpen && node.children.length" class="children">
      <TreeItem 
        v-for="child in node.children" 
        :key="child.id" 
        :node="child"
        @load-children="onLoadChildren"
      />
    </view>
  </view>
</template>

<script>
export default {
  name: 'TreeItem',
  props: {
    node: Object
  },
  methods: {
    toggleNode() {
      if (this.node.hasChildren && !this.node.children.length) {
        // 触发懒加载
        this.$emit('load-children', this.node)
      } else {
        // 切换展开状态
        this.$set(this.node, 'isOpen', !this.node.isOpen)
      }
    },
    onLoadChildren(node) {
      this.$emit('load-children', node)
    }
  }
}
</script>

<style>
.node {
  padding: 10rpx;
  border-bottom: 1px solid #eee;
}
.children {
  margin-left: 40rpx;
}
</style>

3. 主页面使用

<!-- pages/index/index.vue -->
<template>
  <view>
    <TreeItem 
      v-for="node in treeData" 
      :key="node.id" 
      :node="node"
      @load-children="loadChildren"
    />
  </view>
</template>

<script>
import TreeItem from '@/components/TreeItem.vue'

export default {
  components: { TreeItem },
  data() {
    return {
      treeData: [] // 初始数据
    }
  },
  methods: {
    async loadChildren(node) {
      // 模拟异步加载子节点
      const newChildren = await this.fetchChildren(node.id)
      
      // 更新节点数据
      this.$set(node, 'children', newChildren)
      this.$set(node, 'isOpen', true)
      this.$set(node, 'hasChildren', false)
    },
    
    fetchChildren(parentId) {
      // 替换为实际API调用
      return new Promise(resolve => {
        setTimeout(() => {
          resolve([
            { id: parentId * 10 + 1, label: `子节点${parentId}-1`, children: [] },
            { id: parentId * 10 + 2, label: `子节点${parentId}-2`, children: [] }
          ])
        }, 500)
      })
    }
  }
}
</script>

4. 关键点说明

  • 使用递归组件实现无限层级
  • 通过 hasChildren 标记需要懒加载的节点
  • 点击节点时判断是否需要加载子数据
  • 使用 $set 确保响应式更新
  • 通过事件传递实现子组件与父组件通信

5. 优化建议

  • 添加加载状态提示
  • 实现节点选中状态
  • 添加动画效果增强体验
  • 根据需求添加复选框等功能

这样实现的树形组件支持无限层级、懒加载,并且具有良好的扩展性。

回到顶部