jvs-ppt-ui 0.0.10 → 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.
@@ -66,7 +66,7 @@ const {
66
66
  globalThemeColor, globalFontColor, globalFontFamily, globalBgColor, getActiveSlideThemeRecipe, croppingElementId,
67
67
  isMasterEditingView, slideMasters, activeMasterId, activeLayoutId, activeLayout, sortSlideElements,
68
68
  selectedSlideIndexes, isMouseOverSlideList, selectAllSlides, deleteSelectedSlides, copySelectedSlides, cutSelectedSlides, duplicateSelectedSlides, duplicateSelectedElements,
69
- getEffectiveMasterElementsForSlide, getEffectiveSlideBackground, uploadResource
69
+ getEffectiveMasterElementsForSlide, getEffectiveSlideBackground, uploadResource, isReadonly
70
70
  } = useSlides()
71
71
  const activeTab = activeTabRaw as any
72
72
 
@@ -205,7 +205,8 @@ const {
205
205
  applyTextStyleToHtml: (html: string, style: Record<string, any>) => applyTextStyleToHtml(html, style),
206
206
  renderLatexToSvg: (latex: string) => renderLatexToSvg(latex),
207
207
  sanitizeNbspInHtml: (html: string) => sanitizeNbspInHtml(html),
208
- updateTextFontScale: (el: any) => updateTextFontScale(el)
208
+ updateTextFontScale: (el: any) => updateTextFontScale(el),
209
+ isReadonly
209
210
  })
210
211
  const lastMouseDownTarget = ref<HTMLElement | null>(null)
211
212
 
@@ -405,7 +406,8 @@ const {
405
406
  getOverlayWidth: (el: any) => getOverlayWidth(el),
406
407
  getOverlayHeight: (el: any) => getOverlayHeight(el),
407
408
  selectedElementWidth,
408
- selectedElementHeight
409
+ selectedElementHeight,
410
+ isReadonly
409
411
  })
410
412
 
411
413
  const getSelectedCellCount = () => selectedCellCount.value
@@ -1509,7 +1511,8 @@ const { handleGlobalKeyDown } = useCanvasShortcuts({
1509
1511
  getElementPixels: (el: any) => getElementPixels(el),
1510
1512
  shapeFormatPainter,
1511
1513
  notify,
1512
- addHistorySnapshot
1514
+ addHistorySnapshot,
1515
+ isReadonly
1513
1516
  })
1514
1517
 
