vue处理js未知层级的嵌套数组

37 阅读1分钟

纯JavaScript递归处理嵌套数组

// 递归遍历嵌套数组的通用函数
function traverseNestedArray(arr, callback, parent = null, level = 0) {
  if (!Array.isArray(arr)) return
  
  arr.forEach((item, index) => {
    // 对当前项执行回调
    callback(item, index, parent, level)
    
    // 如果有子数组,递归处理
    if (item.children && Array.isArray(item.children) && item.children.length > 0) {
      traverseNestedArray(item.children, callback, item, level + 1)
    }
  })
}

// 使用示例
const data = [
  {
    id: 1,
    name: '节点1',
    children: [
      { id: 11, name: '子节点1-1' }
    ]
  }
]

traverseNestedArray(data, (item, index, parent, level) => {
  console.log(`Level ${level}: ${item.name}, 父节点: ${parent?.name || '无'}`)
})

// 输出:
// Level 0: 节点1, 父节点: 无
// Level 1: 子节点1-1, 父节点: 节点1

vue页面遍历未知层级的嵌套数组

RecursiveItem.vue

<template>
  <div class="recursive-item">
    <!-- 当前节点内容 -->
    <div class="item-content" @click="toggleExpand">
      <span v-if="hasChildren" class="expand-icon">{{ isExpanded ? '▼' : '►' }}</span>
      {{ item.title || item.name }}
    </div>
    
    <!-- 递归渲染子节点 -->
    <transition name="expand">
      <div v-if="hasChildren && isExpanded" class="children">
        <RecursiveItem
          v-for="child in item.children"
          :key="child.id || child.key"
          :item="child"
          :level="level + 1"
          @update-item="handleUpdateItem"
        />
      </div>
    </transition>
  </div>
</template>

<script>
export default {
  name: 'RecursiveItem', // 必须定义name属性以支持递归调用
  props: {
    item: {
      type: Object,
      required: true
    },
    level: {
      type: Number,
      default: 0
    }
  },
  data() {
    return {
      isExpanded: false
    }
  },
  computed: {
    // 判断是否有子节点
    hasChildren() {
      return this.item.children && Array.isArray(this.item.children) && this.item.children.length > 0
    }
  },
  methods: {
    toggleExpand() {
      if (this.hasChildren) {
        this.isExpanded = !this.isExpanded
      }
    },
    handleUpdateItem(data) {
      // 向上冒泡事件
      this.$emit('update-item', data)
    }
  }
}
</script>

<style scoped>
.recursive-item {
  padding-left: 20px;
}

.item-content {
  padding: 8px;
  cursor: pointer;
  border-radius: 4px;
  user-select: none;
  display: flex;
  align-items: center;
}

.item-content:hover {
  background-color: #f5f5f5;
}

.expand-icon {
  margin-right: 8px;
  font-size: 12px;
}

.children {
  border-left: 1px dashed #ddd;
  margin-left: 10px;
}

/* 展开/折叠动画 */
.expand-enter-active,
.expand-leave-active {
  transition: all 0.3s ease;
  overflow: hidden;
}

.expand-enter-from,
.expand-leave-to {
  opacity: 0;
  max-height: 0;
}

.expand-enter-to,
.expand-leave-from {
  opacity: 1;
  max-height: 1000px;
}
</style>

父组件使用:

<template>
  <div class="nested-tree">
    <h3>嵌套数组展示</h3>
    <RecursiveItem
      v-for="rootItem in nestedData"
      :key="rootItem.id || rootItem.key"
      :item="rootItem"
      @update-item="handleUpdateItem"
    />
  </div>
</template>

<script>
import RecursiveItem from './RecursiveItem.vue'

export default {
  name: 'NestedTree',
  components: {
    RecursiveItem
  },
  data() {
    return {
      nestedData: [
        {
          id: 1,
          name: '分类1',
          children: [
            {
              id: 11,
              name: '子分类1-1',
              children: [
                { id: 111, name: '子子分类1-1-1' },
                { id: 112, name: '子子分类1-1-2' }
              ]
            },
            { id: 12, name: '子分类1-2' }
          ]
        },
        {
          id: 2,
          name: '分类2',
          children: [
            { id: 21, name: '子分类2-1' }
          ]
        }
      ]
    }
  },
  methods: {
    handleUpdateItem(data) {
      // 处理从子组件传来的更新事件
      console.log('更新数据:', data)
      // 这里可以根据需要更新nestedData
    }
  }
}
</script>

<style scoped>
.nested-tree {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
  padding: 20px;
  background-color: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
</style>

来源:豆包