jvs-ppt-ui 0.0.14 → 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/check_imgs.html +18 -0
- package/dist/lib/jvs-ppt-ui/iconfont/iconfont.css +187 -27
- package/dist/lib/jvs-ppt-ui/iconfont/iconfont.js +1 -1
- package/dist/lib/jvs-ppt-ui/iconfont/iconfont.json +345 -65
- package/dist/lib/jvs-ppt-ui/iconfont/iconfont.ttf +0 -0
- package/dist/lib/jvs-ppt-ui/iconfont/iconfont.woff +0 -0
- package/dist/lib/jvs-ppt-ui/iconfont/iconfont.woff2 +0 -0
- package/dist/lib/jvs-ppt-ui/img/al.png +0 -0
- package/dist/lib/jvs-ppt-ui/img/d.png +0 -0
- package/dist/lib/jvs-ppt-ui/img/dl.png +0 -0
- package/dist/lib/jvs-ppt-ui/img/l.png +0 -0
- package/dist/lib/jvs-ppt-ui.es.js +44973 -43349
- package/dist/lib/jvs-ppt-ui.umd.js +102 -102
- package/dist/lib/style.css +1 -1
- package/package.json +1 -1
- package/scripts/check-icons.mjs +22 -0
- package/scripts/patch-anim-icons.mjs +45 -0
- package/src/assets/iconfont/iconfont.css +187 -27
- package/src/assets/iconfont/iconfont.js +1 -1
- package/src/assets/iconfont/iconfont.json +345 -65
- package/src/assets/iconfont/iconfont.ttf +0 -0
- package/src/assets/iconfont/iconfont.woff +0 -0
- package/src/assets/iconfont/iconfont.woff2 +0 -0
- package/src/components/CanvasArea.vue +218 -193
- package/src/components/ChartTypeSelector.vue +509 -148
- package/src/components/Header.vue +327 -44
- package/src/components/PresentationGroupChild.vue +44 -13
- package/src/components/PresentationMode.vue +84 -33
- package/src/components/QuickBorderPopover.vue +51 -11
- package/src/components/QuickColorPopover.vue +65 -21
- package/src/components/SidebarLeft.vue +286 -107
- package/src/components/SidebarRight.vue +45 -38
- package/src/components/TemplateGallery.vue +1110 -1237
- package/src/components/ToastNotification.vue +2 -2
- package/src/components/Toolbar.vue +825 -733
- package/src/components/canvas/CanvasQuickToolbar.vue +17 -30
- package/src/components/canvas/elements/CanvasGroupElement.vue +6 -0
- package/src/components/canvas/elements/CanvasShapeElement.vue +198 -38
- package/src/components/canvas/elements/CanvasTextElement.vue +4 -3
- package/src/components/sidebar/common/SidebarAlignGroup.vue +104 -50
- package/src/components/sidebar/common/SidebarBorderAccordion.vue +375 -148
- package/src/components/sidebar/common/SidebarPositionGroup.vue +358 -149
- package/src/components/sidebar/common/SidebarReflectionAccordion.vue +429 -203
- package/src/components/sidebar/common/SidebarShadowAccordion.vue +844 -416
- package/src/components/sidebar/panels/GradientEditor.vue +671 -316
- package/src/components/sidebar/panels/LayoutThumbSvg.vue +33 -16
- package/src/components/sidebar/panels/SidebarAnimationPanel.vue +701 -138
- package/src/components/sidebar/panels/SidebarChartFormatPanel.vue +3 -3
- package/src/components/sidebar/panels/SidebarDesignPanel.vue +1754 -416
- package/src/components/sidebar/panels/SidebarMediaFormatPanel.vue +12 -0
- package/src/components/sidebar/panels/SidebarShapeFormatPanel.vue +813 -174
- package/src/components/sidebar/panels/SidebarTableFormatPanel.vue +2 -2
- package/src/components/sidebar/panels/SidebarTextFormatPanel.vue +1938 -1427
- package/src/components/sidebar/panels/SidebarTransitionPanel.vue +791 -210
- package/src/composables/canvas/useCanvasShortcuts.ts +20 -6
- package/src/composables/canvas/useCanvasText.ts +1 -1
- package/src/composables/canvas/useCanvasTransform.ts +44 -0
- package/src/composables/presetThemes.ts +43 -44
- package/src/composables/sidebar/useSidebarAnimation.ts +101 -25
- package/src/composables/sidebar/useSidebarDesign.ts +30 -11
- package/src/composables/sidebar/useSidebarElementTransform.ts +10 -1
- package/src/composables/sidebar/useSidebarImage.ts +10 -2
- package/src/composables/sidebar/useSidebarShapeFormat.ts +562 -107
- package/src/composables/useSlides.ts +331 -153
- package/src/main.ts +2 -0
- package/src/style.css +926 -359
- package/src/utils/canvas/elementStyles.ts +42 -1
|
@@ -91,6 +91,20 @@ export function useCanvasShortcuts(options: ShortcutsOptions) {
|
|
|
91
91
|
function handleGlobalKeyDown(e: KeyboardEvent) {
|
|
92
92
|
if (e.defaultPrevented) return
|
|
93
93
|
|
|
94
|
+
// 如果焦点在输入框、文本域或富文本编辑区(无论是在 PPT 内部还是在外部宿主中),绝不拦截原生输入行为
|
|
95
|
+
const activeEl = document.activeElement as HTMLElement | null
|
|
96
|
+
const target = (e.target || activeEl) as HTMLElement | null
|
|
97
|
+
if (
|
|
98
|
+
target &&
|
|
99
|
+
(target.tagName === 'INPUT' ||
|
|
100
|
+
target.tagName === 'TEXTAREA' ||
|
|
101
|
+
target.isContentEditable ||
|
|
102
|
+
target.getAttribute('contenteditable') === 'true' ||
|
|
103
|
+
target.closest?.('input, textarea, [contenteditable="true"]'))
|
|
104
|
+
) {
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
|
|
94
108
|
if (isReadonly?.value) {
|
|
95
109
|
if (e.key === 'F5') {
|
|
96
110
|
e.preventDefault()
|
|
@@ -270,13 +284,13 @@ export function useCanvasShortcuts(options: ShortcutsOptions) {
|
|
|
270
284
|
if (isEditingText.value) return
|
|
271
285
|
|
|
272
286
|
// Prevent deleting slides or moving elements when editing other standard input elements
|
|
273
|
-
const
|
|
287
|
+
const currentActiveEl = document.activeElement as HTMLElement | null
|
|
274
288
|
if (
|
|
275
|
-
|
|
276
|
-
(
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
289
|
+
currentActiveEl &&
|
|
290
|
+
(currentActiveEl.tagName === 'INPUT' ||
|
|
291
|
+
currentActiveEl.tagName === 'TEXTAREA' ||
|
|
292
|
+
currentActiveEl.isContentEditable ||
|
|
293
|
+
currentActiveEl.getAttribute('contenteditable') === 'true')
|
|
280
294
|
) {
|
|
281
295
|
return
|
|
282
296
|
}
|
|
@@ -385,7 +385,7 @@ export function useCanvasText(options: TextOptions) {
|
|
|
385
385
|
if (container) {
|
|
386
386
|
container.innerHTML = isShape ? (isSvgPathString(templateDefault) ? '' : templateDefault) : templateDefault
|
|
387
387
|
}
|
|
388
|
-
} else if (prevEl.type === 'text') {
|
|
388
|
+
} else if (prevEl.type === 'text' && (!clickedEl || clickedEl.id !== prevEl.id)) {
|
|
389
389
|
activeSlide.value.elements = activeSlide.value.elements.filter((x: any) => x.id !== prevEl.id)
|
|
390
390
|
if (selectedElementId.value === prevEl.id) {
|
|
391
391
|
selectedElementId.value = null
|
|
@@ -47,6 +47,7 @@ export interface CanvasTransformOptions {
|
|
|
47
47
|
updateLineGeometry?: (el: any, start: [number, number], end: [number, number], absControls?: any, allElements?: any[], parentGroupDim?: { left: number; top: number; width: number; height: number }) => void
|
|
48
48
|
selectedElementWidth?: Ref<string>
|
|
49
49
|
selectedElementHeight?: Ref<string>
|
|
50
|
+
activeSlideVersion?: Ref<number>
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
export function useCanvasTransform(options: CanvasTransformOptions) {
|
|
@@ -349,9 +350,31 @@ export function useCanvasTransform(options: CanvasTransformOptions) {
|
|
|
349
350
|
if (typeof item.height === 'number') item.height = newH
|
|
350
351
|
}
|
|
351
352
|
|
|
353
|
+
if (item.type === 'shape' && item.pathFormula === 'roundRect') {
|
|
354
|
+
const formulaObj = shapeFormulas()[item.pathFormula]
|
|
355
|
+
if (formulaObj && typeof formulaObj.formula === 'function') {
|
|
356
|
+
const kps = Array.isArray(item.keypoints) && item.keypoints.length > 0
|
|
357
|
+
? item.keypoints
|
|
358
|
+
: (formulaObj.defaultValue ? [...formulaObj.defaultValue] : [0.125])
|
|
359
|
+
item.viewBox = [newW, newH]
|
|
360
|
+
const newPath = formulaObj.formula(newW, newH, kps)
|
|
361
|
+
item.content = newPath
|
|
362
|
+
item.path = newPath
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
352
366
|
// Auto-update font scaling for autoFit text boxes
|
|
353
367
|
updateTextFontScale(item)
|
|
354
368
|
|
|
369
|
+
if (selectedElementId.value === item.id) {
|
|
370
|
+
if (options.selectedElementWidth && !isAutoWidthText) {
|
|
371
|
+
options.selectedElementWidth.value = `${newW.toFixed(1)}px`
|
|
372
|
+
}
|
|
373
|
+
if (options.selectedElementHeight && !isAutoHeightText) {
|
|
374
|
+
options.selectedElementHeight.value = `${newH.toFixed(1)}px`
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
355
378
|
// Real-time table resize handling
|
|
356
379
|
if (item.type === 'text' && item.content && typeof item.content === 'string' && item.content.includes('<table')) {
|
|
357
380
|
const canvasDom = document.getElementById(`canvas-el-${item.id}`)
|
|
@@ -431,6 +454,12 @@ export function useCanvasTransform(options: CanvasTransformOptions) {
|
|
|
431
454
|
|
|
432
455
|
for (const state of resizeStates) {
|
|
433
456
|
const item = state.el
|
|
457
|
+
if (item && selectedElementId.value === item.id) {
|
|
458
|
+
const curW = item.style?.width && item.style.width !== 'auto' ? parseFloat(item.style.width) : (typeof item.width === 'number' ? item.width : 0)
|
|
459
|
+
const curH = item.style?.height && item.style.height !== 'auto' ? parseFloat(item.style.height) : (typeof item.height === 'number' ? item.height : 0)
|
|
460
|
+
if (options.selectedElementWidth && curW > 0) options.selectedElementWidth.value = `${curW}px`
|
|
461
|
+
if (options.selectedElementHeight && curH > 0) options.selectedElementHeight.value = `${curH}px`
|
|
462
|
+
}
|
|
434
463
|
if (item && item.type === 'text' && item.content && typeof item.content === 'string' && item.content.includes('<table')) {
|
|
435
464
|
const container = document.getElementById(`editable-${item.id}`)
|
|
436
465
|
if (container) {
|
|
@@ -455,6 +484,7 @@ export function useCanvasTransform(options: CanvasTransformOptions) {
|
|
|
455
484
|
if (isCanvasDragging) isCanvasDragging.value = false
|
|
456
485
|
pauseHistory.value = false
|
|
457
486
|
addHistorySnapshot()
|
|
487
|
+
if (options.activeSlideVersion) options.activeSlideVersion.value++
|
|
458
488
|
|
|
459
489
|
currentElement.value = null
|
|
460
490
|
resizeStates = []
|
|
@@ -566,6 +596,7 @@ export function useCanvasTransform(options: CanvasTransformOptions) {
|
|
|
566
596
|
if (isCanvasDragging) isCanvasDragging.value = false
|
|
567
597
|
pauseHistory.value = false
|
|
568
598
|
addHistorySnapshot()
|
|
599
|
+
if (options.activeSlideVersion) options.activeSlideVersion.value++
|
|
569
600
|
}
|
|
570
601
|
|
|
571
602
|
function startDragKeypoint(e: MouseEvent, el: any, index: number) {
|
|
@@ -627,6 +658,9 @@ export function useCanvasTransform(options: CanvasTransformOptions) {
|
|
|
627
658
|
const geom = getElementPixels(curEl)
|
|
628
659
|
const actualW = geom.width || 200
|
|
629
660
|
const actualH = geom.height || 200
|
|
661
|
+
if (curEl.pathFormula === 'roundRect') {
|
|
662
|
+
curEl.viewBox = [actualW, actualH]
|
|
663
|
+
}
|
|
630
664
|
const viewBoxW = curEl.viewBox?.[0] || 200
|
|
631
665
|
const viewBoxH = curEl.viewBox?.[1] || 200
|
|
632
666
|
|
|
@@ -1030,6 +1064,14 @@ export function useCanvasTransform(options: CanvasTransformOptions) {
|
|
|
1030
1064
|
const newPath = formulaObj.formula(viewBoxW, viewBoxH, curEl.keypoints)
|
|
1031
1065
|
curEl.content = newPath
|
|
1032
1066
|
curEl.path = newPath
|
|
1067
|
+
if (curEl.pathFormula === 'roundRect') {
|
|
1068
|
+
if (curEl.style && curEl.style.borderRadius !== undefined) {
|
|
1069
|
+
delete curEl.style.borderRadius
|
|
1070
|
+
}
|
|
1071
|
+
if (curEl.radius !== undefined) {
|
|
1072
|
+
delete curEl.radius
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1033
1075
|
|
|
1034
1076
|
const parentGroup = findParentGroup(curEl.id)
|
|
1035
1077
|
if (parentGroup && parentGroup.elements) {
|
|
@@ -1057,6 +1099,7 @@ export function useCanvasTransform(options: CanvasTransformOptions) {
|
|
|
1057
1099
|
currentElement.value = null
|
|
1058
1100
|
pauseHistory.value = false
|
|
1059
1101
|
addHistorySnapshot()
|
|
1102
|
+
if (options.activeSlideVersion) options.activeSlideVersion.value++
|
|
1060
1103
|
}
|
|
1061
1104
|
|
|
1062
1105
|
function startDragLinePoint(e: MouseEvent, el: any, type: 'start' | 'end') {
|
|
@@ -1264,6 +1307,7 @@ export function useCanvasTransform(options: CanvasTransformOptions) {
|
|
|
1264
1307
|
}
|
|
1265
1308
|
pauseHistory.value = false
|
|
1266
1309
|
addHistorySnapshot()
|
|
1310
|
+
if (options.activeSlideVersion) options.activeSlideVersion.value++
|
|
1267
1311
|
}
|
|
1268
1312
|
|
|
1269
1313
|
return {
|
|
@@ -69,20 +69,20 @@ export interface StyleRecipe {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
export const defaultStyleRecipe: StyleRecipe = {
|
|
72
|
-
id: '
|
|
73
|
-
name: '
|
|
74
|
-
styleColors: ['#
|
|
72
|
+
id: 'tech-blue',
|
|
73
|
+
name: '商务科技蓝',
|
|
74
|
+
styleColors: ['#2563eb', '#1d4ed8', '#0284c7', '#0ea5e9', '#38bdf8', '#64748b', '#0f172a', '#6366f1'],
|
|
75
75
|
styleFonts: {
|
|
76
76
|
titleFont: 'SourceHanSansCN',
|
|
77
77
|
bodyFont: 'SourceHanSansCN'
|
|
78
78
|
},
|
|
79
|
-
backgroundColor: '#
|
|
80
|
-
bulletPointsColor: '#
|
|
81
|
-
linkColor: '#
|
|
79
|
+
backgroundColor: '#f8fafc',
|
|
80
|
+
bulletPointsColor: '#3b82f6',
|
|
81
|
+
linkColor: '#2563eb',
|
|
82
82
|
linkUnderline: true,
|
|
83
83
|
textStyles: {
|
|
84
84
|
title: {
|
|
85
|
-
color: '#
|
|
85
|
+
color: '#1e3a8a',
|
|
86
86
|
fontSize: 48,
|
|
87
87
|
fontWeight: 'bold',
|
|
88
88
|
letterSpacing: 0,
|
|
@@ -91,8 +91,8 @@ export const defaultStyleRecipe: StyleRecipe = {
|
|
|
91
91
|
uppercase: false
|
|
92
92
|
},
|
|
93
93
|
headline: {
|
|
94
|
-
color: '#
|
|
95
|
-
fontSize:
|
|
94
|
+
color: '#2563eb',
|
|
95
|
+
fontSize: 32,
|
|
96
96
|
fontWeight: 'bold',
|
|
97
97
|
letterSpacing: 0,
|
|
98
98
|
lineHeight: 1.3,
|
|
@@ -100,8 +100,8 @@ export const defaultStyleRecipe: StyleRecipe = {
|
|
|
100
100
|
uppercase: false
|
|
101
101
|
},
|
|
102
102
|
subheadline: {
|
|
103
|
-
color: '#
|
|
104
|
-
fontSize:
|
|
103
|
+
color: '#0284c7',
|
|
104
|
+
fontSize: 20,
|
|
105
105
|
fontWeight: 'normal',
|
|
106
106
|
letterSpacing: 0,
|
|
107
107
|
lineHeight: 1.3,
|
|
@@ -110,7 +110,7 @@ export const defaultStyleRecipe: StyleRecipe = {
|
|
|
110
110
|
},
|
|
111
111
|
normalText: {
|
|
112
112
|
color: '#334155',
|
|
113
|
-
fontSize:
|
|
113
|
+
fontSize: 16,
|
|
114
114
|
fontWeight: 'normal',
|
|
115
115
|
letterSpacing: 0,
|
|
116
116
|
lineHeight: 1.5,
|
|
@@ -119,7 +119,7 @@ export const defaultStyleRecipe: StyleRecipe = {
|
|
|
119
119
|
},
|
|
120
120
|
smallText: {
|
|
121
121
|
color: '#64748b',
|
|
122
|
-
fontSize:
|
|
122
|
+
fontSize: 13,
|
|
123
123
|
fontWeight: 'normal',
|
|
124
124
|
letterSpacing: 0,
|
|
125
125
|
lineHeight: 1.4,
|
|
@@ -128,27 +128,26 @@ export const defaultStyleRecipe: StyleRecipe = {
|
|
|
128
128
|
}
|
|
129
129
|
},
|
|
130
130
|
table: {
|
|
131
|
-
headerBg: '#
|
|
131
|
+
headerBg: '#1e40af',
|
|
132
132
|
headerTextColor: '#ffffff',
|
|
133
|
-
borderColor: '#
|
|
134
|
-
alternateRowBg: '#
|
|
133
|
+
borderColor: '#cbd5e1',
|
|
134
|
+
alternateRowBg: '#eff6ff'
|
|
135
135
|
},
|
|
136
136
|
chart: {
|
|
137
|
-
chartColor: '#
|
|
137
|
+
chartColor: '#0f172a',
|
|
138
138
|
paletteName: 'Chart colors - Mono',
|
|
139
|
-
palette:
|
|
139
|
+
palette: generateMonoPalette('#0f172a', 8),
|
|
140
140
|
labelColor: '#1e293b',
|
|
141
141
|
gridColor: '#e2e8f0'
|
|
142
142
|
},
|
|
143
143
|
shape: {
|
|
144
|
-
fillColor: '#
|
|
145
|
-
borderColor: '#
|
|
144
|
+
fillColor: '#2563eb',
|
|
145
|
+
borderColor: '#1d4ed8',
|
|
146
146
|
textColor: '#ffffff'
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
export const builtinStyleRecipes: StyleRecipe[] = [
|
|
151
|
-
JSON.parse(JSON.stringify(defaultStyleRecipe)),
|
|
152
151
|
{
|
|
153
152
|
id: 'tech-blue',
|
|
154
153
|
name: '商务科技蓝',
|
|
@@ -168,30 +167,11 @@ export const builtinStyleRecipes: StyleRecipe[] = [
|
|
|
168
167
|
chart: { chartColor: '#0f172a', paletteName: 'Chart colors - Mono', palette: generateMonoPalette('#0f172a', 8), labelColor: '#1e293b', gridColor: '#e2e8f0' },
|
|
169
168
|
shape: { fillColor: '#2563eb', borderColor: '#1d4ed8', textColor: '#ffffff' }
|
|
170
169
|
},
|
|
171
|
-
{
|
|
172
|
-
id: 'dark-gold',
|
|
173
|
-
name: '高端暗黑金',
|
|
174
|
-
styleColors: ['#b45309', '#d97706', '#f59e0b', '#fbbf24', '#78350f', '#451a03', '#0f172a', '#1e293b'],
|
|
175
|
-
styleFonts: { titleFont: 'Abhaya Libre', bodyFont: 'Abhaya Libre' },
|
|
176
|
-
backgroundColor: '#0f172a',
|
|
177
|
-
bulletPointsColor: '#f59e0b',
|
|
178
|
-
linkColor: '#fbbf24',
|
|
179
|
-
textStyles: {
|
|
180
|
-
title: { color: '#fbbf24', fontSize: 48, fontWeight: 'bold', letterSpacing: 0, lineHeight: 1.2, paragraphSpacing: 0, uppercase: false },
|
|
181
|
-
headline: { color: '#f59e0b', fontSize: 32, fontWeight: 'bold', letterSpacing: 0, lineHeight: 1.3, paragraphSpacing: 0, uppercase: false },
|
|
182
|
-
subheadline: { color: '#d97706', fontSize: 20, fontWeight: 'normal', letterSpacing: 0, lineHeight: 1.3, paragraphSpacing: 0, uppercase: false },
|
|
183
|
-
normalText: { color: '#e2e8f0', fontSize: 16, fontWeight: 'normal', letterSpacing: 0, lineHeight: 1.5, paragraphSpacing: 0, uppercase: false },
|
|
184
|
-
smallText: { color: '#94a3b8', fontSize: 13, fontWeight: 'normal', letterSpacing: 0, lineHeight: 1.4, paragraphSpacing: 0, uppercase: false }
|
|
185
|
-
},
|
|
186
|
-
table: { headerBg: '#b45309', headerTextColor: '#ffffff', borderColor: '#334155', alternateRowBg: '#1e293b' },
|
|
187
|
-
chart: { chartColor: '#0f172a', paletteName: 'Chart colors - Mono', palette: generateMonoPalette('#0f172a', 8), labelColor: '#f8fafc', gridColor: '#334155' },
|
|
188
|
-
shape: { fillColor: '#d97706', borderColor: '#fbbf24', textColor: '#ffffff' }
|
|
189
|
-
},
|
|
190
170
|
{
|
|
191
171
|
id: 'fresh-green',
|
|
192
172
|
name: '薄荷清新绿',
|
|
193
173
|
styleColors: ['#10b981', '#059669', '#047857', '#065f46', '#84cc16', '#06b6d4', '#1f2937', '#a7f3d0'],
|
|
194
|
-
styleFonts: { titleFont: '
|
|
174
|
+
styleFonts: { titleFont: '抖音美好体', bodyFont: '抖音美好体' },
|
|
195
175
|
backgroundColor: '#f0fdf4',
|
|
196
176
|
bulletPointsColor: '#10b981',
|
|
197
177
|
linkColor: '#059669',
|
|
@@ -210,7 +190,7 @@ export const builtinStyleRecipes: StyleRecipe[] = [
|
|
|
210
190
|
id: 'fashion-purple',
|
|
211
191
|
name: '时尚梦幻紫',
|
|
212
192
|
styleColors: ['#8b5cf6', '#7c3aed', '#6d28d9', '#5b21b6', '#ec4899', '#06b6d4', '#111827', '#ddd6fe'],
|
|
213
|
-
styleFonts: { titleFont: '
|
|
193
|
+
styleFonts: { titleFont: '字魂扁桃体', bodyFont: '字魂扁桃体' },
|
|
214
194
|
backgroundColor: '#f5f3ff',
|
|
215
195
|
bulletPointsColor: '#8b5cf6',
|
|
216
196
|
linkColor: '#7c3aed',
|
|
@@ -225,11 +205,30 @@ export const builtinStyleRecipes: StyleRecipe[] = [
|
|
|
225
205
|
chart: { chartColor: '#111827', paletteName: 'Chart colors - Mono', palette: generateMonoPalette('#111827', 8), labelColor: '#4c1d95', gridColor: '#ddd6fe' },
|
|
226
206
|
shape: { fillColor: '#8b5cf6', borderColor: '#6d28d9', textColor: '#ffffff' }
|
|
227
207
|
},
|
|
208
|
+
{
|
|
209
|
+
id: 'dark-gold',
|
|
210
|
+
name: '高端暗黑金',
|
|
211
|
+
styleColors: ['#b45309', '#d97706', '#f59e0b', '#fbbf24', '#78350f', '#451a03', '#0f172a', '#1e293b'],
|
|
212
|
+
styleFonts: { titleFont: '字魂扁桃体', bodyFont: '字魂扁桃体' },
|
|
213
|
+
backgroundColor: '#121826',
|
|
214
|
+
bulletPointsColor: '#f59e0b',
|
|
215
|
+
linkColor: '#fbbf24',
|
|
216
|
+
textStyles: {
|
|
217
|
+
title: { color: '#fbbf24', fontSize: 48, fontWeight: 'bold', letterSpacing: 0, lineHeight: 1.2, paragraphSpacing: 0, uppercase: false },
|
|
218
|
+
headline: { color: '#f59e0b', fontSize: 32, fontWeight: 'bold', letterSpacing: 0, lineHeight: 1.3, paragraphSpacing: 0, uppercase: false },
|
|
219
|
+
subheadline: { color: '#d97706', fontSize: 20, fontWeight: 'normal', letterSpacing: 0, lineHeight: 1.3, paragraphSpacing: 0, uppercase: false },
|
|
220
|
+
normalText: { color: '#e2e8f0', fontSize: 16, fontWeight: 'normal', letterSpacing: 0, lineHeight: 1.5, paragraphSpacing: 0, uppercase: false },
|
|
221
|
+
smallText: { color: '#94a3b8', fontSize: 13, fontWeight: 'normal', letterSpacing: 0, lineHeight: 1.4, paragraphSpacing: 0, uppercase: false }
|
|
222
|
+
},
|
|
223
|
+
table: { headerBg: '#b45309', headerTextColor: '#ffffff', borderColor: '#334155', alternateRowBg: '#1e293b' },
|
|
224
|
+
chart: { chartColor: '#0f172a', paletteName: 'Chart colors - Mono', palette: generateMonoPalette('#0f172a', 8), labelColor: '#f8fafc', gridColor: '#334155' },
|
|
225
|
+
shape: { fillColor: '#d97706', borderColor: '#fbbf24', textColor: '#ffffff' }
|
|
226
|
+
},
|
|
228
227
|
{
|
|
229
228
|
id: 'warm-amber',
|
|
230
|
-
name: '
|
|
229
|
+
name: '活力阳光橙',
|
|
231
230
|
styleColors: ['#f97316', '#ea580c', '#c2410c', '#f59e0b', '#ef4444', '#475569', '#1e293b', '#fed7aa'],
|
|
232
|
-
styleFonts: { titleFont: '
|
|
231
|
+
styleFonts: { titleFont: '字魂扁桃体', bodyFont: '字魂扁桃体' },
|
|
233
232
|
backgroundColor: '#fffaf5',
|
|
234
233
|
bulletPointsColor: '#f97316',
|
|
235
234
|
linkColor: '#ea580c',
|
|
@@ -16,6 +16,7 @@ export function useSidebarAnimation() {
|
|
|
16
16
|
addAnimation,
|
|
17
17
|
deleteAnimation,
|
|
18
18
|
updateAnimationEffect,
|
|
19
|
+
updateAnimationTargetElement,
|
|
19
20
|
updateAnimationTrigger,
|
|
20
21
|
updateAnimationDuration,
|
|
21
22
|
updateAnimationOrder,
|
|
@@ -73,9 +74,39 @@ export function useSidebarAnimation() {
|
|
|
73
74
|
return null
|
|
74
75
|
}
|
|
75
76
|
|
|
77
|
+
const allSlideElements = computed(() => {
|
|
78
|
+
if (!activeSlide.value?.elements) return []
|
|
79
|
+
const list: any[] = []
|
|
80
|
+
const collect = (els: any[]) => {
|
|
81
|
+
for (const el of els) {
|
|
82
|
+
list.push(el)
|
|
83
|
+
if (el.type === 'group' && Array.isArray(el.elements)) {
|
|
84
|
+
collect(el.elements)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
collect(activeSlide.value.elements)
|
|
89
|
+
return list
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
function inferAnimationType(effect: string): 'in' | 'out' | 'attention' {
|
|
93
|
+
for (const cat of ENTER_ANIMATIONS) {
|
|
94
|
+
if (cat.children?.some(c => c.value === effect)) return 'in'
|
|
95
|
+
}
|
|
96
|
+
for (const cat of EXIT_ANIMATIONS) {
|
|
97
|
+
if (cat.children?.some(c => c.value === effect)) return 'out'
|
|
98
|
+
}
|
|
99
|
+
for (const cat of ATTENTION_ANIMATIONS) {
|
|
100
|
+
if (cat.children?.some(c => c.value === effect)) return 'attention'
|
|
101
|
+
}
|
|
102
|
+
if (effect.includes('Out')) return 'out'
|
|
103
|
+
if (effect.includes('In')) return 'in'
|
|
104
|
+
return 'in'
|
|
105
|
+
}
|
|
106
|
+
|
|
76
107
|
function getElTypeName(type: string): string {
|
|
77
108
|
const types: Record<string, string> = {
|
|
78
|
-
text: '
|
|
109
|
+
text: '文本',
|
|
79
110
|
shape: '形状',
|
|
80
111
|
image: '图片',
|
|
81
112
|
video: '视频',
|
|
@@ -90,7 +121,7 @@ export function useSidebarAnimation() {
|
|
|
90
121
|
if (!activeSlide.value) return ''
|
|
91
122
|
const el = findElementInSlide(elId)
|
|
92
123
|
if (!el) return ''
|
|
93
|
-
const sameTypeEls =
|
|
124
|
+
const sameTypeEls = allSlideElements.value.filter((e: any) => e.type === el.type)
|
|
94
125
|
const idx = sameTypeEls.findIndex((e: any) => e.id === elId)
|
|
95
126
|
return idx !== -1 ? String(idx + 1) : ''
|
|
96
127
|
}
|
|
@@ -118,7 +149,7 @@ export function useSidebarAnimation() {
|
|
|
118
149
|
}
|
|
119
150
|
if (el.type === 'text') {
|
|
120
151
|
if (el.name) return el.name
|
|
121
|
-
return '
|
|
152
|
+
return '文本'
|
|
122
153
|
}
|
|
123
154
|
return getElTypeName(el.type)
|
|
124
155
|
}
|
|
@@ -133,41 +164,59 @@ export function useSidebarAnimation() {
|
|
|
133
164
|
function getDirectionConfig(effect: string, type: 'in' | 'out' | 'attention'): DirectionOption[] {
|
|
134
165
|
if (type === 'attention') return []
|
|
135
166
|
let animationFamily = ''
|
|
136
|
-
if (effect.includes('
|
|
167
|
+
if (effect.includes('fade')) animationFamily = 'fade'
|
|
168
|
+
else if (effect.includes('bounce')) animationFamily = 'bounce'
|
|
137
169
|
else if (effect.includes('back')) animationFamily = 'back'
|
|
138
170
|
else if (effect.includes('slide')) animationFamily = 'slide'
|
|
171
|
+
else if (effect.includes('zoom')) animationFamily = 'zoom'
|
|
139
172
|
else if (effect.includes('lightSpeed')) animationFamily = 'lightSpeed'
|
|
140
173
|
else if (effect.includes('flip')) animationFamily = 'flip'
|
|
141
174
|
|
|
142
175
|
if (!animationFamily) return []
|
|
176
|
+
if (animationFamily === 'fade') {
|
|
177
|
+
return [
|
|
178
|
+
{ label: '从底部', value: 'up', effectValue: type === 'in' ? 'fadeInUp' : 'fadeOutUp' },
|
|
179
|
+
{ label: '从左侧', value: 'left', effectValue: type === 'in' ? 'fadeInLeft' : 'fadeOutLeft' },
|
|
180
|
+
{ label: '从右侧', value: 'right', effectValue: type === 'in' ? 'fadeInRight' : 'fadeOutRight' },
|
|
181
|
+
{ label: '从顶部', value: 'down', effectValue: type === 'in' ? 'fadeInDown' : 'fadeOutDown' },
|
|
182
|
+
]
|
|
183
|
+
}
|
|
143
184
|
if (animationFamily === 'bounce') {
|
|
144
185
|
return [
|
|
145
|
-
{ label: '
|
|
146
|
-
{ label: '
|
|
147
|
-
{ label: '
|
|
148
|
-
{ label: '
|
|
186
|
+
{ label: '从底部', value: 'up', effectValue: type === 'in' ? 'bounceInUp' : 'bounceOutUp' },
|
|
187
|
+
{ label: '从左侧', value: 'left', effectValue: type === 'in' ? 'bounceInLeft' : 'bounceOutLeft' },
|
|
188
|
+
{ label: '从右侧', value: 'right', effectValue: type === 'in' ? 'bounceInRight' : 'bounceOutRight' },
|
|
189
|
+
{ label: '从顶部', value: 'down', effectValue: type === 'in' ? 'bounceInDown' : 'bounceOutDown' },
|
|
149
190
|
]
|
|
150
191
|
}
|
|
151
192
|
if (animationFamily === 'back') {
|
|
152
193
|
return [
|
|
153
|
-
{ label: '
|
|
154
|
-
{ label: '
|
|
155
|
-
{ label: '
|
|
156
|
-
{ label: '
|
|
194
|
+
{ label: '从底部', value: 'up', effectValue: type === 'in' ? 'backInUp' : 'backOutUp' },
|
|
195
|
+
{ label: '从左侧', value: 'left', effectValue: type === 'in' ? 'backInLeft' : 'backOutLeft' },
|
|
196
|
+
{ label: '从右侧', value: 'right', effectValue: type === 'in' ? 'backInRight' : 'backOutRight' },
|
|
197
|
+
{ label: '从顶部', value: 'down', effectValue: type === 'in' ? 'backInDown' : 'backOutDown' },
|
|
157
198
|
]
|
|
158
199
|
}
|
|
159
200
|
if (animationFamily === 'slide') {
|
|
160
201
|
return [
|
|
161
|
-
{ label: '
|
|
162
|
-
{ label: '
|
|
163
|
-
{ label: '
|
|
164
|
-
{ label: '
|
|
202
|
+
{ label: '从底部', value: 'up', effectValue: type === 'in' ? 'slideInUp' : 'slideOutUp' },
|
|
203
|
+
{ label: '从左侧', value: 'left', effectValue: type === 'in' ? 'slideInLeft' : 'slideOutLeft' },
|
|
204
|
+
{ label: '从右侧', value: 'right', effectValue: type === 'in' ? 'slideInRight' : 'slideOutRight' },
|
|
205
|
+
{ label: '从顶部', value: 'down', effectValue: type === 'in' ? 'slideInDown' : 'slideOutDown' },
|
|
206
|
+
]
|
|
207
|
+
}
|
|
208
|
+
if (animationFamily === 'zoom') {
|
|
209
|
+
return [
|
|
210
|
+
{ label: '从底部', value: 'up', effectValue: type === 'in' ? 'zoomInUp' : 'zoomOutUp' },
|
|
211
|
+
{ label: '从左侧', value: 'left', effectValue: type === 'in' ? 'zoomInLeft' : 'zoomOutLeft' },
|
|
212
|
+
{ label: '从右侧', value: 'right', effectValue: type === 'in' ? 'zoomInRight' : 'zoomOutRight' },
|
|
213
|
+
{ label: '从顶部', value: 'down', effectValue: type === 'in' ? 'zoomInDown' : 'zoomOutDown' },
|
|
165
214
|
]
|
|
166
215
|
}
|
|
167
216
|
if (animationFamily === 'lightSpeed') {
|
|
168
217
|
return [
|
|
169
|
-
{ label: '
|
|
170
|
-
{ label: '
|
|
218
|
+
{ label: '从右侧', value: 'right', effectValue: type === 'in' ? 'lightSpeedInRight' : 'lightSpeedOutRight' },
|
|
219
|
+
{ label: '从左侧', value: 'left', effectValue: type === 'in' ? 'lightSpeedInLeft' : 'lightSpeedOutLeft' },
|
|
171
220
|
]
|
|
172
221
|
}
|
|
173
222
|
if (animationFamily === 'flip') {
|
|
@@ -183,7 +232,7 @@ export function useSidebarAnimation() {
|
|
|
183
232
|
if (type === 'attention') return effect
|
|
184
233
|
if (type === 'in') {
|
|
185
234
|
if (effect.includes('bounce')) return 'bounceIn'
|
|
186
|
-
if (effect.includes('
|
|
235
|
+
if (effect.includes('fade')) return 'fadeIn'
|
|
187
236
|
if (effect.includes('zoom')) return 'zoomIn'
|
|
188
237
|
if (effect.includes('back')) return 'backInUp'
|
|
189
238
|
if (effect.includes('slide')) return 'slideInUp'
|
|
@@ -192,7 +241,7 @@ export function useSidebarAnimation() {
|
|
|
192
241
|
if (effect.includes('flip')) return 'flipInX'
|
|
193
242
|
} else if (type === 'out') {
|
|
194
243
|
if (effect.includes('bounce')) return 'bounceOut'
|
|
195
|
-
if (effect.includes('
|
|
244
|
+
if (effect.includes('fade')) return 'fadeOut'
|
|
196
245
|
if (effect.includes('zoom')) return 'zoomOut'
|
|
197
246
|
if (effect.includes('back')) return 'backOutDown'
|
|
198
247
|
if (effect.includes('slide')) return 'slideOutDown'
|
|
@@ -206,6 +255,9 @@ export function useSidebarAnimation() {
|
|
|
206
255
|
function getCurrentDirectionValue(anim: any): string {
|
|
207
256
|
const configs = getDirectionConfig(anim.effect, anim.type)
|
|
208
257
|
if (configs.length === 0) return 'none'
|
|
258
|
+
if (anim.direction && configs.some(c => c.value === anim.direction)) {
|
|
259
|
+
return anim.direction
|
|
260
|
+
}
|
|
209
261
|
const match = configs.find(c => c.effectValue === anim.effect)
|
|
210
262
|
return match ? match.value : configs[0].value
|
|
211
263
|
}
|
|
@@ -216,16 +268,21 @@ export function useSidebarAnimation() {
|
|
|
216
268
|
if (match) {
|
|
217
269
|
updateAnimationEffect(anim.id, anim.type, match.effectValue)
|
|
218
270
|
anim.direction = match.value
|
|
271
|
+
previewAnimationInCanvas(anim.elId, match.effectValue, anim.duration || 1000)
|
|
219
272
|
addHistorySnapshot()
|
|
220
273
|
}
|
|
221
274
|
}
|
|
222
275
|
|
|
223
276
|
const allEffects = computed(() => {
|
|
224
277
|
const list: { label: string; value: string }[] = []
|
|
278
|
+
const seenLabels = new Set<string>()
|
|
225
279
|
const collect = (arr: any[]) => {
|
|
226
280
|
for (const group of arr) {
|
|
227
281
|
for (const item of group.children) {
|
|
228
|
-
|
|
282
|
+
if (!seenLabels.has(item.name)) {
|
|
283
|
+
seenLabels.add(item.name)
|
|
284
|
+
list.push({ label: item.name, value: item.value })
|
|
285
|
+
}
|
|
229
286
|
}
|
|
230
287
|
}
|
|
231
288
|
}
|
|
@@ -237,7 +294,7 @@ export function useSidebarAnimation() {
|
|
|
237
294
|
|
|
238
295
|
function previewAnimationInCanvas(elId: string, effect: string, duration: number) {
|
|
239
296
|
if (!activeSlide.value) return
|
|
240
|
-
const targetEl =
|
|
297
|
+
const targetEl = findElementInSlide(elId)
|
|
241
298
|
const groupId = targetEl?.groupId
|
|
242
299
|
|
|
243
300
|
const targetIds = groupId
|
|
@@ -245,7 +302,8 @@ export function useSidebarAnimation() {
|
|
|
245
302
|
: [elId]
|
|
246
303
|
|
|
247
304
|
for (const id of targetIds) {
|
|
248
|
-
|
|
305
|
+
// 优先在真实内容容器 canvas-content-${id} 上播放动画,避免将选中边框和控制点手柄整体晃动
|
|
306
|
+
const elRef = document.getElementById(`canvas-content-${id}`) || document.getElementById(`canvas-el-${id}`)
|
|
249
307
|
if (elRef) {
|
|
250
308
|
const animationName = `animate__${effect}`
|
|
251
309
|
elRef.style.setProperty('--animate-duration', `${duration}ms`)
|
|
@@ -260,8 +318,9 @@ export function useSidebarAnimation() {
|
|
|
260
318
|
}
|
|
261
319
|
}
|
|
262
320
|
|
|
263
|
-
function handleSelectAnimation(effect: string) {
|
|
264
|
-
|
|
321
|
+
function handleSelectAnimation(effect: string, type?: 'in' | 'out' | 'attention') {
|
|
322
|
+
const animType = type || (activeAnimTab.value as any)
|
|
323
|
+
addAnimation(animType, effect)
|
|
265
324
|
animationPopoverVisible.value = false
|
|
266
325
|
addHistorySnapshot()
|
|
267
326
|
setTimeout(() => {
|
|
@@ -271,6 +330,19 @@ export function useSidebarAnimation() {
|
|
|
271
330
|
}, 50)
|
|
272
331
|
}
|
|
273
332
|
|
|
333
|
+
function handleUpdateAnimationTargetElement(animId: string, newElId: string) {
|
|
334
|
+
updateAnimationTargetElement(animId, newElId)
|
|
335
|
+
if (selectedElementId) {
|
|
336
|
+
selectedElementId.value = newElId
|
|
337
|
+
}
|
|
338
|
+
const anim = activeSlide.value?.animations?.find((a: any) => a.id === animId)
|
|
339
|
+
if (anim) {
|
|
340
|
+
setTimeout(() => {
|
|
341
|
+
previewAnimationInCanvas(newElId, anim.effect, anim.duration || 1000)
|
|
342
|
+
}, 50)
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
274
346
|
function toggleAnimationCard(animId: string, elId?: string) {
|
|
275
347
|
if (elId && selectedElementId) {
|
|
276
348
|
selectedElementId.value = elId
|
|
@@ -292,7 +364,9 @@ export function useSidebarAnimation() {
|
|
|
292
364
|
dragOverIndex,
|
|
293
365
|
activeAnimationsList,
|
|
294
366
|
allEffects,
|
|
367
|
+
allSlideElements,
|
|
295
368
|
findElementInSlide,
|
|
369
|
+
inferAnimationType,
|
|
296
370
|
getElTypeName,
|
|
297
371
|
getElementIndexName,
|
|
298
372
|
getAnimationStepIndex,
|
|
@@ -307,10 +381,12 @@ export function useSidebarAnimation() {
|
|
|
307
381
|
handleDragEnd,
|
|
308
382
|
handleDrop,
|
|
309
383
|
handleSelectAnimation,
|
|
384
|
+
handleUpdateAnimationTargetElement,
|
|
310
385
|
previewAnimationInCanvas,
|
|
311
386
|
toggleAnimationCard,
|
|
312
387
|
deleteAnimation,
|
|
313
388
|
updateAnimationEffect,
|
|
389
|
+
updateAnimationTargetElement,
|
|
314
390
|
updateAnimationTrigger,
|
|
315
391
|
updateAnimationDuration,
|
|
316
392
|
addHistorySnapshot
|
|
@@ -17,16 +17,25 @@ export function useSidebarDesign() {
|
|
|
17
17
|
id: master.id,
|
|
18
18
|
name: master.name || '默认母版',
|
|
19
19
|
background: master.background || '#ffffff',
|
|
20
|
-
layouts: (master.layouts || []).map(layout =>
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
layouts: (master.layouts || []).map(layout => {
|
|
21
|
+
const extraPlaceholders = (layout.type === 'custom' && Array.isArray(layout.elements) && layout.elements.length > 0)
|
|
22
|
+
? []
|
|
23
|
+
: (layout.placeholders || [])
|
|
24
|
+
const rawList = [...(master.elements || []), ...(layout.elements || []), ...extraPlaceholders]
|
|
25
|
+
const seenIds = new Set<string>()
|
|
26
|
+
const deduped = rawList.filter((el: any) => {
|
|
27
|
+
if (!el) return false
|
|
28
|
+
if (el.id && seenIds.has(el.id)) return false
|
|
29
|
+
if (el.id) seenIds.add(el.id)
|
|
30
|
+
return true
|
|
31
|
+
})
|
|
32
|
+
return {
|
|
33
|
+
id: layout.id,
|
|
34
|
+
name: layout.name || '自定义版式',
|
|
35
|
+
background: layout.background || master.background || '#ffffff',
|
|
36
|
+
elements: deduped
|
|
37
|
+
}
|
|
38
|
+
})
|
|
30
39
|
}))
|
|
31
40
|
})
|
|
32
41
|
|
|
@@ -124,7 +133,17 @@ export function useSidebarDesign() {
|
|
|
124
133
|
}
|
|
125
134
|
|
|
126
135
|
const parentMaster = (slideMasters.value || []).find((m: any) => (m.layouts || []).some((l: any) => l.id === masterLayout.id))
|
|
127
|
-
const
|
|
136
|
+
const extraPlaceholders = (masterLayout.type === 'custom' && Array.isArray(masterLayout.elements) && masterLayout.elements.length > 0)
|
|
137
|
+
? []
|
|
138
|
+
: (masterLayout.placeholders || [])
|
|
139
|
+
const rawLayoutElements = [...(masterLayout.elements || []), ...extraPlaceholders]
|
|
140
|
+
const seenIds = new Set<string>()
|
|
141
|
+
const layoutElements = rawLayoutElements.filter((el: any) => {
|
|
142
|
+
if (!el) return false
|
|
143
|
+
if (el.id && seenIds.has(el.id)) return false
|
|
144
|
+
if (el.id) seenIds.add(el.id)
|
|
145
|
+
return true
|
|
146
|
+
})
|
|
128
147
|
const newElements = layoutElements.map((el: any) => {
|
|
129
148
|
const cloned = JSON.parse(JSON.stringify(el))
|
|
130
149
|
const isPh = isLayoutPlaceholderElement(cloned)
|