pipipi-pikachu 5 年之前
父节点
当前提交
7f301cadfd

+ 0 - 1
src/configs/keyCode.ts

@@ -1,5 +1,4 @@
 export enum KEYCODE {
-  S = 83,
   C = 67,
   X = 88,
   Z = 90,

+ 0 - 1
src/store/constants.ts

@@ -9,7 +9,6 @@ export enum MutationTypes {
   SET_EDITORAREA_FOCUS = 'setEditorAreaFocus',
   SET_DISABLE_HOTKEYS_STATE = 'setDisableHotkeysState',
   SET_AVAILABLE_FONTS = 'setAvailableFonts',
-  SET_SAVE_STATE = 'setSaveState',
 
   // slides
   SET_SLIDES = 'setSlides',

+ 1 - 6
src/store/mutations.ts

@@ -1,5 +1,5 @@
 import { MutationTypes } from './constants'
-import { State, SaveState } from './state'
+import { State } from './state'
 import { Slide, PPTElement } from '@/types/slides'
 import { FONT_NAMES } from '@/configs/fontName'
 import { isSupportFontFamily } from '@/utils/index'
@@ -36,7 +36,6 @@ export type Mutations = {
   [MutationTypes.SET_EDITORAREA_FOCUS](state: State, isFocus: boolean): void;
   [MutationTypes.SET_DISABLE_HOTKEYS_STATE](state: State, disable: boolean): void;
   [MutationTypes.SET_AVAILABLE_FONTS](state: State): void;
-  [MutationTypes.SET_SAVE_STATE](state: State, saveState: SaveState ): void;
   [MutationTypes.SET_SLIDES](state: State, slides: Slide[]): void;
   [MutationTypes.ADD_SLIDES](state: State, data: AddSlidesData): void;
   [MutationTypes.SET_SLIDE](state: State, data: SetSlideData): void;
@@ -91,10 +90,6 @@ export const mutations: Mutations = {
     state.availableFonts = FONT_NAMES.filter(font => isSupportFontFamily(font.en))
   },
 
-  [MutationTypes.SET_SAVE_STATE](state, saveState) {
-    state.saveState = saveState
-  },
-
   // slides
 
   [MutationTypes.SET_SLIDES](state, slides) {

+ 0 - 4
src/store/state.ts

@@ -2,8 +2,6 @@ import { Slide } from '@/types/slides'
 import { slides } from '@/mocks/index'
 import { FontName } from '@/configs/fontName'
 
-export type SaveState = 'complete' | 'pending'
-
 export type State = {
   activeElementIdList: string[];
   handleElementId: string;
@@ -13,7 +11,6 @@ export type State = {
   editorAreaFocus: boolean;
   disableHotkeys: boolean;
   availableFonts: FontName[];
-  saveState: SaveState;
   slides: Slide[];
   slideIndex: number;
   cursor: number;
@@ -29,7 +26,6 @@ export const state: State = {
   editorAreaFocus: false,
   disableHotkeys: false,
   availableFonts: [],
-  saveState: 'complete',
   slides: slides,
   slideIndex: 0,
   cursor: -1,

+ 28 - 0
src/types/edit.ts

@@ -0,0 +1,28 @@
+export type ElementOrderCommand = 'up' | 'down' | 'top' | 'bottom'
+
+export enum ElementOrderCommands {
+  UP = 'up',
+  DOWN = 'down',
+  TOP = 'top',
+  BOTTOM = 'bottom',
+}
+
+export type ElementAlignCommand = 'top'| 'bottom' | 'left' | 'right' | 'vertical' | 'horizontal'
+
+export enum ElementAlignCommands {
+  TOP = 'top',
+  BOTTOM = 'bottom',
+  LEFT = 'left',
+  RIGHT = 'right',
+  VERTICAL = 'vertical',
+  HORIZONTAL = 'horizontal',
+}
+
+export type ElementScaleHandler = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
+
+export type ElementLockCommand = 'lock' | 'unlock'
+
+export enum ElementLockCommands {
+  LOCK = 'lock',
+  UNLOCK = 'unlock',
+}

+ 6 - 36
src/types/slides.ts

@@ -19,7 +19,6 @@ export interface PPTElementBorderProps {
 
 export interface PPTTextElement extends PPTElementBaseProps, PPTElementSizeProps, PPTElementBorderProps {
   type: 'text';
-  textType: string;
   content: string;
   rotate?: number;
   fill?: string;
@@ -28,20 +27,18 @@ export interface PPTTextElement extends PPTElementBaseProps, PPTElementSizeProps
   segmentSpacing?: number;
   letterSpacing?: number;
   shadow?: string;
-  padding?: number;
 }
 
-interface ImageClip {
-  range: [[number, number], [number, number]];
-  shape: string;
-}
 export interface PPTImageElement extends PPTElementBaseProps, PPTElementSizeProps, PPTElementBorderProps {
   type: 'image';
   lockRatio: boolean;
   imgUrl: string;
   rotate?: number;
   filter?: string;
-  clip?: ImageClip;
+  clip?: {
+    range: [[number, number], [number, number]];
+    shape: string;
+  };
   flip?: string;
   shadow?: string;
 }
@@ -58,15 +55,6 @@ export interface PPTShapeElement extends PPTElementBaseProps, PPTElementSizeProp
   textAlign?: string;
 }
 
-export interface PPTIconElement extends PPTElementBaseProps, PPTElementSizeProps {
-  type: 'icon';
-  color: string;
-  lockRatio: boolean;
-  svgCode: string;
-  rotate?: number;
-  shadow?: string;
-}
-
 export interface PPTLineElement extends PPTElementBaseProps {
   type: 'line';
   start: [number, number];
@@ -78,23 +66,11 @@ export interface PPTLineElement extends PPTElementBaseProps {
   lineType: string;
 }
 
-export interface BarChartSeries {
-  name: string;
-  data: number[];
-}
-export interface BarChartData {
-  axisData: string[];
-  series: BarChartSeries[];
-}
-export interface PieChartData {
-  name: string;
-  value: number
-}
 export interface PPTChartElement extends PPTElementBaseProps, PPTElementSizeProps, PPTElementBorderProps {
   type: 'chart';
   chartType: string;
   theme: string;
-  data: PieChartData[] | BarChartData;
+  data: Object;
 }
 
 export interface TableCell {
@@ -111,19 +87,13 @@ export interface PPTTableElement extends PPTElementBaseProps, PPTElementSizeProp
   colSizes: number[];
   data: TableCell[][];
 }
-export interface PPTIframeElement extends PPTElementBaseProps, PPTElementSizeProps, PPTElementBorderProps {
-  type: 'iframe';
-  src: string;
-}
 
 export type PPTElement = PPTTextElement | 
                          PPTImageElement | 
                          PPTShapeElement | 
-                         PPTIconElement | 
                          PPTLineElement | 
                          PPTChartElement |
-                         PPTTableElement |
-                         PPTIframeElement
+                         PPTTableElement
 
 export interface PPTAnimation {
   elId: string;

+ 11 - 0
src/utils/index.ts

@@ -169,6 +169,17 @@ export const copyText = (text: string) => {
   })
 }
 
+// 读取剪贴板
+export const readClipboard = () => {
+  if(navigator.clipboard) {
+    navigator.clipboard.readText().then(text => {
+      if(!text) return { err: '剪贴板为空或者不包含文本' }
+      return { text }
+    })
+  }
+  return { err: '浏览器不支持或禁止访问剪贴板' }
+}
+
 // 加密函数
 export const encrypt = (msg: string) => {
   return CryptoJS.AES.encrypt(msg, CRYPTO_KEY).toString()

+ 44 - 0
src/views/Editor/Canvas/utils/alignToCanvas.ts

@@ -0,0 +1,44 @@
+import { PPTElement } from '@/types/slides'
+import { ElementAlignCommand, ElementAlignCommands } from '@/types/edit'
+import { getElementListRange } from './elementRange'
+import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
+
+// 将元素对齐到屏幕
+export const alignElement = (elementList: PPTElement[], activeElementList: PPTElement[], activeElementIdList: string[], command: ElementAlignCommand) => {
+  const viewportWidth = VIEWPORT_SIZE
+  const viewportHeight = VIEWPORT_SIZE * VIEWPORT_ASPECT_RATIO
+  const { minX, maxX, minY, maxY } = getElementListRange(activeElementList)
+
+  const copyOfElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList))
+  for(const element of copyOfElementList) {
+    if(!activeElementIdList.includes(element.elId)) continue
+    
+    if(command === ElementAlignCommands.TOP) {
+      const offsetY = minY - 0
+      element.top = element.top - offsetY            
+    }
+    else if(command === ElementAlignCommands.VERTICAL) {
+      const offsetY = minY + (maxY - minY) / 2 - viewportHeight / 2
+      element.top = element.top - offsetY            
+    }
+    else if(command === ElementAlignCommands.BOTTOM) {
+      const offsetY = maxY - viewportHeight
+      element.top = element.top - offsetY       
+    }
+    
+    else if(command === ElementAlignCommands.LEFT) {
+      const offsetX = minX - 0
+      element.left = element.left - offsetX            
+    }
+    else if(command === ElementAlignCommands.HORIZONTAL) {
+      const offsetX = minX + (maxX - minX) / 2 - viewportWidth / 2
+      element.left = element.left - offsetX            
+    }
+    else if(command === ElementAlignCommands.RIGHT) {
+      const offsetX = maxX - viewportWidth
+      element.left = element.left - offsetX            
+    }
+  }
+  
+  return copyOfElementList
+}

+ 41 - 0
src/views/Editor/Canvas/utils/elementCombine.ts

@@ -0,0 +1,41 @@
+import { createRandomCode } from '@/utils/index'
+import { PPTElement } from '@/types/slides'
+
+// 组合元素(为当前所有激活元素添加一个相同的groupId)
+export const combineElements = (elementList: PPTElement[], activeElementList: PPTElement[], activeElementIdList: string[]) => {
+  if(!activeElementList.length) return null
+
+  let copyOfElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList))
+  const groupId = createRandomCode()
+
+  const combineElementList: PPTElement[] = []
+  for(const element of copyOfElementList) {
+    if(activeElementIdList.includes(element.elId)) {
+      element.groupId = groupId
+      combineElementList.push(element)
+    }
+  }
+
+  // 注意,组合元素的层级应该是连续的,所以需要获取该组元素中最顶层的元素,将组内其他成员从原位置移动到最顶层的元素的下面
+  const combineElementMaxIndex = copyOfElementList.findIndex(_element => _element.elId === combineElementList[combineElementList.length - 1].elId)
+  const combineElementIdList = combineElementList.map(_element => _element.elId)
+  copyOfElementList = copyOfElementList.filter(_element => !combineElementIdList.includes(_element.elId))
+
+  const insertIndex = combineElementMaxIndex - combineElementList.length + 1
+  copyOfElementList.splice(insertIndex, 0, ...combineElementList)
+
+  return copyOfElementList
+}
+
+// 取消组合元素(移除所有被激活元素的groupId)
+export const uncombineElements = (elementList: PPTElement[], activeElementList: PPTElement[], activeElementIdList: string[]) => {
+  if(!activeElementList.length) return null
+  const hasElementInGroup = activeElementList.some(item => item.groupId)
+  if(!hasElementInGroup) return null
+  
+  const copyOfElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList))
+  for(const element of copyOfElementList) {
+    if(activeElementIdList.includes(element.elId) && element.groupId) delete element.groupId
+  }
+  return copyOfElementList
+}

+ 34 - 0
src/views/Editor/Canvas/utils/elementLock.ts

@@ -0,0 +1,34 @@
+import { PPTElement } from '@/types/slides'
+import { ElementLockCommand, ElementLockCommands } from '@/types/edit'
+
+const lock = (copyOfElementList: PPTElement[], handleElement: PPTElement, activeElementIdList: string[]) => {
+  for(const element of copyOfElementList) {
+    if(activeElementIdList.includes(handleElement.elId)) element.isLock = true
+  }
+  return copyOfElementList
+}
+
+const unlock = (copyOfElementList: PPTElement[], handleElement: PPTElement) => {
+  if(handleElement.groupId) {
+    for(const element of copyOfElementList) {
+      if(element.groupId === handleElement.groupId) element.isLock = false
+    }
+    return copyOfElementList
+  }
+  
+  for(const element of copyOfElementList) {
+    if(element.elId === handleElement.elId) {
+      element.isLock = false
+      break
+    }
+  }
+  return copyOfElementList
+}
+
+// 锁定&解锁 元素
+export const lockElement = (elementList: PPTElement[], handleElement: PPTElement, activeElementIdList: string[], command: ElementLockCommand) => {
+  const copyOfElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList))
+
+  if(command === ElementLockCommands.LOCK) return lock(copyOfElementList, handleElement, activeElementIdList)
+  return unlock(copyOfElementList, handleElement)
+}

