1. 修复海报图片

This commit is contained in:
李琦
2026-08-03 16:00:13 +08:00
parent 8543d0bb25
commit 18d7296f01
13 changed files with 1052 additions and 105 deletions

View File

@@ -6,6 +6,12 @@ export const POSTER_SIDES = [
{ key: 3, keyStr: '3', route: '/hb-hm', label: '回门宴', short: 'hm' },
]
/** 布局1=竖版2=横版URL 后缀 /1 /2 */
export const POSTER_LAYOUTS = [
{ value: '1', label: '竖版', desc: '经典竖向 / portrait', mobileLayout: 'portrait', template: 'classic' },
{ value: '2', label: '横版', desc: '左图右文 / landscape', mobileLayout: 'landscape', template: 'split' },
]
export const POSTER_TEMPLATES = [
{ value: 'classic', label: '经典竖版', desc: '顶图 + 正文逐行出现' },
{ value: 'split', label: '左图右文', desc: '竖排书法 + 右侧宴席信息' },
@@ -109,9 +115,34 @@ export function posterSideMeta(side) {
return POSTER_SIDES.find((x) => x.key === s) || POSTER_SIDES[0]
}
/** 解析布局 1|2无法识别默认 1竖版 */
export function resolvePosterLayout(input) {
if (input === 2 || input === '2') return 2
if (input === 1 || input === '1') return 1
const v = String(input || '').trim().toLowerCase()
if (v === 'landscape' || v === 'h' || v === 'heng' || v === '横版') return 2
if (v === 'portrait' || v === 'v' || v === 'shu' || v === '竖版') return 1
return 1
}
export function layoutKey(layout) {
return String(resolvePosterLayout(layout))
}
export function posterLayoutMeta(layout) {
const k = layoutKey(layout)
return POSTER_LAYOUTS.find((x) => x.value === k) || POSTER_LAYOUTS[0]
}
export function posterRoute(side, layout = 1) {
const meta = posterSideMeta(side)
return `${meta.route}/${layoutKey(layout)}`
}
function baseFields(side) {
return {
side: resolvePosterSide(side) || 1,
layout: 1,
template: 'classic',
mobileLayout: 'auto',
enabled: true,
@@ -259,14 +290,6 @@ export function defaultPosterConfig() {
return defaultSide1PosterConfig()
}
export function defaultPosterBundle() {
return {
'1': defaultSide1PosterConfig(),
'2': defaultSide2PosterConfig(),
'3': defaultSide3PosterConfig(),
}
}
export function defaultPosterForSide(side) {
const s = resolvePosterSide(side) || 1
if (s === 3) return defaultSide3PosterConfig()
@@ -274,6 +297,36 @@ export function defaultPosterForSide(side) {
return defaultSide1PosterConfig()
}
/** 按侧 + 布局生成默认配置(竖=classic/portrait横=split/landscape */
export function defaultPosterForLayout(side, layout = 1) {
const s = resolvePosterSide(side) || 1
const l = resolvePosterLayout(layout)
const base = defaultPosterForSide(s)
const meta = posterLayoutMeta(l)
return {
...base,
side: s,
layout: l,
template: meta.template,
mobileLayout: meta.mobileLayout,
}
}
export function defaultSideLayouts(side) {
return {
'1': defaultPosterForLayout(side, 1),
'2': defaultPosterForLayout(side, 2),
}
}
export function defaultPosterBundle() {
return {
'1': defaultSideLayouts(1),
'2': defaultSideLayouts(2),
'3': defaultSideLayouts(3),
}
}
export const LOADING_EFFECTS = [
{ value: 'fade', label: '淡入加载' },
{ value: 'redSeal', label: '红印囍字' },
@@ -292,6 +345,19 @@ function isLegacyFlatPoster(raw) {
return 'title' in raw || 'lines' in raw || 'heroImg' in raw || 'sonName' in raw || 'template' in raw
}
/** 判断对象是否为「单份海报配置」(扁平字段),而非 {1,2} 布局包 */
function isFlatPosterConfig(obj) {
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return false
if (obj['1'] || obj['2'] || obj[1] || obj[2]) {
const hasNestedLayout = [obj['1'], obj['2'], obj[1], obj[2]].some(
(v) => v && typeof v === 'object' && ('title' in v || 'template' in v || 'heroImg' in v || 'lines' in v),
)
if (hasNestedLayout) return false
}
return 'title' in obj || 'lines' in obj || 'heroImg' in obj || 'sonName' in obj || 'template' in obj
|| 'groomName' in obj || 'mobileLayout' in obj || 'pageTitle' in obj
}
function pickSideSource(loaded, side) {
const k = sideKey(side)
if (loaded[k] && typeof loaded[k] === 'object') return loaded[k]
@@ -301,12 +367,33 @@ function pickSideSource(loaded, side) {
return null
}
export function normalizePosterConfig(raw, side = 1) {
const SHARED_COPY_KEYS = [
'sonLabel', 'sonName', 'daughterInLawLabel', 'daughterInLawName',
'groomName', 'brideName', 'dateText', 'lunarText', 'venueText',
'lines', 'heroImg', 'heroFocusX', 'heroFocusY', 'qrCenterImg',
'musicUrl', 'invitePath', 'inviteButtonText', 'showInviteButton',
'enabled', 'pageTitle', 'pageDescription', 'footer', 'title',
'greeting', 'verticalSlogan', 'subSlogan1', 'subSlogan2', 'loveFooter',
'loadingEffect', 'enterEffect',
]
function copySharedFields(from, into) {
if (!from || !into) return into
for (const k of SHARED_COPY_KEYS) {
if (from[k] !== undefined && from[k] !== null && from[k] !== '') {
into[k] = Array.isArray(from[k]) ? from[k].slice() : from[k]
}
}
return into
}
export function normalizePosterConfig(raw, side = 1, layout = 1) {
const s = resolvePosterSide(side) || 1
const base = defaultPosterForSide(s)
const l = resolvePosterLayout(layout)
const base = defaultPosterForLayout(s, l)
const loaded = raw && typeof raw === 'object' ? raw : {}
const lines = Array.isArray(loaded.lines)
? loaded.lines.map((l) => String(l ?? ''))
? loaded.lines.map((x) => String(x ?? ''))
: base.lines
const template = POSTER_TEMPLATES.some((t) => t.value === loaded.template)
? loaded.template
@@ -322,6 +409,7 @@ export function normalizePosterConfig(raw, side = 1) {
...base,
...loaded,
side: s,
layout: l,
template,
mobileLayout,
lines: lines.length ? lines : base.lines,
@@ -353,25 +441,69 @@ export function normalizePosterConfig(raw, side = 1) {
}
}
/** 归一为 { 1, 2, 3 };兼容旧 male/female 与扁平数据 */
/** 将单侧数据归一为 { "1": 竖版, "2": 横版 } */
export function normalizeSideLayouts(raw, side = 1) {
const s = resolvePosterSide(side) || 1
const src = raw && typeof raw === 'object' ? raw : {}
if (!isFlatPosterConfig(src) && (src['1'] || src['2'] || src[1] || src[2])) {
const l1raw = (src['1'] && typeof src['1'] === 'object' ? src['1'] : null)
|| (src[1] && typeof src[1] === 'object' ? src[1] : null)
|| {}
const l2raw = (src['2'] && typeof src['2'] === 'object' ? src['2'] : null)
|| (src[2] && typeof src[2] === 'object' ? src[2] : null)
|| {}
const l1 = normalizePosterConfig(l1raw, s, 1)
l1.mobileLayout = 'portrait'
l1.layout = 1
let l2
if (Object.keys(l2raw).length) {
l2 = normalizePosterConfig(l2raw, s, 2)
} else {
l2 = defaultPosterForLayout(s, 2)
copySharedFields(l1, l2)
}
l2.mobileLayout = 'landscape'
l2.layout = 2
return { '1': l1, '2': l2 }
}
const flat = isFlatPosterConfig(src) ? src : {}
const l1 = normalizePosterConfig(flat, s, 1)
l1.mobileLayout = 'portrait'
l1.layout = 1
const l2 = defaultPosterForLayout(s, 2)
copySharedFields(l1, l2)
l2.mobileLayout = 'landscape'
l2.layout = 2
return { '1': l1, '2': l2 }
}
/** 归一化为 { 1|2|3: { 1|2: cfg } };兼容旧 male/female、扁平与单侧扁平 */
export function normalizePosterBundle(raw) {
const loaded = raw && typeof raw === 'object' ? raw : {}
if (isLegacyFlatPoster(loaded)) {
return {
'1': normalizePosterConfig(loaded, 1),
'2': defaultSide2PosterConfig(),
'3': defaultSide3PosterConfig(),
'1': normalizeSideLayouts(loaded, 1),
'2': defaultSideLayouts(2),
'3': defaultSideLayouts(3),
}
}
return {
'1': normalizePosterConfig(pickSideSource(loaded, 1) || {}, 1),
'2': normalizePosterConfig(pickSideSource(loaded, 2) || {}, 2),
'3': normalizePosterConfig(pickSideSource(loaded, 3) || {}, 3),
'1': normalizeSideLayouts(pickSideSource(loaded, 1) || {}, 1),
'2': normalizeSideLayouts(pickSideSource(loaded, 2) || {}, 2),
'3': normalizeSideLayouts(pickSideSource(loaded, 3) || {}, 3),
}
}
export function pickPosterSide(bundle, side) {
export function pickPosterConfig(bundle, side, layout = 1) {
const normalized = normalizePosterBundle(bundle)
const s = sideKey(side)
return normalized[s]
const l = layoutKey(layout)
return normalized[s]?.[l] || defaultPosterForLayout(s, l)
}
/** @deprecated 兼容旧调用,默认取竖版 */
export function pickPosterSide(bundle, side) {
return pickPosterConfig(bundle, side, 1)
}