jvs-ppt-ui 0.0.15 → 0.0.17

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.
Files changed (31) hide show
  1. package/dist/lib/jvs-ppt-ui.es.js +24062 -23229
  2. package/dist/lib/jvs-ppt-ui.umd.js +101 -101
  3. package/dist/lib/style.css +1 -1
  4. package/package.json +1 -1
  5. package/src/components/CanvasArea.vue +9707 -9667
  6. package/src/components/ChartTypeSelector.vue +34 -30
  7. package/src/components/Header.vue +40 -5
  8. package/src/components/PresentationGroupChild.vue +44 -13
  9. package/src/components/PresentationMode.vue +84 -33
  10. package/src/components/SidebarLeft.vue +96 -18
  11. package/src/components/SidebarRight.vue +16 -9
  12. package/src/components/TemplateGallery.vue +36 -3
  13. package/src/components/Toolbar.vue +1 -1
  14. package/src/components/canvas/elements/CanvasGroupElement.vue +214 -213
  15. package/src/components/canvas/elements/CanvasShapeElement.vue +894 -825
  16. package/src/components/sidebar/common/SidebarBorderAccordion.vue +2 -2
  17. package/src/components/sidebar/common/SidebarReflectionAccordion.vue +2 -2
  18. package/src/components/sidebar/common/SidebarShadowAccordion.vue +2 -2
  19. package/src/components/sidebar/panels/SidebarChartFormatPanel.vue +3 -3
  20. package/src/components/sidebar/panels/SidebarDesignPanel.vue +170 -9
  21. package/src/components/sidebar/panels/SidebarShapeFormatPanel.vue +176 -16
  22. package/src/components/sidebar/panels/SidebarTableFormatPanel.vue +2 -2
  23. package/src/components/sidebar/panels/SidebarTextFormatPanel.vue +3 -3
  24. package/src/components/sidebar/panels/SidebarTransitionPanel.vue +30 -19
  25. package/src/composables/canvas/useCanvasText.ts +44 -18
  26. package/src/composables/canvas/useCanvasTransform.ts +24 -0
  27. package/src/composables/sidebar/useSidebarShapeFormat.ts +248 -17
  28. package/src/composables/sidebar/useSidebarTextFormat.ts +72 -1
  29. package/src/composables/useSlides.ts +9804 -9615
  30. package/src/style.css +4 -2
  31. package/src/utils/canvas/elementStyles.ts +278 -0
@@ -20,7 +20,9 @@ import {
20
20
  getElementBorderGradientCss,
21
21
  getGradientAngleDeg,
22
22
  getSvgGradientCoords,
23
- isImagePlaceholder
23
+ isImagePlaceholder,
24
+ isElementPatternTile,
25
+ getElementPatternTileSize
24
26
  } from '../utils/canvas/elementStyles'
25
27
  import CanvasImageElement from './canvas/elements/CanvasImageElement.vue'
26
28
 
@@ -75,29 +77,59 @@ function observeThumbItem(el: HTMLElement | null, index: number) {
75
77
  if (!el || typeof IntersectionObserver === 'undefined') return
76
78
  if (!thumbObserver) {
77
79
  thumbObserver = new IntersectionObserver((entries) => {
80
+ let mutated = false
78
81
  entries.forEach(entry => {
79
82
  if (entry.isIntersecting) {
80
83
  const idx = Number((entry.target as HTMLElement).dataset.slideIndex)
81
84
  if (!isNaN(idx) && !renderedThumbIndexes.value.has(idx)) {
82
85
  renderedThumbIndexes.value.add(idx)
86
+ mutated = true
83
87
  }
84
88
  }
85
89
  })
90
+ if (mutated) {
91
+ renderedThumbIndexes.value = new Set(renderedThumbIndexes.value)
92
+ }
86
93
  }, {
87
94
  rootMargin: '300px 0px'
88
95
  })
89
96
  }
90
- thumbObserver.observe(el)
97
+ if (!renderedThumbIndexes.value.has(index)) {
98
+ thumbObserver.observe(el)
99
+ }
91
100
  }
92
101
 
93
102
  watch(activeIndex, (newIdx) => {
94
103
  if (!renderedThumbIndexes.value.has(newIdx)) {
95
- renderedThumbIndexes.value.add(newIdx)
104
+ const next = new Set(renderedThumbIndexes.value)
105
+ next.add(newIdx)
106
+ renderedThumbIndexes.value = next
96
107
  }
97
108
  })
