index.vue 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. <template>
  2. <div class="go-edit-align-line">
  3. <div
  4. class="line"
  5. v-for="item in line.lineArr"
  6. :key="item"
  7. :class="[item.includes('row') ? 'row' : 'col', line['select'].has(item) && 'visible']"
  8. :style="useComponentStyle(line['select'].get(item))"
  9. ></div>
  10. </div>
  11. </template>
  12. <script setup lang="ts">
  13. import { reactive, computed, watch } from 'vue'
  14. import { useDesignStore } from '@/store/modules/designStore/designStore'
  15. import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
  16. import { EditCanvasTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
  17. import { useSettingStore } from '@/store/modules/settingStore/settingStore'
  18. import { CreateComponentType, CreateComponentGroupType } from '@/packages/index.d'
  19. import { setComponentPosition } from '@/utils'
  20. import throttle from 'lodash/throttle'
  21. import cloneDeep from 'lodash/cloneDeep'
  22. // 全局颜色
  23. const designStore = useDesignStore()
  24. const chartEditStore = useChartEditStore()
  25. const settingStore = useSettingStore()
  26. // * 线条集合
  27. const line = reactive({
  28. // 行横向row(上中下),列竖线col(左中右)
  29. lineArr: ['rowt', 'rowc', 'rowb', 'coll', 'colc', 'colr'],
  30. // 展示线
  31. select: new Map(),
  32. // 已经吸附
  33. sorptioned: {
  34. x: false,
  35. y: false
  36. }
  37. })
  38. // * 位置计算
  39. const useComponentStyle = (attr?: Partial<{ x: number; y: number }>) => {
  40. if (!attr) return {}
  41. const componentStyle = {
  42. left: `${attr.x ? attr.x : 0}px`,
  43. top: `${attr.y ? attr.y : 0}px`
  44. }
  45. return componentStyle
  46. }
  47. // 颜色
  48. const themeColor = computed(() => {
  49. return designStore.getAppTheme
  50. })
  51. // * 吸附距离
  52. const minDistance = computed(() => {
  53. return settingStore.getChartAlignRange
  54. })
  55. // * 是否开始计算
  56. const isComputedLine = computed(() => {
  57. // IS_DRAG 移动时为 true,Drag Hook里设置
  58. const isDrag = chartEditStore.getEditCanvas[EditCanvasTypeEnum.IS_DRAG]
  59. return isDrag
  60. })
  61. // * 吸附判定
  62. const isSorption = (selectValue: number, componentValue: number) => {
  63. const isSorption = Math.abs(selectValue - componentValue) <= minDistance.value
  64. return isSorption
  65. }
  66. // * 当前目标
  67. const selectId = computed(() => chartEditStore.getTargetChart.selectId)
  68. const selectTarget = computed(() => chartEditStore.getComponentList[chartEditStore.fetchTargetIndex()])
  69. const selectAttr = computed(() => selectTarget.value?.attr || {})
  70. // * 画布坐标
  71. const canvasPositionList = computed(() => {
  72. return {
  73. id: '0',
  74. attr: {
  75. w: cloneDeep(chartEditStore.getEditCanvasConfig.width),
  76. h: cloneDeep(chartEditStore.getEditCanvasConfig.height),
  77. x: 0,
  78. y: 0,
  79. offsetX: 0,
  80. offsetY: 0,
  81. zIndex: 0
  82. }
  83. }
  84. })
  85. // * 监听鼠标移动
  86. watch(
  87. () => chartEditStore.getMousePosition,
  88. throttle(() => {
  89. try {
  90. if (!isComputedLine.value || selectId.value.length !== 1) return
  91. // 获取目标组件数据
  92. const selectW = selectAttr.value.w
  93. const selectH = selectAttr.value.h
  94. // 距离左侧
  95. const selectLeftX = selectAttr.value.x
  96. const selectHalfX = selectLeftX + selectW / 2
  97. const selectRightX = selectLeftX + selectW
  98. const seletX = [selectLeftX, selectHalfX, selectRightX]
  99. // 距离顶部
  100. const selectTopY = selectAttr.value.y
  101. const selectHalfY = selectTopY + selectH / 2
  102. const selectBottomY = selectTopY + selectH
  103. const selectY = [selectTopY, selectHalfY, selectBottomY]
  104. line.select.clear()
  105. line.sorptioned.y = false
  106. // 循环查询所有组件数据
  107. const componentList = chartEditStore.getComponentList.map((e: CreateComponentType | CreateComponentGroupType) => {
  108. return {
  109. id: e.id,
  110. attr: e.attr
  111. }
  112. })
  113. componentList.push(canvasPositionList.value)
  114. // 传入画布数据
  115. line.lineArr.forEach(lineItem => {
  116. componentList.forEach((component: typeof canvasPositionList.value) => {
  117. // 排除自身
  118. if (selectId.value[0] === component.id) return
  119. const componentW = component.attr.w
  120. const componentH = component.attr.h
  121. // 距离左侧
  122. const componentLeftX = component.attr.x
  123. const componentHalfX = componentLeftX + componentW / 2
  124. const componentRightX = componentLeftX + componentW
  125. const componentX = [componentLeftX, componentHalfX, componentRightX]
  126. // 距离顶部
  127. const componentTopY = component.attr.y
  128. const componentHalfY = componentTopY + componentH / 2
  129. const componentBottomY = componentTopY + componentH
  130. const componentY = [componentTopY, componentHalfY, componentBottomY]
  131. // 横线对比的是 Y
  132. if (lineItem.includes('rowt')) {
  133. // 顶部
  134. if (isSorption(selectTopY, componentTopY)) {
  135. line.select.set(lineItem, { y: componentTopY })
  136. setComponentPosition(selectTarget.value, selectLeftX, componentTopY)
  137. }
  138. if (isSorption(selectTopY, componentHalfY)) {
  139. line.select.set(lineItem, { y: componentHalfY })
  140. setComponentPosition(selectTarget.value, selectLeftX, componentHalfY)
  141. }
  142. if (isSorption(selectTopY, componentBottomY)) {
  143. line.select.set(lineItem, { y: componentBottomY })
  144. setComponentPosition(selectTarget.value, selectLeftX, componentBottomY)
  145. }
  146. }
  147. if (lineItem.includes('rowc')) {
  148. // 顶部
  149. if (isSorption(selectHalfY, componentTopY)) {
  150. line.select.set(lineItem, { y: componentTopY })
  151. setComponentPosition(selectTarget.value, selectLeftX, componentTopY - selectH / 2)
  152. }
  153. if (isSorption(selectHalfY, componentHalfY)) {
  154. line.select.set(lineItem, { y: componentHalfY })
  155. setComponentPosition(selectTarget.value, selectLeftX, componentHalfY - selectH / 2)
  156. }
  157. if (isSorption(selectHalfY, componentBottomY)) {
  158. line.select.set(lineItem, { y: componentBottomY })
  159. setComponentPosition(selectTarget.value, selectLeftX, componentBottomY - selectH / 2)
  160. }
  161. }
  162. if (lineItem.includes('rowb')) {
  163. // 顶部
  164. if (isSorption(selectBottomY, componentTopY)) {
  165. line.select.set(lineItem, { y: componentTopY })
  166. setComponentPosition(selectTarget.value, selectLeftX, componentTopY - selectH)
  167. }
  168. if (isSorption(selectBottomY, componentHalfY)) {
  169. line.select.set(lineItem, { y: componentHalfY })
  170. setComponentPosition(selectTarget.value, selectLeftX, componentHalfY - selectH)
  171. }
  172. if (isSorption(selectBottomY, componentBottomY)) {
  173. line.select.set(lineItem, { y: componentBottomY })
  174. setComponentPosition(selectTarget.value, selectLeftX, componentBottomY - selectH)
  175. }
  176. }
  177. // 纵线对比的是 X
  178. if (lineItem.includes('coll')) {
  179. if (isSorption(selectLeftX, componentLeftX)) {
  180. line.select.set(lineItem, { x: componentLeftX })
  181. setComponentPosition(selectTarget.value, componentLeftX, selectTopY)
  182. }
  183. if (isSorption(selectLeftX, componentHalfX)) {
  184. line.select.set(lineItem, { x: componentHalfX })
  185. setComponentPosition(selectTarget.value, componentHalfX, selectTopY)
  186. }
  187. if (isSorption(selectLeftX, componentRightX)) {
  188. line.select.set(lineItem, { x: componentRightX })
  189. setComponentPosition(selectTarget.value, componentRightX, selectTopY)
  190. }
  191. }
  192. if (lineItem.includes('colc')) {
  193. if (isSorption(selectHalfX, componentLeftX)) {
  194. line.select.set(lineItem, { x: componentLeftX })
  195. setComponentPosition(selectTarget.value, componentLeftX - selectW / 2, selectTopY)
  196. }
  197. if (isSorption(selectHalfX, componentHalfX)) {
  198. line.select.set(lineItem, { x: componentHalfX })
  199. setComponentPosition(selectTarget.value, componentHalfX - selectW / 2, selectTopY)
  200. }
  201. if (isSorption(selectHalfX, componentRightX)) {
  202. line.select.set(lineItem, { x: componentRightX })
  203. setComponentPosition(selectTarget.value, componentRightX - selectW / 2, selectTopY)
  204. }
  205. }
  206. if (lineItem.includes('colr')) {
  207. if (isSorption(selectRightX, componentLeftX)) {
  208. line.select.set(lineItem, { x: componentLeftX })
  209. setComponentPosition(selectTarget.value, componentLeftX - selectW, selectTopY)
  210. }
  211. if (isSorption(selectRightX, componentHalfX)) {
  212. line.select.set(lineItem, { x: componentHalfX })
  213. setComponentPosition(selectTarget.value, componentHalfX - selectW, selectTopY)
  214. }
  215. if (isSorption(selectRightX, componentRightX)) {
  216. line.select.set(lineItem, { x: componentRightX })
  217. setComponentPosition(selectTarget.value, componentRightX - selectW, selectTopY)
  218. }
  219. }
  220. })
  221. })
  222. } catch (err) {
  223. console.log(err)
  224. }
  225. }, 200),
  226. {
  227. deep: true
  228. }
  229. )
  230. // * 取消对齐线
  231. watch(
  232. () => isComputedLine.value,
  233. (val: boolean) => {
  234. if (!val) {
  235. line.select.clear()
  236. line.sorptioned.y = false
  237. }
  238. }
  239. )
  240. </script>
  241. <style lang="scss" scoped>
  242. @include go(edit-align-line) {
  243. .line {
  244. z-index: 1;
  245. position: absolute;
  246. top: 0;
  247. left: 0;
  248. display: none;
  249. border-width: 1px;
  250. border-style: solid;
  251. border-color: v-bind('themeColor');
  252. opacity: 0.7;
  253. &.visible {
  254. display: block;
  255. }
  256. }
  257. .row {
  258. width: 100%;
  259. }
  260. .col {
  261. height: 100%;
  262. }
  263. }
  264. </style>