index.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. <template>
  2. <div v-loading="treeNodeLoading" class="cu-tree-node-container" @mousewheel.prevent="treeNodeMousewheel">
  3. <div v-if="!!nodes.label" :id="'tree-node-' + uuid" :style="{zoom: zoomRef + '%'}"
  4. class="cu-tree-node-box horizontal collapsable" @mousedown="treeNodeMouseDown">
  5. <div class="cu-tree-node-view">
  6. <div class="cu-tree-node-label">
  7. <div class="cu-tree-node-label-text">
  8. <div :id="`node-tree-${nodes.key}`" class="cu-tree-node-label-name">
  9. <el-button :loading="nodes.loading" hc-btn type="primary">{{ nodes.label }}</el-button>
  10. </div>
  11. </div>
  12. </div>
  13. <TreeNodeChildren :data="nodes.childNodes" :parentNodes="nodes" @expandClick="expandClick"
  14. @menuClick="poverMenuClick" @nodeClick="nodeLabelClick"
  15. @nodeDblClick="nodeLabelDblClick"/>
  16. </div>
  17. </div>
  18. <!--右键菜单-->
  19. <HcContextMenu v-if="menusData.length > 0" ref="contextMenuRef" :datas="menusData"
  20. @item-click="handleMenuSelect">
  21. <template #mark="{item}">
  22. <HcIcon :fill="treeRefData?.isFirst" :name="item.icon" class="menu-item-icon"/>
  23. <span class="menu-item-name">{{ treeRefData?.isFirst ? '取消标记为首件' : '标记为首件' }}</span>
  24. </template>
  25. <template #sort="{item}">
  26. <HcIcon :line="false" :name="item.icon" class="menu-item-icon"/>
  27. <span class="menu-item-name">{{ item.label }}</span>
  28. </template>
  29. </HcContextMenu>
  30. </div>
  31. </template>
  32. <script setup>
  33. import {watch, ref, onMounted, nextTick} from "vue";
  34. import TreeNodeChildren from "./children.vue";
  35. import wbsApi from "~api/data-fill/wbs"
  36. import {getObjValue, getArrValue, getRandom, isArrItem} from "js-fast-way"
  37. //参数
  38. const props = defineProps({
  39. autoExpandKeys: {
  40. type: Array,
  41. default: () => ([])
  42. },
  43. menus: {
  44. type: Array,
  45. default: () => ([])
  46. },
  47. isMark: {
  48. type: Boolean,
  49. default: false
  50. },
  51. accordion: {
  52. type: Boolean,
  53. default: false
  54. },
  55. projectId: {
  56. type: [String, Number],
  57. default: ''
  58. },
  59. contractId: {
  60. type: [String, Number],
  61. default: ''
  62. },
  63. })
  64. //初始数据
  65. const uuid = getRandom()
  66. const datas = ref({})
  67. const nodes = ref({})
  68. const menusData = ref(props.menus)
  69. const menuMark = ref(props.isMark)
  70. const projectId = ref(props.projectId);
  71. const contractId = ref(props.contractId);
  72. const TreeExpandKey = ref(props.autoExpandKeys)
  73. const treeRefNode = ref(null)
  74. const treeRefData = ref(null)
  75. //监听
  76. watch(() => [
  77. props.autoExpandKeys,
  78. props.menus,
  79. props.isMark,
  80. props.projectId,
  81. props.contractId,
  82. ], ([expandKeys, menus, isMark, UserProjectId, UserContractId]) => {
  83. TreeExpandKey.value = expandKeys
  84. menusData.value = menus
  85. menuMark.value = isMark
  86. projectId.value = UserProjectId
  87. contractId.value = UserContractId
  88. })
  89. //渲染完成
  90. onMounted(() => {
  91. getTreeOneLevel()
  92. })
  93. //导图结构树节点查询
  94. const treeNodeLoading = ref(false)
  95. const getTreeOneLevel = async () => {
  96. treeNodeLoading.value = true
  97. const data = await queryMappingStructureTree({
  98. contractId: contractId.value,
  99. contractIdRelation: '',
  100. primaryKeyId: '',
  101. parentId: '',
  102. })
  103. //处理数据
  104. const res = getObjValue(data[0])
  105. //自动展开第二级
  106. const keys = TreeExpandKey.value || []
  107. if (keys.length > 0 && res?.id) {
  108. const children = await queryMappingStructureTree({
  109. contractId: contractId.value,
  110. contractIdRelation: res?.contractIdRelation,
  111. primaryKeyId: res?.primaryKeyId,
  112. parentId: res?.id,
  113. })
  114. if (children.length > 0) {
  115. await setTreeExpandKey(children, res)
  116. }
  117. }
  118. datas.value = res
  119. setDatasToNodes()
  120. treeNodeLoading.value = false
  121. }
  122. //设置自动展开
  123. const setTreeExpandKey = async (arr, res) => {
  124. const keys = TreeExpandKey.value || []
  125. res.children = []
  126. for (const item of arr) {
  127. if (isArrItem(keys, item?.primaryKeyId)) {
  128. const children = await queryMappingStructureTree({
  129. contractId: contractId.value,
  130. contractIdRelation: item?.contractIdRelation,
  131. primaryKeyId: item?.primaryKeyId,
  132. parentId: item?.id,
  133. })
  134. if (children.length > 0) {
  135. await setTreeExpandKey(children, item)
  136. }
  137. }
  138. res.children.push(item)
  139. }
  140. }
  141. //获取数据
  142. const queryMappingStructureTree = async (form) => {
  143. const {error, code, data} = await wbsApi.queryMappingStructureTree({
  144. ...form,
  145. wbsType: 1
  146. })
  147. //处理数据
  148. if (!error && code === 200) {
  149. return getArrValue(data)
  150. } else {
  151. return []
  152. }
  153. }
  154. //处理为node节点类型的数据
  155. const setDatasToNodes = () => {
  156. const deepData = datas.value
  157. let childNodes = getArrValue(deepData['children'])
  158. nodes.value = {
  159. childNodes: childNodes,
  160. childrenNodes: [],
  161. data: deepData,
  162. parentNodes: {},
  163. expanded: deepData['expanded'] || false,
  164. isExpand: deepData['isExpand'] || false,
  165. isLeaf: deepData['isLeaf'] || false,
  166. loading: deepData['loading'] || false,
  167. key: deepData['primaryKeyId'] || '',
  168. label: deepData['title'] || ''
  169. }
  170. }
  171. //放大缩小
  172. const zoomRef = ref(100)
  173. const treeNodeMousewheel = (event) => {
  174. /* 获取当前页面的缩放比 若未设置zoom缩放比,则为默认100%,即1,原图大小 */
  175. let zoom = parseInt(zoomRef.value + '') || 100
  176. /* event.wheelDelta 获取滚轮滚动值并将滚动值叠加给缩放比zoom wheelDelta统一为±120,其中正数表示为向上滚动,负数表示向下滚动 */
  177. zoom += event.wheelDelta / 12
  178. /* 最小范围 和 最大范围 的图片缩放尺度 */
  179. if (zoom >= 10 && zoom < 200) {
  180. zoomRef.value = zoom
  181. }
  182. return false
  183. }
  184. const isDown = ref(false)
  185. const treeNodeMouseDown = (event) => {
  186. // 阻止默认事件和冒泡
  187. event.preventDefault()
  188. event.stopPropagation()
  189. //获取相关dom元素
  190. let dom = document.getElementById('tree-node-' + uuid)
  191. //获取x坐标和y坐标
  192. let clientX = event.clientX, clientY = event.clientY;
  193. //获取左部和顶部的偏移量
  194. let offsetLeft = dom.offsetLeft, offsetTop = dom.offsetTop;
  195. //开关打开
  196. isDown.value = true;
  197. //设置样式
  198. dom.style.cursor = 'move';
  199. document.onmousemove = (e) => {
  200. if (isDown.value === false) {
  201. return;
  202. }
  203. //获取x和y
  204. let nx = e.clientX;
  205. let ny = e.clientY;
  206. //计算移动后的左偏移量和顶部的偏移量
  207. let nl = nx - (clientX - offsetLeft);
  208. let nt = ny - (clientY - offsetTop);
  209. dom.style.left = nl + 'px';
  210. dom.style.top = nt + 'px';
  211. }
  212. document.onmouseup = () => {
  213. //开关关闭
  214. isDown.value = false;
  215. dom.style.cursor = 'default';
  216. document.onmousemove = null;
  217. document.onmouseup = null;
  218. }
  219. }
  220. //处理自动展开的节点KEY
  221. const getNodeExpandKeys = async (node, newKeys) => {
  222. const parent = node?.parentNodes ?? []
  223. const primaryKeyId = node?.data?.primaryKeyId ?? ''
  224. if (primaryKeyId) {
  225. newKeys.push(primaryKeyId)
  226. await getNodeExpandKeys(parent, newKeys)
  227. }
  228. }
  229. const emit = defineEmits(['expand', 'nodeClick', 'nodeDblClick', 'menuClick']);
  230. //导图结构展开和收缩被点击
  231. const expandClick = ({node, data}) => {
  232. if (!node.children) {
  233. node.expanded = false;
  234. }
  235. node.isExpand = !node.isExpand;
  236. emit('expand', {node, data})
  237. }
  238. //鼠标左键单击事件
  239. const nodeLabelClick = async ({node, data}) => {
  240. if (data?.notExsitChild) {
  241. await awaitNodeClick(node, data)
  242. } else {
  243. let ifChildren = !!(node.childNodes && node.childNodes.length > 0);
  244. if (ifChildren) {
  245. node.expanded = true;
  246. node.isExpand = true;
  247. node.loading = false
  248. await awaitNodeClick(node, data)
  249. } else {
  250. node.loading = true
  251. const children = await queryMappingStructureTree({
  252. contractId: contractId.value,
  253. contractIdRelation: data?.contractIdRelation,
  254. primaryKeyId: data?.primaryKeyId,
  255. parentId: data?.id,
  256. })
  257. node.childNodes = children
  258. await nextTick(async () => {
  259. if (children.length > 0) {
  260. node.expanded = true;
  261. node.isExpand = true;
  262. } else {
  263. node.expanded = false;
  264. node.expanded = false;
  265. }
  266. node.loading = false
  267. await awaitNodeClick(node, data)
  268. })
  269. }
  270. }
  271. }
  272. //处理数据
  273. const awaitNodeClick = async (node, data) => {
  274. let autoKeysArr = []
  275. await getNodeExpandKeys(node, autoKeysArr)
  276. const autoKeys = autoKeysArr.reverse()
  277. emit('nodeClick', {node, data, keys: autoKeys})
  278. }
  279. //双击事件
  280. const nodeLabelDblClick = (item) => {
  281. emit('nodeDblClick', item)
  282. }
  283. //菜单被点击
  284. const contextMenuRef = ref(null)
  285. const poverMenuClick = (event, node, data) => {
  286. const rows = menusData.value || [];
  287. if (rows.length > 0) {
  288. event.preventDefault();
  289. treeRefNode.value = node;
  290. treeRefData.value = data;
  291. contextMenuRef.value?.showMenu(event)
  292. }
  293. }
  294. //鼠标右键菜单被点击
  295. const handleMenuSelect = ({key}) => {
  296. const node = treeRefNode.value;
  297. const data = treeRefData.value;
  298. //如果为标记菜单
  299. if (key === 'mark' && menuMark.value) {
  300. if (data.isFirst === true) {
  301. emit('menuClick', {key: 'cancel_mark', node, data})
  302. } else {
  303. emit('menuClick', {key: 'mark', node, data})
  304. }
  305. } else {
  306. emit('menuClick', {key, node, data})
  307. }
  308. }
  309. </script>
  310. <style lang="scss">
  311. @import "style";
  312. </style>