EditableElement.vue 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. <template>
  2. <div
  3. class="editable-element"
  4. ref="elementRef"
  5. :id="'editable-element-' + elementInfo.elId"
  6. :style="{ zIndex: elementIndex }"
  7. >
  8. <component
  9. :is="currentElementComponent"
  10. :canvasScale="canvasScale"
  11. :elementInfo="elementInfo"
  12. :isActive="isActive"
  13. :isHandleEl="isHandleEl"
  14. :isActiveGroupElement="isActiveGroupElement"
  15. :isMultiSelect="isMultiSelect"
  16. :animationIndex="animationIndex"
  17. :selectElement="selectElement"
  18. :rotateElement="rotateElement"
  19. :scaleElement="scaleElement"
  20. :contextmenus="contextmenus"
  21. ></component>
  22. </div>
  23. </template>
  24. <script lang="ts">
  25. import { computed, defineComponent, PropType } from 'vue'
  26. import { useStore } from 'vuex'
  27. import { State } from '@/store'
  28. import { PPTElement, PPTTextElement, PPTImageElement, PPTShapeElement, PPTLineElement } from '@/types/slides'
  29. import { ContextmenuItem } from '@/components/Contextmenu/types'
  30. import useLockElement from '@/hooks/useLockElement'
  31. import useDeleteElement from '@/hooks/useDeleteElement'
  32. import useCombineElement from '@/hooks/useCombineElement'
  33. import useOrderElement from '@/hooks/useOrderElement'
  34. import useAlignElementToCanvas from '@/hooks/useAlignElementToCanvas'
  35. import useCopyAndPasteElement from '@/hooks/useCopyAndPasteElement'
  36. import { ElementOrderCommands, ElementAlignCommands, ElementScaleHandler } from '@/types/edit'
  37. import ImageElement from './ImageElement/index.vue'
  38. import TextElement from './TextElement/index.vue'
  39. export default defineComponent({
  40. name: 'editable-element',
  41. props: {
  42. elementInfo: {
  43. type: Object as PropType<PPTElement>,
  44. required: true,
  45. },
  46. elementIndex: {
  47. type: Number,
  48. required: true,
  49. },
  50. isActive: {
  51. type: Boolean,
  52. required: true,
  53. },
  54. isHandleEl: {
  55. type: Boolean,
  56. required: true,
  57. },
  58. isActiveGroupElement: {
  59. type: Boolean,
  60. required: true,
  61. },
  62. isMultiSelect: {
  63. type: Boolean,
  64. required: true,
  65. },
  66. animationIndex: {
  67. type: Number,
  68. default: -1,
  69. },
  70. selectElement: {
  71. type: Function as PropType<(e: MouseEvent, element: PPTElement, canMove?: boolean) => void>,
  72. required: true,
  73. },
  74. rotateElement: {
  75. type: Function as PropType<(element: PPTTextElement | PPTImageElement | PPTShapeElement) => void>,
  76. required: true,
  77. },
  78. scaleElement: {
  79. type: Function as PropType<(e: MouseEvent, element: Exclude<PPTElement, PPTLineElement>, command: ElementScaleHandler) => void>,
  80. required: true,
  81. },
  82. },
  83. setup(props) {
  84. const store = useStore<State>()
  85. const canvasScale = computed(() => store.state.canvasScale)
  86. const currentElementComponent = computed(() => {
  87. const elementTypeMap = {
  88. 'image': ImageElement,
  89. 'text': TextElement,
  90. }
  91. return elementTypeMap[props.elementInfo.type] || null
  92. })
  93. const { orderElement } = useOrderElement()
  94. const { alignElementToCanvas } = useAlignElementToCanvas()
  95. const { combineElements, uncombineElements } = useCombineElement()
  96. const { deleteElement } = useDeleteElement()
  97. const { lockElement, unlockElement } = useLockElement()
  98. const { copyElement, cutElement } = useCopyAndPasteElement()
  99. const contextmenus = (): ContextmenuItem[] => {
  100. if(props.elementInfo.isLock) {
  101. return [{
  102. text: '解锁',
  103. icon: 'icon-unlock',
  104. handler: () => unlockElement(props.elementInfo),
  105. }]
  106. }
  107. return [
  108. {
  109. text: '剪切',
  110. subText: 'Ctrl + X',
  111. icon: 'icon-scissor',
  112. handler: cutElement,
  113. },
  114. {
  115. text: '复制',
  116. subText: 'Ctrl + C',
  117. icon: 'icon-copy',
  118. handler: copyElement,
  119. },
  120. { divider: true },
  121. {
  122. text: '层级排序',
  123. icon: 'icon-top-layer',
  124. disable: props.isMultiSelect && !props.elementInfo.groupId,
  125. children: [
  126. { text: '置顶层', handler: () => orderElement(props.elementInfo, ElementOrderCommands.TOP) },
  127. { text: '置底层', handler: () => orderElement(props.elementInfo, ElementOrderCommands.BOTTOM) },
  128. { divider: true },
  129. { text: '上移一层', handler: () => orderElement(props.elementInfo, ElementOrderCommands.UP) },
  130. { text: '下移一层', handler: () => orderElement(props.elementInfo, ElementOrderCommands.DOWN) },
  131. ],
  132. },
  133. {
  134. text: '水平对齐',
  135. icon: 'icon-align-left',
  136. children: [
  137. { text: '水平居中', handler: () => alignElementToCanvas(ElementAlignCommands.HORIZONTAL) },
  138. { text: '左对齐', handler: () => alignElementToCanvas(ElementAlignCommands.LEFT) },
  139. { text: '右对齐', handler: () => alignElementToCanvas(ElementAlignCommands.RIGHT) },
  140. ],
  141. },
  142. {
  143. text: '垂直对齐',
  144. icon: 'icon-align-bottom',
  145. children: [
  146. { text: '垂直居中', handler: () => alignElementToCanvas(ElementAlignCommands.VERTICAL) },
  147. { text: '上对齐', handler: () => alignElementToCanvas(ElementAlignCommands.TOP) },
  148. { text: '下对齐', handler: () => alignElementToCanvas(ElementAlignCommands.BOTTOM) },
  149. ],
  150. },
  151. { divider: true },
  152. {
  153. text: props.elementInfo.groupId ? '取消组合' : '组合',
  154. subText: 'Ctrl + G',
  155. icon: 'icon-block',
  156. handler: props.elementInfo.groupId ? uncombineElements : combineElements,
  157. hide: !props.isMultiSelect,
  158. },
  159. {
  160. text: '锁定',
  161. subText: 'Ctrl + L',
  162. icon: 'icon-lock',
  163. handler: lockElement,
  164. },
  165. {
  166. text: '删除',
  167. subText: 'Delete',
  168. icon: 'icon-delete',
  169. handler: deleteElement,
  170. },
  171. ]
  172. }
  173. return {
  174. canvasScale,
  175. currentElementComponent,
  176. contextmenus,
  177. }
  178. },
  179. })
  180. </script>