EmployeeTreeNode.vue 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. <template>
  2. <view class="tree-node">
  3. <view
  4. :class="['node-row', { 'is-dept': isDept, 'is-user': isUser }]"
  5. @click="handleClick"
  6. >
  7. <view class="toggle-icon" v-if="isDept && !selectable">
  8. <u-icon :name="expanded ? 'arrow-down' : 'arrow-right'" size="16" color="#999" />
  9. </view>
  10. <view class="toggle-icon" v-else-if="isDept && selectable">
  11. <text class="user-dot" style="color: #A78BFA">●</text>
  12. </view>
  13. <view class="toggle-icon" v-else>
  14. <text class="user-dot">●</text>
  15. </view>
  16. <text class="node-label">{{ nodeLabel }}</text>
  17. <u-icon v-if="selectable && nodeId === selectedId" name="checkmark" color="#34D399" size="18" />
  18. <u-icon v-else-if="!selectable && isUser && nodeId === selectedId" name="checkmark" color="#34D399" size="18" />
  19. </view>
  20. <view v-if="isDept && expanded" class="node-children">
  21. <template v-for="child in nodeChildren">
  22. <employee-tree-node
  23. :key="child.id"
  24. :node="child"
  25. :expanded-ids="expandedIds"
  26. :selected-id="selectedId"
  27. :selectable="selectable"
  28. @toggle="$emit('toggle', $event)"
  29. @select="$emit('select', $event)"
  30. />
  31. </template>
  32. </view>
  33. </view>
  34. </template>
  35. <script>
  36. export default {
  37. name: 'EmployeeTreeNode',
  38. props: {
  39. node: {
  40. type: Object,
  41. required: true
  42. },
  43. expandedIds: {
  44. type: [Array, Set],
  45. default: () => []
  46. },
  47. selectedId: {
  48. type: [String, Number],
  49. default: null
  50. },
  51. selectable: {
  52. type: Boolean,
  53. default: false
  54. }
  55. },
  56. computed: {
  57. isDept() {
  58. const hasChildren = this.node.children && this.node.children.length > 0
  59. return hasChildren || this.node.nodeType === 'dept'
  60. },
  61. isUser() {
  62. return !this.isDept || this.selectable
  63. },
  64. nodeId() {
  65. return this.node.userId || this.node.id
  66. },
  67. nodeLabel() {
  68. return this.node.nickName || this.node.label || this.node.userName || this.node.name || ''
  69. },
  70. nodeChildren() {
  71. return this.node.children || []
  72. },
  73. expanded() {
  74. if (this.expandedIds instanceof Set) {
  75. return this.expandedIds.has(this.node.id)
  76. }
  77. return this.expandedIds.includes(this.node.id)
  78. }
  79. },
  80. methods: {
  81. handleClick() {
  82. if (this.isDept) {
  83. this.$emit('toggle', this.node.id)
  84. if (this.selectable) {
  85. this.$emit('select', {
  86. userId: this.node.userId || this.node.id,
  87. nodeId: this.node.id,
  88. nickName: this.nodeLabel,
  89. nodeType: this.node.nodeType || 'dept',
  90. deptType: this.node.deptType || ''
  91. })
  92. }
  93. } else {
  94. this.$emit('select', {
  95. userId: this.nodeId,
  96. nickName: this.nodeLabel,
  97. nodeType: this.node.nodeType || 'user',
  98. deptType: this.node.deptType || ''
  99. })
  100. }
  101. }
  102. }
  103. }
  104. </script>
  105. <style lang="scss" scoped>
  106. .tree-node {
  107. width: 100%;
  108. }
  109. .node-row {
  110. display: flex;
  111. align-items: center;
  112. padding: 20rpx 32rpx 20rpx 16rpx;
  113. gap: 12rpx;
  114. &.is-dept {
  115. padding-left: 16rpx;
  116. }
  117. }
  118. .toggle-icon {
  119. width: 40rpx;
  120. height: 40rpx;
  121. display: flex;
  122. align-items: center;
  123. justify-content: center;
  124. }
  125. .user-dot {
  126. font-size: 16rpx;
  127. color: #999;
  128. }
  129. .node-label {
  130. flex: 1;
  131. font-size: 28rpx;
  132. color: #333;
  133. }
  134. .node-children {
  135. padding-left: 32rpx;
  136. }
  137. </style>