zxl
2 天以前 874e9cce3b2f9b6649ab72047d98e4244a345b3c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<template>
  <view class="tree-node">
    <view class="tree-row" :class="{ active: isActive }"  :style="{ paddingLeft: `${level * 20 + 20}rpx` }" @click="onClick">
      <uni-icons v-if="hasChildren" :type="isExpanded ? 'down' : 'right'" color="#666" size="24"></uni-icons>
      <view v-else class="empty-icon"></view>
      <text class="row-title">{{ node.name }}</text>
      <text v-if="node.count" class="row-meta">({{ node.count }})</text>
      <view class="row-actions">
        <button class="enter-btn" @click="onView">详情</button>
      </view>
    </view>
    <view v-if="isExpanded && hasChildren" class="tree-children">
      <TreeItem
        v-for="child in node.children"
        :key="child.id"
        :node="child"
        :level="level + 1"
        :search-query="searchQuery"
        :expanded-map="expandedMap"
        :active-id="activeId"
        @toggle-expand="forwardToggle"
        @view-node="forwardView"
      />
    </view>
  </view>
  </template>
 
<script>
    import TreeItem from './TreeItem.vue'
export default {
    components: {
        TreeItem
    },
    name: 'TreeItem',
  props: {
    node: { type: Object, required: true, default: () => ({}) },
    level: { type: Number, default: 0 },
    searchQuery: { type: String, default: '' },
    expandedMap: { type: Object, default: () => ({}) },
    activeId: { type: [String, Number], default: '' }
  },
  emits: ['toggle-expand', 'view-node'],
  computed: {
    hasChildren() {
      return Array.isArray(this.node?.children) && this.node.children.length > 0
    },
    isExpanded() {
      const q = (this.searchQuery || '').trim()
      return q ? true : !!this.expandedMap[this.node.id]
    },
    isActive() {
      return String(this.node?.id) === String(this.activeId)
    }
  },
  methods: {
    onClick() {
      if (!this.hasChildren) {
        this.$emit('view-node', this.node)
        return
      }
      this.$emit('toggle-expand', this.node.id)
    },
    onView() {
      const url = `/subpackage/user/project-detail?id=${this.node.id}&name=${encodeURIComponent(this.node.name || '')}`
      uni.navigateTo({ url })
    },
    forwardToggle(id) {
      this.$emit('toggle-expand', id)
    },
    forwardView(node) {
      this.$emit('view-node', node)
    }
  }
}
</script>
 
<style scoped>
.tree-node { width: 100%; }
.tree-row { display: flex; align-items: center; padding: 20rpx 0; box-sizing: border-box; }
.tree-row.active { 
  background: #e8f4fd;  /* 浅蓝底色 */
  border-radius: 8rpx; 
  /* 可选:加轻微边框增强层次感 */
  border: 1px solid #d1e7fd;
}
/* 搭配文字变色,高亮更明显 */
.tree-row.active .row-title {
  color: #1E88E5;
}
.empty-icon { width: 24rpx; height: 24rpx; }
.row-title { font-size: 30rpx; color: #333; margin-left: 10rpx; }
.row-meta { font-size: 26rpx; color: #999; margin-left: 8rpx; }
.row-actions { margin-left: auto; }
.enter-btn { 
  height: 56rpx; 
  line-height: 56rpx; 
  padding: 0 20rpx; 
  border-radius: 8rpx; 
  font-size: 24rpx; 
  color: #1E88E5; 
  background: #E3F2FD; 
  border: none; 
}
.tree-children { width: 100%; overflow: visible; margin-top: 8rpx; }
</style>