EditableElement.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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
  11. :is="currentElementComponent"
  12. :elementInfo="elementInfo"
  13. :selectElement="selectElement"
  14. :contextmenus="contextmenus"
  15. ></component>
  16. </div>
  17. </template>
  18. <script lang="ts" setup>
  19. import { computed, onMounted } from 'vue'
  20. import { storeToRefs } from 'pinia'
  21. import { ElementTypes, type PPTElement } from '@/types/slides'
  22. import type { ContextmenuItem } from '@/components/Contextmenu/types'
  23. import useLockElement from '@/hooks/useLockElement'
  24. import useDeleteElement from '@/hooks/useDeleteElement'
  25. import useCombineElement from '@/hooks/useCombineElement'
  26. import useOrderElement from '@/hooks/useOrderElement'
  27. import useAlignElementToCanvas from '@/hooks/useAlignElementToCanvas'
  28. import useCopyAndPasteElement from '@/hooks/useCopyAndPasteElement'
  29. import useSelectElement from '@/hooks/useSelectElement'
  30. import useHistorySnapshot from '@/hooks/useHistorySnapshot'
  31. import { useSlidesStore, useMainStore } from '@/store'
  32. import message from '@/utils/message'
  33. import { lang } from '@/main'
  34. import { ElementOrderCommands, ElementAlignCommands } from '@/types/edit'
  35. import ImageElement from '@/views/components/element/ImageElement/index.vue'
  36. import TextElement from '@/views/components/element/TextElement/index.vue'
  37. import ShapeElement from '@/views/components/element/ShapeElement/index.vue'
  38. import LineElement from '@/views/components/element/LineElement/index.vue'
  39. import ChartElement from '@/views/components/element/ChartElement/index.vue'
  40. import TableElement from '@/views/components/element/TableElement/index.vue'
  41. import LatexElement from '@/views/components/element/LatexElement/index.vue'
  42. import VideoElement from '@/views/components/element/VideoElement/index.vue'
  43. import AudioElement from '@/views/components/element/AudioElement/index.vue'
  44. import FrameElement from '@/views/components/element/FrameElement/index.vue'
  45. import { initClipboardFocus } from '@/utils/clipboard'
  46. const mainStore = useMainStore()
  47. onMounted(() => {
  48. console.log('ed,onMounted')
  49. initClipboardFocus()
  50. mainStore.setDisableHotkeysState(false)
  51. })
  52. const props = defineProps<{
  53. elementInfo: PPTElement
  54. elementIndex: number
  55. isMultiSelect: boolean
  56. selectElement: (e: MouseEvent | TouchEvent, element: PPTElement, canMove?: boolean) => void
  57. openLinkDialog: () => void
  58. openWebpageLinkEditDialog: (elementId: string, currentUrl: string) => void
  59. }>()
  60. const currentElementComponent = computed<unknown>(() => {
  61. const elementTypeMap = {
  62. [ElementTypes.IMAGE]: ImageElement,
  63. [ElementTypes.TEXT]: TextElement,
  64. [ElementTypes.SHAPE]: ShapeElement,
  65. [ElementTypes.LINE]: LineElement,
  66. [ElementTypes.CHART]: ChartElement,
  67. [ElementTypes.TABLE]: TableElement,
  68. [ElementTypes.LATEX]: LatexElement,
  69. [ElementTypes.VIDEO]: VideoElement,
  70. [ElementTypes.AUDIO]: AudioElement,
  71. [ElementTypes.FRAME]: FrameElement,
  72. }
  73. return elementTypeMap[props.elementInfo.type] || null
  74. })
  75. const { orderElement } = useOrderElement()
  76. const { alignElementToCanvas } = useAlignElementToCanvas()
  77. const { combineElements, uncombineElements } = useCombineElement()
  78. const { deleteElement } = useDeleteElement()
  79. const { lockElement, unlockElement } = useLockElement()
  80. const { copyElement, pasteElement, cutElement } = useCopyAndPasteElement()
  81. const { selectAllElements } = useSelectElement()
  82. const contextmenus = (): ContextmenuItem[] => {
  83. if (props.elementInfo.lock) {
  84. return [{
  85. text: lang.ssUnlock,
  86. handler: () => unlockElement(props.elementInfo),
  87. }]
  88. }
  89. const baseMenu = [
  90. {
  91. text: lang.ssCut,
  92. subText: 'Ctrl + X',
  93. handler: cutElement,
  94. },
  95. {
  96. text: lang.ssCopy,
  97. subText: 'Ctrl + C',
  98. handler: copyElement,
  99. },
  100. {
  101. text: lang.ssPaste,
  102. subText: 'Ctrl + V',
  103. handler: pasteElement,
  104. },
  105. { divider: true },
  106. {
  107. text: lang.ssAlignHCenter,
  108. handler: () => alignElementToCanvas(ElementAlignCommands.HORIZONTAL),
  109. children: [
  110. { text: lang.ssAlignHVCenter, handler: () => alignElementToCanvas(ElementAlignCommands.CENTER), },
  111. { text: lang.ssAlignHCenter, handler: () => alignElementToCanvas(ElementAlignCommands.HORIZONTAL) },
  112. { text: lang.ssAlignLeft, handler: () => alignElementToCanvas(ElementAlignCommands.LEFT) },
  113. { text: lang.ssAlignRight, handler: () => alignElementToCanvas(ElementAlignCommands.RIGHT) },
  114. ],
  115. },
  116. {
  117. text: lang.ssAlignVCenter,
  118. handler: () => alignElementToCanvas(ElementAlignCommands.VERTICAL),
  119. children: [
  120. { text: lang.ssAlignHVCenter, handler: () => alignElementToCanvas(ElementAlignCommands.CENTER) },
  121. { text: lang.ssAlignVCenter, handler: () => alignElementToCanvas(ElementAlignCommands.VERTICAL) },
  122. { text: lang.ssAlignTop, handler: () => alignElementToCanvas(ElementAlignCommands.TOP) },
  123. { text: lang.ssAlignBottom, handler: () => alignElementToCanvas(ElementAlignCommands.BOTTOM) },
  124. ],
  125. },
  126. { divider: true },
  127. {
  128. text: lang.ssBringFront,
  129. disable: props.isMultiSelect && !props.elementInfo.groupId,
  130. handler: () => orderElement(props.elementInfo, ElementOrderCommands.TOP),
  131. children: [
  132. { text: lang.ssBringFront, handler: () => orderElement(props.elementInfo, ElementOrderCommands.TOP) },
  133. { text: lang.ssBringForward, handler: () => orderElement(props.elementInfo, ElementOrderCommands.UP) },
  134. ],
  135. },
  136. {
  137. text: lang.ssSendBack,
  138. disable: props.isMultiSelect && !props.elementInfo.groupId,
  139. handler: () => orderElement(props.elementInfo, ElementOrderCommands.BOTTOM),
  140. children: [
  141. { text: lang.ssSendBack, handler: () => orderElement(props.elementInfo, ElementOrderCommands.BOTTOM) },
  142. { text: lang.ssSendBackward, handler: () => orderElement(props.elementInfo, ElementOrderCommands.DOWN) },
  143. ],
  144. },
  145. { divider: true },
  146. {
  147. text: lang.ssSetLink,
  148. handler: props.openLinkDialog,
  149. },
  150. {
  151. text: props.elementInfo.groupId ? lang.ssUngroupEl : lang.ssGroupEl,
  152. subText: 'Ctrl + G',
  153. handler: props.elementInfo.groupId ? uncombineElements : combineElements,
  154. hide: !props.isMultiSelect,
  155. },
  156. {
  157. text: lang.ssSelectAll,
  158. subText: 'Ctrl + A',
  159. handler: selectAllElements,
  160. },
  161. {
  162. text: lang.ssLock,
  163. subText: 'Ctrl + L',
  164. handler: lockElement,
  165. },
  166. {
  167. text: lang.ssDelete,
  168. subText: 'Delete',
  169. handler: deleteElement,
  170. },
  171. ]
  172. // 为网页元素添加特殊菜单项
  173. if (props.elementInfo.type === ElementTypes.FRAME) {
  174. const frameMenu = [
  175. {
  176. text: lang.ssOpenNewWin,
  177. handler: () => {
  178. const frameElement = props.elementInfo as any
  179. if (frameElement.url) {
  180. window.open(frameElement.url, '_blank')
  181. }
  182. },
  183. },
  184. {
  185. text: lang.ssCopyLink,
  186. handler: () => {
  187. const frameElement = props.elementInfo as any
  188. if (frameElement.url) {
  189. navigator.clipboard.writeText(frameElement.url)
  190. }
  191. },
  192. },
  193. { divider: true },
  194. ]
  195. // 为网页元素过滤掉"设置链接"功能
  196. const filteredBaseMenu = baseMenu.filter(item => item.text !== lang.ssSetLink)
  197. return [...frameMenu, ...filteredBaseMenu]
  198. }
  199. return baseMenu
  200. }
  201. </script>