jvs-ppt-ui 0.0.11 → 0.0.12
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 +21367 -21291
- 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 +1 -1
- package/src/components/CanvasArea.vue +38 -16
- package/src/components/Header.vue +1587 -1564
- package/src/components/PptEditor.vue +5 -0
- package/src/components/SidebarLeft.vue +14 -3
- 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/useSlides.ts +14 -5
|
@@ -80,6 +80,7 @@ const {
|
|
|
80
80
|
aiPptDialogVisible,
|
|
81
81
|
unsplashDialogVisible,
|
|
82
82
|
isPresenting,
|
|
83
|
+
isReadonly,
|
|
83
84
|
importSlides,
|
|
84
85
|
exportSlides,
|
|
85
86
|
currentEditingFormulaEl,
|
|
@@ -110,6 +111,10 @@ const {
|
|
|
110
111
|
uploadMediaResourcesInSlides
|
|
111
112
|
} = slidesState
|
|
112
113
|
|
|
114
|
+
watch(() => props.readonly, (val) => {
|
|
115
|
+
isReadonly.value = !!val
|
|
116
|
+
}, { immediate: true })
|
|
117
|
+
|
|
113
118
|
// 弹层挂载闩锁:首次 visible 后保持挂载(异步组件只加载一次,关闭动画不被 v-if 掐断)
|
|
114
119
|
function latchMount(source: Ref<boolean>) {
|
|
115
120
|
const opened = ref(false)
|
|
@@ -42,7 +42,7 @@ const {
|
|
|
42
42
|
enterMasterView, addMaster, deleteMaster, addLayout, deleteLayout, duplicateLayout,
|
|
43
43
|
clipboard, slideClipboard, handleUnifiedPaste, sortSlideElements, getEffectiveMasterElementsForSlide,
|
|
44
44
|
getEffectiveSlideBackground, isCanvasDragging, activeSlideVersion, pauseHistory,
|
|
45
|
-
activeSidebarTab
|
|
45
|
+
activeSidebarTab, isReadonly
|
|
46
46
|
} = useSlides()
|
|
47
47
|
|
|
48
48
|
const displaySlides = shallowRef<any[]>([...slides.value])
|
|
@@ -2092,7 +2092,7 @@ const masterGroupedLayouts = computed(() => {
|
|
|
2092
2092
|
|
|
2093
2093
|
<!-- Regular Sidebar Layouts -->
|
|
2094
2094
|
<template v-else>
|
|
2095
|
-
<div class="sidebar-add-slide-container" style="padding: 12px 16px; display: flex; flex-shrink: 0;">
|
|
2095
|
+
<div v-if="!isReadonly" class="sidebar-add-slide-container" style="padding: 12px 16px; display: flex; flex-shrink: 0;">
|
|
2096
2096
|
<div class="custom-split-button">
|
|
2097
2097
|
<!-- Main Add Slide Button -->
|
|
2098
2098
|
<button @click="addSlide" class="split-btn-left">
|
|
@@ -2153,7 +2153,7 @@ const masterGroupedLayouts = computed(() => {
|
|
|
2153
2153
|
</button>
|
|
2154
2154
|
</div> -->
|
|
2155
2155
|
|
|
2156
|
-
<Draggable v-if="activeSidebarTab === 'outline'" class="slide-list" ref="slideListRef" :modelValue="displaySlides"
|
|
2156
|
+
<Draggable v-if="activeSidebarTab === 'outline'" :disabled="isReadonly" class="slide-list" ref="slideListRef" :modelValue="displaySlides"
|
|
2157
2157
|
:animation="200" :scroll="true" :scrollSensitivity="50" @end="handleDragEnd" itemKey="id"
|
|
2158
2158
|
@mouseenter="isMouseOverSlideList = true" @mouseleave="isMouseOverSlideList = false">
|
|
2159
2159
|
<template #item="{ element: slide, index }">
|
|
@@ -2759,6 +2759,17 @@ const masterGroupedLayouts = computed(() => {
|
|
|
2759
2759
|
</template>
|
|
2760
2760
|
</template>
|
|
2761
2761
|
|
|
2762
|
+
<template v-else-if="isReadonly">
|
|
2763
|
+
<div class="menu-item" @click="copySlide(rightClickedIndex)">
|
|
2764
|
+
<span class="menu-label">{{ locale === 'zh' ? '复制' : 'Copy' }}</span>
|
|
2765
|
+
<span class="menu-shortcut">Ctrl + C</span>
|
|
2766
|
+
</div>
|
|
2767
|
+
<div class="menu-item" @click="playFromCurrent">
|
|
2768
|
+
<span class="menu-label">{{ locale === 'zh' ? '从当前放映' : 'Play from Current' }}</span>
|
|
2769
|
+
<span class="menu-shortcut">Shift + F5</span>
|
|
2770
|
+
</div>
|
|
2771
|
+
</template>
|
|
2772
|
+
|
|
2762
2773
|
<template v-else>
|
|
2763
2774
|
<div class="menu-item" @click="cutSlide(rightClickedIndex)">
|
|
2764
2775
|
<span class="menu-label">{{ locale === 'zh' ? '剪切' : 'Cut' }}</span>
|
|
@@ -22,7 +22,7 @@ import { getFontVariantStyle } from '../../../configs/fonts'
|
|
|
22
22
|
import { useSlides } from '../../../composables/useSlides'
|
|
23
23
|
import { createTextSelectionOverlay, type TextSelectionOverlayHandle } from '../textSelectionOverlay'
|
|
24
24
|
|
|
25
|
-
const { shapeFormatPainter } = useSlides()
|
|
25
|
+
const { shapeFormatPainter, isReadonly } = useSlides()
|
|
26
26
|
const isTextPaintActive = computed(() => !!(shapeFormatPainter.value && shapeFormatPainter.value.mode === 'text-range'))
|
|
27
27
|
const isTextSelectable = computed(() => (props.isEditingText && props.selectedElementId === props.element.id) || isTextPaintActive.value)
|
|
28
28
|
|
|
@@ -262,7 +262,7 @@ function formatShapeHtml(el: any): string {
|
|
|
262
262
|
<div
|
|
263
263
|
class="element-content shape-element-content"
|
|
264
264
|
style="width: 100%; height: 100%; position: relative;"
|
|
265
|
-
@dblclick.stop.prevent="isImagePlaceholder && isImagePlaceholder(element) ? emit('placeholderUpload', element.id) : (isTextSelectable ? null : emit('startEditing', $event))"
|
|
265
|
+
@dblclick.stop.prevent="isReadonly ? null : (isImagePlaceholder && isImagePlaceholder(element) ? emit('placeholderUpload', element.id) : (isTextSelectable ? null : emit('startEditing', $event)))"
|
|
266
266
|
>
|
|
267
267
|
<!-- Circle Shape (with fill) -->
|
|
268
268
|
<div
|
|
@@ -666,7 +666,7 @@ function formatShapeHtml(el: any): string {
|
|
|
666
666
|
@keydown="emit('textKeyDown', $event)"
|
|
667
667
|
@mousemove="emit('mousemove', $event)"
|
|
668
668
|
@mouseleave="emit('mouseleave', $event)"
|
|
669
|
-
@dblclick.stop.prevent="isImagePlaceholder && isImagePlaceholder(element) ? emit('placeholderUpload', element.id) : (isTextSelectable ? null : emit('startEditing', $event))"
|
|
669
|
+
@dblclick.stop.prevent="isReadonly ? null : (isImagePlaceholder && isImagePlaceholder(element) ? emit('placeholderUpload', element.id) : (isTextSelectable ? null : emit('startEditing', $event)))"
|
|
670
670
|
class="shape-text-overlay"
|
|
671
671
|
:style="{
|
|
672
672
|
display: 'block',
|
|
@@ -4,7 +4,7 @@ import { getFontVariantStyle } from '../../../configs/fonts'
|
|
|
4
4
|
import { useSlides } from '../../../composables/useSlides'
|
|
5
5
|
import { createTextSelectionOverlay, type TextSelectionOverlayHandle } from '../textSelectionOverlay'
|
|
6
6
|
|
|
7
|
-
const { shapeFormatPainter } = useSlides()
|
|
7
|
+
const { shapeFormatPainter, isReadonly } = useSlides()
|
|
8
8
|
const isTextPaintActive = computed(() => !!(shapeFormatPainter.value && shapeFormatPainter.value.mode === 'text-range'))
|
|
9
9
|
const isTextSelectable = computed(() => (props.isEditingText && props.element.id === props.selectedElementId) || isTextPaintActive.value)
|
|
10
10
|
|
|
@@ -176,7 +176,7 @@ function handleMaskMouseDown(e: MouseEvent) {
|
|
|
176
176
|
>
|
|
177
177
|
<!-- Sibling Table Edit Mask -->
|
|
178
178
|
<div
|
|
179
|
-
v-if="element.id === selectedElementId && isTable(element) && !isEditingText"
|
|
179
|
+
v-if="!isReadonly && element.id === selectedElementId && isTable(element) && !isEditingText"
|
|
180
180
|
class="table-edit-mask"
|
|
181
181
|
@mousemove="handleMaskMouseMove"
|
|
182
182
|
@mousedown="handleMaskMouseDown"
|
|
@@ -187,7 +187,7 @@ function handleMaskMouseDown(e: MouseEvent) {
|
|
|
187
187
|
|
|
188
188
|
<!-- Sibling Formula Edit Mask -->
|
|
189
189
|
<div
|
|
190
|
-
v-if="element.id === selectedElementId && element.latex && !isEditingText"
|
|
190
|
+
v-if="!isReadonly && element.id === selectedElementId && element.latex && !isEditingText"
|
|
191
191
|
class="formula-edit-mask"
|
|
192
192
|
@dblclick.stop="emit('formulaDblClick')"
|
|
193
193
|
></div>
|
|
@@ -203,7 +203,7 @@ function handleMaskMouseDown(e: MouseEvent) {
|
|
|
203
203
|
@focusout="emit('focusout', $event)"
|
|
204
204
|
@input="emit('input', $event)"
|
|
205
205
|
@keydown="emit('keydown', $event)"
|
|
206
|
-
@dblclick.stop="isTextSelectable ? null : emit('dblclick', $event)"
|
|
206
|
+
@dblclick.stop="isReadonly ? null : (isTextSelectable ? null : emit('dblclick', $event))"
|
|
207
207
|
@mousemove="emit('mousemove', $event)"
|
|
208
208
|
@mouseleave="emit('mouseleave', $event)"
|
|
209
209
|
:class="{
|
|
@@ -36,6 +36,7 @@ export interface ShortcutsOptions {
|
|
|
36
36
|
shapeFormatPainter?: Ref<any>
|
|
37
37
|
notify: (msg: string) => void
|
|
38
38
|
addHistorySnapshot?: () => void
|
|
39
|
+
isReadonly?: Ref<boolean>
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
export function useCanvasShortcuts(options: ShortcutsOptions) {
|
|
@@ -73,7 +74,8 @@ export function useCanvasShortcuts(options: ShortcutsOptions) {
|
|
|
73
74
|
getElementPixels,
|
|
74
75
|
shapeFormatPainter,
|
|
75
76
|
notify,
|
|
76
|
-
addHistorySnapshot
|
|
77
|
+
addHistorySnapshot,
|
|
78
|
+
isReadonly
|
|
77
79
|
} = options
|
|
78
80
|
|
|
79
81
|
let historyDebounceTimer: any = null
|
|
@@ -89,6 +91,53 @@ export function useCanvasShortcuts(options: ShortcutsOptions) {
|
|
|
89
91
|
function handleGlobalKeyDown(e: KeyboardEvent) {
|
|
90
92
|
if (e.defaultPrevented) return
|
|
91
93
|
|
|
94
|
+
if (isReadonly?.value) {
|
|
95
|
+
if (e.key === 'F5') {
|
|
96
|
+
e.preventDefault()
|
|
97
|
+
if (!isPresenting.value) {
|
|
98
|
+
if (e.shiftKey) {
|
|
99
|
+
startPresentation()
|
|
100
|
+
} else {
|
|
101
|
+
activeIndex.value = 0
|
|
102
|
+
startPresentation()
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'f') {
|
|
108
|
+
showSearchPanel.value = !showSearchPanel.value
|
|
109
|
+
if (showSearchPanel.value) {
|
|
110
|
+
activeSearchTab.value = 'find'
|
|
111
|
+
}
|
|
112
|
+
e.preventDefault()
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
if (e.key === 'Escape') {
|
|
116
|
+
selectedElementId.value = null
|
|
117
|
+
selectedElementIds.value = []
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
if (e.key === 'ArrowUp' || e.key === 'PageUp') {
|
|
121
|
+
if (activeIndex.value > 0) activeIndex.value--
|
|
122
|
+
e.preventDefault()
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
if (e.key === 'ArrowDown' || e.key === 'PageDown') {
|
|
126
|
+
if (activeIndex.value < slides.value.length - 1) activeIndex.value++
|
|
127
|
+
e.preventDefault()
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
if ((e.ctrlKey || e.metaKey) && (e.key === 'c' || e.key === 'C')) {
|
|
131
|
+
copySelectedSlides()
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
if (['Delete', 'Backspace'].includes(e.key) || ((e.ctrlKey || e.metaKey) && ['d', 'g', 'z', 'y', 'v', 'x'].includes(e.key.toLowerCase()))) {
|
|
135
|
+
e.preventDefault()
|
|
136
|
+
e.stopPropagation()
|
|
137
|
+
}
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
|
|
92
141
|
// Ctrl + D (Duplicate / 快速复制副本) - 拦截防止浏览器添加书签
|
|
93
142
|
if ((e.ctrlKey || e.metaKey) && (e.key === 'd' || e.key === 'D')) {
|
|
94
143
|
e.preventDefault()
|
|
@@ -12,6 +12,7 @@ export interface TableOptions {
|
|
|
12
12
|
getOverlayHeight?: (el: any) => string
|
|
13
13
|
selectedElementWidth?: Ref<string>
|
|
14
14
|
selectedElementHeight?: Ref<string>
|
|
15
|
+
isReadonly?: Ref<boolean>
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
interface TableMeasure {
|
|
@@ -57,7 +58,8 @@ export function useCanvasTable(options: TableOptions) {
|
|
|
57
58
|
getOverlayWidth = (el: any) => el?.style?.width || '500px',
|
|
58
59
|
getOverlayHeight = (el: any) => el?.style?.height || '200px',
|
|
59
60
|
selectedElementWidth,
|
|
60
|
-
selectedElementHeight
|
|
61
|
+
selectedElementHeight,
|
|
62
|
+
isReadonly
|
|
61
63
|
} = options
|
|
62
64
|
|
|
63
65
|
const selectedCellCount = ref(0)
|
|
@@ -461,7 +463,7 @@ export function useCanvasTable(options: TableOptions) {
|
|
|
461
463
|
}
|
|
462
464
|
|
|
463
465
|
function selectColumnCells(el: any, colIdx: number) {
|
|
464
|
-
if (!el || !el.id) return
|
|
466
|
+
if (isReadonly?.value || !el || !el.id) return
|
|
465
467
|
selectedElementId.value = el.id
|
|
466
468
|
if (!selectedElementIds.value.includes(el.id)) {
|
|
467
469
|
selectedElementIds.value = [el.id]
|
|
@@ -508,7 +510,7 @@ export function useCanvasTable(options: TableOptions) {
|
|
|
508
510
|
}
|
|
509
511
|
|
|
510
512
|
function selectRowCells(el: any, rowIdx: number) {
|
|
511
|
-
if (!el || !el.id) return
|
|
513
|
+
if (isReadonly?.value || !el || !el.id) return
|
|
512
514
|
selectedElementId.value = el.id
|
|
513
515
|
if (!selectedElementIds.value.includes(el.id)) {
|
|
514
516
|
selectedElementIds.value = [el.id]
|
|
@@ -1297,6 +1299,7 @@ export function useCanvasTable(options: TableOptions) {
|
|
|
1297
1299
|
}
|
|
1298
1300
|
|
|
1299
1301
|
function startTableColResize(e: MouseEvent, el: any, targetColIdx: number) {
|
|
1302
|
+
if (isReadonly?.value) return
|
|
1300
1303
|
e.preventDefault()
|
|
1301
1304
|
e.stopPropagation()
|
|
1302
1305
|
|
|
@@ -1401,6 +1404,7 @@ export function useCanvasTable(options: TableOptions) {
|
|
|
1401
1404
|
}
|
|
1402
1405
|
|
|
1403
1406
|
function startTableRowResize(e: MouseEvent, el: any, targetRowIdx: number) {
|
|
1407
|
+
if (isReadonly?.value) return
|
|
1404
1408
|
e.preventDefault()
|
|
1405
1409
|
e.stopPropagation()
|
|
1406
1410
|
|
|
@@ -1613,6 +1617,7 @@ export function useCanvasTable(options: TableOptions) {
|
|
|
1613
1617
|
}
|
|
1614
1618
|
|
|
1615
1619
|
function handleTableDblClick(e: MouseEvent, el: any) {
|
|
1620
|
+
if (isReadonly?.value) return
|
|
1616
1621
|
e.stopPropagation()
|
|
1617
1622
|
const targetEl = el || selectedElement.value
|
|
1618
1623
|
if (!targetEl) return
|
|
@@ -25,6 +25,7 @@ export interface TextOptions {
|
|
|
25
25
|
renderLatexToSvg: (latex: string) => string
|
|
26
26
|
sanitizeNbspInHtml: (html: string) => string
|
|
27
27
|
updateTextFontScale: (el: any) => void
|
|
28
|
+
isReadonly?: Ref<boolean>
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export function useCanvasText(options: TextOptions) {
|
|
@@ -48,7 +49,8 @@ export function useCanvasText(options: TextOptions) {
|
|
|
48
49
|
applyTextStyleToHtml,
|
|
49
50
|
renderLatexToSvg,
|
|
50
51
|
sanitizeNbspInHtml,
|
|
51
|
-
updateTextFontScale
|
|
52
|
+
updateTextFontScale,
|
|
53
|
+
isReadonly
|
|
52
54
|
} = options
|
|
53
55
|
|
|
54
56
|
const isEditingText = ref(false)
|
|
@@ -56,7 +58,7 @@ export function useCanvasText(options: TextOptions) {
|
|
|
56
58
|
const editingPlaceholderOriginalValue = ref<{ [key: string]: string }>({})
|
|
57
59
|
|
|
58
60
|
function updateElementContent(id: string, text: string) {
|
|
59
|
-
if (!activeSlide.value) return
|
|
61
|
+
if (isReadonly?.value || !activeSlide.value) return
|
|
60
62
|
const el = activeSlide.value.elements.find((item: any) => item.id === id)
|
|
61
63
|
if (el) {
|
|
62
64
|
if (el.type === 'shape') {
|
|
@@ -198,6 +200,7 @@ export function useCanvasText(options: TextOptions) {
|
|
|
198
200
|
}
|
|
199
201
|
|
|
200
202
|
function startEditingShapeText(elOrId: any, mouseEvent?: MouseEvent) {
|
|
203
|
+
if (isReadonly?.value) return
|
|
201
204
|
const elId = typeof elOrId === 'string' ? elOrId : elOrId?.id
|
|
202
205
|
const el = typeof elOrId === 'string'
|
|
203
206
|
? (findElementInSlide(elOrId) || activeSlide.value?.elements.find((x: any) => x.id === elOrId))
|
|
@@ -255,6 +258,7 @@ export function useCanvasText(options: TextOptions) {
|
|
|
255
258
|
}
|
|
256
259
|
|
|
257
260
|
function startEditingTextElement(elId: string, mouseEvent?: MouseEvent) {
|
|
261
|
+
if (isReadonly?.value || !elId) return
|
|
258
262
|
selectedElementId.value = elId
|
|
259
263
|
selectedElementIds.value = [elId]
|
|
260
264
|
isEditingText.value = true
|
|
@@ -406,7 +410,7 @@ export function useCanvasText(options: TextOptions) {
|
|
|
406
410
|
}
|
|
407
411
|
|
|
408
412
|
function handleFormulaDblClick(el: any) {
|
|
409
|
-
if (!el.latex) return
|
|
413
|
+
if (isReadonly?.value || !el.latex) return
|
|
410
414
|
currentEditingFormulaEl.value = el
|
|
411
415
|
formulaDialogVisible.value = true
|
|
412
416
|
}
|
|
@@ -2497,6 +2497,7 @@ watch(formulaDialogVisible, (visible) => {
|
|
|
2497
2497
|
}
|
|
2498
2498
|
})
|
|
2499
2499
|
const isPresenting = ref(false)
|
|
2500
|
+
const isReadonly = ref(false)
|
|
2500
2501
|
const showNotification = ref(false)
|
|
2501
2502
|
const notificationMsg = ref('')
|
|
2502
2503
|
const shapeFormatPainter = ref<any>(null)
|
|
@@ -2958,6 +2959,7 @@ function applyHistoryEntry<T>(entryStrs: string[], currentStrs: string[], list:
|
|
|
2958
2959
|
}
|
|
2959
2960
|
|
|
2960
2961
|
function undo() {
|
|
2962
|
+
if (isReadonly.value) return
|
|
2961
2963
|
if (isMasterEditingView.value) {
|
|
2962
2964
|
// 先落盘防抖中的快照:编辑后 350ms 内撤销不再丢最后一步编辑
|
|
2963
2965
|
flushPendingMasterHistorySnapshot()
|
|
@@ -3009,6 +3011,7 @@ function undo() {
|
|
|
3009
3011
|
}
|
|
3010
3012
|
|
|
3011
3013
|
function redo() {
|
|
3014
|
+
if (isReadonly.value) return
|
|
3012
3015
|
if (isMasterEditingView.value) {
|
|
3013
3016
|
flushPendingMasterHistorySnapshot()
|
|
3014
3017
|
if (masterHistoryIndex.value < masterHistoryStack.value.length - 1) {
|
|
@@ -3183,6 +3186,7 @@ function escapeRegExp(string: string) {
|
|
|
3183
3186
|
}
|
|
3184
3187
|
|
|
3185
3188
|
function addSlide() {
|
|
3189
|
+
if (isReadonly.value) return
|
|
3186
3190
|
const newId = Date.now().toString()
|
|
3187
3191
|
const insertIndex = slides.value.length > 0 ? activeIndex.value + 1 : 0
|
|
3188
3192
|
const curSlide = slides.value[activeIndex.value]
|
|
@@ -3202,6 +3206,7 @@ function addSlide() {
|
|
|
3202
3206
|
}
|
|
3203
3207
|
|
|
3204
3208
|
function deleteSlide(index: number) {
|
|
3209
|
+
if (isReadonly.value) return
|
|
3205
3210
|
const isLastSlide = slides.value.length === 1
|
|
3206
3211
|
if (isLastSlide) {
|
|
3207
3212
|
const newSlide = {
|
|
@@ -3232,7 +3237,7 @@ function deleteSlide(index: number) {
|
|
|
3232
3237
|
}
|
|
3233
3238
|
|
|
3234
3239
|
function pasteSlideData(targetSlideData: any, afterIndex = activeIndex.value, isCut = false) {
|
|
3235
|
-
if (!targetSlideData) return
|
|
3240
|
+
if (isReadonly.value || !targetSlideData) return
|
|
3236
3241
|
|
|
3237
3242
|
if (Array.isArray(targetSlideData)) {
|
|
3238
3243
|
if (targetSlideData.length === 0) return
|
|
@@ -3445,7 +3450,7 @@ function scaleElementIfNeeded(el: any, sX: number, sY: number) {
|
|
|
3445
3450
|
}
|
|
3446
3451
|
|
|
3447
3452
|
function pasteElementsData(elementsArray: any[], options?: { sourceWidth?: number; sourceHeight?: number }) {
|
|
3448
|
-
if (!activeSlide.value || !elementsArray || !Array.isArray(elementsArray) || elementsArray.length === 0) return
|
|
3453
|
+
if (isReadonly.value || !activeSlide.value || !elementsArray || !Array.isArray(elementsArray) || elementsArray.length === 0) return
|
|
3449
3454
|
const pastedIds: string[] = []
|
|
3450
3455
|
const oldIdToNewId = new Map<string, string>()
|
|
3451
3456
|
const oldGroupIdToNewGroupId = new Map<string, string>()
|
|
@@ -3889,7 +3894,7 @@ function selectAllSlides() {
|
|
|
3889
3894
|
}
|
|
3890
3895
|
|
|
3891
3896
|
function deleteSelectedSlides() {
|
|
3892
|
-
if (!slides.value || slides.value.length === 0) return
|
|
3897
|
+
if (isReadonly.value || !slides.value || slides.value.length === 0) return
|
|
3893
3898
|
const sortedIndexes = [...selectedSlideIndexes.value].filter(i => i >= 0 && i < slides.value.length).sort((a, b) => b - a)
|
|
3894
3899
|
if (sortedIndexes.length === 0) {
|
|
3895
3900
|
deleteSlide(activeIndex.value)
|
|
@@ -3942,12 +3947,14 @@ function copySelectedSlides() {
|
|
|
3942
3947
|
}
|
|
3943
3948
|
|
|
3944
3949
|
function cutSelectedSlides() {
|
|
3950
|
+
if (isReadonly.value) return
|
|
3945
3951
|
copySelectedSlides()
|
|
3946
3952
|
deleteSelectedSlides()
|
|
3947
3953
|
notify('已剪切选中的幻灯片')
|
|
3948
3954
|
}
|
|
3949
3955
|
|
|
3950
3956
|
function duplicateSelectedSlides() {
|
|
3957
|
+
if (isReadonly.value) return
|
|
3951
3958
|
const indexes = [...selectedSlideIndexes.value].filter(i => i >= 0 && i < slides.value.length).sort((a, b) => a - b)
|
|
3952
3959
|
if (indexes.length === 0) indexes.push(activeIndex.value)
|
|
3953
3960
|
const lastIndex = indexes[indexes.length - 1]
|
|
@@ -3976,6 +3983,7 @@ function duplicateSelectedSlides() {
|
|
|
3976
3983
|
}
|
|
3977
3984
|
|
|
3978
3985
|
function applyBackground(bg: string) {
|
|
3986
|
+
if (isReadonly.value) return
|
|
3979
3987
|
if (activeSlide.value) {
|
|
3980
3988
|
activeSlide.value.background = bg
|
|
3981
3989
|
activeSlide.value.bgType = 'gradient'
|
|
@@ -4009,7 +4017,7 @@ function addElement(
|
|
|
4009
4017
|
initialDimensions?: { width?: number; height?: number; left?: number; top?: number },
|
|
4010
4018
|
extraOptions?: { textType?: SlideTextType; name?: string; [key: string]: any }
|
|
4011
4019
|
) {
|
|
4012
|
-
if (!activeSlide.value) return
|
|
4020
|
+
if (isReadonly.value || !activeSlide.value) return
|
|
4013
4021
|
|
|
4014
4022
|
const recipe = getActiveSlideThemeRecipe(activeSlide.value)
|
|
4015
4023
|
|
|
@@ -4824,7 +4832,7 @@ function updateGroupBounds(groupEl: SlideElement) {
|
|
|
4824
4832
|
|
|
4825
4833
|
// Delete Element from current slide
|
|
4826
4834
|
function deleteElement(id: string, silent = false) {
|
|
4827
|
-
if (!activeSlide.value) return
|
|
4835
|
+
if (isReadonly.value || !activeSlide.value) return
|
|
4828
4836
|
const hasTopLevel = activeSlide.value.elements.some(item => item.id === id)
|
|
4829
4837
|
if (hasTopLevel) {
|
|
4830
4838
|
activeSlide.value.elements = activeSlide.value.elements.filter(item => item.id !== id)
|
|
@@ -9255,6 +9263,7 @@ export function useSlides() {
|
|
|
9255
9263
|
selectedElementIds,
|
|
9256
9264
|
croppingElementId,
|
|
9257
9265
|
isPresenting,
|
|
9266
|
+
isReadonly,
|
|
9258
9267
|
showNotification,
|
|
9259
9268
|
notificationMsg,
|
|
9260
9269
|
notify,
|