jvs-ppt-ui 0.0.11 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -1
- package/dist/lib/jvs-ppt-ui.es.js +21682 -21520
- package/dist/lib/jvs-ppt-ui.umd.js +85 -85
- package/dist/lib/style.css +1 -1
- package/package.json +1 -1
- package/src/App.vue +2 -2
- package/src/components/CanvasArea.vue +44 -21
- package/src/components/Header.vue +1592 -1564
- package/src/components/PptEditor.vue +5 -0
- package/src/components/PresentationMode.vue +2 -1
- package/src/components/SidebarLeft.vue +17 -5
- package/src/components/TemplateGallery.vue +217 -119
- package/src/components/canvas/elements/CanvasShapeElement.vue +3 -3
- package/src/components/canvas/elements/CanvasTextElement.vue +4 -4
- package/src/composables/canvas/useCanvasShortcuts.ts +50 -1
- package/src/composables/canvas/useCanvasTable.ts +8 -3
- package/src/composables/canvas/useCanvasText.ts +7 -3
- package/src/composables/sidebar/useSidebarElementTransform.ts +17 -4
- package/src/composables/useSlides.ts +33 -14
- package/src/utils/groupFlip.ts +108 -0
package/package.json
CHANGED
package/src/App.vue
CHANGED
|
@@ -47,7 +47,7 @@ async function handleCustomUpload(fileOrBlob: File | Blob, context: UploadResour
|
|
|
47
47
|
<!-- :custom-upload="handleCustomUpload" -->
|
|
48
48
|
<PptEditor ref="editorRef" :custom-upload="handleCustomUpload" :enable-mcp="true" :enable-ai="true"
|
|
49
49
|
ai-api-url="/jvs-dam/api/ppt/ai-replace" :enable-material="true"
|
|
50
|
-
material-
|
|
51
|
-
material-
|
|
50
|
+
material-api-url="/jvs-dam/stock/search" material-page-api-url="/jvs-dam/stock/search"
|
|
51
|
+
material-save-api-url="/jvs-dam/api/dam/material/save"
|
|
52
52
|
@save="handleSave" @ready="handleReady" />
|
|
53
53
|
</template>
|
|
@@ -18,6 +18,7 @@ import CanvasGroupElement from './canvas/elements/CanvasGroupElement.vue'
|
|
|
18
18
|
import CanvasSymbolPanel from './canvas/panels/CanvasSymbolPanel.vue'
|
|
19
19
|
import CanvasSearchPanel from './canvas/panels/CanvasSearchPanel.vue'
|
|
20
20
|
import CanvasSelectionPanel from './canvas/panels/CanvasSelectionPanel.vue'
|
|
21
|
+
import { isTextContainingElement } from '../utils/groupFlip'
|
|
21
22
|
|
|
22
23
|
import { useCanvasTable, cleanTableDOMForSaving } from '../composables/canvas/useCanvasTable'
|
|
23
24
|
import { useCanvasAlignment } from '../composables/canvas/useCanvasAlignment'
|
|
@@ -66,7 +67,7 @@ const {
|
|
|
66
67
|
globalThemeColor, globalFontColor, globalFontFamily, globalBgColor, getActiveSlideThemeRecipe, croppingElementId,
|
|
67
68
|
isMasterEditingView, slideMasters, activeMasterId, activeLayoutId, activeLayout, sortSlideElements,
|
|
68
69
|
selectedSlideIndexes, isMouseOverSlideList, selectAllSlides, deleteSelectedSlides, copySelectedSlides, cutSelectedSlides, duplicateSelectedSlides, duplicateSelectedElements,
|
|
69
|
-
getEffectiveMasterElementsForSlide, getEffectiveSlideBackground, uploadResource
|
|
70
|
+
getEffectiveMasterElementsForSlide, getEffectiveSlideBackground, uploadResource, isReadonly
|
|
70
71
|
} = useSlides()
|
|
71
72
|
const activeTab = activeTabRaw as any
|
|
72
73
|
|
|
@@ -205,7 +206,8 @@ const {
|
|
|
205
206
|
applyTextStyleToHtml: (html: string, style: Record<string, any>) => applyTextStyleToHtml(html, style),
|
|
206
207
|
renderLatexToSvg: (latex: string) => renderLatexToSvg(latex),
|
|
207
208
|
sanitizeNbspInHtml: (html: string) => sanitizeNbspInHtml(html),
|
|
208
|
-
updateTextFontScale: (el: any) => updateTextFontScale(el)
|
|
209
|
+
updateTextFontScale: (el: any) => updateTextFontScale(el),
|
|
210
|
+
isReadonly
|
|
209
211
|
})
|
|
210
212
|
const lastMouseDownTarget = ref<HTMLElement | null>(null)
|
|
211
213
|
|
|
@@ -405,7 +407,8 @@ const {
|
|
|
405
407
|
getOverlayWidth: (el: any) => getOverlayWidth(el),
|
|
406
408
|
getOverlayHeight: (el: any) => getOverlayHeight(el),
|
|
407
409
|
selectedElementWidth,
|
|
408
|
-
selectedElementHeight
|
|
410
|
+
selectedElementHeight,
|
|
411
|
+
isReadonly
|
|
409
412
|
})
|
|
410
413
|
|
|
411
414
|
const getSelectedCellCount = () => selectedCellCount.value
|
|
@@ -1509,7 +1512,8 @@ const { handleGlobalKeyDown } = useCanvasShortcuts({
|
|
|
1509
1512
|
getElementPixels: (el: any) => getElementPixels(el),
|
|
1510
1513
|
shapeFormatPainter,
|
|
1511
1514
|
notify,
|
|
1512
|
-
addHistorySnapshot
|
|
1515
|
+
addHistorySnapshot,
|
|
1516
|
+
isReadonly
|
|
1513
1517
|
})
|
|
1514
1518
|
|
|
1515
1519
|
watch(fitTrigger, () => {
|
|
@@ -2397,6 +2401,7 @@ function getLineConfigFromContent(content: string) {
|
|
|
2397
2401
|
}
|
|
2398
2402
|
|
|
2399
2403
|
function handleCanvasDblClick(e: MouseEvent) {
|
|
2404
|
+
if (isReadonly.value) return
|
|
2400
2405
|
// 1. 如果事件目标本身或其父级就在某元素内
|
|
2401
2406
|
const target = e.target as HTMLElement
|
|
2402
2407
|
const closestElDom = target?.closest?.('.canvas-element, [id^="canvas-el-"]') as HTMLElement | null
|
|
@@ -2555,6 +2560,12 @@ function handleCanvasMouseDown(e: MouseEvent) {
|
|
|
2555
2560
|
return
|
|
2556
2561
|
}
|
|
2557
2562
|
|
|
2563
|
+
if (isReadonly.value) {
|
|
2564
|
+
selectedElementId.value = null
|
|
2565
|
+
selectedElementIds.value = []
|
|
2566
|
+
return
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2558
2569
|
if (shapeFormatPainter.value) {
|
|
2559
2570
|
shapeFormatPainter.value = null
|
|
2560
2571
|
return
|
|
@@ -3256,7 +3267,7 @@ let lineControlPointStartX = 0
|
|
|
3256
3267
|
let lineControlPointStartY = 0
|
|
3257
3268
|
|
|
3258
3269
|
function startDragLineControlPoint(e: MouseEvent, el: any, type: string) {
|
|
3259
|
-
if (el.locked) return
|
|
3270
|
+
if (isReadonly.value || el.locked) return
|
|
3260
3271
|
e.stopPropagation()
|
|
3261
3272
|
e.preventDefault()
|
|
3262
3273
|
|
|
@@ -3578,6 +3589,10 @@ function handleElementMouseDown(e: MouseEvent, el: any) {
|
|
|
3578
3589
|
|
|
3579
3590
|
e.stopPropagation()
|
|
3580
3591
|
|
|
3592
|
+
if (isReadonly.value) {
|
|
3593
|
+
return
|
|
3594
|
+
}
|
|
3595
|
+
|
|
3581
3596
|
if (shapeFormatPainter.value) {
|
|
3582
3597
|
if (shapeFormatPainter.value.mode === 'text-range') {
|
|
3583
3598
|
return
|
|
@@ -3606,6 +3621,7 @@ function handleElementMouseDown(e: MouseEvent, el: any) {
|
|
|
3606
3621
|
}
|
|
3607
3622
|
|
|
3608
3623
|
function handleBorderMouseDown(e: MouseEvent, el: any) {
|
|
3624
|
+
if (isReadonly.value) return
|
|
3609
3625
|
if (e.button !== 0) return
|
|
3610
3626
|
if (isLockedMasterElement(el) || el.locked) {
|
|
3611
3627
|
return
|
|
@@ -3650,6 +3666,7 @@ function handleBorderMouseDown(e: MouseEvent, el: any) {
|
|
|
3650
3666
|
}
|
|
3651
3667
|
|
|
3652
3668
|
function handleElementDblClick(e: MouseEvent, el: any) {
|
|
3669
|
+
if (isReadonly.value) return
|
|
3653
3670
|
let target = el
|
|
3654
3671
|
if (selectedElementIds.value.length > 0 && !selectedElementIds.value.includes(el.id)) {
|
|
3655
3672
|
const activeSelectedEl = findSelectedElementUnderPoint(e.clientX, e.clientY)
|
|
@@ -3762,6 +3779,7 @@ function handleChildDragKeyUp(e: KeyboardEvent) {
|
|
|
3762
3779
|
}
|
|
3763
3780
|
|
|
3764
3781
|
function startChildDrag(e: MouseEvent, child: any, parentGroup: any) {
|
|
3782
|
+
if (isReadonly.value) return
|
|
3765
3783
|
const childContainerEl = document.getElementById(`editable-${child.id}`)
|
|
3766
3784
|
const childCanvasElDom = document.getElementById(`canvas-el-${child.id}`)
|
|
3767
3785
|
const isChildClickInside = (childContainerEl && (childContainerEl === e.target || childContainerEl.contains(e.target as Node))) ||
|
|
@@ -3975,6 +3993,7 @@ function handleChildMouseDown(e: MouseEvent, child: any, parentGroup: any) {
|
|
|
3975
3993
|
checkAndExitEditText(undefined, true)
|
|
3976
3994
|
}
|
|
3977
3995
|
e.stopPropagation()
|
|
3996
|
+
if (isReadonly.value) return
|
|
3978
3997
|
|
|
3979
3998
|
if (shapeFormatPainter.value) {
|
|
3980
3999
|
if (shapeFormatPainter.value.mode === 'text-range') {
|
|
@@ -4039,7 +4058,7 @@ function handleChildMouseDown(e: MouseEvent, child: any, parentGroup: any) {
|
|
|
4039
4058
|
}
|
|
4040
4059
|
|
|
4041
4060
|
function handleChildClick(e: MouseEvent, child: any, parentGroup: any) {
|
|
4042
|
-
if (activeTool.value) return
|
|
4061
|
+
if (isReadonly.value || activeTool.value) return
|
|
4043
4062
|
if (isLockedMasterElement(child)) return
|
|
4044
4063
|
e.stopPropagation()
|
|
4045
4064
|
if (dragOccurred) {
|
|
@@ -4073,6 +4092,7 @@ function handleChildClick(e: MouseEvent, child: any, parentGroup: any) {
|
|
|
4073
4092
|
}
|
|
4074
4093
|
|
|
4075
4094
|
function handleChildDblClick(e: MouseEvent, child: any, parentGroup: any) {
|
|
4095
|
+
if (isReadonly.value) return
|
|
4076
4096
|
e.stopPropagation()
|
|
4077
4097
|
if (isLockedMasterElement(child)) return
|
|
4078
4098
|
// 已在编辑该子元素时的双击 = 原生选词,保持选区不重进编辑
|
|
@@ -4096,6 +4116,7 @@ function handleChildDblClick(e: MouseEvent, child: any, parentGroup: any) {
|
|
|
4096
4116
|
}
|
|
4097
4117
|
|
|
4098
4118
|
function handleElementClick(e: MouseEvent, elId: string) {
|
|
4119
|
+
if (isReadonly.value) return
|
|
4099
4120
|
if (activeTool.value) return
|
|
4100
4121
|
if (elId) {
|
|
4101
4122
|
const targetEl = activeSlide.value?.elements.find(x => x.id === elId)
|
|
@@ -4385,7 +4406,7 @@ function getOverlayStyle(el: any) {
|
|
|
4385
4406
|
width: `${w}px`,
|
|
4386
4407
|
height: `${h}px`,
|
|
4387
4408
|
zIndex: isSelectedActive ? 300 : 100,
|
|
4388
|
-
transform: `rotate(${((parentPixels.rotate || 0) + (el.style?.rotate || 0))}deg) scale(${el
|
|
4409
|
+
transform: `rotate(${((parentPixels.rotate || 0) + (el.style?.rotate || 0))}deg) scale(${!isTextContainingElement(el) && ((el.style as any)?.flipH || el.flipH || el.isFlipH) ? -1 : 1}, ${!isTextContainingElement(el) && ((el.style as any)?.flipV || el.flipV || el.isFlipV) ? -1 : 1})`,
|
|
4389
4410
|
pointerEvents: 'none' as const,
|
|
4390
4411
|
boxSizing: 'border-box' as const,
|
|
4391
4412
|
'--handle-scale': +(100 / (zoomRatio.value || 100)).toFixed(4)
|
|
@@ -4399,7 +4420,7 @@ function getOverlayStyle(el: any) {
|
|
|
4399
4420
|
width: getOverlayWidth(el),
|
|
4400
4421
|
height: getOverlayHeight(el),
|
|
4401
4422
|
zIndex: isSelectedActive ? 300 : 100,
|
|
4402
|
-
transform: el.type === 'line' ? 'none' : `rotate(${el.style.rotate || 0}deg) scale(${el
|
|
4423
|
+
transform: el.type === 'line' ? 'none' : `rotate(${el.style.rotate || 0}deg) scale(${!isTextContainingElement(el) && ((el.style as any)?.flipH || el.flipH || el.isFlipH) ? -1 : 1}, ${!isTextContainingElement(el) && ((el.style as any)?.flipV || el.flipV || el.isFlipV) ? -1 : 1})`,
|
|
4403
4424
|
pointerEvents: 'none' as const,
|
|
4404
4425
|
boxSizing: 'border-box' as const,
|
|
4405
4426
|
'--handle-scale': +(100 / (zoomRatio.value || 100)).toFixed(4)
|
|
@@ -4495,6 +4516,7 @@ function handleTextMouseLeave(_e: MouseEvent, el: any) {
|
|
|
4495
4516
|
}
|
|
4496
4517
|
|
|
4497
4518
|
function startDrag(e: MouseEvent, el: any) {
|
|
4519
|
+
if (isReadonly.value) return
|
|
4498
4520
|
const target = e.target as HTMLElement
|
|
4499
4521
|
const container = document.getElementById(`editable-${el.id}`)
|
|
4500
4522
|
const canvasElDom = document.getElementById(`canvas-el-${el.id}`)
|
|
@@ -7821,7 +7843,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
7821
7843
|
</defs>
|
|
7822
7844
|
</svg>
|
|
7823
7845
|
<!-- Smart Guides & Corner Decorations -->
|
|
7824
|
-
<CanvasSmartGuides :alignment-lines="alignmentLines" :active-slide-id="activeSlide?.id" />
|
|
7846
|
+
<CanvasSmartGuides v-if="!isReadonly" :alignment-lines="alignmentLines" :active-slide-id="activeSlide?.id" />
|
|
7825
7847
|
|
|
7826
7848
|
<!-- Ctrl Drag Copy Origin Replica (原元素在原地 100% 完整原样真实呈现,杜绝空虚高亮块) -->
|
|
7827
7849
|
<template v-if="ctrlDragReplicas.length > 0">
|
|
@@ -7836,7 +7858,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
7836
7858
|
width: (replica.el.type === 'text' && !isMasterOrPlaceholder(replica.el) && (replica.el.autoFit?.type === 'shape' || replica.el.style?.width === 'auto') && !replica.el.content?.includes('<table') && !replica.el.latex && (replica.el.style?.writingMode === 'vertical-rl' || replica.el.style?.writingMode === 'vertical-lr')) ? 'auto' : (replica.el.style?.width || (replica.el.width ? `${replica.el.width}px` : 'auto')),
|
|
7837
7859
|
height: (replica.el.type === 'text' && !isMasterOrPlaceholder(replica.el) && (replica.el.autoFit?.type === 'shape' || replica.el.style?.height === 'auto') && !replica.el.content?.includes('<table') && !replica.el.latex && replica.el.style?.writingMode !== 'vertical-rl' && replica.el.style?.writingMode !== 'vertical-lr') ? 'auto' : (replica.el.style?.height || (replica.el.height ? `${replica.el.height}px` : 'auto')),
|
|
7838
7860
|
zIndex: 2,
|
|
7839
|
-
transform: replica.el.type === 'line' ? 'none' : `rotate(${replica.el.style?.rotate || replica.el.rotate || 0}deg) scale(${replica.el
|
|
7861
|
+
transform: replica.el.type === 'line' ? 'none' : `rotate(${replica.el.style?.rotate || replica.el.rotate || 0}deg) scale(${!isTextContainingElement(replica.el) && ((replica.el.style as any)?.flipH || replica.el.flipH || replica.el.isFlipH) ? -1 : 1}, ${!isTextContainingElement(replica.el) && ((replica.el.style as any)?.flipV || replica.el.flipV || replica.el.isFlipV) ? -1 : 1})`,
|
|
7840
7862
|
opacity: (replica.el.style as any)?.opacity !== undefined ? (replica.el.style as any)?.opacity : 1,
|
|
7841
7863
|
filter: getElementDropShadowFilter(replica.el),
|
|
7842
7864
|
pointerEvents: 'none'
|
|
@@ -7926,7 +7948,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
7926
7948
|
width: el.style?.width || (el.width !== undefined ? `${el.width}px` : 'auto'),
|
|
7927
7949
|
height: el.style?.height || (el.height !== undefined ? `${el.height}px` : 'auto'),
|
|
7928
7950
|
zIndex: 1,
|
|
7929
|
-
transform: `rotate(${el.style?.rotate || el.rotate || 0}deg) scale(${el
|
|
7951
|
+
transform: `rotate(${el.style?.rotate || el.rotate || 0}deg) scale(${!isTextContainingElement(el) && ((el.style as any)?.flipH || el.flipH || el.isFlipH) ? -1 : 1}, ${!isTextContainingElement(el) && ((el.style as any)?.flipV || el.flipV || el.isFlipV) ? -1 : 1})`,
|
|
7930
7952
|
opacity: el.style?.opacity !== undefined ? el.style.opacity : 1,
|
|
7931
7953
|
pointerEvents: 'none'
|
|
7932
7954
|
}">
|
|
@@ -8025,14 +8047,14 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
8025
8047
|
width: (el.type === 'text' && !isMasterOrPlaceholder(el) && (el.autoFit?.type === 'shape' || el.style?.width === 'auto') && !el.content?.includes('<table') && !el.latex && (el.style?.writingMode === 'vertical-rl' || el.style?.writingMode === 'vertical-lr')) ? 'auto' : (el.style?.width || 'auto'),
|
|
8026
8048
|
height: (el.type === 'text' && !isMasterOrPlaceholder(el) && (el.autoFit?.type === 'shape' || el.style?.height === 'auto') && !el.content?.includes('<table') && !el.latex && el.style?.writingMode !== 'vertical-rl' && el.style?.writingMode !== 'vertical-lr') ? 'auto' : (el.style?.height || (el.height ? `${el.height}px` : 'auto')),
|
|
8027
8049
|
zIndex: 3,
|
|
8028
|
-
cursor: (isEditingText && (selectedElementId === el.id || editingElementId === el.id) && (el.type === 'text' || el.type === 'shape') && !el.latex && !el.content?.includes('<table')) ? 'text' : undefined,
|
|
8029
|
-
transform: el.type === 'line' ? 'none' : `rotate(${el.style?.rotate || 0}deg) scale(${el
|
|
8050
|
+
cursor: isReadonly ? 'default' : ((isEditingText && (selectedElementId === el.id || editingElementId === el.id) && (el.type === 'text' || el.type === 'shape') && !el.latex && !el.content?.includes('<table')) ? 'text' : undefined),
|
|
8051
|
+
transform: el.type === 'line' ? 'none' : `rotate(${el.style?.rotate || 0}deg) scale(${!isTextContainingElement(el) && ((el.style as any)?.flipH || el.flipH || el.isFlipH) ? -1 : 1}, ${!isTextContainingElement(el) && ((el.style as any)?.flipV || el.flipV || el.isFlipV) ? -1 : 1})`,
|
|
8030
8052
|
opacity: (el.style as any)?.opacity !== undefined ? (el.style as any)?.opacity : 1,
|
|
8031
8053
|
filter: getElementDropShadowFilter(el),
|
|
8032
8054
|
pointerEvents: (isDragging || isCanvasDragging) ? 'none' : ((isDraggingLine && el.id === selectedElementId) ? 'none' : (isLockedMasterElement(el) ? 'none' : (el.type === 'group' ? 'none' : (el.type === 'line' ? 'none' : (((el.type === 'shape' || !el.type) && !hasElementFill(el) && !hasElementBorder(el) && !isImagePlaceholder(el)) ? 'none' : 'auto')))))
|
|
8033
8055
|
}" @mousedown="handleElementMouseDown($event, el)" @click="handleElementClick($event, el.id)"
|
|
8034
8056
|
@contextmenu.stop.prevent="handleElementContextMenu($event, el)"
|
|
8035
|
-
@dblclick.stop="handleElementDblClick($event, el)"
|
|
8057
|
+
@dblclick.stop="isReadonly ? null : handleElementDblClick($event, el)"
|
|
8036
8058
|
@mouseenter="!(isDragging || isCanvasDragging) && (hoveredElementId = el.id)"
|
|
8037
8059
|
@mouseleave="!(isDragging || isCanvasDragging) && (hoveredElementId = (hoveredElementId === el.id ? null : hoveredElementId))">
|
|
8038
8060
|
|
|
@@ -8124,7 +8146,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
8124
8146
|
@focusout="handleTextBlur($event, el)"
|
|
8125
8147
|
@input="handleInput($event, el)"
|
|
8126
8148
|
@keydown="handleTextKeyDown($event, el)"
|
|
8127
|
-
@dblclick="el.latex ? handleFormulaDblClick(el) : (el.content.includes('<table') ? handleTableDblClick($event, el) : startEditingTextElement(el.id, $event))"
|
|
8149
|
+
@dblclick="isReadonly ? null : (el.latex ? handleFormulaDblClick(el) : (el.content.includes('<table') ? handleTableDblClick($event, el) : startEditingTextElement(el.id, $event)))"
|
|
8128
8150
|
@mousemove="handleTextMouseMove($event, el)"
|
|
8129
8151
|
@mouseleave="handleTextMouseLeave($event, el)"
|
|
8130
8152
|
@table-dbl-click="handleTableDblClick($event, el)"
|
|
@@ -8154,7 +8176,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
8154
8176
|
@text-key-down="handleTextKeyDown($event, el)"
|
|
8155
8177
|
@mousemove="handleTextMouseMove($event, el)"
|
|
8156
8178
|
@mouseleave="handleTextMouseLeave($event, el)"
|
|
8157
|
-
@dblclick="startEditingShapeText(el, $event)"
|
|
8179
|
+
@dblclick="isReadonly ? null : startEditingShapeText(el, $event)"
|
|
8158
8180
|
@start-editing="(ev) => startEditingShapeText(el, ev)"
|
|
8159
8181
|
@placeholder-upload="(id) => triggerPlaceholderImageUpload(id)"
|
|
8160
8182
|
/>
|
|
@@ -8466,7 +8488,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
8466
8488
|
</div>
|
|
8467
8489
|
|
|
8468
8490
|
<!-- Global Selection Overlay (renders on top of all canvas elements to avoid masking) -->
|
|
8469
|
-
<div v-for="selEl in selectedElements" :key="selEl.id" class="global-selection-overlay" :class="{
|
|
8491
|
+
<div v-for="selEl in (isReadonly ? [] : selectedElements)" :key="selEl.id" class="global-selection-overlay" :class="{
|
|
8470
8492
|
'line-selection': selEl.type === 'line',
|
|
8471
8493
|
'locked-selection': selEl.locked,
|
|
8472
8494
|
'table-selection': selEl.type === 'text' && selEl.content && selEl.content.includes('<table'),
|
|
@@ -8636,7 +8658,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
8636
8658
|
</template>
|
|
8637
8659
|
|
|
8638
8660
|
<!-- Draggable border overlay to facilitate dragging and state switching (available in both editing & selected states) -->
|
|
8639
|
-
<div v-if="selEl.type !== 'line' && !selEl.locked && croppingElementId !== selEl.id && selectedElementIds.includes(selEl.id)"
|
|
8661
|
+
<div v-if="!isReadonly && selEl.type !== 'line' && !selEl.locked && croppingElementId !== selEl.id && selectedElementIds.includes(selEl.id)"
|
|
8640
8662
|
class="text-edit-drag-helper"
|
|
8641
8663
|
style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; pointer-events: none; z-index: 200;"
|
|
8642
8664
|
@contextmenu.stop.prevent="handleElementContextMenu($event, selEl)"
|
|
@@ -8697,7 +8719,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
8697
8719
|
|
|
8698
8720
|
<!-- Transform, Resize, Line Endpoints & Crop Handles -->
|
|
8699
8721
|
<CanvasTransformHandles
|
|
8700
|
-
v-if="selectedElementIds.includes(selEl.id)"
|
|
8722
|
+
v-if="!isReadonly && selectedElementIds.includes(selEl.id)"
|
|
8701
8723
|
:element="selEl"
|
|
8702
8724
|
:selected-element-id="selectedElementId"
|
|
8703
8725
|
:selected-element-ids="selectedElementIds"
|
|
@@ -8860,7 +8882,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
8860
8882
|
|
|
8861
8883
|
<!-- Floating Quick Toolbar (No rotation, rendered as sibling of main-slide inside slide-zoom-container) -->
|
|
8862
8884
|
<CanvasQuickToolbar
|
|
8863
|
-
v-if="!isDragging && !isResizing && !isRotating && !isDraggingLine"
|
|
8885
|
+
v-if="!isReadonly && !isDragging && !isResizing && !isRotating && !isDraggingLine"
|
|
8864
8886
|
:selected-element="selectedElement"
|
|
8865
8887
|
:selected-element-ids="selectedElementIds"
|
|
8866
8888
|
:is-dragging="isDragging"
|
|
@@ -8934,11 +8956,12 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
8934
8956
|
|
|
8935
8957
|
<!-- Footer Notes -->
|
|
8936
8958
|
<div class="canvas-footer">
|
|
8937
|
-
<input class="canvas-footer-notes" v-model="activeSlide.notes" placeholder="点击输入演讲者备注" />
|
|
8959
|
+
<input class="canvas-footer-notes" :readonly="isReadonly" v-model="activeSlide.notes" :placeholder="isReadonly ? '演讲者备注' : '点击输入演讲者备注'" />
|
|
8938
8960
|
</div>
|
|
8939
8961
|
|
|
8940
8962
|
<!-- Custom Context Menu Component -->
|
|
8941
8963
|
<CanvasContextMenu
|
|
8964
|
+
v-if="!isReadonly"
|
|
8942
8965
|
v-model:visible="showContextMenu"
|
|
8943
8966
|
:position="contextMenuPos"
|
|
8944
8967
|
:type="contextMenuType"
|