tree-node.vue 4.8 KB

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