index.vue 10 KB

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