+ 170 - 0
src/views/Editor/Canvas/utils/elementOrder.ts

@@ -0,0 +1,170 @@
+import { PPTElement } from '@/types/slides'
+import { ElementOrderCommand, ElementOrderCommands } from '@/types/edit'
+
+// 获取组合元素层级范围(组合成员中的最大层级和最小层级)
+const getCombineElementIndexRange = (elementList: PPTElement[], combineElementList: PPTElement[]) => {
+  const minIndex = elementList.findIndex(_element => _element.elId === combineElementList[0].elId)
+  const maxIndex = elementList.findIndex(_element => _element.elId === combineElementList[combineElementList.length - 1].elId)
+  return { minIndex, maxIndex }
+}
+
+// 上移一层,返回移动后新的元素列表(下移一层逻辑类似)
+const moveUpElement = (elementList: PPTElement[], element: PPTElement) => {
+  const copyOfElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList))
+
+  // 被操作的元素是组合元素成员
+  if(element.groupId) {
+
+    // 获取该组合元素全部成员,以及组合元素层级范围
+    const combineElementList = copyOfElementList.filter(_element => _element.groupId === element.groupId)
+    const { minIndex, maxIndex } = getCombineElementIndexRange(elementList, combineElementList)
+
+    // 无法移动(已经处在顶层)
+    if(maxIndex === elementList.length - 1) return null
+
+    // 该组合元素上一层的元素,以下简称为【元素next】
+    const nextElement = copyOfElementList[maxIndex + 1]
+
+    // 从元素列表中移除该组合元素全部成员
+    const movedElementList = copyOfElementList.splice(minIndex, combineElementList.length)
+
+    // 如果【元素next】也是组合元素成员(另一个组合,不是上面被移除的那一组,以下简称为【组合next】)
+    // 需要获取【组合next】全部成员的长度,将上面移除的组合元素全部成员添加到【组合next】全部成员的上方
+    if(nextElement.groupId) {
+      const nextCombineElementList = copyOfElementList.filter(_element => _element.groupId === nextElement.groupId)
+      copyOfElementList.splice(minIndex + nextCombineElementList.length, 0, ...movedElementList)
+    }
+
+    // 如果【元素next】是单独的元素(非组合成员),将上面移除的组合元素全部成员添加到【元素next】上方
+    else copyOfElementList.splice(minIndex + 1, 0, ...movedElementList)
+  }
+
+  // 被操作的元素是单独的元素(非组合成员)
+  else {
+
+    // 元素在元素列表中的层级
+    const elementIndex = elementList.findIndex(item => item.elId === element.elId)
+
+    // 无法移动(已经处在顶层)
+    if(elementIndex === elementList.length - 1) return null
+
+    // 上一层的元素,以下简称为【元素next】
+    const nextElement = copyOfElementList[elementIndex + 1]
+
+    // 从元素列表中移除被操作的元素
+    const movedElement = copyOfElementList.splice(elementIndex, 1)[0]
+
+    // 如果【元素next】是组合元素成员
+    // 需要获取该组合全部成员的长度,将上面移除的元素添加到该组合全部成员的上方
+    if(nextElement.groupId) {
+      const combineElementList = copyOfElementList.filter(_element => _element.groupId === nextElement.groupId)
+      copyOfElementList.splice(elementIndex + combineElementList.length, 0, movedElement)
+    }
+
+    // 如果【元素next】是单独的元素(非组合成员),将上面移除的元素添加到【元素next】上方
+    else copyOfElementList.splice(elementIndex + 1, 0, movedElement)
+  }
+
+  return copyOfElementList
+}
+
+// 下移一层
+const moveDownElement = (elementList: PPTElement[], element: PPTElement) => {
+  const copyOfElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList))
+
+  if(element.groupId) {
+    const combineElementList = copyOfElementList.filter(_element => _element.groupId === element.groupId)
+    const { minIndex } = getCombineElementIndexRange(elementList, combineElementList)
+    if(minIndex === 0) return null
+    const prevElement = copyOfElementList[minIndex - 1]
+    const movedElementList = copyOfElementList.splice(minIndex, combineElementList.length)
+    if(prevElement.groupId) {
+      const prevCombineElementList = copyOfElementList.filter(_element => _element.groupId === prevElement.groupId)
+      copyOfElementList.splice(minIndex - prevCombineElementList.length, 0, ...movedElementList)
+    }
+    else copyOfElementList.splice(minIndex - 1, 0, ...movedElementList)
+  }
+
+  else {
+    const elementIndex = elementList.findIndex(item => item.elId === element.elId)
+    if(elementIndex === 0) return null
+    const prevElement = copyOfElementList[elementIndex - 1]
+    const movedElement = copyOfElementList.splice(elementIndex, 1)[0]
+    if(prevElement.groupId) {
+      const combineElementList = copyOfElementList.filter(_element => _element.groupId === prevElement.groupId)
+      copyOfElementList.splice(elementIndex - combineElementList.length, 0, movedElement)
+    }
+    else copyOfElementList.splice(elementIndex - 1, 0, movedElement)
+  }
+
+  return copyOfElementList
+}
+
+// 置顶层,返回移动后新的元素列表(置底层逻辑类似)
+const moveTopElement = (elementList: PPTElement[], element: PPTElement) => {
+  const copyOfElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList))
+
+  // 被操作的元素是组合元素成员
+  if(element.groupId) {
+
+    // 获取该组合元素全部成员,以及组合元素层级范围
+    const combineElementList = copyOfElementList.filter(_element => _element.groupId === element.groupId)
+    const { minIndex, maxIndex } = getCombineElementIndexRange(elementList, combineElementList)
+
+    // 无法移动(已经处在顶层)
+    if(maxIndex === elementList.length - 1) return null
+
+    // 从元素列表中移除该组合元素全部成员,然后添加到元素列表最上方
+    const movedElementList = copyOfElementList.splice(minIndex, combineElementList.length)
+    copyOfElementList.push(...movedElementList)
+  }
+
+  // 被操作的元素是单独的元素(非组合成员)
+  else {
+
+    // 元素在元素列表中的层级
+    const elementIndex = elementList.findIndex(item => item.elId === element.elId)
+
+    // 无法移动(已经处在顶层)
+    if(elementIndex === elementList.length - 1) return null
+
+    // 从元素列表中移除该元素,然后添加到元素列表最上方
+    copyOfElementList.splice(elementIndex, 1)
+    copyOfElementList.push(element)
+  }
+
+  return copyOfElementList
+}
+
+// 置底层
+const moveBottomElement = (elementList: PPTElement[], element: PPTElement) => {
+  const copyOfElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList))
+
+  if(element.groupId) {
+    const combineElementList = copyOfElementList.filter(_element => _element.groupId === element.groupId)
+    const { minIndex } = getCombineElementIndexRange(elementList, combineElementList)
+    if(minIndex === 0) return null
+    const movedElementList = copyOfElementList.splice(minIndex, combineElementList.length)
+    copyOfElementList.unshift(...movedElementList)
+  }
+
+  else {
+    const elementIndex = elementList.findIndex(item => item.elId === element.elId)
+    if(elementIndex === 0) return null
+    copyOfElementList.splice(elementIndex, 1)
+    copyOfElementList.unshift(element)
+  }
+
+  return copyOfElementList
+}
+
+export const setElementOrder = (elementList: PPTElement[], element: PPTElement, command: ElementOrderCommand) => {
+  let newElementList = null
+  
+  if(command === ElementOrderCommands.UP) newElementList = moveUpElement(elementList, element)
+  else if(command === ElementOrderCommands.DOWN) newElementList = moveDownElement(elementList, element)
+  else if(command === ElementOrderCommands.TOP) newElementList = moveTopElement(elementList, element)
+  else if(command === ElementOrderCommands.BOTTOM) newElementList = moveBottomElement(elementList, element)
+
+  return newElementList
+}

