tree-node.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. <template>
  2. <view class="hc-tree-node" :class="[nodeItem.level > 1 ? 'pl': '']">
  3. <view class="content-bar" :class="currentNodeKey===nodeItem.key?'current-node-key':''" @click="nodeTap(nodeItem)">
  4. <view class="expand-icon" @click.stop="expandTap(nodeItem)" :class="nodeItem.isExpand?'is-expanded':''">
  5. <text class="i-ri-arrow-right-s-fill" v-if="!nodeItem.isLeaf"/>
  6. </view>
  7. <view class="tree-check" @click.stop="checkClick" v-if="isCheck">
  8. <text class="i-ri-checkbox-blank-line c1" v-if="nodeItem.isCheck === 0"/>
  9. <text class="i-ri-checkbox-fill c2" v-if="nodeItem.isCheck === 1"/>
  10. <text class="i-ri-checkbox-indeterminate-fill c3" v-if="nodeItem.isCheck === 2"/>
  11. </view>
  12. <view class="tree-radio" v-if="isRadio">
  13. <text class="i-ri-checkbox-blank-circle-line c1" v-if="!nodeItem.isRadio"/>
  14. <text class="i-ri-checkbox-circle-fill c2" v-else/>
  15. </view>
  16. <text class="i-ri-loader-line cuIconfont-spin" v-if="nodeItem.loading"/>
  17. <view class="label">
  18. <text>{{nodeItem.label}}</text>
  19. <text class="text-gray-4" v-if="isCounts">【{{nodeItem.data?.submitCounts ?? 0}}】</text>
  20. </view>
  21. </view>
  22. <template v-if="nodeItem.isExpand">
  23. <view class="children-bar" v-if="nodeItem.childNodes && nodeItem.childNodes.length > 0">
  24. <template v-for="items in nodeItem.childNodes">
  25. <hc-tree-node :item="items"
  26. :check="isCheck"
  27. :strictly="isStrictly"
  28. :radio="isRadio"
  29. :currentKey="currentNodeKey"
  30. :counts="isCounts"
  31. @nodeLoad="getLazyLoad"
  32. @nodeTap="nodeTaps"
  33. />
  34. </template>
  35. </view>
  36. </template>
  37. </view>
  38. </template>
  39. <script setup>
  40. import {ref, watch} from "vue";
  41. import {getArrValue} from "js-fast-way";
  42. //参数
  43. const props = defineProps({
  44. item: {
  45. type: Object,
  46. default: () => ({}),
  47. },
  48. check: {
  49. type: Boolean,
  50. default: false,
  51. },
  52. radio: {
  53. type: Boolean,
  54. default: false,
  55. },
  56. strictly: {
  57. type: Boolean,
  58. default: false,
  59. },
  60. currentKey: {
  61. type: String,
  62. default: '',
  63. },
  64. counts: {
  65. type: Boolean,
  66. default: false,
  67. },
  68. })
  69. //事件
  70. const emit = defineEmits(['nodeLoad', 'nodeTap'])
  71. //变量
  72. const nodeItem = ref(props.item)
  73. //参数配置
  74. const isCheck = ref(props.check)
  75. const isRadio = ref(props.radio)
  76. const isStrictly = ref(props.strictly)
  77. const isCounts = ref(props.counts)
  78. const currentNodeKey = ref(props.currentKey)
  79. //监听
  80. watch(() => [
  81. props.item
  82. ], ([item]) => {
  83. nodeItem.value = item
  84. }, {deep: true})
  85. //监听
  86. watch(() => [
  87. props.currentKey
  88. ], ([val]) => {
  89. currentNodeKey.value = val
  90. })
  91. //节点被点击
  92. const nodeTap = (item) => {
  93. expandTap(item)
  94. nodeTaps(item)
  95. }
  96. //展开和收缩节点
  97. const expandTap = (item) => {
  98. const {isLoad, isExpand, childNodes} = item
  99. const children = getArrValue(childNodes)
  100. //加载状态
  101. if (!isLoad) {
  102. item.loading = true
  103. }
  104. //展开和折叠
  105. if (children.length > 0) {
  106. setBrotherExpand(item, item.parentNodes)
  107. item.isExpand = !isExpand
  108. }
  109. //触发事件
  110. emit('nodeLoad', item)
  111. }
  112. //设置兄弟节点展开状态
  113. const setBrotherExpand = (item, nodes) => {
  114. const children = getArrValue(nodes.childNodes)
  115. for (let i = 0; i < children.length; i++) {
  116. if (children[i].key !== item.key) {
  117. nodes.childNodes[i].isExpand = false
  118. }
  119. }
  120. }
  121. //子节点被点击
  122. const nodeTaps = (item) => {
  123. currentNodeKey.value = item.key
  124. emit('nodeTap', item)
  125. }
  126. //懒加载子节点数据
  127. const getLazyLoad = (item) => {
  128. emit('nodeLoad', item)
  129. }
  130. //复选框被点击 0未选中 1选中 2半选
  131. const checkClick = () => {
  132. const {isCheck} = nodeItem.value
  133. if (isStrictly.value) {
  134. //父子级节点不关联选择
  135. nodeItem.value.isCheck = isCheck !== 1 ? 1 : 0
  136. } else {
  137. console.log(nodeItem.value)
  138. // 先设置自己的状态,选中或不选中
  139. nodeItem.value.isCheck = isCheck !== 1 ? 1 : 0
  140. // 设置子节点状态
  141. setChildrenCheck(nodeItem.value.childNodes)
  142. // 设置父节点状态
  143. setParentCheck(nodeItem.value.parentNodes)
  144. }
  145. }
  146. //设置子节点复选框状态
  147. const setChildrenCheck = (children) => {
  148. for (let i = 0; i < children.length; i++) {
  149. children[i].isCheck = nodeItem.value.isCheck
  150. if (children[i].childNodes.length > 0) {
  151. setChildrenCheck(children[i].childNodes)
  152. }
  153. }
  154. }
  155. //设置父节点复选框状态 0未选中 1选中 2半选
  156. const setParentCheck = (nodes) => {
  157. if (nodes.level <= 0) return
  158. const children = getArrValue(nodes.childNodes)
  159. const { isCheck } = nodeItem.value
  160. const result = children.every((item)=> {
  161. return item.isCheck === isCheck
  162. })
  163. if (isCheck === 1) {
  164. nodes.isCheck = result ? 1 : 2
  165. } else {
  166. nodes.isCheck = result ? 0 : 2
  167. }
  168. // 递归设置父节点
  169. if (nodes.parentNodes) {
  170. setParentCheck(nodes.parentNodes)
  171. }
  172. }
  173. </script>