Browse Source

feat: 添加节点吸附、辅助线吸附

jiaxing.liao 2 weeks ago
parent
commit
b9d909f1c0

+ 1 - 1
src/renderer/src/views/designer/sidebar/Method.vue

@@ -1,5 +1,5 @@
 <template>
-  <div style="height: calc(100vh - 146px)" class="w-full flex flex-col">
+  <div style="height: calc(100vh - 150px)" class="w-full flex flex-col">
     <ViewTitle title="函数代码">
       <template #right>
         <el-button type="text" @click="handleAdd"><LuPlus size="14px" /></el-button>

+ 2 - 2
src/renderer/src/views/designer/sidebar/Resource.vue

@@ -122,7 +122,7 @@ const handleAddImage = async () => {
     ]
   })
 
-  paths.forEach(async (path) => {
+  paths?.forEach(async (path) => {
     const fileName = path.split('\\').pop()
     // 复制文件
     await window.electron.ipcRenderer.invoke(
@@ -174,7 +174,7 @@ const handleAddOther = async () => {
     properties: ['openFile', 'multiSelections']
   })
 
-  paths.forEach(async (path) => {
+  paths?.forEach(async (path) => {
     const fileName = path.split('\\').pop()
     // 复制文件
     await window.electron.ipcRenderer.invoke(

+ 51 - 16
src/renderer/src/views/designer/workspace/stage/Node.vue

@@ -1,8 +1,9 @@
 <template>
   <div
-    :style="getStyle"
     ref="nodeRef"
-    :class="schema.type === 'page' ? '' : 'ignore-click widget-wrapper'"
+    :style="getStyle"
+    :class="schema.type === 'page' ? '' : 'ignore-click widget-node'"
+    :widget-id="schema.id"
     @click.stop="handleSelect"
     v-if="!schema.hidden"
   >
@@ -44,11 +45,11 @@
         center: true,
         middle: true
       }"
-      :maxSnapElementGuidelineDistance="50"
+      :maxSnapElementGuidelineDistance="100"
       :elementGuidelines="elementGridelines"
-      :controlPadding="4"
       :verticalGuidelines="verticalGuidelines"
       :horizontalGuidelines="horizontalGuidelines"
+      :controlPadding="4"
       @render="onRender"
       @drag="onDrag"
       @resize="onResize"
@@ -59,12 +60,14 @@
 <script setup lang="ts">
 import type { BaseWidget } from '@/types/baseWidget'
 import type { Page } from '@/types/page'
+import type { StageState } from './type'
 
 import { computed, type CSSProperties, ref, inject } from 'vue'
 import { useDrop } from 'vue-hooks-plus'
 import { createWidget } from '@/model'
 import LvglWidgets from '@/lvgl-widgets'
 import { useProjectStore } from '@/store/modules/project'
+import { useMutationObserver } from '@vueuse/core'
 
 import Moveable from 'vue3-moveable'
 import { getAddWidgetIndex } from '@/utils'
@@ -74,6 +77,8 @@ defineOptions({
 })
 
 const page = inject<Page>('page')
+const pageState = inject<StageState>('state')
+const pageEl = inject<HTMLElement>('pageEl')
 
 const props = defineProps<{
   // 父级容器 拖拽缩放设置
@@ -93,17 +98,6 @@ const nodeRef = ref<HTMLDivElement>()
 const selected = computed(() =>
   projectStore.activeWidgets.map((item) => item.id).includes(props.schema.id)
 )
-// 辅助线
-const verticalGuidelines = computed(() => {
-  return projectStore.activePage?.referenceLine
-    .filter((item) => item.type === 'vertical')
-    .map((item) => item.value)
-})
-const horizontalGuidelines = computed(() => {
-  return projectStore.activePage?.referenceLine
-    .filter((item) => item.type === 'horizontal')
-    .map((item) => item.value)
-})
 
 // 组件样式
 const getStyle = computed((): CSSProperties => {
@@ -121,7 +115,48 @@ const getStyle = computed((): CSSProperties => {
 })
 
 // 吸附辅助线
-const elementGridelines = ref([])
+const elementGridelines = ref<Element[]>([])
+// 垂直辅助线
+const verticalGuidelines = computed(() => {
+  return (
+    (pageState?.showReferenceLine &&
+      projectStore.activePage?.referenceLine
+        .filter((item) => item.type === 'horizontal')
+        .map((item) => item.value)) ||
+    []
+  )
+})
+// 水平辅助线
+const horizontalGuidelines = computed(() => {
+  return (
+    (pageState?.showReferenceLine &&
+      projectStore.activePage?.referenceLine
+        .filter((item) => item.type === 'vertical')
+        .map((item) => item.value)) ||
+    []
+  )
+})
+
+// 监听页面节点变化 设置元素辅助线
+useMutationObserver(
+  pageEl,
+  (mutations) => {
+    mutations.forEach(() => {
+      const els = document.querySelectorAll('.widget-node')
+      elementGridelines.value = []
+      els.forEach((el) => {
+        // 排除自己
+        if (el.attributes['widget-id']?.value !== props.schema.id) {
+          elementGridelines.value.push(el)
+        }
+      })
+    })
+  },
+  {
+    childList: true,
+    subtree: true
+  }
+)
 
 // 拖拽放置事件处理
 useDrop(nodeRef, {

+ 5 - 3
src/renderer/src/views/designer/workspace/stage/index.vue

@@ -86,7 +86,7 @@ import type { Page } from '@/types/page'
 import { ref, reactive, watch, nextTick, provide } from 'vue'
 import Scaleplate from './Scaleplate.vue'
 import DesignerCanvas from './DesignerCanvas.vue'
-import { throttle } from 'lodash'
+import { throttle } from 'lodash-es'
 import {
   LuGrid3X3,
   LuRuler,
@@ -103,8 +103,6 @@ const props = defineProps<{
   page?: Page
 }>()
 
-provide('page', props.page)
-
 const projectStore = useProjectStore()
 const appStore = useAppStore()
 const canvasRef = ref<InstanceType<typeof DesignerCanvas>>()
@@ -254,6 +252,10 @@ const handleSelectComponent = (startX: number, startY: number, endX: number, end
   projectStore.setSelectWidgets(select)
 }
 /* ====================处理框选多个组件======================== */
+
+provide('page', props.page)
+provide('state', state)
+provide('pageEl', boxRef)
 </script>
 
 <style lang="less" scoped>