+ 2 - 2
src/views/Editor/Canvas/utils/elementRange.ts

@@ -1,7 +1,7 @@
-import { PPTElement, PPTTextElement, PPTImageElement, PPTShapeElement, PPTIconElement } from '@/types/slides'
+import { PPTElement, PPTTextElement, PPTImageElement, PPTShapeElement } from '@/types/slides'
 
 // 获取矩形旋转后在画布中的位置范围
-export const getRectRotatedRange = (element: PPTTextElement | PPTImageElement | PPTShapeElement | PPTIconElement) => {
+export const getRectRotatedRange = (element: PPTTextElement | PPTImageElement | PPTShapeElement) => {
   const { left, top, width, height, rotate = 0 } = element
 
   const radius = Math.sqrt( Math.pow(width, 2) + Math.pow(height, 2) ) / 2

+ 2 - 2
src/views/Editor/Canvas/utils/elementRotate.ts

@@ -1,4 +1,4 @@
-import { PPTTextElement, PPTImageElement, PPTShapeElement, PPTIconElement } from '@/types/slides'
+import { PPTTextElement, PPTImageElement, PPTShapeElement } from '@/types/slides'
 import { OPERATE_KEYS } from '@/configs/element'
 
 // 给定一个坐标,计算该坐标到(0, 0)点连线的弧度值
@@ -11,7 +11,7 @@ export const getAngleFromCoordinate = (x: number, y: number) => {
 }
 
 // 计算元素被旋转一定角度后,八个操作点的新坐标
-export const getRotateElementPoints = (element: PPTTextElement | PPTImageElement | PPTShapeElement | PPTIconElement, angle: number) => {
+export const getRotateElementPoints = (element: PPTTextElement | PPTImageElement | PPTShapeElement, angle: number) => {
   const { left, top, width, height } = element
 
   const radius = Math.sqrt( Math.pow(width, 2) + Math.pow(height, 2) ) / 2

+ 1 - 8
src/views/Editor/index.vue

@@ -45,9 +45,6 @@ export default defineComponent({
     const thumbnailsFocus = computed(() => store.state.thumbnailsFocus)
     const disableHotkeys = computed(() => store.state.disableHotkeys)
 
-    const save = () => {
-      message.success('save')
-    }
     const copy = () => {
       message.success('copy')
     }
@@ -89,11 +86,7 @@ export default defineComponent({
       if(shiftKey && !shiftKeyDown.value) shiftKeyDown.value = true
       
       if(!editorAreaFocus.value && !thumbnailsFocus.value) return      
-      
-      if(ctrlKey && keyCode === KEYCODE.S) {
-        e.preventDefault()
-        save()
-      }
+
       if(ctrlKey && keyCode === KEYCODE.C) {
         e.preventDefault()
         copy()

+ 27 - 17
src/views/_common/_element/EditableElement.vue

@@ -23,6 +23,16 @@
 import { computed, defineComponent, PropType } from 'vue'
 import { PPTElement } from '@/types/slides'
 
+import {
+  ElementOrderCommand,
+  ElementOrderCommands,
+  ElementAlignCommand,
+  ElementAlignCommands,
+  ElementScaleHandler,
+  ElementLockCommand,
+  ElementLockCommands,
+} from '@/types/edit'
+
 import ImageElement from './ImageElement.index.vue'
 import TextElement from './TextElement.index.vue'
 
@@ -62,11 +72,11 @@ export default defineComponent({
       required: true,
     },
     scaleElement: {
-      type: Function as PropType<(e: MouseEvent, element: PPTElement, direction: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9) => void>,
+      type: Function as PropType<(e: MouseEvent, element: PPTElement, command: ElementScaleHandler) => void>,
       required: true,
     },
-    updateZIndex: {
-      type: Function as PropType<(element: PPTElement, operation: 'up' | 'down' | 'top' | 'bottom') => void>,
+    orderElement: {
+      type: Function as PropType<(element: PPTElement, command: ElementOrderCommand) => void>,
       required: true,
     },
     combineElements: {
@@ -78,7 +88,7 @@ export default defineComponent({
       required: true,
     },
     alignElement: {
-      type: Function as PropType<(direction: 'top' | 'verticalCenter' | 'bottom' | 'left' | 'horizontalCenter' | 'right') => void>,
+      type: Function as PropType<(command: ElementAlignCommand) => void>,
       required: true,
     },
     deleteElement: {
@@ -86,7 +96,7 @@ export default defineComponent({
       required: true,
     },
     lockElement: {
-      type: Function as PropType<(element: PPTElement, handle: 'lock' | 'unlock') => void>,
+      type: Function as PropType<(element: PPTElement, command: ElementLockCommand) => void>,
       required: true,
     },
     copyElement: {
@@ -112,7 +122,7 @@ export default defineComponent({
         return [{
           text: '解锁', 
           icon: 'icon-unlock',
-          action: () => props.lockElement(props.elementInfo, 'unlock'),
+          action: () => props.lockElement(props.elementInfo, ElementLockCommands.UNLOCK),
         }]
       }
 
@@ -135,29 +145,29 @@ export default defineComponent({
           icon: 'icon-top-layer',
           disable: props.isMultiSelect && !props.elementInfo.groupId,
           children: [
-            { text: '置顶层', action: () => props.updateZIndex(props.elementInfo, 'top') },
-            { text: '置底层', action: () => props.updateZIndex(props.elementInfo, 'bottom') },
+            { text: '置顶层', action: () => props.orderElement(props.elementInfo, ElementOrderCommands.TOP) },
+            { text: '置底层', action: () => props.orderElement(props.elementInfo, ElementOrderCommands.BOTTOM) },
             { divider: true },
-            { text: '上移一层', action: () => props.updateZIndex(props.elementInfo, 'up') },
-            { text: '下移一层', action: () => props.updateZIndex(props.elementInfo, 'down') },
+            { text: '上移一层', action: () => props.orderElement(props.elementInfo, ElementOrderCommands.UP) },
+            { text: '下移一层', action: () => props.orderElement(props.elementInfo, ElementOrderCommands.DOWN) },
           ],
         },
         {
           text: '水平对齐',
           icon: 'icon-align-left',
           children: [
-            { text: '水平居中', action: () => props.alignElement('horizontalCenter') },
-            { text: '左对齐', action: () => props.alignElement('left') },
-            { text: '右对齐', action: () => props.alignElement('right') },
+            { text: '水平居中', action: () => props.alignElement(ElementAlignCommands.HORIZONTAL) },
+            { text: '左对齐', action: () => props.alignElement(ElementAlignCommands.LEFT) },
+            { text: '右对齐', action: () => props.alignElement(ElementAlignCommands.RIGHT) },
           ],
         },
         {
           text: '垂直对齐',
           icon: 'icon-align-bottom',
           children: [
-            { text: '垂直居中', action: () => props.alignElement('verticalCenter') },
-            { text: '上对齐', action: () => props.alignElement('top') },
-            { text: '下对齐', action: () => props.alignElement('bottom') },
+            { text: '垂直居中', action: () => props.alignElement(ElementAlignCommands.VERTICAL) },
+            { text: '上对齐', action: () => props.alignElement(ElementAlignCommands.TOP) },
+            { text: '下对齐', action: () => props.alignElement(ElementAlignCommands.BOTTOM) },
           ],
         },
         { divider: true },
@@ -172,7 +182,7 @@ export default defineComponent({
           text: '锁定',
           subText: 'Ctrl + L',
           icon: 'icon-lock',
-          action: () => props.lockElement(props.elementInfo, 'lock'),
+          action: () => props.lockElement(props.elementInfo, ElementLockCommands.LOCK),
         },
         {
           text: '删除',