hc-tree.vue 4.9 KB

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