1515
1518
  watch(fitTrigger, () => {
@@ -2397,6 +2400,7 @@ function getLineConfigFromContent(content: string) {
2397
2400
  }
2398
2401
 
2399
2402
  function handleCanvasDblClick(e: MouseEvent) {
2403
+ if (isReadonly.value) return
2400
2404
  // 1. 如果事件目标本身或其父级就在某元素内
2401
2405
  const target = e.target as HTMLElement
2402
2406
  const closestElDom = target?.closest?.('.canvas-element, [id^="canvas-el-"]') as HTMLElement | null
@@ -2555,6 +2559,12 @@ function handleCanvasMouseDown(e: MouseEvent) {
2555
2559
  return
2556
2560
  }
2557
2561
 
2562
+ if (isReadonly.value) {
2563
+ selectedElementId.value = null
2564
+ selectedElementIds.value = []
2565
+ return
2566
+ }
2567
+
2558
2568
  if (shapeFormatPainter.value) {
2559
2569
  shapeFormatPainter.value = null
2560
2570
  return
@@ -3256,7 +3266,7 @@ let lineControlPointStartX = 0
3256
3266
  let lineControlPointStartY = 0
3257
3267
 
3258
3268
  function startDragLineControlPoint(e: MouseEvent, el: any, type: string) {
3259
- if (el.locked) return
3269
+ if (isReadonly.value || el.locked) return
3260
3270
  e.stopPropagation()
3261
3271
  e.preventDefault()
3262
3272
 
@@ -3578,6 +3588,10 @@ function handleElementMouseDown(e: MouseEvent, el: any) {
3578
3588
 
3579
3589
  e.stopPropagation()
3580
3590
 
3591
+ if (isReadonly.value) {
3592
+ return
3593
+ }
3594
+
3581
3595
  if (shapeFormatPainter.value) {
3582
3596
  if (shapeFormatPainter.value.mode === 'text-range') {
3583
3597
  return
@@ -3606,6 +3620,7 @@ function handleElementMouseDown(e: MouseEvent, el: any) {
3606
3620
  }
3607
3621
 
3608
3622
  function handleBorderMouseDown(e: MouseEvent, el: any) {
3623
+ if (isReadonly.value) return
3609
3624
  if (e.button !== 0) return
3610
3625
  if (isLockedMasterElement(el) || el.locked) {
3611
3626
  return
@@ -3650,6 +3665,7 @@ function handleBorderMouseDown(e: MouseEvent, el: any) {
3650
3665
  }
3651
3666
 
3652
3667
  function handleElementDblClick(e: MouseEvent, el: any) {
3668
+ if (isReadonly.value) return
3653
3669
  let target = el
3654
3670
  if (selectedElementIds.value.length > 0 && !selectedElementIds.value.includes(el.id)) {
3655
3671
  const activeSelectedEl = findSelectedElementUnderPoint(e.clientX, e.clientY)
@@ -3762,6 +3778,7 @@ function handleChildDragKeyUp(e: KeyboardEvent) {
3762
3778
  }
3763
3779
 
3764
3780
  function startChildDrag(e: MouseEvent, child: any, parentGroup: any) {
3781
+ if (isReadonly.value) return
3765
3782
  const childContainerEl = document.getElementById(`editable-${child.id}`)
3766
3783
  const childCanvasElDom = document.getElementById(`canvas-el-${child.id}`)
3767
3784
  const isChildClickInside = (childContainerEl && (childContainerEl === e.target || childContainerEl.contains(e.target as Node))) ||
@@ -3975,6 +3992,7 @@ function handleChildMouseDown(e: MouseEvent, child: any, parentGroup: any) {
3975
3992
  checkAndExitEditText(undefined, true)
3976
3993
  }
3977
3994
  e.stopPropagation()
3995
+ if (isReadonly.value) return
3978
3996
 
3979
3997
  if (shapeFormatPainter.value) {
3980
3998
  if (shapeFormatPainter.value.mode === 'text-range') {
@@ -4039,7 +4057,7 @@ function handleChildMouseDown(e: MouseEvent, child: any, parentGroup: any) {
4039
4057
  }
4040
4058
 
4041
4059
  function handleChildClick(e: MouseEvent, child: any, parentGroup: any) {
4042
- if (activeTool.value) return
4060
+ if (isReadonly.value || activeTool.value) return
4043
4061
  if (isLockedMasterElement(child)) return
4044
4062
  e.stopPropagation()
4045
4063
  if (dragOccurred) {
@@ -4073,6 +4091,7 @@ function handleChildClick(e: MouseEvent, child: any, parentGroup: any) {
4073
4091
  }
4074
4092
 
4075
4093
  function handleChildDblClick(e: MouseEvent, child: any, parentGroup: any) {
4094
+ if (isReadonly.value) return
4076
4095
  e.stopPropagation()
4077
4096
  if (isLockedMasterElement(child)) return
4078
4097
  // 已在编辑该子元素时的双击 = 原生选词,保持选区不重进编辑
@@ -4096,6 +4115,7 @@ function handleChildDblClick(e: MouseEvent, child: any, parentGroup: any) {
4096
4115
  }
4097
4116
 
4098
4117
  function handleElementClick(e: MouseEvent, elId: string) {
4118
+ if (isReadonly.value) return
4099
4119
  if (activeTool.value) return
4100
4120
  if (elId) {
4101
4121
  const targetEl = activeSlide.value?.elements.find(x => x.id === elId)
@@ -4495,6 +4515,7 @@ function handleTextMouseLeave(_e: MouseEvent, el: any) {
4495
4515
  }
4496
4516
 
4497
4517
  function startDrag(e: MouseEvent, el: any) {
4518
+ if (isReadonly.value) return
4498
4519
  const target = e.target as HTMLElement
4499
4520
  const container = document.getElementById(`editable-${el.id}`)
4500
4521
  const canvasElDom = document.getElementById(`canvas-el-${el.id}`)
@@ -7821,7 +7842,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
7821
7842
  </defs>
7822
7843
  </svg>
7823
7844
  <!-- Smart Guides & Corner Decorations -->
7824
- <CanvasSmartGuides :alignment-lines="alignmentLines" :active-slide-id="activeSlide?.id" />
7845
+ <CanvasSmartGuides v-if="!isReadonly" :alignment-lines="alignmentLines" :active-slide-id="activeSlide?.id" />
7825
7846
 
7826
7847
  <!-- Ctrl Drag Copy Origin Replica (原元素在原地 100% 完整原样真实呈现,杜绝空虚高亮块) -->
7827
7848
  <template v-if="ctrlDragReplicas.length > 0">
@@ -8025,14 +8046,14 @@ async function handleGlobalPaste(e: ClipboardEvent) {
8025
8046
  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
8047
  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
8048
  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,
8049
+ cursor: isReadonly ? 'default' : ((isEditingText && (selectedElementId === el.id || editingElementId === el.id) && (el.type === 'text' || el.type === 'shape') && !el.latex && !el.content?.includes('<table')) ? 'text' : undefined),
8029
8050
  transform: el.type === 'line' ? 'none' : `rotate(${el.style?.rotate || 0}deg) scale(${el.type !== 'text' && ((el.style as any)?.flipH || el.flipH || el.isFlipH) ? -1 : 1}, ${el.type !== 'text' && ((el.style as any)?.flipV || el.flipV || el.isFlipV) ? -1 : 1})`,
8030
8051
  opacity: (el.style as any)?.opacity !== undefined ? (el.style as any)?.opacity : 1,
8031
8052
  filter: getElementDropShadowFilter(el),
8032
8053
  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
8054
  }" @mousedown="handleElementMouseDown($event, el)" @click="handleElementClick($event, el.id)"
8034
8055
  @contextmenu.stop.prevent="handleElementContextMenu($event, el)"
8035
- @dblclick.stop="handleElementDblClick($event, el)"
8056
+ @dblclick.stop="isReadonly ? null : handleElementDblClick($event, el)"
8036
8057
  @mouseenter="!(isDragging || isCanvasDragging) && (hoveredElementId = el.id)"
8037
8058
  @mouseleave="!(isDragging || isCanvasDragging) && (hoveredElementId = (hoveredElementId === el.id ? null : hoveredElementId))">
8038
8059
 
@@ -8124,7 +8145,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
8124
8145
  @focusout="handleTextBlur($event, el)"
8125
8146
  @input="handleInput($event, el)"
8126
8147
  @keydown="handleTextKeyDown($event, el)"
8127
- @dblclick="el.latex ? handleFormulaDblClick(el) : (el.content.includes('<table') ? handleTableDblClick($event, el) : startEditingTextElement(el.id, $event))"
8148
+ @dblclick="isReadonly ? null : (el.latex ? handleFormulaDblClick(el) : (el.content.includes('<table') ? handleTableDblClick($event, el) : startEditingTextElement(el.id, $event)))"
8128
8149
  @mousemove="handleTextMouseMove($event, el)"
8129
8150
  @mouseleave="handleTextMouseLeave($event, el)"
8130
8151
  @table-dbl-click="handleTableDblClick($event, el)"
@@ -8154,7 +8175,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
8154
8175
  @text-key-down="handleTextKeyDown($event, el)"
8155
8176
  @mousemove="handleTextMouseMove($event, el)"
8156
8177
  @mouseleave="handleTextMouseLeave($event, el)"
8157
- @dblclick="startEditingShapeText(el, $event)"
8178
+ @dblclick="isReadonly ? null : startEditingShapeText(el, $event)"
8158
8179
  @start-editing="(ev) => startEditingShapeText(el, ev)"
8159
8180
  @placeholder-upload="(id) => triggerPlaceholderImageUpload(id)"
8160
8181
  />
@@ -8466,7 +8487,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
8466
8487
  </div>
8467
8488
 
8468
8489
  <!-- 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="{
8490
+ <div v-for="selEl in (isReadonly ? [] : selectedElements)" :key="selEl.id" class="global-selection-overlay" :class="{
8470
8491
  'line-selection': selEl.type === 'line',
8471
8492
  'locked-selection': selEl.locked,
8472
8493
  'table-selection': selEl.type === 'text' && selEl.content && selEl.content.includes('<table'),
@@ -8636,7 +8657,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
8636
8657
  </template>
8637
8658
 
8638
8659
  <!-- 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)"
8660
+ <div v-if="!isReadonly && selEl.type !== 'line' && !selEl.locked && croppingElementId !== selEl.id && selectedElementIds.includes(selEl.id)"
8640
8661
  class="text-edit-drag-helper"
8641
8662
  style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; pointer-events: none; z-index: 200;"
8642
8663
  @contextmenu.stop.prevent="handleElementContextMenu($event, selEl)"
@@ -8697,7 +8718,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
8697
8718
 
8698
8719
  <!-- Transform, Resize, Line Endpoints & Crop Handles -->
8699
8720
  <CanvasTransformHandles
8700
- v-if="selectedElementIds.includes(selEl.id)"
8721
+ v-if="!isReadonly && selectedElementIds.includes(selEl.id)"
8701
8722
  :element="selEl"
8702
8723
  :selected-element-id="selectedElementId"
8703
8724
  :selected-element-ids="selectedElementIds"
@@ -8860,7 +8881,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
8860
8881
 
8861
8882
  <!-- Floating Quick Toolbar (No rotation, rendered as sibling of main-slide inside slide-zoom-container) -->
8862
8883
  <CanvasQuickToolbar
8863
- v-if="!isDragging && !isResizing && !isRotating && !isDraggingLine"
8884
+ v-if="!isReadonly && !isDragging && !isResizing && !isRotating && !isDraggingLine"
8864
8885
  :selected-element="selectedElement"
8865
8886
  :selected-element-ids="selectedElementIds"
8866
8887
  :is-dragging="isDragging"
@@ -8934,11 +8955,12 @@ async function handleGlobalPaste(e: ClipboardEvent) {
8934
8955
 
8935
8956
  <!-- Footer Notes -->
8936
8957
  <div class="canvas-footer">
8937
- <input class="canvas-footer-notes" v-model="activeSlide.notes" placeholder="点击输入演讲者备注" />
8958
+ <input class="canvas-footer-notes" :readonly="isReadonly" v-model="activeSlide.notes" :placeholder="isReadonly ? '演讲者备注' : '点击输入演讲者备注'" />
8938
8959
  </div>
8939
8960
 
8940
8961
  <!-- Custom Context Menu Component -->
8941
8962
  <CanvasContextMenu
8963
+ v-if="!isReadonly"
8942
8964
  v-model:visible="showContextMenu"
8943
8965
  :position="contextMenuPos"
8944
8966
  :type="contextMenuType"