Jelajahi Sumber

fix: 修改拖入画布事件拦截问题

jiaxing.liao 1 Minggu lalu
induk
melakukan
2017fcac30

+ 4 - 18
src/renderer/src/store/modules/app.ts

@@ -6,54 +6,39 @@ import { useI18n } from 'vue-i18n'
 import { useLocalStorage } from '@vueuse/core'
 
 /**
- * @description: 应用状态管理
+ * App state store.
  */
 export const useAppStore = defineStore('app', () => {
-  // 显示通用配置
   const showGeneralModal = ref(false)
-  // 屏幕布局
   const screenLayout = ref<'vertical' | 'horizontal'>('vertical')
-  // 语言
   const lang = useLocalStorage<'zh_CN' | 'en_US'>('lang', 'zh_CN')
-  // 主题
   const theme = useLocalStorage('theme', 'dark')
-  // 编辑器
   const editor = useLocalStorage('editor', {
     fontSize: 14
   })
-  // 显示链接
   const showLink = ref(true)
-  // 显示下载
   const showDownload = ref(true)
-  // 加载中
   const loading = ref(false)
-  // 联调工具路径
   const sunmicroPath = useLocalStorage('sunmicroPath', '')
-  // 底部工具
   const showComposite = ref(false)
-  // 底部工具当前tab
   const compositeTabAcitve = ref('log')
-  // 拖拽中
   const draging = ref(false)
+  const libraryDragging = ref(false)
 
   const { locale } = useI18n()
 
   const getLocale = computed(() => (lang.value === 'zh_CN' ? zhCn : en))
 
-  // 设置语言
   function setLang(val: 'zh_CN' | 'en_US') {
     lang.value = val
     locale.value = val
   }
 
-  // 设置主题
   function setTheme(val: 'light' | 'dark') {
     theme.value = val
-    // 切换html样式
     document.querySelector('html')?.setAttribute('class', val)
   }
 
-  // 切换布局
   function toggleLayout() {
     screenLayout.value = screenLayout.value === 'vertical' ? 'horizontal' : 'vertical'
   }
@@ -74,6 +59,7 @@ export const useAppStore = defineStore('app', () => {
     sunmicroPath,
     showComposite,
     compositeTabAcitve,
-    draging
+    draging,
+    libraryDragging
   }
 })

+ 4 - 0
src/renderer/src/views/designer/sidebar/components/CompLibaryItem.vue

@@ -17,6 +17,7 @@
 <script setup lang="ts">
 import { computed, ref } from 'vue'
 import { useDrag } from 'vue-hooks-plus'
+import { useAppStore } from '@/store/modules/app'
 
 import type { ICustomWidget } from '@/types'
 
@@ -30,13 +31,16 @@ const img = computed(() => {
 
 const libaryItemRef = ref<HTMLElement>()
 const draging = ref(false)
+const appStore = useAppStore()
 
 useDrag(props.comp.widget, libaryItemRef, {
   onDragStart: () => {
     draging.value = true
+    appStore.libraryDragging = true
   },
   onDragEnd: () => {
     draging.value = false
+    appStore.libraryDragging = false
   }
 })
 </script>

+ 4 - 0
src/renderer/src/views/designer/sidebar/components/LibaryItem.vue

@@ -17,19 +17,23 @@ import type { IComponentModelConfig } from '@/lvgl-widgets/type'
 
 import { ref } from 'vue'
 import { useDrag } from 'vue-hooks-plus'
+import { useAppStore } from '@/store/modules/app'
 
 const props = defineProps<{
   comp: IComponentModelConfig
 }>()
 const libaryItemRef = ref<HTMLElement>()
 const draging = ref(false)
+const appStore = useAppStore()
 
 useDrag(props.comp, libaryItemRef, {
   onDragStart: () => {
     draging.value = true
+    appStore.libraryDragging = true
   },
   onDragEnd: () => {
     draging.value = false
+    appStore.libraryDragging = false
   }
 })
 </script>

+ 4 - 0
src/renderer/src/views/designer/workspace/composite/eventEdit/config.ts

@@ -845,6 +845,10 @@ function createSchemaDefaultValue(defaultRoot: any, schema: ComponentSchema) {
 function cloneActionSchema(schema: ComponentSchema, targetField = 'payload'): ComponentSchema {
   const cloned = klona(schema)
   cloned.field = targetField
+  if (cloned.componentProps?.defaultCollapsed) {
+    // 关闭默认折叠
+    cloned.componentProps.defaultCollapsed = false
+  }
   return cloned
 }
 

+ 19 - 0
src/renderer/src/views/designer/workspace/composite/eventEdit/index.vue

@@ -1,4 +1,10 @@
 <template>
+  <el-tooltip content="回到根节点">
+    <el-button link class="absolute left-10px top-10px" @click="handleToRoot"
+      ><LuCrosshair size="14px"
+    /></el-button>
+  </el-tooltip>
+
   <div ref="containerRef" class="w-full h-full"></div>
 </template>
 
@@ -13,6 +19,7 @@ import { bfsWalk } from 'simple-mind-map/src/utils'
 import { klona } from 'klona'
 import { v4 } from 'uuid'
 import { useElementSize, useResizeObserver, watchOnce } from '@vueuse/core'
+import { LuCrosshair } from 'vue-icons-plus/lu'
 
 import defaultTheme from './theme'
 import NodeItem from './NodeItem.vue'
@@ -43,6 +50,17 @@ const getAddNode = (parentId: string, parentType: 'root' | 'event' | 'target'):
   parentType
 })
 
+const handleToRoot = () => {
+  mindMap.value?.view.reset()
+}
+
+const resetMindMapView = async () => {
+  if (!mindMap.value) return
+
+  await nextTick()
+  mindMap.value.view.reset()
+}
+
 const createBuiltinActionNode = (
   targetId: string,
   targetData: WidgetEventTargetData,
@@ -368,6 +386,7 @@ const buildRootNode = async () => {
   mindMapData.value = rootNode
   await nextTick()
   mindMap.value?.setData(rootNode)
+  await resetMindMapView()
 }
 
 const inheritParentAppContext = (childApp: ReturnType<typeof createApp>, parent?: AppContext) => {

+ 35 - 72
src/renderer/src/views/designer/workspace/stage/Node.vue

@@ -11,7 +11,6 @@
     @contextmenu.stop="handleContextmenu"
     :ignore-drag="ignoreDrag && !schema.props?.flags?.includes('LV_OBJ_FLAG_IGNORE_LAYOUT')"
   >
-    <!-- 控件 -->
     <component
       v-bind="widgetProps"
       :is="widget"
@@ -22,7 +21,6 @@
       class="absolute left-0 top-0 w-full h-full"
       :style="{ ...layoutStyle, ...dropStyle }"
     >
-      <!-- 子节点 -->
       <NodeItem
         v-for="(child, index) in children"
         :key="child.id"
@@ -34,7 +32,6 @@
       />
     </div>
   </div>
-  <!-- 右键菜单 -->
   <ContextMenu
     ref="contextMenuRef"
     :id="schema.id"
@@ -49,14 +46,14 @@ import type { Page } from '@/types/page'
 import type { StageState } from './type'
 import type { CSSProperties } from 'vue'
 
-import { computed, ref, inject, watch, nextTick, onBeforeUnmount } from 'vue'
+import { computed, inject, nextTick, onBeforeUnmount, ref, watch } from 'vue'
 import { useDrop, useMouse } from 'vue-hooks-plus'
 import { createWidget } from '@/model'
 import LvglWidgets from '@/lvgl-widgets'
 import { useProjectStore } from '@/store/modules/project'
 import { useAppStore } from '@/store/modules/app'
 import { useActionStore } from '@/store/modules/action'
-import { has, isEmpty, get } from 'lodash-es'
+import { get, has, isEmpty } from 'lodash-es'
 
 import ContextMenu from './ContextMenu.vue'
 import { getAddWidgetIndex, isDescendant } from '@/utils'
@@ -70,33 +67,22 @@ const page = inject<Page>('page')
 const pageState = inject<StageState>('state')
 
 const props = defineProps<{
-  // 父级容器 拖拽缩放设置
   rootContainer: HTMLElement
-  // 传入节点模型数据
   schema: BaseWidget | Page
-  // 传入样式 如页面样式
   style?: CSSProperties
-  // 层级
   zIndex?: number
-  // 是否可拖拽
   ignoreDrag?: boolean
 }>()
 
 const projectStore = useProjectStore()
 const appStore = useAppStore()
 const actionStore = useActionStore()
-// 获取lvgl vue控件
+
 const widget = computed(() => LvglWidgets[props.schema.type]?.component)
-// 节点容器放置ref
 const nodeRef = ref<HTMLDivElement>()
-// 当前层级
 const zIndex = ref(props.zIndex)
-
-// 放置效果样式
 const dropStyle = ref<CSSProperties>({})
 
-// 子节点
-// 默认为子节点 当子节点为可切换容器时 根据activeIndex展示下一级节点
 const children = computed(() => {
   const { schema } = props
 
@@ -105,7 +91,6 @@ const children = computed(() => {
     : schema.children
 })
 
-// 控件属性
 const widgetProps = computed(() => {
   const { schema } = props
 
@@ -115,30 +100,30 @@ const widgetProps = computed(() => {
     state: schema?.state,
     styles: schema?.style,
     id: schema?.id,
-    // 处理可切换子节点容器属性
     ...(has(schema.props, 'activeIndex') && schema.children ? { children: schema.children } : {})
   }
 })
 
-// 忽略拖拽
 const ignoreChildrenDrag = computed(() => {
   const { schema } = props
   return schema.props?.layout === 'flex'
 })
 
-// 组件样式
+const shouldIgnoreLibraryDrop = computed(() => {
+  const { schema } = props
+  return appStore.libraryDragging && schema.type !== 'page' && !schema.children
+})
+
 const getStyle = computed((): CSSProperties => {
   const { style = {}, schema } = props
 
   const other: CSSProperties = {}
-  // 显示所有控件边框
   if (pageState?.showBorder && schema.type !== 'page') {
     other.border = '1px dashed #000'
     other.transform = `translate(${schema.props.x - 1}px, ${schema.props.y - 1}px)`
   }
 
   let rotate = ''
-  // 存在旋转属性
   if (schema.props?.rotation) {
     rotate = `rotate(${schema.props.rotation}deg)`
     if (schema.props?.pivot) {
@@ -151,14 +136,12 @@ const getStyle = computed((): CSSProperties => {
   }
 
   let scale = 1
-  // 存在缩放属性且打开
   if (schema.props?.openScale) {
     scale = (schema.props?.scale ?? 256) / 256
   }
 
-  let parentStyle = klona(style)
+  const parentStyle = klona(style)
 
-  // 忽略父级布局
   if (schema.props.flags?.includes('LV_OBJ_FLAG_IGNORE_LAYOUT')) {
     delete parentStyle.position
     delete parentStyle.transform
@@ -169,18 +152,15 @@ const getStyle = computed((): CSSProperties => {
     left: 0,
     top: 0,
     transform: `translate(${schema.props.x}px, ${schema.props.y}px) ${rotate}`,
-    width: schema.props?.width * scale + 'px',
-    height: schema.props?.height * scale + 'px',
+    width: `${schema.props?.width * scale}px`,
+    height: `${schema.props?.height * scale}px`,
     zIndex: zIndex.value,
+    pointerEvents: shouldIgnoreLibraryDrop.value ? 'none' : undefined,
     ...parentStyle,
     ...other
-    // ...dropStyle.value
   }
 })
 
-/**
- * 布局样式
- */
 const layoutStyle = ref<CSSProperties>({})
 watch(
   () => props.schema.props,
@@ -215,7 +195,6 @@ watch(
       }
     }
 
-    // 获取子节点容器样式
     const getChildStyle = LvglWidgets[props.schema.type]?.getChildStyle
     if (typeof getChildStyle === 'function') {
       style = {
@@ -231,12 +210,8 @@ watch(
   }
 )
 
-/**
- * 子元素样式
- */
 const childStyle = computed((): CSSProperties => {
   const { schema } = props
-  // flex布局,子元素移除transform,
   if (schema.props?.layout === 'flex') {
     return {
       transform: 'none',
@@ -247,24 +222,37 @@ const childStyle = computed((): CSSProperties => {
   return {}
 })
 
-// 拖拽放置事件处理
+const getDropPosition = (event?: DragEvent) => {
+  const element = nodeRef.value
+  const rect = element?.getBoundingClientRect()
+  const logicalWidth = props.schema.props?.width ?? element?.offsetWidth ?? element?.clientWidth
+  const logicalHeight = props.schema.props?.height ?? element?.offsetHeight ?? element?.clientHeight
+  if (!rect || !logicalWidth || !logicalHeight || !event) {
+    return { x: 0, y: 0 }
+  }
+
+  const scaleX = rect.width / logicalWidth || 1
+  const scaleY = rect.height / logicalHeight || 1
+
+  return {
+    x: Math.round((event.clientX - rect.left) / scaleX),
+    y: Math.round((event.clientY - rect.top) / scaleY)
+  }
+}
+
 useDrop(nodeRef, {
-  // 元素放置
   onDom: (content, event) => {
     const { schema } = props
 
     dropStyle.value = {}
-    event?.stopPropagation()
     if (!content || !schema?.children) return
 
-    // 创建控件
-    const { offsetX = 0, offsetY = 0 } = event || {}
+    const { x, y } = getDropPosition(event)
     const index = getAddWidgetIndex(page!, content?.key)
     const newWidget = createWidget(content, index, !!content?.id)
-    newWidget.props.x = offsetX
-    newWidget.props.y = offsetY
+    newWidget.props.x = x
+    newWidget.props.y = y
 
-    // 添加到前面
     if (has(schema.props, 'activeIndex')) {
       const parent = get(schema.children, schema.props.activeIndex)
       parent?.children?.push(newWidget)
@@ -273,11 +261,11 @@ useDrop(nodeRef, {
     }
 
     projectStore.setSelectWidgets([newWidget])
+    event?.stopPropagation()
   },
   onDragOver: () => {
     if (props.schema.type !== 'page' && props.schema.children) {
       dropStyle.value = {
-        // 放置阴影效果
         boxShadow: '0px 0px 10px #00fcfc'
       }
     }
@@ -287,9 +275,7 @@ useDrop(nodeRef, {
   }
 })
 
-// 监听鼠标位置
 const nodeState = useMouse(nodeRef)
-// 可放置标识
 let dropFlag = false
 let offsetX = 0
 let offsetY = 0
@@ -329,12 +315,9 @@ watch(nodeState, (state) => {
   if (schema?.children && schema.type !== 'page') {
     const { elementW, elementH, elementX, elementY, clientX, clientY } = state
 
-    // 停止拖拽且允许放置时剪切到容器内
     if (dropFlag && !appStore.draging) {
       dropFlag = false
       clearDropMouseupListener()
-      // 放置到容器内
-      // 获取在容器的真实位置
       const scale = elementW / schema.props.width
       const x = elementX / scale - offsetX
       const y = elementY / scale - offsetY
@@ -343,9 +326,7 @@ watch(nodeState, (state) => {
       projectStore.activeWidgets[0].props.y = y
 
       const node = klona(projectStore.activeWidgets[0])
-      // 删除原来的节点
       actionStore.onDeleteById(node.id)
-      // 添加到前面
       if (has(schema.props, 'activeIndex')) {
         const parent = get(schema.children, schema.props.activeIndex)
         parent?.children?.push(node)
@@ -354,12 +335,7 @@ watch(nodeState, (state) => {
       }
       projectStore.activeWidgets = [node]
     }
-    // 鼠标在节点内部
-    // 处于节点拖拽状态
-    // 当前选中节点只有一个
-    // 且非自身节点
-    // 非自身后代节点
-    // 非页面节点
+
     if (
       elementX > 0 &&
       elementX < elementW &&
@@ -371,11 +347,9 @@ watch(nodeState, (state) => {
       !isDescendant(schema, projectStore.activeWidgets[0].id) &&
       schema.type !== 'page'
     ) {
-      // 获取当前鼠标位置全部控件节点元素
       const elements = document
         .elementsFromPoint(clientX, clientY)
         ?.filter((el) => el.className?.includes?.('widget-node'))
-      // 获取第一个元素不是自身的元素
       const targetEl = elements.find((el) => {
         const id = el.attributes.getNamedItem('widget-id')?.value
         return id !== projectStore.activeWidget?.id
@@ -383,9 +357,7 @@ watch(nodeState, (state) => {
       const targetId = targetEl?.attributes.getNamedItem('widget-id')?.value
 
       if (targetId === schema.id) {
-        // 最上层的容器
         dropStyle.value = {
-          // 放置阴影效果
           boxShadow: '0px 0px 10px #00fcfc'
         }
         dropFlag = true
@@ -393,23 +365,19 @@ watch(nodeState, (state) => {
       } else if (!isEmpty(dropStyle.value)) {
         dropFlag = false
         clearDropMouseupListener()
-        // 容器外
         dropStyle.value = {}
       }
     } else if (!isEmpty(dropStyle.value)) {
       dropFlag = false
       clearDropMouseupListener()
-      // 容器外
       dropStyle.value = {}
     }
   }
 })
 
-// 选择节点
 const handleSelect = (e: MouseEvent) => {
   if (props.schema.type !== 'page') {
     e.stopPropagation()
-    // 判断当前是否按住ctrl
     if (e.ctrlKey) {
       projectStore.activeWidgets.push(props.schema as BaseWidget)
     } else {
@@ -418,11 +386,9 @@ const handleSelect = (e: MouseEvent) => {
   } else {
     projectStore.setSelectWidgets([])
   }
-  // 关闭右键菜单
   contextMenuRef.value?.handleClose()
 }
 
-/******************************右键菜单*********************************/
 const position = ref({
   top: 0,
   left: 0,
@@ -436,7 +402,6 @@ const triggerRef = ref({
   getBoundingClientRect: () => position.value
 })
 
-// 处理右键菜单
 const handleContextmenu = (event: MouseEvent) => {
   const { clientX, clientY } = event
   position.value = DOMRect.fromRect({
@@ -446,10 +411,8 @@ const handleContextmenu = (event: MouseEvent) => {
   event.preventDefault()
   contextMenuRef.value?.handleOpen()
 
-  // 没选中当前节点时 右键选中节点
   if (!projectStore.activeWidgets.map((item) => item.id).includes(props.schema.id)) {
     if (props.schema.type !== 'page') {
-      // 判断当前是否按住ctrl
       if (event.ctrlKey) {
         projectStore.activeWidgets.push(props.schema as BaseWidget)
       } else {