jvs-ppt-ui 0.0.15 → 0.0.16
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/dist/lib/jvs-ppt-ui.es.js +20301 -19823
- package/dist/lib/jvs-ppt-ui.umd.js +99 -99
- package/dist/lib/style.css +1 -1
- package/package.json +1 -1
- package/src/components/CanvasArea.vue +14 -115
- package/src/components/ChartTypeSelector.vue +34 -30
- package/src/components/Header.vue +40 -5
- package/src/components/PresentationGroupChild.vue +44 -13
- package/src/components/PresentationMode.vue +84 -33
- package/src/components/SidebarLeft.vue +96 -18
- package/src/components/SidebarRight.vue +6 -3
- package/src/components/Toolbar.vue +1 -1
- package/src/components/canvas/elements/CanvasShapeElement.vue +91 -34
- package/src/components/sidebar/common/SidebarBorderAccordion.vue +2 -2
- package/src/components/sidebar/common/SidebarReflectionAccordion.vue +2 -2
- package/src/components/sidebar/common/SidebarShadowAccordion.vue +2 -2
- package/src/components/sidebar/panels/SidebarChartFormatPanel.vue +3 -3
- package/src/components/sidebar/panels/SidebarDesignPanel.vue +170 -9
- package/src/components/sidebar/panels/SidebarShapeFormatPanel.vue +176 -16
- package/src/components/sidebar/panels/SidebarTableFormatPanel.vue +2 -2
- package/src/components/sidebar/panels/SidebarTextFormatPanel.vue +2 -2
- package/src/components/sidebar/panels/SidebarTransitionPanel.vue +30 -19
- package/src/composables/canvas/useCanvasTransform.ts +24 -0
- package/src/composables/sidebar/useSidebarShapeFormat.ts +248 -17
- package/src/composables/useSlides.ts +12 -0
- package/src/style.css +4 -2
- package/src/utils/canvas/elementStyles.ts +22 -0
package/package.json
CHANGED
|
@@ -5197,8 +5197,8 @@ const selectedElementKeypoints = computed(() => {
|
|
|
5197
5197
|
const actualH = geom.height || 200
|
|
5198
5198
|
const viewBoxW = el.viewBox?.[0] || 200
|
|
5199
5199
|
const viewBoxH = el.viewBox?.[1] || 200
|
|
5200
|
-
const w = viewBoxW
|
|
5201
|
-
const h = viewBoxH
|
|
5200
|
+
const w = el.pathFormula === 'roundRect' ? actualW : viewBoxW
|
|
5201
|
+
const h = el.pathFormula === 'roundRect' ? actualH : viewBoxH
|
|
5202
5202
|
|
|
5203
5203
|
return el.keypoints.map((keypoint: any, index: number) => {
|
|
5204
5204
|
const getBaseSize = formulaObj.getBaseSize?.[index]
|
|
@@ -8154,7 +8154,7 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
8154
8154
|
: (el.type === 'line'
|
|
8155
8155
|
? `${el.style?.left}_${el.style?.top}_${el.style?.width}_${el.style?.height}_${el.start?.join(',')}_${el.end?.join(',')}_${el.broken?.join(',')}_${el.broken2?.join(',')}_${el.curve?.join(',')}`
|
|
8156
8156
|
: 'frozen'))
|
|
8157
|
-
: `${el.id}_${el.style?.left}_${el.style?.top}_${el.style?.width}_${el.style?.height}_${el.style?.rotate}_${el.type === 'line' ? `${el.start?.join(',')}_${el.end?.join(',')}_${el.broken?.join(',')}_${el.broken2?.join(',')}_${el.curve?.join(',')}_` : ''}${selectedElementIds.includes(el.id)}_${selectedElementId === el.id}_${isEditingText}_${activeTab}_${activeSlideVersion}`
|
|
8157
|
+
: `${el.id}_${el.style?.left}_${el.style?.top}_${el.style?.width}_${el.style?.height}_${el.style?.rotate}_${el.type === 'line' ? `${el.start?.join(',')}_${el.end?.join(',')}_${el.broken?.join(',')}_${el.broken2?.join(',')}_${el.curve?.join(',')}_` : ''}${selectedElementIds.includes(el.id)}_${selectedElementId === el.id}_${isEditingText}_${activeTab}_${activeSlideVersion}_${(el.style as any)?.reflection?.enabled}_${(el.style as any)?.reflection?.distance}_${(el.style as any)?.reflection?.size}_${(el.style as any)?.reflection?.blur}_${(el.style as any)?.reflection?.opacity}`
|
|
8158
8158
|
]"
|
|
8159
8159
|
:class="{
|
|
8160
8160
|
selected: selectedElementIds.includes(el.id),
|
|
@@ -8428,118 +8428,17 @@ async function handleGlobalPaste(e: ClipboardEvent) {
|
|
|
8428
8428
|
|
|
8429
8429
|
<!-- Shape element -->
|
|
8430
8430
|
<template v-else-if="el.type === 'shape'">
|
|
8431
|
-
<
|
|
8432
|
-
|
|
8433
|
-
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
|
|
8437
|
-
|
|
8438
|
-
|
|
8439
|
-
|
|
8440
|
-
|
|
8441
|
-
|
|
8442
|
-
<svg viewBox="0 0 100 100" preserveAspectRatio="none"
|
|
8443
|
-
style="width: 100%; height: 100%; display: block; overflow: visible;"
|
|
8444
|
-
:style="{ filter: (el.shadow && el.shadow.type === 'inner') ? `url(#shape-inner-shadow-${el.id})` : '' }">
|
|
8445
|
-
<polygon points="50,0 0,100 100,100"
|
|
8446
|
-
:fill="el.style.fill !== undefined ? el.style.fill : '#f59e0b'"
|
|
8447
|
-
:stroke="el.style.borderColor || '#d97706'"
|
|
8448
|
-
:stroke-width="el.style.borderWidth ? parseInt(el.style.borderWidth) * 1.5 : 3"
|
|
8449
|
-
:stroke-dasharray="el.style.borderStyle === 'dashed' ? '5 5' : ''" />
|
|
8450
|
-
</svg>
|
|
8451
|
-
</div>
|
|
8452
|
-
<div v-else-if="el.content && (el.content.trim().startsWith('M') || el.content.trim().startsWith('m'))"
|
|
8453
|
-
style="width: 100%; height: 100%; overflow: visible;">
|
|
8454
|
-
<svg overflow="visible" style="width: 100%; height: 100%; display: block; overflow: visible;"
|
|
8455
|
-
:style="{ filter: (el.shadow && el.shadow.type === 'inner') ? `url(#shape-inner-shadow-${el.id})` : '' }">
|
|
8456
|
-
<g
|
|
8457
|
-
:transform="el.shapType === 'arc' ? `scale(${getElementWidthHeight(el).width / (el.viewBox ? el.viewBox[0] : 200)}, ${getElementWidthHeight(el).height / (el.viewBox ? el.viewBox[1] : 200)})` : `translate(${(el.style.borderWidth ? parseFloat(el.style.borderWidth) : 2) / 2}, ${(el.style.borderWidth ? parseFloat(el.style.borderWidth) : 2) / 2}) scale(${Math.max(0, getElementWidthHeight(el).width - (el.style.borderWidth ? parseFloat(el.style.borderWidth) : 2)) / (el.viewBox ? el.viewBox[0] : 200)}, ${Math.max(0, getElementWidthHeight(el).height - (el.style.borderWidth ? parseFloat(el.style.borderWidth) : 2)) / (el.viewBox ? el.viewBox[1] : 200)})`">
|
|
8458
|
-
<path vector-effect="non-scaling-stroke" :d="el.content"
|
|
8459
|
-
:fill="getElementFill(el) !== undefined ? getElementFill(el) : '#3b82f6'"
|
|
8460
|
-
:stroke="getElementBorderColor(el) || 'none'" :stroke-width="getElementBorderWidth(el)"
|
|
8461
|
-
:stroke-linecap="(el.style?.strokeLinecap || el.borderLineCap || el.lineCap || (el.shapType === 'arc' ? 'round' : undefined)) as any"
|
|
8462
|
-
:stroke-dasharray="el.style?.borderStyle === 'dashed' || (el as any).borderType === 'dashed' ? '5 5' : ''" />
|
|
8463
|
-
</g>
|
|
8464
|
-
</svg>
|
|
8465
|
-
</div>
|
|
8466
|
-
<div v-else :style="{
|
|
8467
|
-
width: '100%',
|
|
8468
|
-
height: '100%',
|
|
8469
|
-
backgroundColor: (getElementFill(el) !== undefined && getElementFill(el) !== 'none') ? getElementFill(el) : (getElementFill(el) === 'none' ? 'transparent' : '#10b981'),
|
|
8470
|
-
borderRadius: '4px',
|
|
8471
|
-
borderWidth: `${getElementBorderWidth(el)}px`,
|
|
8472
|
-
borderStyle: el.style?.borderStyle || (el as any).borderType || 'solid',
|
|
8473
|
-
borderColor: getElementBorderColor(el) || 'transparent',
|
|
8474
|
-
filter: (el.shadow && el.shadow.type === 'inner') ? `url(#shape-inner-shadow-${el.id})` : ''
|
|
8475
|
-
}"></div>
|
|
8476
|
-
|
|
8477
|
-
<div class="shape-text-overlay-wrapper"
|
|
8478
|
-
style="position: absolute; display: flex; flex-direction: column; pointer-events: none; box-sizing: border-box;"
|
|
8479
|
-
:style="{
|
|
8480
|
-
left: isEllipseShape(el) ? '14.6%' : '0px',
|
|
8481
|
-
top: isEllipseShape(el) ? '14.6%' : '0px',
|
|
8482
|
-
width: isEllipseShape(el) ? '70.8%' : '100%',
|
|
8483
|
-
height: isEllipseShape(el) ? '70.8%' : '100%',
|
|
8484
|
-
justifyContent: (el.style.verticalAlign === 'top' || el.style.verticalAlign === 'up') ? 'flex-start' : ((el.style.verticalAlign === 'bottom' || el.style.verticalAlign === 'down') ? 'flex-end' : 'center'),
|
|
8485
|
-
padding: isEllipseShape(el) ? '0px' : (el.style.padding || '4.8px 9.6px 4.8px 9.6px')
|
|
8486
|
-
}">
|
|
8487
|
-
<div class="text-reflection-wrapper" style="position: relative; width: 100%; height: auto;">
|
|
8488
|
-
<div class="shape-text-overlay" :style="{
|
|
8489
|
-
display: 'block',
|
|
8490
|
-
width: '100%',
|
|
8491
|
-
maxWidth: '100%',
|
|
8492
|
-
outline: 'none',
|
|
8493
|
-
padding: '0px',
|
|
8494
|
-
boxSizing: 'content-box',
|
|
8495
|
-
overflow: 'wrap',
|
|
8496
|
-
wordBreak: 'break-word',
|
|
8497
|
-
pointerEvents: 'none',
|
|
8498
|
-
userSelect: 'none',
|
|
8499
|
-
color: el.style.textColor || el.style.color || (el.style.fill === '#ffffff' || el.style.fill === '#FFFFFF' ? '#475569' : '#ffffff'),
|
|
8500
|
-
fontFamily: el.style.fontFamily || 'inherit',
|
|
8501
|
-
fontSize: el.style.fontSize || '16px',
|
|
8502
|
-
fontWeight: el.style.fontWeight || ((getSafeShapeText(el).includes('<')) ? 'normal' : 'normal'),
|
|
8503
|
-
fontStyle: (el.style as any).fontStyle || 'normal',
|
|
8504
|
-
textDecoration: (el.style.textDecoration as any) || 'none',
|
|
8505
|
-
lineHeight: el.style.lineHeight || 1.35,
|
|
8506
|
-
textAlign: (el.style.textAlign as any) || 'center',
|
|
8507
|
-
whiteSpace: (el.style as any).whiteSpace || 'normal',
|
|
8508
|
-
'--paragraphSpace': `${(el.style as any).paragraphSpace === undefined ? 3 : (el.style as any).paragraphSpace}px`
|
|
8509
|
-
}" v-html="normalizeHtmlFontFamily(getSafeShapeText(el))">
|
|
8510
|
-
</div>
|
|
8511
|
-
|
|
8512
|
-
<!-- Shape Text Reflection Clone -->
|
|
8513
|
-
<div v-if="(el.style as any).textReflection && (el.style as any).textReflection.enabled"
|
|
8514
|
-
class="text-element-reflection"
|
|
8515
|
-
style="position: absolute; left: 0; width: 100%; pointer-events: none; z-index: 1;" :style="{
|
|
8516
|
-
top: `calc(100% + ${(el.style as any).textReflection.distance !== undefined ? (el.style as any).textReflection.distance : 20}px - ${getReflectionLeadingCompensation(el)}px)`,
|
|
8517
|
-
transform: 'translateY(100%) scaleY(-1)',
|
|
8518
|
-
opacity: (100 - ((el.style as any).textReflection.opacity !== undefined ? (el.style as any).textReflection.opacity : 10)) / 100,
|
|
8519
|
-
filter: (el.style as any).textReflection.blur ? `blur(${(((el.style as any).textReflection.blur !== undefined ? (el.style as any).textReflection.blur : 0) * 1.33).toFixed(1)}px)` : 'none',
|
|
8520
|
-
['-webkit-mask-image']: `linear-gradient(to bottom, rgba(0,0,0,1) 0%, rgba(0,0,0,0) ${((el.style as any).textReflection.size !== undefined ? (el.style as any).textReflection.size : 8)}%, rgba(0,0,0,0) 100%)`,
|
|
8521
|
-
['mask-image']: `linear-gradient(to bottom, rgba(0,0,0,1) 0%, rgba(0,0,0,0) ${((el.style as any).textReflection.size !== undefined ? (el.style as any).textReflection.size : 8)}%, rgba(0,0,0,0) 100%)`,
|
|
8522
|
-
color: el.style.textColor || el.style.color || (el.style.fill === '#ffffff' || el.style.fill === '#FFFFFF' ? '#475569' : '#ffffff'),
|
|
8523
|
-
fontFamily: el.style.fontFamily || 'inherit',
|
|
8524
|
-
fontSize: el.style.fontSize || '16px',
|
|
8525
|
-
fontWeight: el.style.fontWeight || 'normal',
|
|
8526
|
-
fontStyle: (el.style as any).fontStyle || 'normal',
|
|
8527
|
-
textDecoration: (el.style.textDecoration as any) || 'none',
|
|
8528
|
-
['-webkit-text-stroke']: getTextStrokeStyle(el),
|
|
8529
|
-
['paint-order']: (el.style as any).enableTextStroke ? 'stroke fill' : 'normal',
|
|
8530
|
-
['text-shadow']: (el.style as any).textShadow || 'none',
|
|
8531
|
-
lineHeight: el.style.lineHeight || 1.35,
|
|
8532
|
-
textAlign: (el.style.textAlign as any) || 'center',
|
|
8533
|
-
whiteSpace: (el.style as any).whiteSpace || 'normal',
|
|
8534
|
-
transformOrigin: 'top left',
|
|
8535
|
-
height: 'auto'
|
|
8536
|
-
}"
|
|
8537
|
-
v-html="el.style.textBackground && el.style.textBackground !== 'transparent'
|
|
8538
|
-
? `<span style='background-color: ${el.style.textBackground}; display: inline;'>${normalizeHtmlFontFamily(getSafeShapeText(el))}</span>`
|
|
8539
|
-
: normalizeHtmlFontFamily(getSafeShapeText(el))">
|
|
8540
|
-
</div>
|
|
8541
|
-
</div>
|
|
8542
|
-
</div>
|
|
8431
|
+
<CanvasShapeElement
|
|
8432
|
+
:element="el"
|
|
8433
|
+
:active-slide="activeSlide"
|
|
8434
|
+
:is-reflection="true"
|
|
8435
|
+
:theme-recipe="getActiveSlideThemeRecipe(activeSlide)"
|
|
8436
|
+
:normalize-html-font-family="normalizeHtmlFontFamily"
|
|
8437
|
+
:get-text-stroke-style="getTextStrokeStyle"
|
|
8438
|
+
:get-element-text-shadow="getElementTextShadow"
|
|
8439
|
+
:get-reflection-leading-compensation="getReflectionLeadingCompensation"
|
|
8440
|
+
:is-image-placeholder="isImagePlaceholder"
|
|
8441
|
+
/>
|
|
8543
8442
|
</template>
|
|
8544
8443
|
|
|
8545
8444
|
<!-- Image element -->
|
|
@@ -122,7 +122,7 @@ function handleSelect(chartType: string, isMini = false) {
|
|
|
122
122
|
<div class="chart-card-item" @click="handleSelect('donut')">
|
|
123
123
|
<div class="chart-card-thumb">
|
|
124
124
|
<div class="chart-thumb-box">
|
|
125
|
-
<svg viewBox="0 0 36 36" width="
|
|
125
|
+
<svg viewBox="0 0 36 36" width="72" height="72" fill="none">
|
|
126
126
|
<path d="M18 5 A13 13 0 0 1 31 18 L25 18 A7 7 0 0 0 18 11 Z" fill="#2563eb" />
|
|
127
127
|
<path d="M28 18 A10 10 0 0 1 18 28 L18 23 A5 5 0 0 0 23 18 Z" fill="#60a5fa" />
|
|
128
128
|
<path d="M18 26 A8 8 0 0 1 10 18 L14 18 A4 4 0 0 0 18 22 Z" fill="#93c5fd" />
|
|
@@ -153,7 +153,7 @@ function handleSelect(chartType: string, isMini = false) {
|
|
|
153
153
|
<div class="chart-card-item" @click="handleSelect('bar')">
|
|
154
154
|
<div class="chart-card-thumb">
|
|
155
155
|
<div class="chart-thumb-box">
|
|
156
|
-
<svg viewBox="0 0 36 36" width="
|
|
156
|
+
<svg viewBox="0 0 36 36" width="72" height="72" fill="none">
|
|
157
157
|
<rect x="5" y="7" width="6" height="5" fill="#93c5fd" />
|
|
158
158
|
<rect x="5" y="13" width="6" height="8" fill="#60a5fa" />
|
|
159
159
|
<rect x="5" y="22" width="6" height="7" fill="#2563eb" />
|
|
@@ -177,7 +177,7 @@ function handleSelect(chartType: string, isMini = false) {
|
|
|
177
177
|
<div class="chart-card-item" @click="handleSelect('bar')">
|
|
178
178
|
<div class="chart-card-thumb">
|
|
179
179
|
<div class="chart-thumb-box">
|
|
180
|
-
<svg viewBox="0 0 36 36" width="
|
|
180
|
+
<svg viewBox="0 0 36 36" width="72" height="72" fill="none">
|
|
181
181
|
<rect x="5" y="16" width="6" height="6" fill="#60a5fa" />
|
|
182
182
|
<rect x="5" y="23" width="6" height="6" fill="#2563eb" />
|
|
183
183
|
|
|
@@ -214,7 +214,7 @@ function handleSelect(chartType: string, isMini = false) {
|
|
|
214
214
|
<div class="chart-card-item" @click="handleSelect('line')">
|
|
215
215
|
<div class="chart-card-thumb">
|
|
216
216
|
<div class="chart-thumb-box">
|
|
217
|
-
<svg viewBox="0 0 36 36" width="
|
|
217
|
+
<svg viewBox="0 0 36 36" width="72" height="72" fill="none">
|
|
218
218
|
<path d="M5 23 L12 16 L20 21 L31 10" stroke="#2563eb" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />
|
|
219
219
|
<path d="M5 19 L12 23 L20 14 L31 17" stroke="#93c5fd" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />
|
|
220
220
|
<line x1="3" y1="28" x2="33" y2="28" stroke="#cbd5e1" stroke-width="1.2" />
|
|
@@ -228,7 +228,7 @@ function handleSelect(chartType: string, isMini = false) {
|
|
|
228
228
|
<div class="chart-card-item" @click="handleSelect('line')">
|
|
229
229
|
<div class="chart-card-thumb">
|
|
230
230
|
<div class="chart-thumb-box">
|
|
231
|
-
<svg viewBox="0 0 36 36" width="
|
|
231
|
+
<svg viewBox="0 0 36 36" width="72" height="72" fill="none">
|
|
232
232
|
<path d="M5 22 L13 22 L21 16 L31 10" stroke="#2563eb" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
|
233
233
|
<line x1="3" y1="28" x2="33" y2="28" stroke="#cbd5e1" stroke-width="1.2" />
|
|
234
234
|
</svg>
|
|
@@ -264,7 +264,7 @@ function handleSelect(chartType: string, isMini = false) {
|
|
|
264
264
|
<div class="chart-card-item" @click="handleSelect('horizontal-bar')">
|
|
265
265
|
<div class="chart-card-thumb">
|
|
266
266
|
<div class="chart-thumb-box">
|
|
267
|
-
<svg viewBox="0 0 36 36" width="
|
|
267
|
+
<svg viewBox="0 0 36 36" width="72" height="72" fill="none">
|
|
268
268
|
<rect x="7" y="6" width="10" height="5" fill="#2563eb" />
|
|
269
269
|
<rect x="18" y="6" width="8" height="5" fill="#60a5fa" />
|
|
270
270
|
<rect x="7" y="15" width="14" height="5" fill="#2563eb" />
|
|
@@ -295,7 +295,7 @@ function handleSelect(chartType: string, isMini = false) {
|
|
|
295
295
|
<div class="chart-card-item" @click="handleSelect('area')">
|
|
296
296
|
<div class="chart-card-thumb">
|
|
297
297
|
<div class="chart-thumb-box">
|
|
298
|
-
<svg viewBox="0 0 36 36" width="
|
|
298
|
+
<svg viewBox="0 0 36 36" width="72" height="72" fill="none">
|
|
299
299
|
<path d="M5 24 C10 24 15 14 20 16 C25 18 28 21 31 19 L31 29 L5 29 Z" fill="#60a5fa" opacity="0.5" />
|
|
300
300
|
<path d="M5 27 C10 27 15 20 20 22 C25 24 28 26 31 25 L31 29 L5 29 Z" fill="#2563eb" opacity="0.8" />
|
|
301
301
|
<line x1="3" y1="29" x2="33" y2="29" stroke="#cbd5e1" stroke-width="1.2" />
|
|
@@ -360,26 +360,33 @@ function handleSelect(chartType: string, isMini = false) {
|
|
|
360
360
|
display: flex;
|
|
361
361
|
flex-direction: column;
|
|
362
362
|
width: 100%;
|
|
363
|
+
height: 100%;
|
|
363
364
|
box-sizing: border-box;
|
|
364
365
|
}
|
|
365
366
|
|
|
366
367
|
.chart-category-tabs {
|
|
367
368
|
display: flex;
|
|
368
369
|
align-items: center;
|
|
369
|
-
gap:
|
|
370
|
+
gap: 8px;
|
|
370
371
|
padding-bottom: 10px;
|
|
371
372
|
overflow-x: hidden;
|
|
372
373
|
border-bottom: 1px solid #f1f5f9;
|
|
373
374
|
}
|
|
374
375
|
|
|
375
376
|
.chart-tab-pill {
|
|
376
|
-
|
|
377
|
+
width: 60px;
|
|
378
|
+
height: 36px;
|
|
379
|
+
padding: 0 9px;
|
|
380
|
+
display: inline-flex;
|
|
381
|
+
align-items: center;
|
|
382
|
+
justify-content: center;
|
|
383
|
+
box-sizing: border-box;
|
|
377
384
|
border-radius: 6px;
|
|
378
385
|
font-size: 12px;
|
|
379
|
-
font-weight:
|
|
386
|
+
font-weight: 400;
|
|
380
387
|
color: #475569;
|
|
381
388
|
background: #ffffff;
|
|
382
|
-
border: 1px solid #
|
|
389
|
+
border: 1px solid #E5E7EB;
|
|
383
390
|
cursor: pointer;
|
|
384
391
|
user-select: none;
|
|
385
392
|
white-space: nowrap;
|
|
@@ -393,14 +400,16 @@ function handleSelect(chartType: string, isMini = false) {
|
|
|
393
400
|
}
|
|
394
401
|
|
|
395
402
|
.chart-tab-pill.active {
|
|
396
|
-
background:
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
403
|
+
background: rgba(30, 111, 255, 0.12);
|
|
404
|
+
background-clip: padding-box;
|
|
405
|
+
color: #1E6FFF;
|
|
406
|
+
border-color: rgba(30, 111, 255, 0.12);
|
|
407
|
+
font-weight: 400;
|
|
400
408
|
}
|
|
401
409
|
|
|
402
410
|
.chart-scroll-content {
|
|
403
|
-
|
|
411
|
+
flex: 1;
|
|
412
|
+
min-height: 0;
|
|
404
413
|
overflow-y: auto;
|
|
405
414
|
overflow-x: hidden;
|
|
406
415
|
padding-right: 4px;
|
|
@@ -452,8 +461,8 @@ function handleSelect(chartType: string, isMini = false) {
|
|
|
452
461
|
}
|
|
453
462
|
|
|
454
463
|
.chart-card-thumb {
|
|
455
|
-
width:
|
|
456
|
-
height:
|
|
464
|
+
width: 120px;
|
|
465
|
+
height: 88px;
|
|
457
466
|
background: #f8fafc;
|
|
458
467
|
border-radius: 6px;
|
|
459
468
|
display: flex;
|
|
@@ -463,20 +472,20 @@ function handleSelect(chartType: string, isMini = false) {
|
|
|
463
472
|
}
|
|
464
473
|
|
|
465
474
|
.chart-thumb-box {
|
|
466
|
-
width:
|
|
467
|
-
height:
|
|
468
|
-
border:
|
|
469
|
-
|
|
470
|
-
background: #ffffff;
|
|
475
|
+
width: 72px;
|
|
476
|
+
height: 72px;
|
|
477
|
+
border: none;
|
|
478
|
+
background: transparent;
|
|
471
479
|
display: flex;
|
|
472
480
|
align-items: center;
|
|
473
481
|
justify-content: center;
|
|
474
482
|
transition: all 0.15s ease;
|
|
475
483
|
}
|
|
476
484
|
|
|
485
|
+
.chart-thumb-box svg,
|
|
477
486
|
.chart-thumb-box img {
|
|
478
|
-
width:
|
|
479
|
-
height:
|
|
487
|
+
width: 72px;
|
|
488
|
+
height: 72px;
|
|
480
489
|
object-fit: contain;
|
|
481
490
|
display: block;
|
|
482
491
|
}
|
|
@@ -494,11 +503,6 @@ function handleSelect(chartType: string, isMini = false) {
|
|
|
494
503
|
background: #f1f5f9;
|
|
495
504
|
}
|
|
496
505
|
|
|
497
|
-
.chart-card-item:hover .chart-thumb-box {
|
|
498
|
-
border-color: #3b82f6;
|
|
499
|
-
border-style: solid;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
506
|
.chart-card-item:hover .chart-card-name {
|
|
503
507
|
color: #2563eb;
|
|
504
508
|
}
|
|
@@ -1758,7 +1758,33 @@ const generatePptxBlob = async (): Promise<Blob | null> => {
|
|
|
1758
1758
|
const match = bgStr.match(/url\(['"]?([^'"]+)['"]?\)/)
|
|
1759
1759
|
if (match && match[1]) {
|
|
1760
1760
|
let bgUrl = match[1]
|
|
1761
|
-
if (
|
|
1761
|
+
if (/^data:image\/svg\+xml/i.test(bgUrl)) {
|
|
1762
|
+
const tiledPng = await new Promise<string | undefined>((resolve) => {
|
|
1763
|
+
const img = new Image()
|
|
1764
|
+
img.onload = () => {
|
|
1765
|
+
try {
|
|
1766
|
+
const canvas = document.createElement('canvas')
|
|
1767
|
+
canvas.width = Math.max(1, Math.min(4096, Math.round(logicalWidth.value || 1280)))
|
|
1768
|
+
canvas.height = Math.max(1, Math.min(4096, Math.round(logicalHeight.value || 720)))
|
|
1769
|
+
const ctx = canvas.getContext('2d')
|
|
1770
|
+
if (!ctx) return resolve(undefined)
|
|
1771
|
+
const pat = ctx.createPattern(img, 'repeat')
|
|
1772
|
+
if (pat) {
|
|
1773
|
+
ctx.fillStyle = pat
|
|
1774
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
|
1775
|
+
} else {
|
|
1776
|
+
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
|
|
1777
|
+
}
|
|
1778
|
+
resolve(canvas.toDataURL('image/png'))
|
|
1779
|
+
} catch {
|
|
1780
|
+
resolve(undefined)
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
img.onerror = () => resolve(undefined)
|
|
1784
|
+
img.src = bgUrl
|
|
1785
|
+
})
|
|
1786
|
+
pptxBg = tiledPng ? { data: tiledPng } : { color: 'FFFFFF', transparency: 0 }
|
|
1787
|
+
} else if (bgUrl.startsWith('data:image')) {
|
|
1762
1788
|
pptxBg = { data: bgUrl }
|
|
1763
1789
|
} else {
|
|
1764
1790
|
if (!bgUrl.startsWith('http') && !bgUrl.startsWith('data:')) {
|
|
@@ -2208,8 +2234,7 @@ const generatePptxBlob = async (): Promise<Blob | null> => {
|
|
|
2208
2234
|
const patternUrl = getElementPatternUrl(el)
|
|
2209
2235
|
const g = getElementGradientConfig(el)
|
|
2210
2236
|
|
|
2211
|
-
// SVG
|
|
2212
|
-
// (画布上图案填充本身是 preserveAspectRatio="none" 拉伸铺满,与 a:stretch 语义一致)
|
|
2237
|
+
// SVG 图案填充 PowerPoint 无法直接渲染 SVG a:blipFill,先按形状显示尺寸以 1:1 平铺栅格化为 PNG
|
|
2213
2238
|
const rasterizeSvgFill = (svgUrl: string, widthPx: number, heightPx: number): Promise<string | undefined> => {
|
|
2214
2239
|
return new Promise((resolve) => {
|
|
2215
2240
|
const img = new Image()
|
|
@@ -2220,7 +2245,13 @@ const generatePptxBlob = async (): Promise<Blob | null> => {
|
|
|
2220
2245
|
canvas.height = Math.max(1, Math.min(4096, Math.round(heightPx)))
|
|
2221
2246
|
const ctx = canvas.getContext('2d')
|
|
2222
2247
|
if (!ctx) return resolve(undefined)
|
|
2223
|
-
ctx.
|
|
2248
|
+
const pat = ctx.createPattern(img, 'repeat')
|
|
2249
|
+
if (pat) {
|
|
2250
|
+
ctx.fillStyle = pat
|
|
2251
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
|
2252
|
+
} else {
|
|
2253
|
+
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
|
|
2254
|
+
}
|
|
2224
2255
|
resolve(canvas.toDataURL('image/png'))
|
|
2225
2256
|
} catch {
|
|
2226
2257
|
resolve(undefined)
|
|
@@ -2232,8 +2263,12 @@ const generatePptxBlob = async (): Promise<Blob | null> => {
|
|
|
2232
2263
|
}
|
|
2233
2264
|
|
|
2234
2265
|
const buildNativeShapeFill = async (): Promise<any> => {
|
|
2235
|
-
//
|
|
2266
|
+
// 图片/图案填充 → 原生 a:blipFill
|
|
2236
2267
|
if (patternUrl) {
|
|
2268
|
+
if (/^data:image\/svg\+xml/i.test(patternUrl)) {
|
|
2269
|
+
const pngUrl = await rasterizeSvgFill(patternUrl, pixelWidth, pixelHeight)
|
|
2270
|
+
return pngUrl ? { type: 'image', data: pngUrl } : undefined
|
|
2271
|
+
}
|
|
2237
2272
|
const pUrl = await resolveMediaSrc(patternUrl, 'image')
|
|
2238
2273
|
if (pUrl && /^data:image\/svg\+xml/i.test(pUrl)) {
|
|
2239
2274
|
const pngUrl = await rasterizeSvgFill(pUrl, pixelWidth, pixelHeight)
|
|
@@ -26,7 +26,9 @@ import {
|
|
|
26
26
|
getGradientAngleDeg,
|
|
27
27
|
getSvgGradientCoords,
|
|
28
28
|
getHexColorWithoutAlpha,
|
|
29
|
-
getAlphaFromColor
|
|
29
|
+
getAlphaFromColor,
|
|
30
|
+
isElementPatternTile,
|
|
31
|
+
getElementPatternTileSize
|
|
30
32
|
} from '../utils/canvas/elementStyles'
|
|
31
33
|
|
|
32
34
|
const props = defineProps<{
|
|
@@ -48,6 +50,21 @@ function resolvePx(val: any, parentPx: number): number {
|
|
|
48
50
|
const selfW = computed(() => resolvePx(props.el?.style?.width, props.parentW))
|
|
49
51
|
const selfH = computed(() => resolvePx(props.el?.style?.height, props.parentH))
|
|
50
52
|
|
|
53
|
+
function getGroupShapePatternTransform(el: any, isTriangle = false): string | undefined {
|
|
54
|
+
if (!isElementPatternTile(el)) return undefined
|
|
55
|
+
const w = Math.max(1, selfW.value || 100)
|
|
56
|
+
const h = Math.max(1, selfH.value || 100)
|
|
57
|
+
if (isTriangle) {
|
|
58
|
+
return `scale(${+(100 / w).toFixed(4)}, ${+(100 / h).toFixed(4)})`
|
|
59
|
+
}
|
|
60
|
+
const vbW = el.viewBox ? el.viewBox[0] : 200
|
|
61
|
+
const vbH = el.viewBox ? el.viewBox[1] : 200
|
|
62
|
+
const bw = el.shapType === 'arc' ? 0 : (el.style?.borderWidth ? parseFloat(el.style.borderWidth) : 2)
|
|
63
|
+
const effW = Math.max(1, w - bw)
|
|
64
|
+
const effH = Math.max(1, h - bw)
|
|
65
|
+
return `scale(${+(vbW / effW).toFixed(4)}, ${+(vbH / effH).toFixed(4)})`
|
|
66
|
+
}
|
|
67
|
+
|
|
51
68
|
const boxStyle = computed(() => {
|
|
52
69
|
const el = props.el || {}
|
|
53
70
|
const st = el.style || {}
|
|
@@ -326,9 +343,9 @@ const MARKER_PATHS: Record<string, string> = {
|
|
|
326
343
|
height: '100%',
|
|
327
344
|
backgroundColor: getElementPattern(el) ? 'transparent' : ((getElementGradientCss(el) || (getElementFill(el) !== undefined && getElementFill(el) !== 'none')) ? (getElementGradientCss(el) ? 'transparent' : getElementFill(el)) : (getElementFill(el) === 'none' ? 'transparent' : '#3b82f6')),
|
|
328
345
|
backgroundImage: getElementPattern(el) ? `url('${getElementPattern(el)}')` : (getElementGradientCss(el) || 'none'),
|
|
329
|
-
backgroundSize: getElementPattern(el) ? 'cover' : undefined,
|
|
330
|
-
backgroundPosition: getElementPattern(el) ? 'center center' : undefined,
|
|
331
|
-
backgroundRepeat: getElementPattern(el) ? 'no-repeat' : undefined,
|
|
346
|
+
backgroundSize: getElementPattern(el) ? (isElementPatternTile(el) ? `${getElementPatternTileSize(el).w}px ${getElementPatternTileSize(el).h}px` : 'cover') : undefined,
|
|
347
|
+
backgroundPosition: getElementPattern(el) ? (isElementPatternTile(el) ? '0 0' : 'center center') : undefined,
|
|
348
|
+
backgroundRepeat: getElementPattern(el) ? (isElementPatternTile(el) ? 'repeat' : 'no-repeat') : undefined,
|
|
332
349
|
borderRadius: '50%',
|
|
333
350
|
borderWidth: `${getElementBorderWidth(el)}px`,
|
|
334
351
|
borderStyle: el.style?.borderStyle || (el as any).borderType || 'solid',
|
|
@@ -341,9 +358,16 @@ const MARKER_PATHS: Record<string, string> = {
|
|
|
341
358
|
<svg viewBox="0 0 100 100" preserveAspectRatio="none"
|
|
342
359
|
style="width: 100%; height: 100%; display: block; overflow: visible;">
|
|
343
360
|
<defs>
|
|
344
|
-
<pattern v-if="getElementPattern(el)" :id="`pres-group-shape-pattern-${el.id}`"
|
|
345
|
-
|
|
346
|
-
|
|
361
|
+
<pattern v-if="getElementPattern(el)" :id="`pres-group-shape-pattern-${el.id}`"
|
|
362
|
+
:width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : '100%'"
|
|
363
|
+
:height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : '100%'"
|
|
364
|
+
:patternUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
|
|
365
|
+
:patternContentUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
|
|
366
|
+
:patternTransform="getGroupShapePatternTransform(el, true)">
|
|
367
|
+
<image :href="getElementPattern(el)"
|
|
368
|
+
:width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : 1"
|
|
369
|
+
:height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : 1"
|
|
370
|
+
preserveAspectRatio="none" />
|
|
347
371
|
</pattern>
|
|
348
372
|
<template v-if="getElementGradientConfig(el)">
|
|
349
373
|
<radialGradient
|
|
@@ -397,9 +421,16 @@ const MARKER_PATHS: Record<string, string> = {
|
|
|
397
421
|
style="width: 100%; height: 100%; overflow: visible;">
|
|
398
422
|
<svg overflow="visible" style="width: 100%; height: 100%; display: block; overflow: visible;">
|
|
399
423
|
<defs>
|
|
400
|
-
<pattern v-if="getElementPattern(el)" :id="`pres-group-shape-pattern-${el.id}`"
|
|
401
|
-
|
|
402
|
-
|
|
424
|
+
<pattern v-if="getElementPattern(el)" :id="`pres-group-shape-pattern-${el.id}`"
|
|
425
|
+
:width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : '100%'"
|
|
426
|
+
:height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : '100%'"
|
|
427
|
+
:patternUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
|
|
428
|
+
:patternContentUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
|
|
429
|
+
:patternTransform="getGroupShapePatternTransform(el, false)">
|
|
430
|
+
<image :href="getElementPattern(el)"
|
|
431
|
+
:width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : 1"
|
|
432
|
+
:height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : 1"
|
|
433
|
+
preserveAspectRatio="none" />
|
|
403
434
|
</pattern>
|
|
404
435
|
<template v-if="getElementGradientConfig(el)">
|
|
405
436
|
<radialGradient
|
|
@@ -458,9 +489,9 @@ const MARKER_PATHS: Record<string, string> = {
|
|
|
458
489
|
height: '100%',
|
|
459
490
|
backgroundColor: getElementPattern(el) ? 'transparent' : ((getElementGradientCss(el) || (getElementFill(el) !== undefined && getElementFill(el) !== 'none')) ? (getElementGradientCss(el) ? 'transparent' : getElementFill(el)) : (getElementFill(el) === 'none' ? 'transparent' : '#10b981')),
|
|
460
491
|
backgroundImage: getElementPattern(el) ? `url('${getElementPattern(el)}')` : (getElementGradientCss(el) || 'none'),
|
|
461
|
-
backgroundSize: getElementPattern(el) ? 'cover' : undefined,
|
|
462
|
-
backgroundPosition: getElementPattern(el) ? 'center center' : undefined,
|
|
463
|
-
backgroundRepeat: getElementPattern(el) ? 'no-repeat' : undefined,
|
|
492
|
+
backgroundSize: getElementPattern(el) ? (isElementPatternTile(el) ? `${getElementPatternTileSize(el).w}px ${getElementPatternTileSize(el).h}px` : 'cover') : undefined,
|
|
493
|
+
backgroundPosition: getElementPattern(el) ? (isElementPatternTile(el) ? '0 0' : 'center center') : undefined,
|
|
494
|
+
backgroundRepeat: getElementPattern(el) ? (isElementPatternTile(el) ? 'repeat' : 'no-repeat') : undefined,
|
|
464
495
|
borderRadius: el.style?.borderRadius !== undefined ? el.style.borderRadius : '0px',
|
|
465
496
|
borderWidth: `${getElementBorderWidth(el)}px`,
|
|
466
497
|
borderStyle: el.style?.borderStyle || (el as any).borderType || 'solid',
|