update
This commit is contained in:
parent
7246f89bc0
commit
d57cdafa5d
|
@ -101,7 +101,7 @@ $subMenuWidth: 120px;
|
||||||
}
|
}
|
||||||
.contextmenu-item {
|
.contextmenu-item {
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
color: #666;
|
color: #555;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
|
@ -4,6 +4,16 @@ export enum ClipPathTypes {
|
||||||
POLYGON = 'polygon',
|
POLYGON = 'polygon',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ClipPaths {
|
||||||
|
RECT = 'rect',
|
||||||
|
ROUNDRECT = 'roundRect',
|
||||||
|
ELLIPSE = 'ellipse',
|
||||||
|
TRIANGLE = 'triangle',
|
||||||
|
PENTAGON = 'pentagon',
|
||||||
|
RHOMBUS = 'rhombus',
|
||||||
|
STAR = 'star',
|
||||||
|
}
|
||||||
|
|
||||||
export const CLIPPATHS = {
|
export const CLIPPATHS = {
|
||||||
rect: {
|
rect: {
|
||||||
name: '矩形',
|
name: '矩形',
|
||||||
|
|
|
@ -39,7 +39,7 @@ export interface PPTImageElement extends PPTElementBaseProps, PPTElementSizeProp
|
||||||
filter?: string;
|
filter?: string;
|
||||||
clip?: {
|
clip?: {
|
||||||
range: [[number, number], [number, number]];
|
range: [[number, number], [number, number]];
|
||||||
shape: 'rect' | 'ellipse' | 'polygon';
|
shape: 'rect' | 'roundRect' | 'ellipse' | 'triangle' | 'pentagon' | 'rhombus' | 'star';
|
||||||
};
|
};
|
||||||
flip?: { x?: number, y?: number };
|
flip?: { x?: number, y?: number };
|
||||||
shadow?: string;
|
shadow?: string;
|
||||||
|
|
|
@ -35,12 +35,35 @@
|
||||||
v-for="(line, index) in alignmentLines" :key="index"
|
v-for="(line, index) in alignmentLines" :key="index"
|
||||||
:type="line.type" :axis="line.axis" :length="line.length"
|
:type="line.type" :axis="line.axis" :length="line.length"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<EditableElement
|
||||||
|
v-for="(element, index) in elementList"
|
||||||
|
:key="element.elId"
|
||||||
|
:elementInfo="element"
|
||||||
|
:elementIndex="index + 1"
|
||||||
|
:isActive="activeElementIdList.includes(element.elId)"
|
||||||
|
:isHandleEl="element.elId === handleElementId"
|
||||||
|
:isActiveGroupElement="activeGroupElementId === element.elId"
|
||||||
|
:isMultiSelect="activeElementIdList.length > 1"
|
||||||
|
:canvasScale="canvasScale"
|
||||||
|
:selectElement="selectElement"
|
||||||
|
:rotateElement="rotateElement"
|
||||||
|
:scaleElement="scaleElement"
|
||||||
|
:orderElement="orderElement"
|
||||||
|
:combineElements="combineElements"
|
||||||
|
:uncombineElements="uncombineElements"
|
||||||
|
:alignElement="alignElement"
|
||||||
|
:deleteElement="deleteElement"
|
||||||
|
:lockElement="lockElement"
|
||||||
|
:copyElement="copyElement"
|
||||||
|
:cutElement="cutElement"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, onMounted, onUnmounted, reactive, ref, watch } from 'vue'
|
import { computed, defineComponent, reactive, ref, watch } from 'vue'
|
||||||
import { useStore } from 'vuex'
|
import { useStore } from 'vuex'
|
||||||
import { State } from '@/store/state'
|
import { State } from '@/store/state'
|
||||||
import { MutationTypes } from '@/store/constants'
|
import { MutationTypes } from '@/store/constants'
|
||||||
|
@ -51,6 +74,7 @@ import { getImageDataURL } from '@/utils/image'
|
||||||
import useDropImage from '@/hooks/useDropImage'
|
import useDropImage from '@/hooks/useDropImage'
|
||||||
import useSetViewportSize from './hooks/useSetViewportSize'
|
import useSetViewportSize from './hooks/useSetViewportSize'
|
||||||
|
|
||||||
|
import EditableElement from '@/views/_common/_element/EditableElement.vue'
|
||||||
import MouseSelection from './MouseSelection.vue'
|
import MouseSelection from './MouseSelection.vue'
|
||||||
import SlideBackground from './SlideBackground.vue'
|
import SlideBackground from './SlideBackground.vue'
|
||||||
import AlignmentLine, { AlignmentLineProps } from './AlignmentLine.vue'
|
import AlignmentLine, { AlignmentLineProps } from './AlignmentLine.vue'
|
||||||
|
@ -58,12 +82,21 @@ import AlignmentLine, { AlignmentLineProps } from './AlignmentLine.vue'
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'v-canvas',
|
name: 'v-canvas',
|
||||||
components: {
|
components: {
|
||||||
|
EditableElement,
|
||||||
MouseSelection,
|
MouseSelection,
|
||||||
SlideBackground,
|
SlideBackground,
|
||||||
AlignmentLine,
|
AlignmentLine,
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const store = useStore<State>()
|
const store = useStore<State>()
|
||||||
|
const elementList = computed(() => {
|
||||||
|
const currentSlide = store.getters.currentSlide
|
||||||
|
return currentSlide ? JSON.parse(JSON.stringify(currentSlide.elements)) : []
|
||||||
|
})
|
||||||
|
const activeElementIdList = computed(() => store.state.activeElementIdList)
|
||||||
|
const handleElementId = computed(() => store.state.handleElementId)
|
||||||
|
const activeGroupElementId = ref('')
|
||||||
|
|
||||||
const viewportRef = ref<HTMLElement | null>(null)
|
const viewportRef = ref<HTMLElement | null>(null)
|
||||||
const isShowGridLines = ref(false)
|
const isShowGridLines = ref(false)
|
||||||
const alignmentLines = ref<AlignmentLineProps[]>([])
|
const alignmentLines = ref<AlignmentLineProps[]>([])
|
||||||
|
@ -162,6 +195,40 @@ export default defineComponent({
|
||||||
if(editorAreaFocus.value) store.commit(MutationTypes.SET_EDITORAREA_FOCUS, false)
|
if(editorAreaFocus.value) store.commit(MutationTypes.SET_EDITORAREA_FOCUS, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selectElement = () => {
|
||||||
|
console.log('selectElement')
|
||||||
|
}
|
||||||
|
const rotateElement = () => {
|
||||||
|
console.log('rotateElement')
|
||||||
|
}
|
||||||
|
const scaleElement = () => {
|
||||||
|
console.log('scaleElement')
|
||||||
|
}
|
||||||
|
const orderElement = () => {
|
||||||
|
console.log('orderElement')
|
||||||
|
}
|
||||||
|
const combineElements = () => {
|
||||||
|
console.log('combineElements')
|
||||||
|
}
|
||||||
|
const uncombineElements = () => {
|
||||||
|
console.log('uncombineElements')
|
||||||
|
}
|
||||||
|
const alignElement = () => {
|
||||||
|
console.log('alignElement')
|
||||||
|
}
|
||||||
|
const deleteElement = () => {
|
||||||
|
console.log('deleteElement')
|
||||||
|
}
|
||||||
|
const lockElement = () => {
|
||||||
|
console.log('lockElement')
|
||||||
|
}
|
||||||
|
const copyElement = () => {
|
||||||
|
console.log('copyElement')
|
||||||
|
}
|
||||||
|
const cutElement = () => {
|
||||||
|
console.log('cutElement')
|
||||||
|
}
|
||||||
|
|
||||||
const contextmenus = (): ContextmenuItem[] => {
|
const contextmenus = (): ContextmenuItem[] => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
@ -179,6 +246,10 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
elementList,
|
||||||
|
activeElementIdList,
|
||||||
|
handleElementId,
|
||||||
|
activeGroupElementId,
|
||||||
canvasRef,
|
canvasRef,
|
||||||
viewportRef,
|
viewportRef,
|
||||||
viewportStyles,
|
viewportStyles,
|
||||||
|
@ -189,6 +260,17 @@ export default defineComponent({
|
||||||
currentSlide,
|
currentSlide,
|
||||||
isShowGridLines,
|
isShowGridLines,
|
||||||
alignmentLines,
|
alignmentLines,
|
||||||
|
selectElement,
|
||||||
|
rotateElement,
|
||||||
|
scaleElement,
|
||||||
|
orderElement,
|
||||||
|
combineElements,
|
||||||
|
uncombineElements,
|
||||||
|
alignElement,
|
||||||
|
deleteElement,
|
||||||
|
lockElement,
|
||||||
|
copyElement,
|
||||||
|
cutElement,
|
||||||
contextmenus,
|
contextmenus,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
:isHandleEl="isHandleEl"
|
:isHandleEl="isHandleEl"
|
||||||
:isActiveGroupElement="isActiveGroupElement"
|
:isActiveGroupElement="isActiveGroupElement"
|
||||||
:isMultiSelect="isMultiSelect"
|
:isMultiSelect="isMultiSelect"
|
||||||
:animationIndex="-1"
|
:animationIndex="animationIndex"
|
||||||
:selectElement="selectElement"
|
:selectElement="selectElement"
|
||||||
:rotateElement="rotateElement"
|
:rotateElement="rotateElement"
|
||||||
:scaleElement="scaleElement"
|
:scaleElement="scaleElement"
|
||||||
|
@ -36,8 +36,8 @@ import {
|
||||||
ElementLockCommands,
|
ElementLockCommands,
|
||||||
} from '@/types/edit'
|
} from '@/types/edit'
|
||||||
|
|
||||||
import ImageElement from './ImageElement.index.vue'
|
import ImageElement from './ImageElement/index.vue'
|
||||||
import TextElement from './TextElement.index.vue'
|
import TextElement from './TextElement/index.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'editable-element',
|
name: 'editable-element',
|
||||||
|
@ -70,6 +70,10 @@ export default defineComponent({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
animationIndex: {
|
||||||
|
type: Number,
|
||||||
|
default: -1,
|
||||||
|
},
|
||||||
selectElement: {
|
selectElement: {
|
||||||
type: Function as PropType<(e: MouseEvent, element: PPTElement, canMove?: boolean) => void>,
|
type: Function as PropType<(e: MouseEvent, element: PPTElement, canMove?: boolean) => void>,
|
||||||
required: true,
|
required: true,
|
||||||
|
|
|
@ -20,8 +20,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import SvgWrapper from '@/components/SvgWrapper.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'element-border',
|
name: 'element-border',
|
||||||
|
components: {
|
||||||
|
SvgWrapper,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
width: {
|
width: {
|
||||||
type: Number,
|
type: Number,
|
||||||
|
|
|
@ -51,6 +51,8 @@
|
||||||
import { computed, defineComponent, onMounted, onUnmounted, PropType, reactive, ref } from 'vue'
|
import { computed, defineComponent, onMounted, onUnmounted, PropType, reactive, ref } from 'vue'
|
||||||
import { KEYCODE } from '@/configs/keyCode'
|
import { KEYCODE } from '@/configs/keyCode'
|
||||||
|
|
||||||
|
import SvgWrapper from '@/components/SvgWrapper.vue'
|
||||||
|
|
||||||
type ClipDataRange = [[number, number], [number, number]]
|
type ClipDataRange = [[number, number], [number, number]]
|
||||||
|
|
||||||
interface ClipData {
|
interface ClipData {
|
||||||
|
@ -72,6 +74,9 @@ type ScaleClipRangeType = 't-l' | 't-r' | 'b-l' | 'b-r'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'image-clip-handler',
|
name: 'image-clip-handler',
|
||||||
|
components: {
|
||||||
|
SvgWrapper,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
imgUrl: {
|
imgUrl: {
|
||||||
type: String,
|
type: String,
|
||||||
|
|
|
@ -23,8 +23,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import SvgWrapper from '@/components/SvgWrapper.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'image-ellipse-border',
|
name: 'image-ellipse-border',
|
||||||
|
components: {
|
||||||
|
SvgWrapper,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
width: {
|
width: {
|
||||||
type: Number,
|
type: Number,
|
||||||
|
|
|
@ -20,8 +20,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import SvgWrapper from '@/components/SvgWrapper.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'image-polygon-border',
|
name: 'image-polygon-border',
|
||||||
|
components: {
|
||||||
|
SvgWrapper,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
width: {
|
width: {
|
||||||
type: Number,
|
type: Number,
|
||||||
|
|
|
@ -23,8 +23,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import SvgWrapper from '@/components/SvgWrapper.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'image-rect-border',
|
name: 'image-rect-border',
|
||||||
|
components: {
|
||||||
|
SvgWrapper,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
width: {
|
width: {
|
||||||
type: Number,
|
type: Number,
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
transform: flip,
|
transform: flip,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<RectBorder
|
<ImageRectBorder
|
||||||
v-if="clipShape.type === 'rect'"
|
v-if="clipShape.type === 'rect'"
|
||||||
:width="elementInfo.width"
|
:width="elementInfo.width"
|
||||||
:height="elementInfo.height"
|
:height="elementInfo.height"
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
:borderWidth="elementInfo.borderWidth"
|
:borderWidth="elementInfo.borderWidth"
|
||||||
:borderStyle="elementInfo.borderStyle"
|
:borderStyle="elementInfo.borderStyle"
|
||||||
/>
|
/>
|
||||||
<EllipseBorder
|
<ImageEllipseBorder
|
||||||
v-else-if="clipShape.type === 'ellipse'"
|
v-else-if="clipShape.type === 'ellipse'"
|
||||||
:width="elementInfo.width"
|
:width="elementInfo.width"
|
||||||
:height="elementInfo.height"
|
:height="elementInfo.height"
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
:borderWidth="elementInfo.borderWidth"
|
:borderWidth="elementInfo.borderWidth"
|
||||||
:borderStyle="elementInfo.borderStyle"
|
:borderStyle="elementInfo.borderStyle"
|
||||||
/>
|
/>
|
||||||
<PolygonBorder
|
<ImagePolygonBorder
|
||||||
v-else-if="clipShape.type === 'polygon'"
|
v-else-if="clipShape.type === 'polygon'"
|
||||||
:width="elementInfo.width"
|
:width="elementInfo.width"
|
||||||
:height="elementInfo.height"
|
:height="elementInfo.height"
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
:style="point.style"
|
:style="point.style"
|
||||||
@mousedown.stop="scaleElement($event, elementInfo, point.direction)"
|
@mousedown.stop="scaleElement($event, elementInfo, point.direction)"
|
||||||
/>
|
/>
|
||||||
<RotateHandle
|
<RotateHandler
|
||||||
class="el-rotate-handle"
|
class="el-rotate-handle"
|
||||||
:style="{left: scaleWidth / 2 + 'px'}"
|
:style="{left: scaleWidth / 2 + 'px'}"
|
||||||
@mousedown.stop="rotateElement(elementInfo)"
|
@mousedown.stop="rotateElement(elementInfo)"
|
||||||
|
@ -186,6 +186,9 @@ export default defineComponent({
|
||||||
type: Function as PropType<(e: MouseEvent, element: PPTImageElement, command: ElementScaleHandler) => void>,
|
type: Function as PropType<(e: MouseEvent, element: PPTImageElement, command: ElementScaleHandler) => void>,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
contextmenus: {
|
||||||
|
type: Function,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const clipingImageElId = ref('')
|
const clipingImageElId = ref('')
|
||||||
|
@ -291,6 +294,7 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
scaleWidth,
|
||||||
isCliping,
|
isCliping,
|
||||||
imgPosition,
|
imgPosition,
|
||||||
clipShape,
|
clipShape,
|
||||||
|
|
|
@ -0,0 +1,266 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="editable-element text"
|
||||||
|
:class="{ 'lock': elementInfo.isLock }"
|
||||||
|
:style="{
|
||||||
|
top: elementInfo.top + 'px',
|
||||||
|
left: elementInfo.left + 'px',
|
||||||
|
width: elementInfo.width + 'px',
|
||||||
|
transform: `rotate(${elementInfo.rotate}deg)`,
|
||||||
|
}"
|
||||||
|
@mousedown="handleSelectElement($event, false)"
|
||||||
|
>
|
||||||
|
<div class="element-content"
|
||||||
|
:style="{
|
||||||
|
backgroundColor: elementInfo.fill,
|
||||||
|
opacity: elementInfo.opacity,
|
||||||
|
textShadow: elementInfo.shadow,
|
||||||
|
lineHeight: elementInfo.lineHeight,
|
||||||
|
letterSpacing: (elementInfo.letterSpacing || 0) + 'px',
|
||||||
|
}"
|
||||||
|
v-contextmenu="contextmenus"
|
||||||
|
>
|
||||||
|
<ElementBorder
|
||||||
|
:width="elementInfo.width"
|
||||||
|
:height="elementInfo.height"
|
||||||
|
:borderColor="elementInfo.borderColor"
|
||||||
|
:borderWidth="elementInfo.borderWidth"
|
||||||
|
:borderStyle="elementInfo.borderStyle"
|
||||||
|
/>
|
||||||
|
<div class="text-content"
|
||||||
|
v-html="elementInfo.content"
|
||||||
|
:contenteditable="isActive && !elementInfo.isLock"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="operate"
|
||||||
|
:class="{
|
||||||
|
'show': isActive,
|
||||||
|
'multi-select': isMultiSelect && isActive,
|
||||||
|
'selected': isHandleEl
|
||||||
|
}"
|
||||||
|
:style="{ transform: `scale(${1 / canvasScale})` }"
|
||||||
|
v-contextmenu="contextmenus"
|
||||||
|
>
|
||||||
|
<BorderLine
|
||||||
|
class="el-border-line"
|
||||||
|
v-for="line in borderLines"
|
||||||
|
:key="line.type"
|
||||||
|
:type="line.type"
|
||||||
|
:style="line.style"
|
||||||
|
:isWide="true"
|
||||||
|
@mousedown="handleSelectElement($event)"
|
||||||
|
/>
|
||||||
|
<template v-if="!elementInfo.isLock && (isActiveGroupElement || !isMultiSelect)">
|
||||||
|
<ResizablePoint class="el-resizable-point"
|
||||||
|
v-for="point in resizablePoints"
|
||||||
|
:key="point.type"
|
||||||
|
:type="point.type"
|
||||||
|
:style="point.style"
|
||||||
|
@mousedown.stop="scaleElement($event, elementInfo, point.direction)"
|
||||||
|
/>
|
||||||
|
<RotateHandler
|
||||||
|
class="el-rotate-handle"
|
||||||
|
:style="{ left: scaleWidth / 2 + 'px' }"
|
||||||
|
@mousedown.stop="rotateElement(elementInfo)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<AnimationIndex v-if="animationIndex !== -1" :animationIndex="animationIndex" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, ref, PropType } from 'vue'
|
||||||
|
|
||||||
|
import { PPTTextElement } from '@/types/slides'
|
||||||
|
import { ElementScaleHandler, OperateResizablePointTypes, OperateBorderLineTypes } from '@/types/edit'
|
||||||
|
|
||||||
|
import { CLIPPATHS, ClipPathTypes } from '@/configs/imageClip'
|
||||||
|
import { OPERATE_KEYS } from '@/configs/element'
|
||||||
|
|
||||||
|
import ElementBorder from '@/views/_common/_element/ElementBorder.vue'
|
||||||
|
import RotateHandler from '@/views/_common/_operate/RotateHandler.vue'
|
||||||
|
import ResizablePoint from '@/views/_common/_operate/ResizablePoint.vue'
|
||||||
|
import BorderLine from '@/views/_common/_operate/BorderLine.vue'
|
||||||
|
import AnimationIndex from '@/views/_common/_operate/AnimationIndex.vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'slide-element-text',
|
||||||
|
components: {
|
||||||
|
ElementBorder,
|
||||||
|
RotateHandler,
|
||||||
|
ResizablePoint,
|
||||||
|
BorderLine,
|
||||||
|
AnimationIndex,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
elementInfo: {
|
||||||
|
type: Object as PropType<PPTTextElement>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
canvasScale: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
isActive: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
isHandleEl: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
isActiveGroupElement: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
isMultiSelect: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
animationIndex: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
selectElement: {
|
||||||
|
type: Function as PropType<(e: MouseEvent, element: PPTTextElement, canMove?: boolean) => void>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
rotateElement: {
|
||||||
|
type: Function as PropType<(element: PPTTextElement) => void>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
scaleElement: {
|
||||||
|
type: Function as PropType<(e: MouseEvent, element: PPTTextElement, command: ElementScaleHandler) => void>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
contextmenus: {
|
||||||
|
type: Function,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const scaleWidth = computed(() => props.elementInfo ? props.elementInfo.width * props.canvasScale : 0)
|
||||||
|
const scaleHeight = computed(() => props.elementInfo ? props.elementInfo.height * props.canvasScale : 0)
|
||||||
|
|
||||||
|
const resizablePoints = computed(() => {
|
||||||
|
return [
|
||||||
|
{ type: OperateResizablePointTypes.TL, direction: OPERATE_KEYS.LEFT_TOP, style: {} },
|
||||||
|
{ type: OperateResizablePointTypes.TC, direction: OPERATE_KEYS.TOP, style: {left: scaleWidth.value / 2 + 'px'} },
|
||||||
|
{ type: OperateResizablePointTypes.TR, direction: OPERATE_KEYS.RIGHT_TOP, style: {left: scaleWidth.value + 'px'} },
|
||||||
|
{ type: OperateResizablePointTypes.ML, direction: OPERATE_KEYS.LEFT, style: {top: scaleHeight.value / 2 + 'px'} },
|
||||||
|
{ type: OperateResizablePointTypes.MR, direction: OPERATE_KEYS.RIGHT, style: {left: scaleWidth.value + 'px', top: scaleHeight.value / 2 + 'px'} },
|
||||||
|
{ type: OperateResizablePointTypes.BL, direction: OPERATE_KEYS.LEFT_BOTTOM, style: {top: scaleHeight.value + 'px'} },
|
||||||
|
{ type: OperateResizablePointTypes.BC, direction: OPERATE_KEYS.BOTTOM, style: {left: scaleWidth.value / 2 + 'px', top: scaleHeight.value + 'px'} },
|
||||||
|
{ type: OperateResizablePointTypes.BR, direction: OPERATE_KEYS.RIGHT_BOTTOM, style: {left: scaleWidth.value + 'px', top: scaleHeight.value + 'px'} },
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const borderLines = computed(() => {
|
||||||
|
return [
|
||||||
|
{ type: OperateBorderLineTypes.T, style: {width: scaleWidth.value + 'px'} },
|
||||||
|
{ type: OperateBorderLineTypes.B, style: {top: scaleHeight.value + 'px', width: scaleWidth.value + 'px'} },
|
||||||
|
{ type: OperateBorderLineTypes.L, style: {height: scaleHeight.value + 'px'} },
|
||||||
|
{ type: OperateBorderLineTypes.R, style: {left: scaleWidth.value + 'px', height: scaleHeight.value + 'px'} },
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleSelectElement = (e: MouseEvent, canMove = true) => {
|
||||||
|
if(props.elementInfo.isLock) return
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
|
props.selectElement(e, props.elementInfo, canMove)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
scaleWidth,
|
||||||
|
resizablePoints,
|
||||||
|
borderLines,
|
||||||
|
handleSelectElement,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.editable-element {
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
&.lock .el-border-line {
|
||||||
|
border-color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .el-border-line {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.lock .element-content {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.element-content {
|
||||||
|
position: relative;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
.text-content {
|
||||||
|
position: relative;
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .text-content {
|
||||||
|
word-break: break-word;
|
||||||
|
font-family: '微软雅黑';
|
||||||
|
outline: 0;
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background-color: rgba(27, 110, 232, 0.3);
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style-type: disc;
|
||||||
|
padding-inline-start: 30px;
|
||||||
|
li {
|
||||||
|
list-style-type: disc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ol {
|
||||||
|
list-style-type: decimal;
|
||||||
|
padding-inline-start: 30px;
|
||||||
|
li {
|
||||||
|
list-style-type: decimal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.operate {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 100;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
.el-border-line,
|
||||||
|
.el-resizable-point,
|
||||||
|
.el-rotate-handle {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.multi-select:not(.selected) .el-border-line {
|
||||||
|
border-color: rgba($color: $themeColor, $alpha: .5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-border-line,
|
||||||
|
.el-resizable-point,
|
||||||
|
.el-rotate-handle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue