hc-tree.vue 5.6 KB

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