EditableElement.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. <template>
  2. <div
  3. class="editable-element"
  4. ref="elementRef"
  5. :id="`editable-element-${elementInfo.id}`"
  6. :style="{
  7. zIndex: elementIndex
  8. }"
  9. >
  10. <component :is="currentElementComponent" :elementInfo="elementInfo" :selectElement="selectElement" :contextmenus="contextmenus" />
  11. </div>
  12. </template>
  13. <script lang="ts" setup>
  14. import { computed } from "vue"
  15. import { ElementTypes, ElementSubtypeTypes, type PPTElement } from "@/types/slides"
  16. import type { ContextmenuItem } from "@/components/Contextmenu/types"
  17. import useLockElement from "@/hooks/useLockElement"
  18. import useDeleteElement from "@/hooks/useDeleteElement"
  19. import useCombineElement from "@/hooks/useCombineElement"
  20. import useOrderElement from "@/hooks/useOrderElement"
  21. import useAlignElementToCanvas from "@/hooks/useAlignElementToCanvas"
  22. import useCopyAndPasteElement from "@/hooks/useCopyAndPasteElement"
  23. import useSelectElement from "@/hooks/useSelectElement"
  24. import { ElementOrderCommands, ElementAlignCommands } from "@/types/edit"
  25. import ImageElement from "@/views/components/element/ImageElement/index.vue"
  26. import TextElement from "@/views/components/element/TextElement/index.vue"
  27. import ShapeElement from "@/views/components/element/ShapeElement/index.vue"
  28. import LineElement from "@/views/components/element/LineElement/index.vue"
  29. import ChartElement from "@/views/components/element/ChartElement/index.vue"
  30. import TableElement from "@/views/components/element/TableElement/index.vue"
  31. import LatexElement from "@/views/components/element/LatexElement/index.vue"
  32. import VideoElement from "@/views/components/element/VideoElement/index.vue"
  33. import AudioElement from "@/views/components/element/AudioElement/index.vue"
  34. import cloudCoachElement from "@/views/components/element/cloudCoachElement"
  35. import enjoyElement from "@/views/components/element/enjoyElement"
  36. import listeningPracticeElement from "@/views/components/element/listeningPracticeElement"
  37. const props = defineProps<{
  38. elementInfo: PPTElement
  39. elementIndex: number
  40. isMultiSelect: boolean
  41. selectElement: (e: MouseEvent | TouchEvent, element: PPTElement, canMove?: boolean) => void
  42. openLinkDialog: () => void
  43. }>()
  44. const currentElementComponent = computed<unknown>(() => {
  45. const elementTypeMap = {
  46. [ElementTypes.IMAGE]: ImageElement,
  47. [ElementTypes.TEXT]: TextElement,
  48. [ElementTypes.SHAPE]: ShapeElement,
  49. [ElementTypes.LINE]: LineElement,
  50. [ElementTypes.CHART]: ChartElement,
  51. [ElementTypes.TABLE]: TableElement,
  52. [ElementTypes.LATEX]: LatexElement,
  53. [ElementTypes.ELF]: null
  54. }
  55. const elementSubtypeMap = {
  56. [ElementSubtypeTypes.AUDIO]: AudioElement,
  57. [ElementSubtypeTypes.VIDEO]: VideoElement,
  58. [ElementSubtypeTypes.SING_PLAY]: cloudCoachElement,
  59. [ElementSubtypeTypes.ENJOY]: enjoyElement,
  60. [ElementSubtypeTypes.LISTENING_PRACTICE]: listeningPracticeElement
  61. }
  62. return elementTypeMap[props.elementInfo.type] || elementSubtypeMap[props.elementInfo.subtype] || null
  63. })
  64. const { orderElement } = useOrderElement()
  65. const { alignElementToCanvas } = useAlignElementToCanvas()
  66. const { combineElements, uncombineElements } = useCombineElement()
  67. const { deleteElement } = useDeleteElement()
  68. const { lockElement, unlockElement } = useLockElement()
  69. const { copyElement, pasteElement, cutElement } = useCopyAndPasteElement()
  70. const { selectAllElements } = useSelectElement()
  71. const contextmenus = (): ContextmenuItem[] => {
  72. if (props.elementInfo.lock) {
  73. return [
  74. {
  75. text: "解锁",
  76. handler: () => unlockElement(props.elementInfo)
  77. }
  78. ]
  79. }
  80. return [
  81. {
  82. text: "剪切",
  83. subText: "Ctrl + X",
  84. handler: cutElement
  85. },
  86. {
  87. text: "复制",
  88. subText: "Ctrl + C",
  89. handler: copyElement
  90. },
  91. {
  92. text: "粘贴",
  93. subText: "Ctrl + V",
  94. handler: pasteElement
  95. },
  96. { divider: true },
  97. {
  98. text: "水平居中",
  99. handler: () => alignElementToCanvas(ElementAlignCommands.HORIZONTAL),
  100. children: [
  101. { text: "水平垂直居中", handler: () => alignElementToCanvas(ElementAlignCommands.CENTER) },
  102. { text: "水平居中", handler: () => alignElementToCanvas(ElementAlignCommands.HORIZONTAL) },
  103. { text: "左对齐", handler: () => alignElementToCanvas(ElementAlignCommands.LEFT) },
  104. { text: "右对齐", handler: () => alignElementToCanvas(ElementAlignCommands.RIGHT) }
  105. ]
  106. },
  107. {
  108. text: "垂直居中",
  109. handler: () => alignElementToCanvas(ElementAlignCommands.VERTICAL),
  110. children: [
  111. { text: "水平垂直居中", handler: () => alignElementToCanvas(ElementAlignCommands.CENTER) },
  112. { text: "垂直居中", handler: () => alignElementToCanvas(ElementAlignCommands.VERTICAL) },
  113. { text: "顶部对齐", handler: () => alignElementToCanvas(ElementAlignCommands.TOP) },
  114. { text: "底部对齐", handler: () => alignElementToCanvas(ElementAlignCommands.BOTTOM) }
  115. ]
  116. },
  117. { divider: true },
  118. {
  119. text: "置于顶层",
  120. disable: props.isMultiSelect && !props.elementInfo.groupId,
  121. handler: () => orderElement(props.elementInfo, ElementOrderCommands.TOP),
  122. children: [
  123. { text: "置于顶层", handler: () => orderElement(props.elementInfo, ElementOrderCommands.TOP) },
  124. { text: "上移一层", handler: () => orderElement(props.elementInfo, ElementOrderCommands.UP) }
  125. ]
  126. },
  127. {
  128. text: "置于底层",
  129. disable: props.isMultiSelect && !props.elementInfo.groupId,
  130. handler: () => orderElement(props.elementInfo, ElementOrderCommands.BOTTOM),
  131. children: [
  132. { text: "置于底层", handler: () => orderElement(props.elementInfo, ElementOrderCommands.BOTTOM) },
  133. { text: "下移一层", handler: () => orderElement(props.elementInfo, ElementOrderCommands.DOWN) }
  134. ]
  135. },
  136. { divider: true },
  137. {
  138. text: "设置链接",
  139. handler: props.openLinkDialog
  140. },
  141. {
  142. text: props.elementInfo.groupId ? "取消组合" : "组合",
  143. subText: "Ctrl + G",
  144. handler: props.elementInfo.groupId ? uncombineElements : combineElements,
  145. hide: !props.isMultiSelect
  146. },
  147. {
  148. text: "全选",
  149. subText: "Ctrl + A",
  150. handler: selectAllElements
  151. },
  152. {
  153. text: "锁定",
  154. subText: "Ctrl + L",
  155. handler: lockElement
  156. },
  157. {
  158. text: "删除",
  159. subText: "Delete",
  160. handler: deleteElement
  161. }
  162. ]
  163. }
  164. </script>