tree-node.vue 5.1 KB

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