wbs.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <template>
  2. <div class="hc-contract-info-wbs relative h-full">
  3. <div class="wbs-template-body">
  4. <div v-loading="leftLoading" class="left">
  5. <hc-card-item scrollbar :title="`${leftNum}项`">
  6. <el-tree
  7. ref="leftTreeRef" :data="leftTreeData" show-checkbox node-key="id" :props="treeProps"
  8. highlight-current :expand-on-click-node="false" @node-expand="nodeLeftTreeExpand"
  9. @check-change="checkLeftTreeChange"
  10. />
  11. </hc-card-item>
  12. </div>
  13. <div class="btn-action">
  14. <div class="relative">
  15. <div class="relative">
  16. <el-button hc-btn :type="leftNum <= 0 ? '' : 'primary'" :disabled="leftNum <= 0" @click="addTreeClick">
  17. <i class="i-ri-arrow-right-line" />
  18. </el-button>
  19. </div>
  20. <div class="relative mt-14px">
  21. <el-button hc-btn :type="rightNum <= 0 ? '' : 'primary'" :disabled="rightNum <= 0" @click="delTreeClick">
  22. <i class="i-ri-arrow-left-line" />
  23. </el-button>
  24. </div>
  25. </div>
  26. </div>
  27. <div v-loading="rightLoading" class="right">
  28. <hc-card-item scrollbar :title="`${rightNum}项`">
  29. <el-tree
  30. ref="rightTreeRef" :data="rightTreeData" show-checkbox node-key="id" :props="treeProps"
  31. highlight-current :expand-on-click-node="false" :default-expanded-keys="rightExpands"
  32. @check-change="checkRightTreeChange"
  33. />
  34. </hc-card-item>
  35. </div>
  36. </div>
  37. <div class="action">
  38. <el-button hc-btn class="mr-4" :loading="submitLoading" @click="saveAndExit">保存并退出</el-button>
  39. <el-button hc-btn type="primary" :loading="submitLoading" @click="saveAndNextStep">保存并进入下一步</el-button>
  40. </div>
  41. </div>
  42. </template>
  43. <script setup>
  44. import { nextTick, onMounted, ref, watch } from 'vue'
  45. import { useAppStore } from '~src/store'
  46. import { deepClone, getArrValue, getObjValue, isNullES } from 'js-fast-way'
  47. import mainApi from '~api/project/project'
  48. import treeApi from '~api/wbs/tree'
  49. //缓存
  50. const store = useAppStore()
  51. //双向绑定
  52. const modelData = defineModel('modelValue', {
  53. default: {},
  54. })
  55. //监听数据
  56. const formModel = ref({})
  57. watch(() => modelData.value, (data) => {
  58. formModel.value = data
  59. }, { immediate: true, deep: true })
  60. //监听
  61. const userInfo = ref(store.getUserInfo)
  62. watch(() => store.getUserInfo, (info) => {
  63. userInfo.value = info
  64. }, { immediate: true, deep: true })
  65. //渲染完成
  66. onMounted(() => {
  67. getWbsTreeList()
  68. })
  69. //树配置
  70. const leftTreeRef = ref(null)
  71. const rightTreeRef = ref(null)
  72. const treeProps = {
  73. children: 'children',
  74. label: 'title',
  75. }
  76. //获取WBS树列表
  77. const wbsId = ref('')
  78. const wbsTreeList = ref([])
  79. const isLoading = ref(false)
  80. const getWbsTreeList = async () => {
  81. }
  82. //左边树
  83. const leftLoading = ref(false)
  84. const leftTreeData = ref([])
  85. //获取右边数据
  86. const rightLoading = ref(false)
  87. const rightTreeData = ref([])
  88. const getRightTreeApi = async () => {
  89. }
  90. //左边树被展开
  91. const rightExpands = ref([])
  92. const nodeLeftTreeExpand = (data) => {
  93. rightExpands.value = rightExpands.value.concat([data.id])
  94. }
  95. //左边树复选框被点击
  96. const leftNum = ref(0)
  97. const checkLeftTreeChange = () => {
  98. const e = leftTreeRef.value
  99. let checkNum = e.getCheckedKeys().length
  100. let halfNum = e.getHalfCheckedKeys().length
  101. leftNum.value = checkNum + halfNum
  102. }
  103. //右边树复选框被点击
  104. const rightNum = ref(0)
  105. const checkRightTreeChange = () => {
  106. const e = rightTreeRef.value
  107. let checkNum = e.getCheckedKeys().length
  108. let halfNum = e.getHalfCheckedKeys().length
  109. rightNum.value = checkNum + halfNum
  110. }
  111. //左边树新增到右边
  112. const addTreeClick = () => {
  113. const left = leftTreeRef.value, right = rightTreeRef.value
  114. if (rightTreeData.value.length < 1) {
  115. //直接把左边勾选的树复制到右侧
  116. let allTree = deepClone(leftTreeData.value)
  117. const halfKeys = right.getHalfCheckedKeys()
  118. const keys = right.getCheckedKeys().concat(halfKeys)
  119. getRightTree(allTree, keys)
  120. rightTreeData.value = allTree
  121. } else {
  122. //只增加右侧树没有的节点,不会覆盖右侧树多余的节点
  123. let checkNodes = left.getCheckedNodes(false, true)
  124. let rIdMap = new Map()
  125. checkNodes.forEach((data) => {
  126. rIdMap.set(data.id, data)
  127. })
  128. let lIdSet = new Set()
  129. getRightTreeData(lIdSet, rightTreeData.value)
  130. //在右侧id减去左侧有的id,剩下的就是新增的id
  131. lIdSet.forEach((id) => {
  132. rIdMap.delete(id)
  133. })
  134. let addMap = new Map()
  135. rIdMap.forEach((data) => {
  136. if (data.parentId !== '0' && data.parentId !== 0) {
  137. //在左侧树能找到父节点的,新增
  138. let lNode = right.getNode(data.parentId)
  139. if (lNode) addMap.set(data, lNode.data.id)
  140. }
  141. })
  142. //把半选和选中的数组key合并
  143. const halfKeys = left.getHalfCheckedKeys()
  144. const keys = left.getCheckedKeys().concat(halfKeys)
  145. const myArray = Array.from(addMap.keys())
  146. let myright = deepClone(myArray)
  147. getRightTree(myright, keys)
  148. myright.forEach((data) => {
  149. right.append(data, data.parentId)
  150. })
  151. }
  152. }
  153. //获取右边树
  154. const getRightTree = (arr, keys) => {
  155. //对比所有的node和选中的key
  156. for (let i = arr.length - 1; i >= 0; i--) {
  157. let isIn = false
  158. for (let j = keys.length - 1; j >= 0; j--) {
  159. if (keys[j] === arr[i].id) {
  160. isIn = true
  161. //已经匹配到的key移除,节省性能
  162. keys.splice(j, 1)
  163. break
  164. }
  165. }
  166. if (isIn) {
  167. //包含在选中的节点,如果有childer继续递归判断
  168. if (arr[i].children && arr[i].children.length) {
  169. getRightTree(arr[i].children, keys)
  170. }
  171. } else {
  172. //不包含在选中key的node删除
  173. arr.splice(i, 1)
  174. }
  175. }
  176. }
  177. const getRightTreeData = (set, arr) => {
  178. arr.forEach((data) => {
  179. set.add(data.id)
  180. if (data.children && data.children.length) {
  181. getRightTreeData(set, data.children)
  182. }
  183. })
  184. }
  185. //右边树移除
  186. const delTreeClick = () => {
  187. const left = leftTreeRef.value, right = rightTreeRef.value
  188. let delNodes = right.getCheckedNodes()
  189. //只把选中的节点移除
  190. delNodes.forEach((node) => {
  191. right.remove(node)
  192. left.setChecked(node.id, false)
  193. })
  194. checkLeftTreeChange()
  195. checkRightTreeChange()
  196. }
  197. //保存并退出
  198. const saveAndExit = async () => {
  199. /*const isRes = await saveDataApi()
  200. if (!isRes) return
  201. emit('close', dataInfo.value)*/
  202. }
  203. //保存并进入下一步
  204. const saveAndNextStep = async () => {
  205. /*const isRes = await saveDataApi()
  206. if (!isRes) return
  207. emit('next', dataInfo.value)*/
  208. }
  209. const submitLoading = ref(false)
  210. </script>
  211. <style lang="scss">
  212. @import './style/wbs';
  213. </style>