123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- <template>
- <div
- class="editable-element"
- ref="elementRef"
- :id="`editable-element-${elementInfo.id}`"
- :style="{
- zIndex: elementIndex,
- }"
- >
- <component
- :is="currentElementComponent"
- :elementInfo="elementInfo"
- :selectElement="selectElement"
- :contextmenus="contextmenus"
- ></component>
- </div>
- </template>
- <script lang="ts" setup>
- import { computed } from 'vue'
- import { storeToRefs } from 'pinia'
- import { ElementTypes, type PPTElement } from '@/types/slides'
- import type { ContextmenuItem } from '@/components/Contextmenu/types'
- import useLockElement from '@/hooks/useLockElement'
- import useDeleteElement from '@/hooks/useDeleteElement'
- import useCombineElement from '@/hooks/useCombineElement'
- import useOrderElement from '@/hooks/useOrderElement'
- import useAlignElementToCanvas from '@/hooks/useAlignElementToCanvas'
- import useCopyAndPasteElement from '@/hooks/useCopyAndPasteElement'
- import useSelectElement from '@/hooks/useSelectElement'
- import useHistorySnapshot from '@/hooks/useHistorySnapshot'
- import { useSlidesStore } from '@/store'
- import message from '@/utils/message'
- import { ElementOrderCommands, ElementAlignCommands } from '@/types/edit'
- import ImageElement from '@/views/components/element/ImageElement/index.vue'
- import TextElement from '@/views/components/element/TextElement/index.vue'
- import ShapeElement from '@/views/components/element/ShapeElement/index.vue'
- import LineElement from '@/views/components/element/LineElement/index.vue'
- import ChartElement from '@/views/components/element/ChartElement/index.vue'
- import TableElement from '@/views/components/element/TableElement/index.vue'
- import LatexElement from '@/views/components/element/LatexElement/index.vue'
- import VideoElement from '@/views/components/element/VideoElement/index.vue'
- import AudioElement from '@/views/components/element/AudioElement/index.vue'
- import FrameElement from '@/views/components/element/FrameElement/index.vue'
- const props = defineProps<{
- elementInfo: PPTElement
- elementIndex: number
- isMultiSelect: boolean
- selectElement: (e: MouseEvent | TouchEvent, element: PPTElement, canMove?: boolean) => void
- openLinkDialog: () => void
- openWebpageLinkEditDialog: (elementId: string, currentUrl: string) => void
- }>()
- const currentElementComponent = computed<unknown>(() => {
- const elementTypeMap = {
- [ElementTypes.IMAGE]: ImageElement,
- [ElementTypes.TEXT]: TextElement,
- [ElementTypes.SHAPE]: ShapeElement,
- [ElementTypes.LINE]: LineElement,
- [ElementTypes.CHART]: ChartElement,
- [ElementTypes.TABLE]: TableElement,
- [ElementTypes.LATEX]: LatexElement,
- [ElementTypes.VIDEO]: VideoElement,
- [ElementTypes.AUDIO]: AudioElement,
- [ElementTypes.FRAME]: FrameElement,
- }
- return elementTypeMap[props.elementInfo.type] || null
- })
- const { orderElement } = useOrderElement()
- const { alignElementToCanvas } = useAlignElementToCanvas()
- const { combineElements, uncombineElements } = useCombineElement()
- const { deleteElement } = useDeleteElement()
- const { lockElement, unlockElement } = useLockElement()
- const { copyElement, pasteElement, cutElement } = useCopyAndPasteElement()
- const { selectAllElements } = useSelectElement()
- const contextmenus = (): ContextmenuItem[] => {
- if (props.elementInfo.lock) {
- return [{
- text: '解锁',
- handler: () => unlockElement(props.elementInfo),
- }]
- }
- const baseMenu = [
- {
- text: '剪切',
- subText: 'Ctrl + X',
- handler: cutElement,
- },
- {
- text: '复制',
- subText: 'Ctrl + C',
- handler: copyElement,
- },
- {
- text: '粘贴',
- subText: 'Ctrl + V',
- handler: pasteElement,
- },
- { divider: true },
- {
- text: '水平居中',
- handler: () => alignElementToCanvas(ElementAlignCommands.HORIZONTAL),
- children: [
- { text: '水平垂直居中', handler: () => alignElementToCanvas(ElementAlignCommands.CENTER), },
- { text: '水平居中', handler: () => alignElementToCanvas(ElementAlignCommands.HORIZONTAL) },
- { text: '左对齐', handler: () => alignElementToCanvas(ElementAlignCommands.LEFT) },
- { text: '右对齐', handler: () => alignElementToCanvas(ElementAlignCommands.RIGHT) },
- ],
- },
- {
- text: '垂直居中',
- handler: () => alignElementToCanvas(ElementAlignCommands.VERTICAL),
- children: [
- { text: '水平垂直居中', handler: () => alignElementToCanvas(ElementAlignCommands.CENTER) },
- { text: '垂直居中', handler: () => alignElementToCanvas(ElementAlignCommands.VERTICAL) },
- { text: '顶部对齐', handler: () => alignElementToCanvas(ElementAlignCommands.TOP) },
- { text: '底部对齐', handler: () => alignElementToCanvas(ElementAlignCommands.BOTTOM) },
- ],
- },
- { divider: true },
- {
- text: '置于顶层',
- disable: props.isMultiSelect && !props.elementInfo.groupId,
- handler: () => orderElement(props.elementInfo, ElementOrderCommands.TOP),
- children: [
- { text: '置于顶层', handler: () => orderElement(props.elementInfo, ElementOrderCommands.TOP) },
- { text: '上移一层', handler: () => orderElement(props.elementInfo, ElementOrderCommands.UP) },
- ],
- },
- {
- text: '置于底层',
- disable: props.isMultiSelect && !props.elementInfo.groupId,
- handler: () => orderElement(props.elementInfo, ElementOrderCommands.BOTTOM),
- children: [
- { text: '置于底层', handler: () => orderElement(props.elementInfo, ElementOrderCommands.BOTTOM) },
- { text: '下移一层', handler: () => orderElement(props.elementInfo, ElementOrderCommands.DOWN) },
- ],
- },
- { divider: true },
- {
- text: '设置链接',
- handler: props.openLinkDialog,
- },
- {
- text: props.elementInfo.groupId ? '取消组合' : '组合',
- subText: 'Ctrl + G',
- handler: props.elementInfo.groupId ? uncombineElements : combineElements,
- hide: !props.isMultiSelect,
- },
- {
- text: '全选',
- subText: 'Ctrl + A',
- handler: selectAllElements,
- },
- {
- text: '锁定',
- subText: 'Ctrl + L',
- handler: lockElement,
- },
- {
- text: '删除',
- subText: 'Delete',
- handler: deleteElement,
- },
- ]
- // 为网页元素添加特殊菜单项
- if (props.elementInfo.type === ElementTypes.FRAME) {
- const frameMenu = [
- // {
- // text: '修改链接',
- // handler: () => {
- // const frameElement = props.elementInfo as any
- // if (frameElement.url) {
- // props.openWebpageLinkEditDialog(frameElement.id, frameElement.url)
- // }
- // },
- // },
- {
- text: '在新窗口打开',
- handler: () => {
- const frameElement = props.elementInfo as any
- if (frameElement.url) {
- window.open(frameElement.url, '_blank')
- }
- },
- },
- {
- text: '复制链接',
- handler: () => {
- const frameElement = props.elementInfo as any
- if (frameElement.url) {
- navigator.clipboard.writeText(frameElement.url)
- }
- },
- },
- { divider: true },
- ]
- // 为网页元素过滤掉"设置链接"功能
- const filteredBaseMenu = baseMenu.filter(item => item.text !== '设置链接')
- return [...frameMenu, ...filteredBaseMenu]
- }
- return baseMenu
- }
- </script>
|