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
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
normalizeTextType
|
|
14
14
|
} from '../utils/textTypes'
|
|
15
15
|
import { applyGroupGeometryFlip } from '../utils/groupFlip'
|
|
16
|
+
import { getFontVariants, getFontVariantStyle } from '../configs/fonts'
|
|
16
17
|
import type { CustomUploadHandler, UploadResourceContext, UploadResourceType } from '../types'
|
|
17
18
|
|
|
18
19
|
export type { SlideTextType }
|
|
@@ -65,6 +66,8 @@ export interface SlideElement {
|
|
|
65
66
|
link?: string
|
|
66
67
|
locked?: boolean
|
|
67
68
|
isMaster?: boolean
|
|
69
|
+
isPlaceholder?: boolean
|
|
70
|
+
placeholder?: any
|
|
68
71
|
hasCustomZIndex?: boolean
|
|
69
72
|
groupId?: string
|
|
70
73
|
elements?: SlideElement[]
|
|
@@ -266,6 +269,10 @@ export interface Slide {
|
|
|
266
269
|
notes?: string
|
|
267
270
|
animations?: PPTAnimation[]
|
|
268
271
|
transition?: string
|
|
272
|
+
transitionDuration?: number
|
|
273
|
+
autoAdvance?: boolean
|
|
274
|
+
autoAdvanceDelay?: number
|
|
275
|
+
clickAdvance?: boolean
|
|
269
276
|
schema?: any
|
|
270
277
|
}
|
|
271
278
|
|
|
@@ -822,18 +829,17 @@ export function createDefaultSlideMasters(W: number = 1280, H: number = 720): Sl
|
|
|
822
829
|
type: 'shape',
|
|
823
830
|
name: '图片占位符',
|
|
824
831
|
content: 'rect',
|
|
825
|
-
text: '
|
|
832
|
+
text: '',
|
|
833
|
+
isPlaceholder: true,
|
|
834
|
+
placeholder: { type: 'image', prompt: '图片占位符' },
|
|
826
835
|
style: {
|
|
827
836
|
left: `${marginX}px`,
|
|
828
837
|
top: '80px',
|
|
829
838
|
width: `${colW}px`,
|
|
830
839
|
height: `${Math.max(200, H - 80 - 55)}px`,
|
|
831
|
-
fill: '#
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
borderStyle: 'dashed',
|
|
835
|
-
borderRadius: '4px',
|
|
836
|
-
textColor: '#94a3b8',
|
|
840
|
+
fill: '#d9dde3',
|
|
841
|
+
borderRadius: '12px',
|
|
842
|
+
textColor: '#475569',
|
|
837
843
|
fontSize: '16px',
|
|
838
844
|
textAlign: 'center'
|
|
839
845
|
}
|
|
@@ -1024,20 +1030,45 @@ function applyBackgroundToAll() {
|
|
|
1024
1030
|
if (!activeSlide.value) return
|
|
1025
1031
|
const bg = activeSlide.value.background
|
|
1026
1032
|
const type = activeSlide.value.bgType || 'color'
|
|
1033
|
+
const imgMode = (activeSlide.value as any).bgImageMode
|
|
1034
|
+
const isPattern = (activeSlide.value as any).isPattern
|
|
1035
|
+
const bgPatternType = (activeSlide.value as any).bgPatternType
|
|
1036
|
+
const bgPatternFg = (activeSlide.value as any).bgPatternFg
|
|
1037
|
+
const bgPatternBg = (activeSlide.value as any).bgPatternBg
|
|
1038
|
+
const lastImgUrl = (activeSlide.value as any)._lastBgImageUrl
|
|
1027
1039
|
slides.value.forEach(slide => {
|
|
1028
1040
|
slide.background = bg
|
|
1029
1041
|
slide.bgType = type
|
|
1042
|
+
if (imgMode !== undefined) (slide as any).bgImageMode = imgMode
|
|
1043
|
+
if (isPattern !== undefined) (slide as any).isPattern = isPattern
|
|
1044
|
+
if (bgPatternType !== undefined) (slide as any).bgPatternType = bgPatternType
|
|
1045
|
+
if (bgPatternFg !== undefined) (slide as any).bgPatternFg = bgPatternFg
|
|
1046
|
+
if (bgPatternBg !== undefined) (slide as any).bgPatternBg = bgPatternBg
|
|
1047
|
+
if (lastImgUrl !== undefined) (slide as any)._lastBgImageUrl = lastImgUrl
|
|
1030
1048
|
})
|
|
1031
1049
|
if (isMasterEditingView.value || slideMasters.value.length > 0) {
|
|
1032
1050
|
slideMasters.value.forEach(m => {
|
|
1033
1051
|
m.background = bg
|
|
1034
1052
|
m.bgType = type
|
|
1053
|
+
if (imgMode !== undefined) (m as any).bgImageMode = imgMode
|
|
1054
|
+
if (isPattern !== undefined) (m as any).isPattern = isPattern
|
|
1055
|
+
if (bgPatternType !== undefined) (m as any).bgPatternType = bgPatternType
|
|
1056
|
+
if (bgPatternFg !== undefined) (m as any).bgPatternFg = bgPatternFg
|
|
1057
|
+
if (bgPatternBg !== undefined) (m as any).bgPatternBg = bgPatternBg
|
|
1058
|
+
if (lastImgUrl !== undefined) (m as any)._lastBgImageUrl = lastImgUrl
|
|
1035
1059
|
m.layouts?.forEach(l => {
|
|
1036
1060
|
l.background = bg
|
|
1037
1061
|
l.bgType = type
|
|
1062
|
+
if (imgMode !== undefined) (l as any).bgImageMode = imgMode
|
|
1063
|
+
if (isPattern !== undefined) (l as any).isPattern = isPattern
|
|
1064
|
+
if (bgPatternType !== undefined) (l as any).bgPatternType = bgPatternType
|
|
1065
|
+
if (bgPatternFg !== undefined) (l as any).bgPatternFg = bgPatternFg
|
|
1066
|
+
if (bgPatternBg !== undefined) (l as any).bgPatternBg = bgPatternBg
|
|
1067
|
+
if (lastImgUrl !== undefined) (l as any)._lastBgImageUrl = lastImgUrl
|
|
1038
1068
|
})
|
|
1039
1069
|
})
|
|
1040
1070
|
}
|
|
1071
|
+
addHistorySnapshot()
|
|
1041
1072
|
notify('已应用背景到全部幻灯片')
|
|
1042
1073
|
}
|
|
1043
1074
|
|
|
@@ -1099,13 +1130,139 @@ function applyThemeToAll() {
|
|
|
1099
1130
|
}
|
|
1100
1131
|
|
|
1101
1132
|
function applyFontToAll() {
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1133
|
+
const rawFont = globalFontFamily.value
|
|
1134
|
+
const hasExplicitFont = Boolean(rawFont && rawFont !== '默认字体')
|
|
1135
|
+
const targetColor = globalFontColor.value
|
|
1136
|
+
|
|
1137
|
+
const updateHtmlFontAndColor = (htmlStr: string, effectiveFont: string) => {
|
|
1138
|
+
if (!htmlStr || typeof htmlStr !== 'string') return htmlStr
|
|
1139
|
+
let res = htmlStr
|
|
1140
|
+
if (/font-family\s*:/i.test(res)) {
|
|
1141
|
+
res = res.replace(/font-family\s*:\s*[^;'"\}]+;?/gi, `font-family: ${effectiveFont};`)
|
|
1142
|
+
}
|
|
1143
|
+
if (/<font\b[^>]*\bface=/i.test(res)) {
|
|
1144
|
+
res = res.replace(/(<font\b[^>]*?\bface\s*=\s*['"])([^'"]+)(['"])/gi, `$1${effectiveFont}$3`)
|
|
1145
|
+
}
|
|
1146
|
+
if (targetColor) {
|
|
1147
|
+
if (/(?:^|[;"'\s\{])color\s*:/i.test(res)) {
|
|
1148
|
+
res = res.replace(/(^|[;"'\s\{])(color\s*:\s*)([^;'"\}]+)/gi, `$1$2${targetColor}`)
|
|
1149
|
+
}
|
|
1150
|
+
if (/<font\b[^>]*\bcolor=/i.test(res)) {
|
|
1151
|
+
res = res.replace(/(<font\b[^>]*?\bcolor\s*=\s*['"])([^'"]+)(['"])/gi, `$1${targetColor}$3`)
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
return res
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
const applyToElementList = (elements: any[], slideRecipe: any) => {
|
|
1158
|
+
if (!Array.isArray(elements)) return
|
|
1159
|
+
elements.forEach((el: any) => {
|
|
1160
|
+
if (!el || typeof el !== 'object') return
|
|
1161
|
+
if (el.type === 'group' && Array.isArray(el.elements)) {
|
|
1162
|
+
applyToElementList(el.elements, slideRecipe)
|
|
1163
|
+
return
|
|
1164
|
+
}
|
|
1165
|
+
if (el.type === 'text' || el.type === 'shape') {
|
|
1166
|
+
if (!el.style) el.style = {}
|
|
1167
|
+
const isHeading = el.textType === 'title' || el.textType === 'headline' || (el.id && String(el.id).startsWith('title'))
|
|
1168
|
+
const fallbackFont = isHeading
|
|
1169
|
+
? (slideRecipe?.styleFonts?.titleFont || 'SourceHanSansCN')
|
|
1170
|
+
: (slideRecipe?.styleFonts?.bodyFont || 'SourceHanSansCN')
|
|
1171
|
+
const effectiveFont = hasExplicitFont ? rawFont : fallbackFont
|
|
1172
|
+
|
|
1173
|
+
el.style.fontFamily = hasExplicitFont ? rawFont : effectiveFont
|
|
1174
|
+
el.fontFamily = hasExplicitFont ? rawFont : effectiveFont
|
|
1175
|
+
|
|
1176
|
+
const variants = getFontVariants(effectiveFont)
|
|
1177
|
+
const firstVariant = variants[0]?.value || 'Regular'
|
|
1178
|
+
el.style.fontFamilyStyle = firstVariant
|
|
1179
|
+
const vStyle = getFontVariantStyle(effectiveFont, firstVariant)
|
|
1180
|
+
if (vStyle) {
|
|
1181
|
+
el.style.fontWeight = vStyle.fontWeight
|
|
1182
|
+
el.style.fontStyle = vStyle.fontStyle
|
|
1183
|
+
if (vStyle.faceName) {
|
|
1184
|
+
;(el.style as any).fontVariantFace = vStyle.faceName
|
|
1185
|
+
} else {
|
|
1186
|
+
delete (el.style as any).fontVariantFace
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
if (targetColor) {
|
|
1191
|
+
el.style.color = targetColor
|
|
1192
|
+
el.style.textColor = targetColor
|
|
1193
|
+
el.color = targetColor
|
|
1194
|
+
el.textColor = targetColor
|
|
1195
|
+
if (el.text && typeof el.text === 'object') {
|
|
1196
|
+
el.text.defaultColor = targetColor
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
if (el.content && typeof el.content === 'string') {
|
|
1201
|
+
el.content = updateHtmlFontAndColor(el.content, effectiveFont)
|
|
1202
|
+
}
|
|
1203
|
+
if (el.text) {
|
|
1204
|
+
if (typeof el.text === 'string') {
|
|
1205
|
+
el.text = updateHtmlFontAndColor(el.text, effectiveFont)
|
|
1206
|
+
} else if (typeof el.text.content === 'string') {
|
|
1207
|
+
el.text.content = updateHtmlFontAndColor(el.text.content, effectiveFont)
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
if (typeof document !== 'undefined') {
|
|
1212
|
+
const container = document.getElementById(`editable-${el.id}`)
|
|
1213
|
+
if (container) {
|
|
1214
|
+
container.style.fontFamily = effectiveFont
|
|
1215
|
+
if (targetColor) {
|
|
1216
|
+
container.style.color = targetColor
|
|
1217
|
+
}
|
|
1218
|
+
container.querySelectorAll('*').forEach((child: any) => {
|
|
1219
|
+
if (child.style) {
|
|
1220
|
+
if (child.style.fontFamily) {
|
|
1221
|
+
child.style.fontFamily = effectiveFont
|
|
1222
|
+
}
|
|
1223
|
+
if (targetColor && child.style.color) {
|
|
1224
|
+
child.style.color = targetColor
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
if (child.tagName === 'FONT') {
|
|
1228
|
+
child.setAttribute('face', effectiveFont)
|
|
1229
|
+
child.style.fontFamily = effectiveFont
|
|
1230
|
+
if (targetColor) {
|
|
1231
|
+
child.setAttribute('color', targetColor)
|
|
1232
|
+
child.style.color = targetColor
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
})
|
|
1236
|
+
if (!el.content || !el.content.includes('<table')) {
|
|
1237
|
+
if (el.type === 'shape') {
|
|
1238
|
+
if (typeof el.text === 'string') {
|
|
1239
|
+
el.text = container.innerHTML
|
|
1240
|
+
} else if (el.text && typeof el.text === 'object') {
|
|
1241
|
+
el.text.content = container.innerHTML
|
|
1242
|
+
}
|
|
1243
|
+
} else {
|
|
1244
|
+
el.content = container.innerHTML
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1106
1249
|
}
|
|
1107
1250
|
})
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
slides.value.forEach(slide => {
|
|
1254
|
+
const slideRecipe = getActiveSlideThemeRecipe(slide)
|
|
1255
|
+
applyToElementList(slide.elements, slideRecipe)
|
|
1108
1256
|
})
|
|
1257
|
+
|
|
1258
|
+
if (isMasterEditingView.value && activeSlide.value?.elements) {
|
|
1259
|
+
const masterRecipe = getActiveSlideThemeRecipe(activeSlide.value)
|
|
1260
|
+
applyToElementList(activeSlide.value.elements, masterRecipe)
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
markAllPagesDirty()
|
|
1264
|
+
addHistorySnapshot()
|
|
1265
|
+
activeSlideVersion.value++
|
|
1109
1266
|
notify('已统一全局字体')
|
|
1110
1267
|
}
|
|
1111
1268
|
|
|
@@ -1646,6 +1803,24 @@ function resolveSlideMasterAndLayout(slide?: any): { masterId: string, layoutId:
|
|
|
1646
1803
|
}
|
|
1647
1804
|
}
|
|
1648
1805
|
|
|
1806
|
+
function formatBareUrlWithMode(bg: any, mode?: string, isPattern?: boolean): any {
|
|
1807
|
+
if (typeof bg !== 'string') return bg
|
|
1808
|
+
const trimmed = bg.trim()
|
|
1809
|
+
if (!trimmed.startsWith('url(')) return bg
|
|
1810
|
+
const closeParenIdx = trimmed.lastIndexOf(')')
|
|
1811
|
+
if (closeParenIdx === -1) return bg
|
|
1812
|
+
const afterUrl = trimmed.slice(closeParenIdx + 1).trim()
|
|
1813
|
+
if (afterUrl) return bg
|
|
1814
|
+
const urlPart = trimmed.slice(0, closeParenIdx + 1)
|
|
1815
|
+
if (trimmed.includes('data:image/svg+xml') || isPattern) {
|
|
1816
|
+
return `${urlPart} left top / auto repeat`
|
|
1817
|
+
}
|
|
1818
|
+
if (mode === 'contain') return `${urlPart} center center / contain no-repeat`
|
|
1819
|
+
if (mode === 'cover') return `${urlPart} center center / 60% auto no-repeat`
|
|
1820
|
+
if (mode === 'tile') return `${urlPart} left top / 25% auto repeat`
|
|
1821
|
+
return `${urlPart} center center / 100% 100% no-repeat`
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1649
1824
|
export function getEffectiveSlideBackground(slide?: any): any {
|
|
1650
1825
|
if (!slide) return '#ffffff'
|
|
1651
1826
|
|
|
@@ -1653,16 +1828,30 @@ export function getEffectiveSlideBackground(slide?: any): any {
|
|
|
1653
1828
|
if (isMasterEditingView.value) {
|
|
1654
1829
|
const master = slideMasters.value.find(m => m.id === activeMasterId.value) || slideMasters.value[0]
|
|
1655
1830
|
const layout = activeLayoutId.value ? master?.layouts?.find(l => l.id === activeLayoutId.value) : null
|
|
1656
|
-
|
|
1831
|
+
if (layout?.background && layout.background !== '#ffffff' && layout.background !== 'none') {
|
|
1832
|
+
return formatBareUrlWithMode(layout.background, (layout as any).bgImageMode, (layout as any).isPattern)
|
|
1833
|
+
}
|
|
1834
|
+
if (master?.background) {
|
|
1835
|
+
return formatBareUrlWithMode(master.background, (master as any).bgImageMode, (master as any).isPattern)
|
|
1836
|
+
}
|
|
1837
|
+
return '#ffffff'
|
|
1657
1838
|
}
|
|
1658
1839
|
|
|
1659
1840
|
// 2. 如果当前幻灯片自身设置了明确的独立自定义背景(包含 url、gradient 或非白色自定义色)
|
|
1660
|
-
const
|
|
1841
|
+
const hasSlideBgSpecial = typeof slide.background === 'string' && (
|
|
1842
|
+
slide.background.includes('url(') ||
|
|
1843
|
+
slide.background.includes('gradient(') ||
|
|
1844
|
+
slide.bgType === 'image' ||
|
|
1845
|
+
slide.bgType === 'gradient'
|
|
1846
|
+
)
|
|
1847
|
+
const directBg = hasSlideBgSpecial
|
|
1848
|
+
? (slide.background || slide.fill || slide.schema?.bgColor)
|
|
1849
|
+
: (slide.schema?.bgColor || slide.background || slide.fill)
|
|
1661
1850
|
if (directBg) {
|
|
1662
1851
|
if (typeof directBg === 'string') {
|
|
1663
1852
|
const trimmed = directBg.trim().toLowerCase()
|
|
1664
1853
|
if (trimmed.includes('url(') || trimmed.includes('gradient(') || (!['#ffffff', '#fff', 'white', 'transparent', 'none', ''].includes(trimmed))) {
|
|
1665
|
-
return directBg
|
|
1854
|
+
return formatBareUrlWithMode(directBg, slide.bgImageMode, slide.isPattern)
|
|
1666
1855
|
}
|
|
1667
1856
|
} else if (typeof directBg === 'object' && (directBg.image || directBg.gradient || (directBg.color && !['#ffffff', '#fff', 'white'].includes(String(directBg.color).toLowerCase())))) {
|
|
1668
1857
|
return directBg
|
|
@@ -1675,13 +1864,13 @@ export function getEffectiveSlideBackground(slide?: any): any {
|
|
|
1675
1864
|
const layout = layoutId && master ? master.layouts?.find(l => l && l.id === layoutId) : null
|
|
1676
1865
|
|
|
1677
1866
|
if (layout?.background && layout.background !== '#ffffff' && layout.background !== 'none') {
|
|
1678
|
-
return layout.background
|
|
1867
|
+
return formatBareUrlWithMode(layout.background, (layout as any).bgImageMode, (layout as any).isPattern)
|
|
1679
1868
|
}
|
|
1680
1869
|
if (master?.background && master.background !== '#ffffff' && master.background !== 'none') {
|
|
1681
|
-
return master.background
|
|
1870
|
+
return formatBareUrlWithMode(master.background, (master as any).bgImageMode, (master as any).isPattern)
|
|
1682
1871
|
}
|
|
1683
1872
|
|
|
1684
|
-
return directBg
|
|
1873
|
+
return directBg ? formatBareUrlWithMode(directBg, slide.bgImageMode, slide.isPattern) : '#ffffff'
|
|
1685
1874
|
}
|
|
1686
1875
|
|
|
1687
1876
|
function applyStyleRecipeToMaster(
|
|
@@ -2033,8 +2222,8 @@ function getThemeUsageSlides(themeId: string): number[] {
|
|
|
2033
2222
|
const pages: number[] = []
|
|
2034
2223
|
const hasAnyExplicitTheme = slides.value.some(s => !!s.themeId)
|
|
2035
2224
|
slides.value.forEach((slide, idx) => {
|
|
2036
|
-
const effId = slide.themeId || (hasAnyExplicitTheme ? '
|
|
2037
|
-
if (effId === themeId) {
|
|
2225
|
+
const effId = slide.themeId || (hasAnyExplicitTheme ? 'tech-blue' : 'tech-blue')
|
|
2226
|
+
if (effId === themeId || (themeId === 'tech-blue' && (effId === 'default-recipe' || !effId))) {
|
|
2038
2227
|
pages.push(idx + 1)
|
|
2039
2228
|
}
|
|
2040
2229
|
})
|
|
@@ -2062,8 +2251,8 @@ function getActiveSlideThemeRecipe(targetSlide?: any): StyleRecipe {
|
|
|
2062
2251
|
} else if (activeSlide.value) {
|
|
2063
2252
|
effSlide = activeSlide.value
|
|
2064
2253
|
}
|
|
2065
|
-
const themeId = effSlide?.themeId || (isMasterEditingView.value ? ((slideMasters.value.find(m => m.id === activeMasterId.value) as any)?.themeId || '
|
|
2066
|
-
const recipe = builtinStyleRecipes.find(r => r.id === themeId) || defaultStyleRecipe
|
|
2254
|
+
const themeId = effSlide?.themeId || (isMasterEditingView.value ? ((slideMasters.value.find(m => m.id === activeMasterId.value) as any)?.themeId || 'tech-blue') : 'tech-blue')
|
|
2255
|
+
const recipe = builtinStyleRecipes.find(r => r.id === themeId || (themeId === 'default-recipe' && r.id === 'tech-blue')) || defaultStyleRecipe
|
|
2067
2256
|
return recipe
|
|
2068
2257
|
}
|
|
2069
2258
|
|
|
@@ -2159,13 +2348,22 @@ const activeSlide = computed<Slide>({
|
|
|
2159
2348
|
if (prop === 'id') return target.id
|
|
2160
2349
|
if (prop === 'title') return target.name || '母版编辑'
|
|
2161
2350
|
if (prop === 'subtitle') return ''
|
|
2162
|
-
if (prop === 'background') return target.background
|
|
2351
|
+
if (prop === 'background') return target.background !== undefined ? target.background : (master?.background || '#ffffff')
|
|
2163
2352
|
if (prop === 'bgType') {
|
|
2164
2353
|
if (target.bgType) return target.bgType
|
|
2165
2354
|
if (master?.bgType) return master.bgType
|
|
2166
2355
|
const effBg = target.background || master?.background
|
|
2167
2356
|
return normalizeSlideBackground(effBg).bgType || 'color'
|
|
2168
2357
|
}
|
|
2358
|
+
if (prop === 'bgImageMode') {
|
|
2359
|
+
return (target as any).bgImageMode || (master as any)?.bgImageMode || 'stretch'
|
|
2360
|
+
}
|
|
2361
|
+
if (prop === 'isPattern') {
|
|
2362
|
+
return (target as any).isPattern ?? (master as any)?.isPattern ?? false
|
|
2363
|
+
}
|
|
2364
|
+
if (prop === '_lastBgImageUrl') {
|
|
2365
|
+
return (target as any)._lastBgImageUrl !== undefined ? (target as any)._lastBgImageUrl : ((master as any)?._lastBgImageUrl || '')
|
|
2366
|
+
}
|
|
2169
2367
|
if (prop === 'themeId') return target.themeId || (master as any)?.themeId || 'default-recipe'
|
|
2170
2368
|
if (prop === 'themeName') return target.themeName || (master as any)?.themeName || ''
|
|
2171
2369
|
if (prop === 'themeColors') return (master as any)?.themeColors || globalThemeColor.value
|
|
@@ -2173,10 +2371,16 @@ const activeSlide = computed<Slide>({
|
|
|
2173
2371
|
if (!target.elements) target.elements = []
|
|
2174
2372
|
// 确保版式或母版中的占位符存在于 target.elements 中,使用户在母版视图下可以直接点击选中、拖拽调整与编辑
|
|
2175
2373
|
if (target.placeholders && Array.isArray(target.placeholders)) {
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2374
|
+
if (target.type === 'custom' && target.elements.length > 0) {
|
|
2375
|
+
target.placeholders = target.elements.filter((x: any) => x && (x.isPlaceholder || x.placeholder))
|
|
2376
|
+
target._placeholdersMerged = true
|
|
2377
|
+
} else if (!target._placeholdersMerged) {
|
|
2378
|
+
for (const ph of target.placeholders) {
|
|
2379
|
+
if (!target.elements.some((el: any) => el.id === ph.id || (Math.abs((el.left || 0) - (ph.left || 0)) < 5 && Math.abs((el.top || 0) - (ph.top || 0)) < 5))) {
|
|
2380
|
+
target.elements.push(ph)
|
|
2381
|
+
}
|
|
2179
2382
|
}
|
|
2383
|
+
target._placeholdersMerged = true
|
|
2180
2384
|
}
|
|
2181
2385
|
}
|
|
2182
2386
|
// 🌟 兜底保障:若当前选中的是顶级母版(target === master),且母版本身 elements 为空且无 placeholders(如自定义极简母版):
|
|
@@ -2220,27 +2424,39 @@ const activeSlide = computed<Slide>({
|
|
|
2220
2424
|
if (prop === 'elements') {
|
|
2221
2425
|
target.elements = value
|
|
2222
2426
|
if (Array.isArray(value)) {
|
|
2223
|
-
|
|
2224
|
-
if (phs.length > 0) {
|
|
2225
|
-
target.placeholders = phs
|
|
2226
|
-
}
|
|
2427
|
+
target.placeholders = value.filter((x: any) => x && (x.isPlaceholder || x.placeholder))
|
|
2227
2428
|
}
|
|
2228
2429
|
return true
|
|
2229
2430
|
}
|
|
2230
2431
|
if (prop === 'background') {
|
|
2432
|
+
const prevBg = target.background
|
|
2231
2433
|
target.background = value
|
|
2232
2434
|
if (target === master && master.layouts) {
|
|
2233
2435
|
master.layouts.forEach((l: any) => {
|
|
2234
|
-
if (!l.background || l.background === '#ffffff'
|
|
2436
|
+
if (!l.background || l.background === '#ffffff' || l.background === prevBg) {
|
|
2437
|
+
l.background = value
|
|
2438
|
+
}
|
|
2235
2439
|
})
|
|
2236
2440
|
}
|
|
2237
2441
|
return true
|
|
2238
2442
|
}
|
|
2239
2443
|
if (prop === 'bgType') {
|
|
2444
|
+
const prevType = target.bgType
|
|
2240
2445
|
target.bgType = value
|
|
2241
2446
|
if (target === master && master.layouts) {
|
|
2242
2447
|
master.layouts.forEach((l: any) => {
|
|
2243
|
-
if (!l.bgType || l.bgType === 'color'
|
|
2448
|
+
if (!l.bgType || l.bgType === 'color' || l.bgType === prevType) {
|
|
2449
|
+
l.bgType = value
|
|
2450
|
+
}
|
|
2451
|
+
})
|
|
2452
|
+
}
|
|
2453
|
+
return true
|
|
2454
|
+
}
|
|
2455
|
+
if (prop === 'bgImageMode' || prop === 'isPattern' || prop === '_lastBgImageUrl') {
|
|
2456
|
+
target[prop] = value
|
|
2457
|
+
if (target === master && master.layouts) {
|
|
2458
|
+
master.layouts.forEach((l: any) => {
|
|
2459
|
+
l[prop] = value
|
|
2244
2460
|
})
|
|
2245
2461
|
}
|
|
2246
2462
|
return true
|
|
@@ -2362,11 +2578,23 @@ const formatedAnimations = computed(() => {
|
|
|
2362
2578
|
return formatted
|
|
2363
2579
|
})
|
|
2364
2580
|
|
|
2581
|
+
function findElementInSlideElements(id: string, elements: any[] = activeSlide.value?.elements || []): any {
|
|
2582
|
+
if (!id || !elements || !elements.length) return null
|
|
2583
|
+
for (const el of elements) {
|
|
2584
|
+
if (el.id === id) return el
|
|
2585
|
+
if (el.type === 'group' && Array.isArray(el.elements)) {
|
|
2586
|
+
const found = findElementInSlideElements(id, el.elements)
|
|
2587
|
+
if (found) return found
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
return null
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2365
2593
|
function addAnimation(type: 'in' | 'out' | 'attention', effect: string) {
|
|
2366
2594
|
if (!activeSlide.value || !selectedElementId.value) return
|
|
2367
2595
|
if (!activeSlide.value.animations) activeSlide.value.animations = []
|
|
2368
2596
|
|
|
2369
|
-
const targetEl = activeSlide.value.elements
|
|
2597
|
+
const targetEl = findElementInSlideElements(selectedElementId.value, activeSlide.value.elements)
|
|
2370
2598
|
if (!targetEl) return
|
|
2371
2599
|
|
|
2372
2600
|
const groupId = targetEl.groupId
|
|
@@ -2400,7 +2628,7 @@ function deleteAnimation(id: string) {
|
|
|
2400
2628
|
const targetAnim = activeSlide.value.animations.find(a => a.id === id)
|
|
2401
2629
|
if (!targetAnim) return
|
|
2402
2630
|
|
|
2403
|
-
const targetEl = activeSlide.value.elements
|
|
2631
|
+
const targetEl = findElementInSlideElements(targetAnim.elId, activeSlide.value.elements)
|
|
2404
2632
|
const groupId = targetEl?.groupId
|
|
2405
2633
|
|
|
2406
2634
|
if (groupId) {
|
|
@@ -2419,7 +2647,7 @@ function updateAnimationDuration(id: string, duration: number) {
|
|
|
2419
2647
|
const targetAnim = activeSlide.value.animations.find(a => a.id === id)
|
|
2420
2648
|
if (!targetAnim) return
|
|
2421
2649
|
|
|
2422
|
-
const targetEl = activeSlide.value.elements
|
|
2650
|
+
const targetEl = findElementInSlideElements(targetAnim.elId, activeSlide.value.elements)
|
|
2423
2651
|
const groupId = targetEl?.groupId
|
|
2424
2652
|
|
|
2425
2653
|
if (groupId) {
|
|
@@ -2447,7 +2675,7 @@ function updateAnimationEffect(id: string, type: 'in' | 'out' | 'attention', eff
|
|
|
2447
2675
|
const targetAnim = activeSlide.value.animations.find(a => a.id === id)
|
|
2448
2676
|
if (!targetAnim) return
|
|
2449
2677
|
|
|
2450
|
-
const targetEl = activeSlide.value.elements
|
|
2678
|
+
const targetEl = findElementInSlideElements(targetAnim.elId, activeSlide.value.elements)
|
|
2451
2679
|
const groupId = targetEl?.groupId
|
|
2452
2680
|
|
|
2453
2681
|
if (groupId) {
|
|
@@ -2463,6 +2691,15 @@ function updateAnimationEffect(id: string, type: 'in' | 'out' | 'attention', eff
|
|
|
2463
2691
|
}
|
|
2464
2692
|
}
|
|
2465
2693
|
|
|
2694
|
+
function updateAnimationTargetElement(id: string, newElId: string) {
|
|
2695
|
+
if (!activeSlide.value || !activeSlide.value.animations || !newElId) return
|
|
2696
|
+
const targetAnim = activeSlide.value.animations.find(a => a.id === id)
|
|
2697
|
+
if (!targetAnim) return
|
|
2698
|
+
targetAnim.elId = newElId
|
|
2699
|
+
addHistorySnapshot()
|
|
2700
|
+
notify('已将动画修改到对应元素')
|
|
2701
|
+
}
|
|
2702
|
+
|
|
2466
2703
|
function updateAnimationOrder(oldIndex: number, newIndex: number) {
|
|
2467
2704
|
if (!activeSlide.value || !activeSlide.value.animations) return
|
|
2468
2705
|
if (oldIndex === undefined || newIndex === undefined || oldIndex === newIndex) return
|
|
@@ -4030,8 +4267,10 @@ function addElement(
|
|
|
4030
4267
|
const typeConf = TEXT_TYPE_CONFIGS[textType] || TEXT_TYPE_CONFIGS.normalText
|
|
4031
4268
|
const textCfg = recipe.textStyles?.[textType] || defaultStyleRecipe.textStyles[textType] || defaultStyleRecipe.textStyles.normalText
|
|
4032
4269
|
const isHeading = textType === 'title' || textType === 'headline'
|
|
4033
|
-
const textFont =
|
|
4034
|
-
|
|
4270
|
+
const textFont = (globalFontFamily.value && globalFontFamily.value !== '默认字体')
|
|
4271
|
+
? globalFontFamily.value
|
|
4272
|
+
: (textCfg.fontFamily || (isHeading ? (recipe.styleFonts?.titleFont || 'SourceHanSansCN') : (recipe.styleFonts?.bodyFont || 'SourceHanSansCN')))
|
|
4273
|
+
const textColor = globalFontColor.value || textCfg.color || (textType === 'smallText' ? '#64748b' : (isHeading ? (recipe.textStyles?.title?.color || '#0f172a') : (recipe.textStyles?.normalText?.color || '#334155')))
|
|
4035
4274
|
const fontSize = `${textCfg.fontSize || typeConf.defaultFontSize}px`
|
|
4036
4275
|
const fontWeight = textCfg.fontWeight || typeConf.defaultFontWeight
|
|
4037
4276
|
const lineHeight = textCfg.lineHeight || typeConf.defaultLineHeight
|
|
@@ -4062,9 +4301,11 @@ function addElement(
|
|
|
4062
4301
|
const shapeFill = recipe.shape?.fillColor || (recipe.styleColors && recipe.styleColors[0]) || '#2563eb'
|
|
4063
4302
|
const shapeBorder = recipe.shape?.borderColor || (recipe.styleColors && recipe.styleColors[1]) || '#1d4ed8'
|
|
4064
4303
|
const shapeTextCol = recipe.shape?.textColor || '#ffffff'
|
|
4065
|
-
const shapeFont = (
|
|
4066
|
-
?
|
|
4067
|
-
: '
|
|
4304
|
+
const shapeFont = (globalFontFamily.value && globalFontFamily.value !== '默认字体')
|
|
4305
|
+
? globalFontFamily.value
|
|
4306
|
+
: ((recipe.styleFonts?.bodyFont && recipe.styleFonts.bodyFont !== '默认字体')
|
|
4307
|
+
? recipe.styleFonts.bodyFont
|
|
4308
|
+
: 'SourceHanSansCN')
|
|
4068
4309
|
|
|
4069
4310
|
style = {
|
|
4070
4311
|
top: '40%',
|
|
@@ -4274,10 +4515,10 @@ function addElementAt(
|
|
|
4274
4515
|
let style: any = {}
|
|
4275
4516
|
|
|
4276
4517
|
if (type === 'text') {
|
|
4277
|
-
const textColor = recipe.textStyles?.title?.color || recipe.styleColors?.[0] ||
|
|
4278
|
-
const textFont = (
|
|
4279
|
-
?
|
|
4280
|
-
: (
|
|
4518
|
+
const textColor = globalFontColor.value || recipe.textStyles?.title?.color || recipe.styleColors?.[0] || recipe.textStyles?.normalText?.color || '#0f172a'
|
|
4519
|
+
const textFont = (globalFontFamily.value && globalFontFamily.value !== '默认字体')
|
|
4520
|
+
? globalFontFamily.value
|
|
4521
|
+
: ((recipe.styleFonts?.bodyFont && recipe.styleFonts.bodyFont !== '默认字体') ? recipe.styleFonts.bodyFont : 'SourceHanSansCN')
|
|
4281
4522
|
content = `<div style="color:${textColor}; font-family:${textFont};"><br></div>`
|
|
4282
4523
|
style = {
|
|
4283
4524
|
top: `${topPct.toFixed(2)}%`,
|
|
@@ -4306,9 +4547,11 @@ function addElementAt(
|
|
|
4306
4547
|
const shapeFill = recipe.shape?.fillColor || (recipe.styleColors && recipe.styleColors[0]) || '#2563eb'
|
|
4307
4548
|
const shapeBorder = recipe.shape?.borderColor || (recipe.styleColors && recipe.styleColors[1]) || '#1d4ed8'
|
|
4308
4549
|
const shapeTextCol = recipe.shape?.textColor || '#ffffff'
|
|
4309
|
-
const shapeFont = (
|
|
4310
|
-
?
|
|
4311
|
-
: '
|
|
4550
|
+
const shapeFont = (globalFontFamily.value && globalFontFamily.value !== '默认字体')
|
|
4551
|
+
? globalFontFamily.value
|
|
4552
|
+
: ((recipe.styleFonts?.bodyFont && recipe.styleFonts.bodyFont !== '默认字体')
|
|
4553
|
+
? recipe.styleFonts.bodyFont
|
|
4554
|
+
: 'SourceHanSansCN')
|
|
4312
4555
|
|
|
4313
4556
|
style = {
|
|
4314
4557
|
top: `${topPct.toFixed(2)}%`,
|
|
@@ -4354,7 +4597,9 @@ function addElementAt(
|
|
|
4354
4597
|
content = ''
|
|
4355
4598
|
const chartPalette = (recipe.chart?.palette && recipe.chart.palette.length > 0) ? [...recipe.chart.palette] : (recipe.styleColors || ['#2563eb', '#3b82f6', '#60a5fa'])
|
|
4356
4599
|
const chartTextCol = recipe.chart?.textColor || recipe.chart?.labelColor || '#1e293b'
|
|
4357
|
-
const chartFont =
|
|
4600
|
+
const chartFont = (globalFontFamily.value && globalFontFamily.value !== '默认字体')
|
|
4601
|
+
? globalFontFamily.value
|
|
4602
|
+
: (recipe.chart?.fontFamily || recipe.styleFonts?.bodyFont || 'SourceHanSansCN')
|
|
4358
4603
|
|
|
4359
4604
|
style = {
|
|
4360
4605
|
top: `${topPct.toFixed(2)}%`,
|
|
@@ -4379,6 +4624,9 @@ function addElementAt(
|
|
|
4379
4624
|
|
|
4380
4625
|
if (type === 'text') {
|
|
4381
4626
|
newElement.verticalAlign = 'middle'
|
|
4627
|
+
newElement.color = style.color
|
|
4628
|
+
newElement.textColor = style.color
|
|
4629
|
+
newElement.fontFamily = style.fontFamily
|
|
4382
4630
|
}
|
|
4383
4631
|
|
|
4384
4632
|
if (type === 'shape') {
|
|
@@ -8847,16 +9095,18 @@ export function useSlides() {
|
|
|
8847
9095
|
function addMaster(name = '自定义母版') {
|
|
8848
9096
|
const newMasterId = 'master-' + Date.now().toString()
|
|
8849
9097
|
const newLayoutId = 'layout-' + Date.now().toString()
|
|
8850
|
-
const dateStr = getCurrentFormattedDate()
|
|
8851
9098
|
const W = logicalWidth.value || 1280
|
|
8852
9099
|
const H = logicalHeight.value || 720
|
|
8853
9100
|
|
|
8854
|
-
const
|
|
9101
|
+
const createPlaceholders = (scope: 'master' | 'layout') => [
|
|
8855
9102
|
{
|
|
8856
|
-
id:
|
|
8857
|
-
type: 'text',
|
|
9103
|
+
id: `ph-title-${scope}-${Date.now().toString(36)}`,
|
|
9104
|
+
type: 'text' as const,
|
|
8858
9105
|
name: '母版标题占位符',
|
|
8859
9106
|
content: '<div>单击此处编辑母版标题样式</div>',
|
|
9107
|
+
isMaster: true,
|
|
9108
|
+
isPlaceholder: true,
|
|
9109
|
+
placeholder: { type: 'title', prompt: '单击此处编辑母版标题样式' },
|
|
8860
9110
|
style: {
|
|
8861
9111
|
left: '50px',
|
|
8862
9112
|
top: '40px',
|
|
@@ -8868,10 +9118,13 @@ export function useSlides() {
|
|
|
8868
9118
|
}
|
|
8869
9119
|
},
|
|
8870
9120
|
{
|
|
8871
|
-
id:
|
|
8872
|
-
type: 'text',
|
|
9121
|
+
id: `ph-body-${scope}-${Date.now().toString(36)}`,
|
|
9122
|
+
type: 'text' as const,
|
|
8873
9123
|
name: '母版文本占位符',
|
|
8874
9124
|
content: '<div>• 单击此处编辑母版文本样式</div><div style="padding-left: 24px;">• 第二级</div><div style="padding-left: 48px;">• 第三级</div><div style="padding-left: 72px;">• 第四级</div><div style="padding-left: 96px;">• 第五级</div>',
|
|
9125
|
+
isMaster: true,
|
|
9126
|
+
isPlaceholder: true,
|
|
9127
|
+
placeholder: { type: 'body', prompt: '单击此处编辑母版文本样式' },
|
|
8875
9128
|
style: {
|
|
8876
9129
|
left: '50px',
|
|
8877
9130
|
top: '130px',
|
|
@@ -8881,51 +9134,6 @@ export function useSlides() {
|
|
|
8881
9134
|
color: '#475569',
|
|
8882
9135
|
lineHeight: '1.6'
|
|
8883
9136
|
}
|
|
8884
|
-
},
|
|
8885
|
-
{
|
|
8886
|
-
id: 'ph-date-' + Date.now().toString(),
|
|
8887
|
-
type: 'text',
|
|
8888
|
-
name: '日期占位符',
|
|
8889
|
-
content: `<div>${dateStr}</div>`,
|
|
8890
|
-
style: {
|
|
8891
|
-
left: '50px',
|
|
8892
|
-
top: `${H - 48}px`,
|
|
8893
|
-
width: '220px',
|
|
8894
|
-
height: '30px',
|
|
8895
|
-
fontSize: '12px',
|
|
8896
|
-
color: '#94a3b8',
|
|
8897
|
-
textAlign: 'left'
|
|
8898
|
-
}
|
|
8899
|
-
},
|
|
8900
|
-
{
|
|
8901
|
-
id: 'ph-footer-' + Date.now().toString(),
|
|
8902
|
-
type: 'text',
|
|
8903
|
-
name: '页脚占位符',
|
|
8904
|
-
content: '<div>页脚区</div>',
|
|
8905
|
-
style: {
|
|
8906
|
-
left: `${Math.round(W / 2 - 110)}px`,
|
|
8907
|
-
top: `${H - 48}px`,
|
|
8908
|
-
width: '220px',
|
|
8909
|
-
height: '30px',
|
|
8910
|
-
fontSize: '12px',
|
|
8911
|
-
color: '#94a3b8',
|
|
8912
|
-
textAlign: 'center'
|
|
8913
|
-
}
|
|
8914
|
-
},
|
|
8915
|
-
{
|
|
8916
|
-
id: 'ph-num-' + Date.now().toString(),
|
|
8917
|
-
type: 'text',
|
|
8918
|
-
name: '页码占位符',
|
|
8919
|
-
content: '<div><#></div>',
|
|
8920
|
-
style: {
|
|
8921
|
-
left: `${W - 270}px`,
|
|
8922
|
-
top: `${H - 48}px`,
|
|
8923
|
-
width: '220px',
|
|
8924
|
-
height: '30px',
|
|
8925
|
-
fontSize: '12px',
|
|
8926
|
-
color: '#94a3b8',
|
|
8927
|
-
textAlign: 'right'
|
|
8928
|
-
}
|
|
8929
9137
|
}
|
|
8930
9138
|
]
|
|
8931
9139
|
|
|
@@ -8935,13 +9143,14 @@ export function useSlides() {
|
|
|
8935
9143
|
background: '#ffffff',
|
|
8936
9144
|
themeColors: ['#3b82f6', '#f59e0b', '#ef4444', '#10b981', '#8b5cf6'],
|
|
8937
9145
|
fontFamily: 'SourceHanSansCN',
|
|
8938
|
-
elements:
|
|
9146
|
+
elements: [],
|
|
9147
|
+
placeholders: createPlaceholders('master'),
|
|
8939
9148
|
layouts: [
|
|
8940
9149
|
{
|
|
8941
9150
|
id: newLayoutId,
|
|
8942
9151
|
name: '默认版式',
|
|
8943
9152
|
type: 'title-body',
|
|
8944
|
-
elements:
|
|
9153
|
+
elements: createPlaceholders('layout'),
|
|
8945
9154
|
placeholders: []
|
|
8946
9155
|
}
|
|
8947
9156
|
]
|
|
@@ -8956,16 +9165,17 @@ export function useSlides() {
|
|
|
8956
9165
|
const targetMaster = slideMasters.value.find(m => m.id === masterId)
|
|
8957
9166
|
if (!targetMaster) return
|
|
8958
9167
|
const newLayoutId = 'layout-' + Date.now().toString() + Math.random().toString(36).substring(2, 6)
|
|
8959
|
-
const dateStr = getCurrentFormattedDate()
|
|
8960
9168
|
const W = logicalWidth.value || 1280
|
|
8961
|
-
const H = logicalHeight.value || 720
|
|
8962
9169
|
|
|
8963
9170
|
const layoutElements: SlideElement[] = [
|
|
8964
9171
|
{
|
|
8965
|
-
id: 'ph-title-' + Date.now().toString(),
|
|
9172
|
+
id: 'ph-title-' + Date.now().toString(36) + Math.random().toString(36).substring(2, 6),
|
|
8966
9173
|
type: 'text',
|
|
8967
9174
|
name: '母版标题占位符',
|
|
8968
9175
|
content: '<div>单击此处编辑母版标题样式</div>',
|
|
9176
|
+
isMaster: true,
|
|
9177
|
+
isPlaceholder: true,
|
|
9178
|
+
placeholder: { type: 'title', prompt: '单击此处编辑母版标题样式' },
|
|
8969
9179
|
style: {
|
|
8970
9180
|
left: '50px',
|
|
8971
9181
|
top: '40px',
|
|
@@ -8975,51 +9185,6 @@ export function useSlides() {
|
|
|
8975
9185
|
fontWeight: 'bold',
|
|
8976
9186
|
color: '#1e293b'
|
|
8977
9187
|
}
|
|
8978
|
-
},
|
|
8979
|
-
{
|
|
8980
|
-
id: 'ph-date-' + Date.now().toString(),
|
|
8981
|
-
type: 'text',
|
|
8982
|
-
name: '日期占位符',
|
|
8983
|
-
content: `<div>${dateStr}</div>`,
|
|
8984
|
-
style: {
|
|
8985
|
-
left: '50px',
|
|
8986
|
-
top: `${H - 48}px`,
|
|
8987
|
-
width: '220px',
|
|
8988
|
-
height: '30px',
|
|
8989
|
-
fontSize: '12px',
|
|
8990
|
-
color: '#94a3b8',
|
|
8991
|
-
textAlign: 'left'
|
|
8992
|
-
}
|
|
8993
|
-
},
|
|
8994
|
-
{
|
|
8995
|
-
id: 'ph-footer-' + Date.now().toString(),
|
|
8996
|
-
type: 'text',
|
|
8997
|
-
name: '页脚占位符',
|
|
8998
|
-
content: '<div>页脚区</div>',
|
|
8999
|
-
style: {
|
|
9000
|
-
left: `${Math.round(W / 2 - 110)}px`,
|
|
9001
|
-
top: `${H - 48}px`,
|
|
9002
|
-
width: '220px',
|
|
9003
|
-
height: '30px',
|
|
9004
|
-
fontSize: '12px',
|
|
9005
|
-
color: '#94a3b8',
|
|
9006
|
-
textAlign: 'center'
|
|
9007
|
-
}
|
|
9008
|
-
},
|
|
9009
|
-
{
|
|
9010
|
-
id: 'ph-num-' + Date.now().toString(),
|
|
9011
|
-
type: 'text',
|
|
9012
|
-
name: '页码占位符',
|
|
9013
|
-
content: '<div><#></div>',
|
|
9014
|
-
style: {
|
|
9015
|
-
left: `${W - 270}px`,
|
|
9016
|
-
top: `${H - 48}px`,
|
|
9017
|
-
width: '220px',
|
|
9018
|
-
height: '30px',
|
|
9019
|
-
fontSize: '12px',
|
|
9020
|
-
color: '#94a3b8',
|
|
9021
|
-
textAlign: 'right'
|
|
9022
|
-
}
|
|
9023
9188
|
}
|
|
9024
9189
|
]
|
|
9025
9190
|
|
|
@@ -9167,14 +9332,17 @@ export function useSlides() {
|
|
|
9167
9332
|
type: 'shape',
|
|
9168
9333
|
name: '图片占位符',
|
|
9169
9334
|
content: 'rect',
|
|
9170
|
-
text: '
|
|
9335
|
+
text: '',
|
|
9336
|
+
isPlaceholder: true,
|
|
9337
|
+
placeholder: { type: 'image', prompt: '图片占位符' },
|
|
9171
9338
|
style: {
|
|
9172
9339
|
left: '50px',
|
|
9173
9340
|
top: '140px',
|
|
9174
9341
|
width: `${W - 100}px`,
|
|
9175
9342
|
height: `${Math.max(200, H - 210)}px`,
|
|
9176
|
-
fill: '#
|
|
9177
|
-
|
|
9343
|
+
fill: '#d9dde3',
|
|
9344
|
+
borderRadius: '12px',
|
|
9345
|
+
textColor: '#475569',
|
|
9178
9346
|
fontSize: '16px',
|
|
9179
9347
|
textAlign: 'center'
|
|
9180
9348
|
}
|
|
@@ -9186,6 +9354,8 @@ export function useSlides() {
|
|
|
9186
9354
|
textType: 'smallText',
|
|
9187
9355
|
name: '页码占位符',
|
|
9188
9356
|
content: '<div><#></div>',
|
|
9357
|
+
isPlaceholder: true,
|
|
9358
|
+
placeholder: { type: 'slide-number', prompt: '<#>' },
|
|
9189
9359
|
style: {
|
|
9190
9360
|
left: `${W - 270}px`,
|
|
9191
9361
|
top: `${H - 48}px`,
|
|
@@ -9204,6 +9374,8 @@ export function useSlides() {
|
|
|
9204
9374
|
textType: 'smallText',
|
|
9205
9375
|
name: '日期占位符',
|
|
9206
9376
|
content: `<div>${dateStr}</div>`,
|
|
9377
|
+
isPlaceholder: true,
|
|
9378
|
+
placeholder: { type: 'date', prompt: dateStr },
|
|
9207
9379
|
style: {
|
|
9208
9380
|
left: '50px',
|
|
9209
9381
|
top: `${H - 48}px`,
|
|
@@ -9220,6 +9392,8 @@ export function useSlides() {
|
|
|
9220
9392
|
type: 'text',
|
|
9221
9393
|
name: '页脚占位符',
|
|
9222
9394
|
content: '<div>页脚文本描述</div>',
|
|
9395
|
+
isPlaceholder: true,
|
|
9396
|
+
placeholder: { type: 'footer', prompt: '页脚文本描述' },
|
|
9223
9397
|
style: {
|
|
9224
9398
|
left: `${Math.round(W / 2 - 110)}px`,
|
|
9225
9399
|
top: `${H - 48}px`,
|
|
@@ -9233,7 +9407,10 @@ export function useSlides() {
|
|
|
9233
9407
|
}
|
|
9234
9408
|
|
|
9235
9409
|
if (!targetObj.elements) targetObj.elements = []
|
|
9236
|
-
if (newEl)
|
|
9410
|
+
if (newEl) {
|
|
9411
|
+
newEl.isMaster = true
|
|
9412
|
+
newEl.isPlaceholder = true
|
|
9413
|
+
}
|
|
9237
9414
|
targetObj.elements.push(newEl)
|
|
9238
9415
|
selectedElementId.value = newId
|
|
9239
9416
|
selectedElementIds.value = [newId]
|
|
@@ -9354,6 +9531,7 @@ export function useSlides() {
|
|
|
9354
9531
|
updateAnimationDuration,
|
|
9355
9532
|
updateAnimationTrigger,
|
|
9356
9533
|
updateAnimationEffect,
|
|
9534
|
+
updateAnimationTargetElement,
|
|
9357
9535
|
updateAnimationOrder,
|
|
9358
9536
|
currentAnimationIndex,
|
|
9359
9537
|
execNext,
|