hc-tree.vue 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. <template>
  2. <view class="hc-tree-box">
  3. <template v-if="nodeData.length > 0" v-for="item in nodeData" :key="item.key">
  4. <hc-tree-node :item="item"
  5. :check="isCheck"
  6. :strictly="isStrictly"
  7. :radio="isRadio"
  8. @nodeLoad="treeLoadTap"
  9. @nodeTap="treeNodeTap"
  10. />
  11. </template>
  12. <template v-else>
  13. <view class="no-data">暂无相关数据</view>
  14. </template>
  15. </view>
  16. </template>
  17. <script setup>
  18. import {onMounted, ref} from 'vue'
  19. import {getArrValue} from "js-fast-way";
  20. //参数
  21. const props = defineProps({
  22. nodeKey: {
  23. type: String,
  24. default: 'primaryKeyId',
  25. },
  26. labelKey: {
  27. type: String,
  28. default: 'title',
  29. },
  30. leafKey: {
  31. type: String,
  32. default: 'notExsitChild',
  33. },
  34. check: {
  35. type: Boolean,
  36. default: false,
  37. },
  38. //父子是否不关联
  39. strictly: {
  40. type: Boolean,
  41. default: false,
  42. },
  43. checkKey: {
  44. type: Array,
  45. default: () => ([]),
  46. },
  47. radio: {
  48. type: Boolean,
  49. default: false,
  50. },
  51. radioKey: {
  52. type: String,
  53. default: '',
  54. },
  55. })
  56. //节点数据
  57. const nodeData = ref([]);
  58. //参数配置
  59. const isCheck = ref(props.check)
  60. const isRadio = ref(props.radio)
  61. const isStrictly = ref(props.strictly)
  62. //事件
  63. const emit = defineEmits(['load', 'nodeTap'])
  64. //渲染完成
  65. onMounted(() => {
  66. getLazyLoad({
  67. level: 0,
  68. data: {},
  69. parentNodes: {},
  70. isLoad: false
  71. })
  72. })
  73. const treeLoadTap = (item) => {
  74. getLazyLoad(item)
  75. }
  76. //懒加载数据
  77. const getLazyLoad = (item) => {
  78. if (!item.isLoad) {
  79. emit('load', item, (data) => {
  80. setLazyLoad(item, data)
  81. })
  82. }
  83. }
  84. //处理获取到的数据
  85. const setLazyLoad = (data, arr) => {
  86. const children = getArrValue(arr), newNodeData = []
  87. if (children.length <= 0) {
  88. data.loading = false
  89. data.isExpand = false
  90. data.isLeaf = true
  91. data.isLoad = true
  92. //如果是跟节点,直接赋值
  93. if (data.level === 0) {
  94. nodeData.value = newNodeData
  95. }
  96. } else {
  97. for (let i = 0; i < children.length; i++) {
  98. const item = children[i]
  99. newNodeData.push({
  100. level: data.level + 1,
  101. key: item[props.nodeKey] ?? '',
  102. label: item[props.labelKey] ?? '',
  103. isLeaf: item[props.leafKey] ?? false,
  104. isExpand: false,
  105. loading: false,
  106. childNodes: [],
  107. isCheck: ifCheckData(data, item),
  108. isRadio: false,
  109. data: item,
  110. parentNodes: data,
  111. isLoad: item[props.leafKey] ?? false,
  112. })
  113. }
  114. //设置父级数据
  115. data.childNodes = newNodeData
  116. data.loading = false
  117. data.isExpand = setBrotherExpand(data.parentNodes)
  118. data.isLoad = true
  119. //如果是跟节点,直接赋值
  120. if (data.level === 0) {
  121. nodeData.value = newNodeData
  122. }
  123. }
  124. }
  125. //设置兄弟节点展开状态
  126. const setBrotherExpand = (nodes) => {
  127. const children = getArrValue(nodes.childNodes)
  128. for (let i = 0; i < children.length; i++) {
  129. nodes.childNodes[i].isExpand = false
  130. }
  131. return true
  132. }
  133. //0未选中 1选中 2半选
  134. const ifCheckData = (data, item) => {
  135. const keys = getArrValue(props.checkKey)
  136. if (keys.indexOf(item[props.nodeKey]) > -1) {
  137. return 1
  138. } else {
  139. return 0
  140. }
  141. }
  142. //树节点被点击
  143. const treeNodeTap = (item) => {
  144. emit('nodeTap', item)
  145. }
  146. //获取选中的key
  147. const getCheckKeys = async () => {
  148. const nodes = nodeData.value
  149. const nodeKeys = {
  150. nodes: [], keys: [],
  151. halfNodes: [], halfKeys: [],
  152. }
  153. if (!isCheck.value) {
  154. return nodeKeys
  155. }
  156. await forCheckKeys(nodes, nodeKeys)
  157. return nodeKeys
  158. }
  159. //遍历获取选中的key
  160. const forCheckKeys = async (nodes, nodeKeys) => {
  161. for (let i = 0; i < nodes.length; i++) {
  162. const item = nodes[i]
  163. //0未选中 1选中 2半选
  164. if (nodes[i].isCheck === 1) {
  165. nodeKeys.nodes.push(item)
  166. nodeKeys.keys.push(item.key)
  167. } else if (nodes[i].isCheck === 2) {
  168. nodeKeys.halfNodes.push(item)
  169. nodeKeys.halfKeys.push(item.key)
  170. }
  171. if (item.childNodes.length > 0) {
  172. await forCheckKeys(item.childNodes, nodeKeys)
  173. }
  174. }
  175. }
  176. //导出方法函数
  177. defineExpose({
  178. getCheckKeys
  179. })
  180. </script>
  181. <style lang="scss">
  182. @import "./style.scss";
  183. </style>