98
109
 
99
- watch(() => slides.value.length, () => {
100
- renderedThumbIndexes.value = new Set([0, 1, 2, 3, 4, 5, activeIndex.value])
110
+ watch(() => slides.value.length, (newLen, oldLen) => {
111
+ const delta = typeof oldLen === 'number' ? newLen - oldLen : 0
112
+ const isBulkImport = typeof oldLen === 'number' && delta > 5 && activeIndex.value === 0
113
+ if (isBulkImport) {
114
+ if (thumbObserver) {
115
+ thumbObserver.disconnect()
116
+ thumbObserver = null
117
+ }
118
+ renderedThumbIndexes.value = new Set<number>([0, 1, 2, 3, 4, 5, activeIndex.value])
119
+ return
120
+ }
121
+ const next = new Set<number>([0, 1, 2, 3, 4, 5, activeIndex.value])
122
+ renderedThumbIndexes.value.forEach(idx => {
123
+ if (idx >= 0 && idx < newLen) {
124
+ next.add(idx)
125
+ }
126
+ if (delta > 0 && delta <= 5 && idx + delta >= 0 && idx + delta < newLen) {
127
+ next.add(idx + delta)
128
+ }
129
+ })
130
+ if (activeIndex.value - 1 >= 0) next.add(activeIndex.value - 1)
131
+ if (activeIndex.value + 1 < newLen) next.add(activeIndex.value + 1)
132
+ renderedThumbIndexes.value = next
101
133
  })
102
134
 
103
135
  onUnmounted(() => {
@@ -744,6 +776,7 @@ const addSlideWithLayout = (layoutId: string) => {
744
776
  newSlide.subtitle = ''
745
777
  }
746
778
 
779
+ activeSlideVersion.value++
747
780
  presetPopoverVisible.value = false
748
781
  }
749
782
 
@@ -1035,6 +1068,22 @@ function getElementWidthHeight(el: any, parentGroup?: any, anchorGroup?: any): {
1035
1068
  return { width, height }
1036
1069
  }
1037
1070
 
1071
+ function getShapePatternTransform(el: any, isTriangle = false): string | undefined {
1072
+ if (!isElementPatternTile(el)) return undefined
1073
+ const dim = getElementWidthHeight(el)
1074
+ const w = Math.max(1, dim.width || 100)
1075
+ const h = Math.max(1, dim.height || 100)
1076
+ if (isTriangle) {
1077
+ return `scale(${+(100 / w).toFixed(4)}, ${+(100 / h).toFixed(4)})`
1078
+ }
1079
+ const vbW = el.viewBox ? el.viewBox[0] : 200
1080
+ const vbH = el.viewBox ? el.viewBox[1] : 200
1081
+ const bw = el.shapType === 'arc' ? 0 : (el.style?.borderWidth ? parseFloat(el.style.borderWidth) : 2)
1082
+ const effW = Math.max(1, w - bw)
1083
+ const effH = Math.max(1, h - bw)
1084
+ return `scale(${+(vbW / effW).toFixed(4)}, ${+(vbH / effH).toFixed(4)})`
1085
+ }
1086
+
1038
1087
  function getTextStrokeStyle(el: any) {
1039
1088
  if (!el.style || !el.style.enableTextStroke) return 'none'
1040
1089
  const width = el.style.textStrokeWidth || '1px'
@@ -2290,8 +2339,9 @@ const masterGroupedLayouts = computed(() => {
2290
2339
  height: '100%',
2291
2340
  backgroundColor: getElementPattern(el) ? 'transparent' : ((getElementGradientCss(el) || (getElementFill(el) !== undefined && getElementFill(el) !== 'none')) ? (getElementGradientCss(el) ? 'transparent' : getElementFill(el)) : (getElementFill(el) === 'none' ? 'transparent' : '#3b82f6')),
2292
2341
  backgroundImage: getElementPattern(el) ? `url('${getElementPattern(el)}')` : (getElementGradientCss(el) || 'none'),
2293
- backgroundSize: getElementPattern(el) ? 'cover' : undefined,
2294
- backgroundPosition: getElementPattern(el) ? 'center center' : undefined,
2342
+ backgroundSize: getElementPattern(el) ? (isElementPatternTile(el) ? `${getElementPatternTileSize(el).w}px ${getElementPatternTileSize(el).h}px` : 'cover') : undefined,
2343
+ backgroundPosition: getElementPattern(el) ? (isElementPatternTile(el) ? '0 0' : 'center center') : undefined,
2344
+ backgroundRepeat: getElementPattern(el) ? (isElementPatternTile(el) ? 'repeat' : 'no-repeat') : undefined,
2295
2345
  borderRadius: '50%',
2296
2346
  borderWidth: el.style?.borderWidth || '0px',
2297
2347
  borderStyle: el.style?.borderStyle || 'solid',
@@ -2302,8 +2352,16 @@ const masterGroupedLayouts = computed(() => {
2302
2352
  :viewBox="`0 0 ${el.viewBox ? el.viewBox[0] : (el.width || 200)} ${el.viewBox ? el.viewBox[1] : (el.height || 200)}`"
2303
2353
  preserveAspectRatio="none" style="width: 100%; height: 100%; display: block; overflow: visible;">
2304
2354
  <defs>
2305
- <pattern v-if="getElementPattern(el)" :id="`thumb-master-pat-${slide.id || index}-${el.id}`" width="100%" height="100%" patternContentUnits="objectBoundingBox">
2306
- <image :href="getElementPattern(el)" width="1" height="1" preserveAspectRatio="none" />
2355
+ <pattern v-if="getElementPattern(el)" :id="`thumb-master-pat-${slide.id || index}-${el.id}`"
2356
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : '100%'"
2357
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : '100%'"
2358
+ :patternUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
2359
+ :patternContentUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
2360
+ :patternTransform="getShapePatternTransform(el, false)">
2361
+ <image :href="getElementPattern(el)"
2362
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : 1"
2363
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : 1"
2364
+ preserveAspectRatio="none" />
2307
2365
  </pattern>
2308
2366
  <template v-if="getElementGradientConfig(el)">
2309
2367
  <radialGradient v-if="getElementGradientConfig(el)!.path && getElementGradientConfig(el)!.path !== 'line'"
@@ -2345,7 +2403,9 @@ const masterGroupedLayouts = computed(() => {
2345
2403
  <div v-else :style="{
2346
2404
  backgroundColor: getElementPattern(el) ? 'transparent' : ((getElementGradientCss(el) || (getElementFill(el) !== undefined && getElementFill(el) !== 'none')) ? (getElementGradientCss(el) ? 'transparent' : getElementFill(el)) : (getElementFill(el) === 'none' ? 'transparent' : '#3b82f6')),
2347
2405
  backgroundImage: getElementPattern(el) ? `url('${getElementPattern(el)}')` : (getElementGradientCss(el) || 'none'),
2348
- backgroundSize: getElementPattern(el) ? 'cover' : undefined,
2406
+ backgroundSize: getElementPattern(el) ? (isElementPatternTile(el) ? `${getElementPatternTileSize(el).w}px ${getElementPatternTileSize(el).h}px` : 'cover') : undefined,
2407
+ backgroundPosition: getElementPattern(el) ? (isElementPatternTile(el) ? '0 0' : 'center center') : undefined,
2408
+ backgroundRepeat: getElementPattern(el) ? (isElementPatternTile(el) ? 'repeat' : 'no-repeat') : undefined,
2349
2409
  width: '100%',
2350
2410
  height: '100%',
2351
2411
  borderRadius: el.style?.borderRadius || '0px',
@@ -2463,8 +2523,9 @@ const masterGroupedLayouts = computed(() => {
2463
2523
  height: '100%',
2464
2524
  backgroundColor: getElementPattern(el) ? 'transparent' : ((getElementGradientCss(el) || (getElementFill(el) !== undefined && getElementFill(el) !== 'none')) ? (getElementGradientCss(el) ? 'transparent' : getElementFill(el)) : (getElementFill(el) === 'none' ? 'transparent' : '#3b82f6')),
2465
2525
  backgroundImage: getElementPattern(el) ? `url('${getElementPattern(el)}')` : (getElementGradientCss(el) || 'none'),
2466
- backgroundSize: getElementPattern(el) ? 'cover' : undefined,
2467
- backgroundPosition: getElementPattern(el) ? 'center center' : undefined,
2526
+ backgroundSize: getElementPattern(el) ? (isElementPatternTile(el) ? `${getElementPatternTileSize(el).w}px ${getElementPatternTileSize(el).h}px` : 'cover') : undefined,
2527
+ backgroundPosition: getElementPattern(el) ? (isElementPatternTile(el) ? '0 0' : 'center center') : undefined,
2528
+ backgroundRepeat: getElementPattern(el) ? (isElementPatternTile(el) ? 'repeat' : 'no-repeat') : undefined,
2468
2529
  borderRadius: '50%',
2469
2530
  borderWidth: `${getElementBorderWidth(el)}px`,
2470
2531
  borderStyle: el.style?.borderStyle || (el as any).borderType || 'solid',
@@ -2478,8 +2539,16 @@ const masterGroupedLayouts = computed(() => {
2478
2539
  style="width: 100%; height: 100%; display: block; overflow: visible;"
2479
2540
  :style="{ filter: (el.shadow && el.shadow.type === 'inner') ? `url(#thumb-shape-inner-shadow-${el.id})` : '' }">
2480
2541
  <defs>
2481
- <pattern v-if="getElementPattern(el)" :id="`thumb-shape-pattern-${slide.id || index}-${el.id}`" width="100%" height="100%" patternContentUnits="objectBoundingBox">
2482
- <image :href="getElementPattern(el)" width="1" height="1" preserveAspectRatio="none" />
2542
+ <pattern v-if="getElementPattern(el)" :id="`thumb-shape-pattern-${slide.id || index}-${el.id}`"
2543
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : '100%'"
2544
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : '100%'"
2545
+ :patternUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
2546
+ :patternContentUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
2547
+ :patternTransform="getShapePatternTransform(el, true)">
2548
+ <image :href="getElementPattern(el)"
2549
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : 1"
2550
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : 1"
2551
+ preserveAspectRatio="none" />
2483
2552
  </pattern>
2484
2553
  <template v-if="getElementGradientConfig(el)">
2485
2554
  <radialGradient v-if="getElementGradientConfig(el)!.path && getElementGradientConfig(el)!.path !== 'line'"
@@ -2523,8 +2592,16 @@ const masterGroupedLayouts = computed(() => {
2523
2592
  <svg overflow="visible" style="width: 100%; height: 100%; display: block; overflow: visible;"
2524
2593
  :style="{ filter: (el.shadow && el.shadow.type === 'inner') ? `url(#thumb-shape-inner-shadow-${el.id})` : '' }">
2525
2594
  <defs>
2526
- <pattern v-if="getElementPattern(el)" :id="`thumb-shape-pattern-${slide.id || index}-${el.id}`" width="100%" height="100%" patternContentUnits="objectBoundingBox">
2527
- <image :href="getElementPattern(el)" width="1" height="1" preserveAspectRatio="none" />
2595
+ <pattern v-if="getElementPattern(el)" :id="`thumb-shape-pattern-${slide.id || index}-${el.id}`"
2596
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : '100%'"
2597
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : '100%'"
2598
+ :patternUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
2599
+ :patternContentUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
2600
+ :patternTransform="getShapePatternTransform(el, false)">
2601
+ <image :href="getElementPattern(el)"
2602
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : 1"
2603
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : 1"
2604
+ preserveAspectRatio="none" />
2528
2605
  </pattern>
2529
2606
  <template v-if="getElementGradientConfig(el)">
2530
2607
  <radialGradient v-if="getElementGradientConfig(el)!.path && getElementGradientConfig(el)!.path !== 'line'"
@@ -2572,8 +2649,9 @@ const masterGroupedLayouts = computed(() => {
2572
2649
  height: '100%',
2573
2650
  backgroundColor: getElementPattern(el) ? 'transparent' : ((isImagePlaceholder(el) && !getElementGradientCss(el)) ? ((getElementFill(el) && getElementFill(el) !== '#f1f5f9' && getElementFill(el) !== '#f8fafc') ? getElementFill(el) : '#d9dde3') : ((getElementGradientCss(el) || (getElementFill(el) !== undefined && getElementFill(el) !== 'none')) ? (getElementGradientCss(el) ? 'transparent' : getElementFill(el)) : (getElementFill(el) === 'none' ? 'transparent' : '#10b981'))),
2574
2651
  backgroundImage: getElementPattern(el) ? `url('${getElementPattern(el)}')` : (getElementGradientCss(el) || 'none'),
2575
- backgroundSize: getElementPattern(el) ? 'cover' : undefined,
2576
- backgroundPosition: getElementPattern(el) ? 'center center' : undefined,
2652
+ backgroundSize: getElementPattern(el) ? (isElementPatternTile(el) ? `${getElementPatternTileSize(el).w}px ${getElementPatternTileSize(el).h}px` : 'cover') : undefined,
2653
+ backgroundPosition: getElementPattern(el) ? (isElementPatternTile(el) ? '0 0' : 'center center') : undefined,
2654
+ backgroundRepeat: getElementPattern(el) ? (isElementPatternTile(el) ? 'repeat' : 'no-repeat') : undefined,
2577
2655
  borderRadius: isImagePlaceholder(el) ? ((el.style?.borderRadius && el.style.borderRadius !== '0px' && el.style.borderRadius !== '4px') ? el.style.borderRadius : '12px') : (el.style?.borderRadius !== undefined ? el.style.borderRadius : '0px'),
2578
2656
  borderWidth: (isImagePlaceholder(el) && (el.style as any)?.enableBorder !== true) ? '0px' : `${getElementBorderWidth(el)}px`,
2579
2657
  borderStyle: el.style?.borderStyle || (el as any).borderType || 'solid',
@@ -465,12 +465,15 @@ function applyTextStyleToHtml(html: string, styles: any) {
465
465
  paragraphs.forEach(applyTextStyles)
466
466
  }
467
467
 
468
- // 综合处理字体与样式覆盖:如果指定了 fontFamily,清理/更新所有块级和内联节点的原有硬编码字体
469
- if (styles.fontFamily) {
468
+ // 综合处理字体与样式覆盖:如果指定了 fontFamily 或 fontSize,清理/更新所有块级和内联节点的原有硬编码值
469
+ if (styles.fontFamily || styles.fontSize) {
470
470
  div.querySelectorAll('p, div, li, h1, h2, h3, h4, h5, h6, span, b, strong, i, em, a').forEach((elem: any) => {
471
- if (elem.style && elem.style.fontFamily) {
471
+ if (styles.fontFamily && elem.style && elem.style.fontFamily) {
472
472
  elem.style.fontFamily = styles.fontFamily
473
473
  }
474
+ if (styles.fontSize && elem.style && elem.style.fontSize) {
475
+ elem.style.fontSize = styles.fontSize
476
+ }
474
477
  })
475
478
  }
476
479
 
@@ -571,11 +574,12 @@ function setStyleVal(prop: string, val: any) {
571
574
  }
572
575
  }
573
576
 
574
- if (prop === 'fontFamily') {
577
+ if (prop === 'fontFamily' || prop === 'fontSize') {
575
578
  const container = document.getElementById(`editable-${el.id}`)
576
579
  if (container) {
577
- container.style.fontFamily = val
578
- container.innerHTML = applyTextStyleToHtml(container.innerHTML, { fontFamily: val })
580
+ if (prop === 'fontFamily') container.style.fontFamily = val
581
+ else container.style.fontSize = val
582
+ container.innerHTML = applyTextStyleToHtml(container.innerHTML, { [prop]: val })
579
583
  if (el.type === 'shape') {
580
584
  el.text = container.innerHTML
581
585
  } else {
@@ -916,6 +920,9 @@ const {
916
920
  gradientDirection,
917
921
  updateElementGradient,
918
922
  shapePatternFill,
923
+ shapePatternType,
924
+ shapePatternFg,
925
+ shapePatternBg,
919
926
  handleShapePatternUpload,
920
927
  clearShapePatternFill,
921
928
  styleBorderStyle,
@@ -1709,9 +1716,9 @@ function handleUndockToPopover() {
1709
1716
  v-model:gradient-angle="gradientAngle"
1710
1717
  @update:element-gradient="updateElementGradient"
1711
1718
  v-model:shape-pattern-fill="shapePatternFill"
1712
- :shape-pattern-type="shapeFillType"
1713
- :shape-pattern-fg="fillHex"
1714
- :shape-pattern-bg="fillHex"
1719
+ v-model:shape-pattern-type="shapePatternType"
1720
+ v-model:shape-pattern-fg="shapePatternFg"
1721
+ v-model:shape-pattern-bg="shapePatternBg"
1715
1722
  :fill-opacity="fillAlpha"
1716
1723
  :is-border-enabled="enableBorder"
1717
1724
  v-model:is-border-expanded="isBorderExpanded"
@@ -11,6 +11,7 @@ import {
11
11
  VideoPlay
12
12
  } from '@element-plus/icons-vue'
13
13
  import { useSlides, DEFAULT_MATERIAL_PAGE_API_URL } from '../composables/useSlides'
14
+ import { isImagePlaceholder } from '../utils/canvas/elementStyles'
14
15
 
15
16
  const {
16
17
  addElement,
@@ -20,7 +21,12 @@ const {
20
21
  materialPageApiUrl,
21
22
  materialHeaders,
22
23
  activeTab,
23
- materialPopoverVisible
24
+ materialPopoverVisible,
25
+ activeSlide,
26
+ selectedElementId,
27
+ placeholderMaterialTargetId,
28
+ resolveActivePlaceholderTargetId,
29
+ replacePlaceholderElement
24
30
  } = useSlides()
25
31
 
26
32
  export interface MaterialItem {
@@ -682,7 +688,7 @@ function formatMaterialTitle(title?: string, fallback = '素材', maxLen = 12):
682
688
  return text.length > maxLen ? `${text.slice(0, maxLen)}...` : text
683
689
  }
684
690
 
685
- // 点击素材卡片插入画布(区分视频与图片/矢量/插画)
691
+ // 点击素材卡片插入画布(区分视频与图片/矢量/插画,若当前处于图片占位符替换状态则优先替换占位符)
686
692
  function handleSelectMaterial(meta: MaterialItem) {
687
693
  const isVid = isVideoMaterial(meta)
688
694
  const targetUrl = meta.sourceUrl || meta.url || meta.downloadUrl || meta.previewUrl
@@ -692,9 +698,36 @@ function handleSelectMaterial(meta: MaterialItem) {
692
698
  return
693
699
  }
694
700
 
695
- let initialDimensions: { width?: number; height?: number } | undefined = undefined
696
701
  const rawW = meta.originalWidth || meta.width
697
702
  const rawH = meta.originalHeight || meta.height
703
+
704
+ const currentSelectedEl = selectedElementId?.value
705
+ ? activeSlide?.value?.elements?.find((el: any) => el.id === selectedElementId.value)
706
+ : null
707
+
708
+ const explicitTargetId =
709
+ placeholderMaterialTargetId?.value ||
710
+ (typeof window !== 'undefined' ? (window as any).__jvsPlaceholderTargetId : null)
711
+
712
+ const targetPlaceholderId =
713
+ (resolveActivePlaceholderTargetId ? resolveActivePlaceholderTargetId() : null) ||
714
+ (explicitTargetId && (!currentSelectedEl || currentSelectedEl.id === explicitTargetId)
715
+ ? explicitTargetId
716
+ : (currentSelectedEl && isImagePlaceholder(currentSelectedEl) ? currentSelectedEl.id : null))
717
+
718
+ if (targetPlaceholderId && replacePlaceholderElement) {
719
+ const replaced = replacePlaceholderElement(targetPlaceholderId, targetUrl, {
720
+ isVideo: isVid,
721
+ width: Number(rawW) || undefined,
722
+ height: Number(rawH) || undefined,
723
+ notifyMessage: isVid
724
+ ? `已使用视频 "${formatMaterialTitle(meta.title, '高清视频', 12)}" 替换占位符`
725
+ : `已使用素材 "${formatMaterialTitle(meta.title, '图片', 12)}" 替换占位符图片`
726
+ })
727
+ if (replaced) return
728
+ }
729
+
730
+ let initialDimensions: { width?: number; height?: number } | undefined = undefined
698
731
  if (rawW && rawH && rawW > 0 && rawH > 0) {
699
732
  const maxWidth = 480
700
733
  const maxHeight = 360
@@ -1359,7 +1359,7 @@ async function handleAudioFileChange(e: Event) {
1359
1359
  </el-popover>
1360
1360
 
1361
1361
  <!-- Chart Popover -->
1362
- <el-popover trigger="click" placement="bottom-start" :width="516" popper-class="chart-popover-popper"
1362
+ <el-popover trigger="click" placement="bottom-start" :width="568" popper-class="chart-popover-popper"
1363
1363
  v-model:visible="chartPopoverVisible" :show-arrow="false" :offset="2">
1364
1364
  <template #reference>
1365
1365
  <div class="toolbar-btn-stacked" :class="{ 'btn-active': chartPopoverVisible }" title="插入图表"