hc-tree.vue 5.7 KB

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