Browse Source

解决云教练因为iframe原因和外面的事件冲突的问题

黄琪勇 3 months ago
parent
commit
428d9a0a07

+ 5 - 4
src/hooks/useCreateElement.ts

@@ -320,16 +320,17 @@ export default () => {
    * 创建云教练元素
    * @param url 云教练地址
    */
-  const createCloudCoachElement = (url: string) => {
+  const createCloudCoachElement = (url: string, width = 500, height = 300) => {
     createElement({
       type: "cloudCoach",
       id: nanoid(10),
-      width: 500,
-      height: 300,
+      width,
+      height,
       rotate: 0,
       left: (viewportSize.value - 500) / 2,
       top: (viewportSize.value * viewportRatio.value - 300) / 2,
-      url
+      url,
+      isMove: false
     })
   }
 

+ 7 - 7
src/store/keyboard.ts

@@ -1,4 +1,4 @@
-import { defineStore } from 'pinia'
+import { defineStore } from "pinia"
 
 export interface KeyboardState {
   ctrlKeyState: boolean
@@ -6,17 +6,17 @@ export interface KeyboardState {
   spaceKeyState: boolean
 }
 
-export const useKeyboardStore = defineStore('keyboard', {
+export const useKeyboardStore = defineStore("keyboard", {
   state: (): KeyboardState => ({
     ctrlKeyState: false, // ctrl键按下状态
     shiftKeyState: false, // shift键按下状态
-    spaceKeyState: false, // space键按下状态
+    spaceKeyState: false // space键按下状态
   }),
 
   getters: {
     ctrlOrShiftKeyActive(state) {
       return state.ctrlKeyState || state.shiftKeyState
-    },
+    }
   },
 
   actions: {
@@ -28,6 +28,6 @@ export const useKeyboardStore = defineStore('keyboard', {
     },
     setSpaceKeyState(active: boolean) {
       this.spaceKeyState = active
-    },
-  },
-})
+    }
+  }
+})

+ 43 - 43
src/store/main.ts

@@ -1,13 +1,13 @@
-import { customAlphabet } from 'nanoid'
-import { defineStore } from 'pinia'
-import { ToolbarStates } from '@/types/toolbar'
-import type { CreatingElement, ShapeFormatPainter, TextFormatPainter } from '@/types/edit'
-import type { DialogForExportTypes } from '@/types/export'
-import { type TextAttrs, defaultRichTextAttrs } from '@/utils/prosemirror/utils'
-import { SYS_FONTS } from '@/configs/font'
-import { isSupportFont } from '@/utils/font'
+import { customAlphabet } from "nanoid"
+import { defineStore } from "pinia"
+import { ToolbarStates } from "@/types/toolbar"
+import type { CreatingElement, ShapeFormatPainter, TextFormatPainter } from "@/types/edit"
+import type { DialogForExportTypes } from "@/types/export"
+import { type TextAttrs, defaultRichTextAttrs } from "@/utils/prosemirror/utils"
+import { SYS_FONTS } from "@/configs/font"
+import { isSupportFont } from "@/utils/font"
 
-import { useSlidesStore } from './slides'
+import { useSlidesStore } from "./slides"
 
 export interface MainState {
   activeElementIdList: string[]
@@ -40,14 +40,14 @@ export interface MainState {
   showNotesPanel: boolean
 }
 
-const nanoid = customAlphabet('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')
+const nanoid = customAlphabet("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
 export const databaseId = nanoid(10)
 
-export const useMainStore = defineStore('main', {
+export const useMainStore = defineStore("main", {
   state: (): MainState => ({
     activeElementIdList: [], // 被选中的元素ID集合,包含 handleElementId
-    handleElementId: '', // 正在操作的元素ID
-    activeGroupElementId: '', // 组合元素成员中,被选中可独立操作的元素ID
+    handleElementId: "", // 正在操作的元素ID
+    activeGroupElementId: "", // 组合元素成员中,被选中可独立操作的元素ID
     hiddenElementIdList: [], // 被隐藏的元素ID集合
     canvasPercentage: 90, // 画布可视区域百分比
     canvasScale: 1, // 画布缩放比例(基于宽度{{slidesStore.viewportSize}}像素)
@@ -61,18 +61,18 @@ export const useMainStore = defineStore('main', {
     creatingCustomShape: false, // 正在绘制任意多边形
     availableFonts: SYS_FONTS, // 当前环境可用字体
     toolbarState: ToolbarStates.SLIDE_DESIGN, // 右侧工具栏状态
-    clipingImageElementId: '', // 当前正在裁剪的图片ID  
+    clipingImageElementId: "", // 当前正在裁剪的图片ID
     richTextAttrs: defaultRichTextAttrs, // 富文本状态
     selectedTableCells: [], // 选中的表格单元格
     isScaling: false, // 正在进行元素缩放
     selectedSlidesIndex: [], // 当前被选中的页面索引集合
-    dialogForExport: '', // 导出面板
+    dialogForExport: "", // 导出面板
     databaseId, // 标识当前应用的indexedDB数据库ID
     textFormatPainter: null, // 文字格式刷
     shapeFormatPainter: null, // 形状格式刷
     showSelectPanel: false, // 打开选择面板
     showSearchPanel: false, // 打开查找替换面板
-    showNotesPanel: false, // 打开批注面板
+    showNotesPanel: false // 打开批注面板
   }),
 
   getters: {
@@ -82,99 +82,99 @@ export const useMainStore = defineStore('main', {
       if (!currentSlide || !currentSlide.elements) return []
       return currentSlide.elements.filter(element => state.activeElementIdList.includes(element.id))
     },
-  
+
     handleElement(state) {
       const slidesStore = useSlidesStore()
       const currentSlide = slidesStore.currentSlide
       if (!currentSlide || !currentSlide.elements) return null
       return currentSlide.elements.find(element => state.handleElementId === element.id) || null
-    },
+    }
   },
 
   actions: {
     setActiveElementIdList(activeElementIdList: string[]) {
       if (activeElementIdList.length === 1) this.handleElementId = activeElementIdList[0]
-      else this.handleElementId = ''
-      
+      else this.handleElementId = ""
+
       this.activeElementIdList = activeElementIdList
     },
-    
+
     setHandleElementId(handleElementId: string) {
       this.handleElementId = handleElementId
     },
-    
+
     setActiveGroupElementId(activeGroupElementId: string) {
       this.activeGroupElementId = activeGroupElementId
     },
-    
+
     setHiddenElementIdList(hiddenElementIdList: string[]) {
       this.hiddenElementIdList = hiddenElementIdList
     },
-  
+
     setCanvasPercentage(percentage: number) {
       this.canvasPercentage = percentage
     },
-  
+
     setCanvasScale(scale: number) {
       this.canvasScale = scale
     },
-  
+
     setCanvasDragged(isDragged: boolean) {
       this.canvasDragged = isDragged
     },
-  
+
     setThumbnailsFocus(isFocus: boolean) {
       this.thumbnailsFocus = isFocus
     },
-  
+
     setEditorareaFocus(isFocus: boolean) {
       this.editorAreaFocus = isFocus
     },
-  
+
     setDisableHotkeysState(disable: boolean) {
       this.disableHotkeys = disable
     },
-  
+
     setGridLineSize(size: number) {
       this.gridLineSize = size
     },
-  
+
     setRulerState(show: boolean) {
       this.showRuler = show
     },
-  
+
     setCreatingElement(element: CreatingElement | null) {
       this.creatingElement = element
     },
-  
+
     setCreatingCustomShapeState(state: boolean) {
       this.creatingCustomShape = state
     },
-  
+
     setAvailableFonts() {
       this.availableFonts = SYS_FONTS.filter(font => isSupportFont(font.value))
     },
-  
+
     setToolbarState(toolbarState: ToolbarStates) {
       this.toolbarState = toolbarState
     },
-  
+
     setClipingImageElementId(elId: string) {
       this.clipingImageElementId = elId
     },
-  
+
     setRichtextAttrs(attrs: TextAttrs) {
       this.richTextAttrs = attrs
     },
-  
+
     setSelectedTableCells(cells: string[]) {
       this.selectedTableCells = cells
     },
-  
+
     setScalingState(isScaling: boolean) {
       this.isScaling = isScaling
     },
-    
+
     updateSelectedSlidesIndex(selectedSlidesIndex: number[]) {
       this.selectedSlidesIndex = selectedSlidesIndex
     },
@@ -201,6 +201,6 @@ export const useMainStore = defineStore('main', {
 
     setNotesPanelState(show: boolean) {
       this.showNotesPanel = show
-    },
-  },
-})
+    }
+  }
+})

+ 2 - 2
src/store/screen.ts

@@ -1,10 +1,10 @@
-import { defineStore } from 'pinia'
+import { defineStore } from "pinia"
 
 export interface ScreenState {
   screening: boolean
 }
 
-export const useScreenStore = defineStore('screen', {
+export const useScreenStore = defineStore("screen", {
   state: (): ScreenState => ({
     screening: false // 是否进入放映状态
   }),

+ 44 - 51
src/store/slides.ts

@@ -1,10 +1,10 @@
-import { defineStore } from 'pinia'
-import tinycolor from 'tinycolor2'
-import { omit } from 'lodash'
-import type { Slide, SlideTheme, PPTElement, PPTAnimation } from '@/types/slides'
-import { slides } from '@/mocks/slides'
-import { theme } from '@/mocks/theme'
-import { layouts } from '@/mocks/layout'
+import { defineStore } from "pinia"
+import tinycolor from "tinycolor2"
+import { omit } from "lodash"
+import type { Slide, SlideTheme, PPTElement, PPTAnimation } from "@/types/slides"
+import { slides } from "@/mocks/slides"
+import { theme } from "@/mocks/theme"
+import { layouts } from "@/mocks/layout"
 
 interface RemovePropData {
   id: string
@@ -31,21 +31,21 @@ export interface SlidesState {
   viewportRatio: number
 }
 
-export const useSlidesStore = defineStore('slides', {
+export const useSlidesStore = defineStore("slides", {
   state: (): SlidesState => ({
-    title: '未命名演示文稿', // 幻灯片标题
+    title: "未命名演示文稿", // 幻灯片标题
     theme: theme, // 主题样式
     slides: slides, // 幻灯片页面数据
     slideIndex: 0, // 当前页面索引
     viewportSize: 1000, // 可视区域宽度基数
-    viewportRatio: 0.5625, // 可视区域比例,默认16:9
+    viewportRatio: 0.5625 // 可视区域比例,默认16:9
   }),
 
   getters: {
     currentSlide(state) {
       return state.slides[state.slideIndex]
     },
-  
+
     currentSlideAnimations(state) {
       const currentSlide = state.slides[state.slideIndex]
       if (!currentSlide?.animations) return []
@@ -68,16 +68,14 @@ export const useSlidesStore = defineStore('slides', {
 
       const formatedAnimations: FormatedAnimation[] = []
       for (const animation of animations) {
-        if (animation.trigger === 'click' || !formatedAnimations.length) {
+        if (animation.trigger === "click" || !formatedAnimations.length) {
           formatedAnimations.push({ animations: [animation], autoNext: false })
-        }
-        else if (animation.trigger === 'meantime') {
+        } else if (animation.trigger === "meantime") {
           const last = formatedAnimations[formatedAnimations.length - 1]
           last.animations = last.animations.filter(item => item.elId !== animation.elId)
           last.animations.push(animation)
           formatedAnimations[formatedAnimations.length - 1] = last
-        }
-        else if (animation.trigger === 'auto') {
+        } else if (animation.trigger === "auto") {
           const last = formatedAnimations[formatedAnimations.length - 1]
           last.autoNext = true
           formatedAnimations[formatedAnimations.length - 1] = last
@@ -86,50 +84,45 @@ export const useSlidesStore = defineStore('slides', {
       }
       return formatedAnimations
     },
-  
+
     layouts(state) {
-      const {
-        themeColor,
-        fontColor,
-        fontName,
-        backgroundColor,
-      } = state.theme
-  
-      const subColor = tinycolor(fontColor).isDark() ? 'rgba(230, 230, 230, 0.5)' : 'rgba(180, 180, 180, 0.5)'
-  
+      const { themeColor, fontColor, fontName, backgroundColor } = state.theme
+
+      const subColor = tinycolor(fontColor).isDark() ? "rgba(230, 230, 230, 0.5)" : "rgba(180, 180, 180, 0.5)"
+
       const layoutsString = JSON.stringify(layouts)
         .replace(/{{themeColor}}/g, themeColor)
         .replace(/{{fontColor}}/g, fontColor)
         .replace(/{{fontName}}/g, fontName)
         .replace(/{{backgroundColor}}/g, backgroundColor)
         .replace(/{{subColor}}/g, subColor)
-      
+
       return JSON.parse(layoutsString)
-    },
+    }
   },
 
   actions: {
     setTitle(title: string) {
-      if (!title) this.title = '未命名演示文稿'
+      if (!title) this.title = "未命名演示文稿"
       else this.title = title
     },
 
     setTheme(themeProps: Partial<SlideTheme>) {
       this.theme = { ...this.theme, ...themeProps }
     },
-  
+
     setViewportSize(size: number) {
       this.viewportSize = size
     },
-  
+
     setViewportRatio(viewportRatio: number) {
       this.viewportRatio = viewportRatio
     },
-  
+
     setSlides(slides: Slide[]) {
       this.slides = slides
     },
-  
+
     addSlide(slide: Slide | Slide[]) {
       const slides = Array.isArray(slide) ? slide : [slide]
       for (const slide of slides) {
@@ -140,12 +133,12 @@ export const useSlidesStore = defineStore('slides', {
       this.slides.splice(addIndex, 0, ...slides)
       this.slideIndex = addIndex
     },
-  
+
     updateSlide(props: Partial<Slide>, slideId?: string) {
       const slideIndex = slideId ? this.slides.findIndex(item => item.id === slideId) : this.slideIndex
       this.slides[slideIndex] = { ...this.slides[slideIndex], ...props }
     },
-  
+
     removeSlideProps(data: RemovePropData) {
       const { id, propName } = data
 
@@ -154,11 +147,11 @@ export const useSlidesStore = defineStore('slides', {
       }) as Slide[]
       this.slides = slides
     },
-  
+
     deleteSlide(slideId: string | string[]) {
       const slidesId = Array.isArray(slideId) ? slideId : [slideId]
       const slides: Slide[] = JSON.parse(JSON.stringify(this.slides))
-  
+
       const deleteSlidesIndex = []
       for (const deletedId of slidesId) {
         const index = slides.findIndex(item => item.id === deletedId)
@@ -176,18 +169,18 @@ export const useSlidesStore = defineStore('slides', {
         slides.splice(index, 1)
       }
       let newIndex = Math.min(...deleteSlidesIndex)
-  
+
       const maxIndex = slides.length - 1
       if (newIndex > maxIndex) newIndex = maxIndex
-  
+
       this.slideIndex = newIndex
       this.slides = slides
     },
-  
+
     updateSlideIndex(index: number) {
       this.slideIndex = index
     },
-  
+
     addElement(element: PPTElement | PPTElement[]) {
       const elements = Array.isArray(element) ? element : [element]
       const currentSlideEls = this.slides[this.slideIndex].elements
@@ -201,29 +194,29 @@ export const useSlidesStore = defineStore('slides', {
       const newEls = currentSlideEls.filter(item => !elementIdList.includes(item.id))
       this.slides[this.slideIndex].elements = newEls
     },
-  
+
     updateElement(data: UpdateElementData) {
       const { id, props, slideId } = data
-      const elIdList = typeof id === 'string' ? [id] : id
+      const elIdList = typeof id === "string" ? [id] : id
 
       const slideIndex = slideId ? this.slides.findIndex(item => item.id === slideId) : this.slideIndex
       const slide = this.slides[slideIndex]
       const elements = slide.elements.map(el => {
         return elIdList.includes(el.id) ? { ...el, ...props } : el
       })
-      this.slides[slideIndex].elements = (elements as PPTElement[])
+      this.slides[slideIndex].elements = elements as PPTElement[]
     },
-  
+
     removeElementProps(data: RemovePropData) {
       const { id, propName } = data
-      const propsNames = typeof propName === 'string' ? [propName] : propName
-  
+      const propsNames = typeof propName === "string" ? [propName] : propName
+
       const slideIndex = this.slideIndex
       const slide = this.slides[slideIndex]
       const elements = slide.elements.map(el => {
         return el.id === id ? omit(el, propsNames) : el
       })
-      this.slides[slideIndex].elements = (elements as PPTElement[])
-    },
-  },
-})
+      this.slides[slideIndex].elements = elements as PPTElement[]
+    }
+  }
+})

+ 34 - 34
src/store/snapshot.ts

@@ -1,19 +1,19 @@
-import { defineStore } from 'pinia'
-import type { IndexableTypeArray } from 'dexie'
-import { db, type Snapshot } from '@/utils/database'
+import { defineStore } from "pinia"
+import type { IndexableTypeArray } from "dexie"
+import { db, type Snapshot } from "@/utils/database"
 
-import { useSlidesStore } from './slides'
-import { useMainStore } from './main'
+import { useSlidesStore } from "./slides"
+import { useMainStore } from "./main"
 
 export interface ScreenState {
   snapshotCursor: number
   snapshotLength: number
 }
 
-export const useSnapshotStore = defineStore('snapshot', {
+export const useSnapshotStore = defineStore("snapshot", {
   state: (): ScreenState => ({
     snapshotCursor: -1, // 历史快照指针
-    snapshotLength: 0, // 历史快照长度
+    snapshotLength: 0 // 历史快照长度
   }),
 
   getters: {
@@ -22,7 +22,7 @@ export const useSnapshotStore = defineStore('snapshot', {
     },
     canRedo(state) {
       return state.snapshotCursor < state.snapshotLength - 1
-    },
+    }
   },
 
   actions: {
@@ -35,96 +35,96 @@ export const useSnapshotStore = defineStore('snapshot', {
 
     async initSnapshotDatabase() {
       const slidesStore = useSlidesStore()
-  
+
       const newFirstSnapshot = {
         index: slidesStore.slideIndex,
-        slides: slidesStore.slides,
+        slides: slidesStore.slides
       }
       await db.snapshots.add(newFirstSnapshot)
       this.setSnapshotCursor(0)
       this.setSnapshotLength(1)
     },
-  
+
     async addSnapshot() {
       const slidesStore = useSlidesStore()
 
       // 获取当前indexeddb中全部快照的ID
-      const allKeys = await db.snapshots.orderBy('id').keys()
-  
+      const allKeys = await db.snapshots.orderBy("id").keys()
+
       let needDeleteKeys: IndexableTypeArray = []
-  
+
       // 记录需要删除的快照ID
       // 若当前快照指针不处在最后一位,那么再添加快照时,应该将当前指针位置后面的快照全部删除,对应的实际情况是:
       // 用户撤回多次后,再进行操作(添加快照),此时原先被撤销的快照都应该被删除
       if (this.snapshotCursor >= 0 && this.snapshotCursor < allKeys.length - 1) {
         needDeleteKeys = allKeys.slice(this.snapshotCursor + 1)
       }
-  
+
       // 添加新快照
       const snapshot = {
         index: slidesStore.slideIndex,
-        slides: slidesStore.slides,
+        slides: slidesStore.slides
       }
       await db.snapshots.add(snapshot)
-  
+
       // 计算当前快照长度,用于设置快照指针的位置(此时指针应该处在最后一位,即:快照长度 - 1)
       let snapshotLength = allKeys.length - needDeleteKeys.length + 1
-  
+
       // 快照数量超过长度限制时,应该将头部多余的快照删除
       const snapshotLengthLimit = 20
       if (snapshotLength > snapshotLengthLimit) {
         needDeleteKeys.push(allKeys[0])
         snapshotLength--
       }
-  
+
       // 快照数大于1时,需要保证撤回操作后维持页面焦点不变:也就是将倒数第二个快照对应的索引设置为当前页的索引
       // https://github.com/pipipi-pikachu/PPTist/issues/27
       if (snapshotLength >= 2) {
         db.snapshots.update(allKeys[snapshotLength - 2] as number, { index: slidesStore.slideIndex })
       }
-  
+
       await db.snapshots.bulkDelete(needDeleteKeys)
-  
+
       this.setSnapshotCursor(snapshotLength - 1)
       this.setSnapshotLength(snapshotLength)
     },
-  
+
     async unDo() {
       if (this.snapshotCursor <= 0) return
 
       const slidesStore = useSlidesStore()
       const mainStore = useMainStore()
-  
+
       const snapshotCursor = this.snapshotCursor - 1
-      const snapshots: Snapshot[] = await db.snapshots.orderBy('id').toArray()
+      const snapshots: Snapshot[] = await db.snapshots.orderBy("id").toArray()
       const snapshot = snapshots[snapshotCursor]
       const { index, slides } = snapshot
-  
+
       const slideIndex = index > slides.length - 1 ? slides.length - 1 : index
-  
+
       slidesStore.setSlides(slides)
       slidesStore.updateSlideIndex(slideIndex)
       this.setSnapshotCursor(snapshotCursor)
       mainStore.setActiveElementIdList([])
     },
-  
+
     async reDo() {
       if (this.snapshotCursor >= this.snapshotLength - 1) return
 
       const slidesStore = useSlidesStore()
       const mainStore = useMainStore()
-  
+
       const snapshotCursor = this.snapshotCursor + 1
-      const snapshots: Snapshot[] = await db.snapshots.orderBy('id').toArray()
+      const snapshots: Snapshot[] = await db.snapshots.orderBy("id").toArray()
       const snapshot = snapshots[snapshotCursor]
       const { index, slides } = snapshot
-  
+
       const slideIndex = index > slides.length - 1 ? slides.length - 1 : index
-  
+
       slidesStore.setSlides(slides)
       slidesStore.updateSlideIndex(slideIndex)
       this.setSnapshotCursor(snapshotCursor)
       mainStore.setActiveElementIdList([])
-    },
-  },
-})
+    }
+  }
+})

+ 3 - 0
src/types/slides.ts

@@ -613,10 +613,13 @@ export interface PPTAudioElement extends PPTBaseElement {
  * type: 元素类型(cloudCoach)
  *
  * url: 云练习地址
+ *
+ * isMove: 当前元素 是否在移动
  */
 export interface PPTCloudCoachElement extends PPTBaseElement {
   type: "cloudCoach"
   url: string
+  isMove: boolean
 }
 
 export type PPTElement =

+ 58 - 59
src/views/Editor/Canvas/hooks/useDragElement.ts

@@ -1,16 +1,12 @@
-import type { Ref } from 'vue'
-import { storeToRefs } from 'pinia'
-import { useMainStore, useSlidesStore, useKeyboardStore } from '@/store'
-import type { PPTElement } from '@/types/slides'
-import type { AlignmentLineProps } from '@/types/edit'
-import { getRectRotatedRange, uniqAlignLines, type AlignLine } from '@/utils/element'
-import useHistorySnapshot from '@/hooks/useHistorySnapshot'
-
-export default (
-  elementList: Ref<PPTElement[]>,
-  alignmentLines: Ref<AlignmentLineProps[]>,
-  canvasScale: Ref<number>,
-) => {
+import type { Ref } from "vue"
+import { storeToRefs } from "pinia"
+import { useMainStore, useSlidesStore, useKeyboardStore } from "@/store"
+import type { PPTElement } from "@/types/slides"
+import type { AlignmentLineProps } from "@/types/edit"
+import { getRectRotatedRange, uniqAlignLines, type AlignLine } from "@/utils/element"
+import useHistorySnapshot from "@/hooks/useHistorySnapshot"
+
+export default (elementList: Ref<PPTElement[]>, alignmentLines: Ref<AlignmentLineProps[]>, canvasScale: Ref<number>) => {
   const slidesStore = useSlidesStore()
   const { activeElementIdList, activeGroupElementId } = storeToRefs(useMainStore())
   const { shiftKeyState } = storeToRefs(useKeyboardStore())
@@ -24,21 +20,27 @@ export default (
 
     if (!activeElementIdList.value.includes(element.id)) return
     let isMouseDown = true
+    /* 选中移动的时候 云教练模块标记移动中 */
+    elementList.value.map(item => {
+      if (activeElementIdList.value.includes(item.id) && item.type === "cloudCoach") {
+        item.isMove = true
+      }
+    })
 
     const edgeWidth = viewportSize.value
     const edgeHeight = viewportSize.value * viewportRatio.value
-    
+
     const sorptionRange = 5
 
     const originElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList.value))
     const originActiveElementList = originElementList.filter(el => activeElementIdList.value.includes(el.id))
-  
+
     const elOriginLeft = element.left
     const elOriginTop = element.top
     const elOriginWidth = element.width
-    const elOriginHeight = ('height' in element && element.height) ? element.height : 0
-    const elOriginRotate = ('rotate' in element && element.rotate) ? element.rotate : 0
-  
+    const elOriginHeight = "height" in element && element.height ? element.height : 0
+    const elOriginRotate = "rotate" in element && element.rotate ? element.rotate : 0
+
     const startPageX = isTouchEvent ? e.changedTouches[0].pageX : e.pageX
     const startPageY = isTouchEvent ? e.changedTouches[0].pageY : e.pageY
 
@@ -53,31 +55,30 @@ export default (
     let verticalLines: AlignLine[] = []
 
     for (const el of elementList.value) {
-      if (el.type === 'line') continue
+      if (el.type === "line") continue
       if (isActiveGroupElement && el.id === element.id) continue
       if (!isActiveGroupElement && activeElementIdList.value.includes(el.id)) continue
 
       let left, top, width, height
-      if ('rotate' in el && el.rotate) {
+      if ("rotate" in el && el.rotate) {
         const { xRange, yRange } = getRectRotatedRange({
           left: el.left,
           top: el.top,
           width: el.width,
           height: el.height,
-          rotate: el.rotate,
+          rotate: el.rotate
         })
         left = xRange[0]
         top = yRange[0]
         width = xRange[1] - xRange[0]
         height = yRange[1] - yRange[0]
-      }
-      else {
+      } else {
         left = el.left
         top = el.top
         width = el.width
         height = el.height
       }
-      
+
       const right = left + width
       const bottom = top + height
       const centerX = top + height / 2
@@ -104,7 +105,7 @@ export default (
 
     horizontalLines.push(edgeTopLine, edgeBottomLine, edgeHorizontalCenterLine)
     verticalLines.push(edgeLeftLine, edgeRightLine, edgeVerticalCenterLine)
-    
+
     // 对齐吸附线去重
     horizontalLines = uniqAlignLines(horizontalLines)
     verticalLines = uniqAlignLines(verticalLines)
@@ -118,11 +119,10 @@ export default (
       // 如果误操作标记为true,表示当前还处在误操作范围内,但仍然需要继续计算检查后续操作是否还处于误操作
       // 如果误操作标记为false,表示已经脱离了误操作范围,不需要再次计算
       if (isMisoperation !== false) {
-        isMisoperation = Math.abs(startPageX - currentPageX) < sorptionRange && 
-                         Math.abs(startPageY - currentPageY) < sorptionRange
+        isMisoperation = Math.abs(startPageX - currentPageX) < sorptionRange && Math.abs(startPageY - currentPageY) < sorptionRange
       }
       if (!isMouseDown || isMisoperation) return
-      
+
       let moveX = (currentPageX - startPageX) / canvasScale.value
       let moveY = (currentPageY - startPageY) / canvasScale.value
 
@@ -146,54 +146,49 @@ export default (
             top: targetTop,
             width: elOriginWidth,
             height: elOriginHeight,
-            rotate: elOriginRotate,
+            rotate: elOriginRotate
           })
           targetMinX = xRange[0]
           targetMaxX = xRange[1]
           targetMinY = yRange[0]
           targetMaxY = yRange[1]
-        }
-        else if (element.type === 'line') {
+        } else if (element.type === "line") {
           targetMinX = targetLeft
           targetMaxX = targetLeft + Math.max(element.start[0], element.end[0])
           targetMinY = targetTop
           targetMaxY = targetTop + Math.max(element.start[1], element.end[1])
-        }
-        else {
+        } else {
           targetMinX = targetLeft
           targetMaxX = targetLeft + elOriginWidth
           targetMinY = targetTop
           targetMaxY = targetTop + elOriginHeight
         }
-      }
-      else {
+      } else {
         const leftValues = []
         const topValues = []
         const rightValues = []
         const bottomValues = []
-        
+
         for (let i = 0; i < originActiveElementList.length; i++) {
           const element = originActiveElementList[i]
           const left = element.left + moveX
           const top = element.top + moveY
           const width = element.width
-          const height = ('height' in element && element.height) ? element.height : 0
-          const rotate = ('rotate' in element && element.rotate) ? element.rotate : 0
+          const height = "height" in element && element.height ? element.height : 0
+          const rotate = "rotate" in element && element.rotate ? element.rotate : 0
 
-          if ('rotate' in element && element.rotate) {
+          if ("rotate" in element && element.rotate) {
             const { xRange, yRange } = getRectRotatedRange({ left, top, width, height, rotate })
             leftValues.push(xRange[0])
             topValues.push(yRange[0])
             rightValues.push(xRange[1])
             bottomValues.push(yRange[1])
-          }
-          else if (element.type === 'line') {
+          } else if (element.type === "line") {
             leftValues.push(left)
             topValues.push(top)
             rightValues.push(left + Math.max(element.start[0], element.end[0]))
             bottomValues.push(top + Math.max(element.start[1], element.end[1]))
-          }
-          else {
+          } else {
             leftValues.push(left)
             topValues.push(top)
             rightValues.push(left + width)
@@ -206,7 +201,7 @@ export default (
         targetMinY = Math.min(...topValues)
         targetMaxY = Math.max(...bottomValues)
       }
-      
+
       const targetCenterX = targetMinX + (targetMaxX - targetMinX) / 2
       const targetCenterY = targetMinY + (targetMaxY - targetMinY) / 2
 
@@ -219,21 +214,21 @@ export default (
         const { value, range } = horizontalLines[i]
         const min = Math.min(...range, targetMinX, targetMaxX)
         const max = Math.max(...range, targetMinX, targetMaxX)
-        
+
         if (Math.abs(targetMinY - value) < sorptionRange && !isHorizontalAdsorbed) {
           targetTop = targetTop - (targetMinY - value)
           isHorizontalAdsorbed = true
-          _alignmentLines.push({type: 'horizontal', axis: {x: min - 50, y: value}, length: max - min + 100})
+          _alignmentLines.push({ type: "horizontal", axis: { x: min - 50, y: value }, length: max - min + 100 })
         }
         if (Math.abs(targetMaxY - value) < sorptionRange && !isHorizontalAdsorbed) {
           targetTop = targetTop - (targetMaxY - value)
           isHorizontalAdsorbed = true
-          _alignmentLines.push({type: 'horizontal', axis: {x: min - 50, y: value}, length: max - min + 100})
+          _alignmentLines.push({ type: "horizontal", axis: { x: min - 50, y: value }, length: max - min + 100 })
         }
         if (Math.abs(targetCenterY - value) < sorptionRange && !isHorizontalAdsorbed) {
           targetTop = targetTop - (targetCenterY - value)
           isHorizontalAdsorbed = true
-          _alignmentLines.push({type: 'horizontal', axis: {x: min - 50, y: value}, length: max - min + 100})
+          _alignmentLines.push({ type: "horizontal", axis: { x: min - 50, y: value }, length: max - min + 100 })
         }
       }
       for (let i = 0; i < verticalLines.length; i++) {
@@ -244,21 +239,21 @@ export default (
         if (Math.abs(targetMinX - value) < sorptionRange && !isVerticalAdsorbed) {
           targetLeft = targetLeft - (targetMinX - value)
           isVerticalAdsorbed = true
-          _alignmentLines.push({type: 'vertical', axis: {x: value, y: min - 50}, length: max - min + 100})
+          _alignmentLines.push({ type: "vertical", axis: { x: value, y: min - 50 }, length: max - min + 100 })
         }
         if (Math.abs(targetMaxX - value) < sorptionRange && !isVerticalAdsorbed) {
           targetLeft = targetLeft - (targetMaxX - value)
           isVerticalAdsorbed = true
-          _alignmentLines.push({type: 'vertical', axis: {x: value, y: min - 50}, length: max - min + 100})
+          _alignmentLines.push({ type: "vertical", axis: { x: value, y: min - 50 }, length: max - min + 100 })
         }
         if (Math.abs(targetCenterX - value) < sorptionRange && !isVerticalAdsorbed) {
           targetLeft = targetLeft - (targetCenterX - value)
           isVerticalAdsorbed = true
-          _alignmentLines.push({type: 'vertical', axis: {x: value, y: min - 50}, length: max - min + 100})
+          _alignmentLines.push({ type: "vertical", axis: { x: value, y: min - 50 }, length: max - min + 100 })
         }
       }
       alignmentLines.value = _alignmentLines
-      
+
       // 单选状态下,或者当前选中的多个元素中存在正在操作的元素时,仅修改正在操作的元素的位置
       if (activeElementIdList.value.length === 1 || isActiveGroupElement) {
         elementList.value = elementList.value.map(el => {
@@ -278,13 +273,13 @@ export default (
               return {
                 ...el,
                 left: targetLeft,
-                top: targetTop,
+                top: targetTop
               }
             }
             return {
               ...el,
               left: el.left + (targetLeft - handleElement.left),
-              top: el.top + (targetTop - handleElement.top),
+              top: el.top + (targetTop - handleElement.top)
             }
           }
           return el
@@ -294,7 +289,12 @@ export default (
 
     const handleMouseup = (e: MouseEvent | TouchEvent) => {
       isMouseDown = false
-      
+      /* 选中取消移动的时候 云教练模块取消标记移动中 */
+      elementList.value.map(item => {
+        if (activeElementIdList.value.includes(item.id) && item.type === "cloudCoach") {
+          item.isMove = false
+        }
+      })
       document.ontouchmove = null
       document.ontouchend = null
       document.onmousemove = null
@@ -314,14 +314,13 @@ export default (
     if (isTouchEvent) {
       document.ontouchmove = handleMousemove
       document.ontouchend = handleMouseup
-    }
-    else {
+    } else {
       document.onmousemove = handleMousemove
       document.onmouseup = handleMouseup
     }
   }
 
   return {
-    dragElement,
+    dragElement
   }
-}
+}

+ 19 - 1
src/views/components/element/cloudCoachElement/cloudCoachElement.vue

@@ -16,6 +16,12 @@
         @mousedown="$event => handleSelectElement($event)"
         @touchstart="$event => handleSelectElement($event)"
       >
+        <div
+          v-if="isShowMask"
+          @mousedown.stop="$event => handleSelectElement($event, false)"
+          @touchstart.stop="$event => handleSelectElement($event, false)"
+          class="mask"
+        ></div>
         <cloudCoachPlayer :url="elementInfo.url" :width="elementInfo.width" :height="elementInfo.height" :scale="canvasScale" />
         <div
           :class="['handler-border', item]"
@@ -35,6 +41,7 @@ import { useMainStore } from "@/store"
 import type { PPTCloudCoachElement } from "@/types/slides"
 import type { ContextmenuItem } from "@/components/Contextmenu/types"
 import cloudCoachPlayer from "./cloudCoachPlayer"
+import { computed } from "vue"
 
 const props = defineProps<{
   elementInfo: PPTCloudCoachElement
@@ -42,8 +49,13 @@ const props = defineProps<{
   contextmenus: () => ContextmenuItem[] | null
 }>()
 
-const { canvasScale } = storeToRefs(useMainStore())
+const mainStore = useMainStore()
+const { canvasScale } = storeToRefs(mainStore)
 
+/* 当没有选中 或者 拖动过程中加一个遮罩,以免事件进入 iframe 无法触发*/
+const isShowMask = computed(() => {
+  return mainStore.handleElementId !== props.elementInfo.id || props.elementInfo.isMove
+})
 const handleSelectElement = (e: MouseEvent | TouchEvent, canMove = true) => {
   if (props.elementInfo.lock) return
   e.stopPropagation()
@@ -66,6 +78,12 @@ const handleSelectElement = (e: MouseEvent | TouchEvent, canMove = true) => {
 .element-content {
   width: 100%;
   height: 100%;
+  position: relative;
+  .mask {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+  }
 }
 .handler-border {
   position: absolute;