|
<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 { 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 { 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' |
|
|
|
const props = defineProps<{ |
|
elementInfo: PPTElement |
|
elementIndex: number |
|
isMultiSelect: boolean |
|
selectElement: (e: MouseEvent | TouchEvent, element: PPTElement, canMove?: boolean) => void |
|
openLinkDialog: () => 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, |
|
} |
|
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), |
|
}] |
|
} |
|
|
|
return [ |
|
{ |
|
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, |
|
}, |
|
] |
|
} |
|
</script> |