fix: 升级了一些视觉

This commit is contained in:
2026-08-12 13:52:54 +08:00
parent 1f9ac09829
commit 5fb0f9a1a7
27 changed files with 5032 additions and 826 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,37 +1,37 @@
<template>
<div class="min-h-screen w-full flex items-center justify-center bg-[#Fdfbf7] relative overflow-hidden">
<div class="min-h-screen w-full flex items-center justify-center bg-[var(--c-paper)] relative overflow-hidden">
<!-- 背景装饰 -->
<div class="absolute -top-24 -right-24 w-80 h-80 rounded-full bg-[#a88c6b]/10 blur-3xl"></div>
<div class="absolute -top-24 -right-24 w-80 h-80 rounded-full bg-[var(--c-primary)]/10 blur-3xl"></div>
<div class="absolute -bottom-24 -left-24 w-80 h-80 rounded-full bg-[#e8a5b2]/10 blur-3xl"></div>
<div class="absolute inset-0 pointer-events-none opacity-[0.04]"
style="background-image: radial-gradient(#a88c6b 1px, transparent 1px); background-size: 22px 22px;"></div>
style="background-image: radial-gradient(var(--c-primary) 1px, transparent 1px); background-size: 22px 22px;"></div>
<div class="relative z-10 w-[360px] bg-white/80 backdrop-blur-xl border border-[#a88c6b]/15 rounded-3xl shadow-2xl p-9 animate-fade-up">
<div class="relative z-10 w-[360px] bg-white/80 backdrop-blur-xl border border-[var(--c-primary)]/15 rounded-3xl shadow-2xl p-9 animate-fade-up">
<div class="text-center mb-8">
<div class="text-3xl mb-3 text-[#a88c6b] font-serif">❀</div>
<h1 class="text-xl tracking-[0.3em] text-[#2c2c2c] font-light">婚礼邀请函 · 后台</h1>
<p class="text-[11px] text-[#8A8680] tracking-[0.25em] mt-2">制作者 · 年糕崽崽</p>
<div class="text-3xl mb-3 text-[var(--c-primary)] font-serif">❀</div>
<h1 class="text-xl tracking-[0.3em] text-[var(--c-ink)] font-light">婚礼邀请函 · 后台</h1>
<p class="text-[11px] text-[var(--c-muted)] tracking-[0.25em] mt-2">制作者 · 年糕崽崽</p>
</div>
<a-form :model="form" layout="vertical" @finish="onSubmit">
<a-form-item name="account" :rules="[{ required: true, message: '请输入账号' }]">
<a-input v-model:value="form.account" size="large" placeholder="管理员账号">
<template #prefix><i class="fa-solid fa-user text-[#a88c6b]"></i></template>
<template #prefix><i class="fa-solid fa-user text-[var(--c-primary)]"></i></template>
</a-input>
</a-form-item>
<a-form-item name="password" :rules="[{ required: true, message: '请输入密码' }]">
<a-input-password v-model:value="form.password" size="large" placeholder="密码">
<template #prefix><i class="fa-solid fa-lock text-[#a88c6b]"></i></template>
<template #prefix><i class="fa-solid fa-lock text-[var(--c-primary)]"></i></template>
</a-input-password>
</a-form-item>
<a-button type="primary" html-type="submit" size="large" block :loading="loading"
class="!bg-[#a88c6b] hover:!bg-[#967b5c] !border-[#a88c6b] !rounded-xl !tracking-[0.2em]">
class="!bg-[var(--c-primary)] hover:!bg-[var(--c-primary-deep)] !border-[var(--c-primary)] !rounded-xl !tracking-[0.2em]">
登 录
</a-button>
</a-form>
<div class="mt-6 text-center">
<router-link to="/" class="text-[12px] text-[#a88c6b] hover:underline">返回请柬预览</router-link>
<router-link to="/" class="text-[12px] text-[var(--c-primary)] hover:underline">返回请柬预览</router-link>
</div>
</div>
</div>

View File

@@ -4,13 +4,13 @@
<div v-if="view === 'list'" class="admin-panel">
<div class="flex justify-between items-center mb-4 gap-3 flex-wrap">
<div>
<div class="text-sm text-[#2c2c2c]">婚礼迎宾易拉宝</div>
<div class="text-[11px] text-[#8A8680] mt-0.5">
<div class="text-sm text-[var(--c-ink)]">婚礼迎宾易拉宝</div>
<div class="text-[11px] text-[var(--c-muted)] mt-0.5">
共 {{ list.length }} 张 · 酒店门口欢迎海报(新人姓名 / 日期 / WEDDING),非按贵宾制
</div>
</div>
<div class="flex gap-2">
<a-button size="small" type="primary" class="!bg-[#a88c6b] !border-[#a88c6b]" @click="openCreate">
<a-button size="small" type="primary" class="!bg-[var(--c-primary)] !border-[var(--c-primary)]" @click="openCreate">
<i class="fa-solid fa-plus mr-1"></i>新建海报
</a-button>
<a-button size="small" :loading="listLoading" @click="loadList">
@@ -33,11 +33,11 @@
<span class="admin-pill admin-pill--gold">{{ templateLabel(record.template) }}</span>
</template>
<template v-else-if="column.key === 'updated_at'">
<span class="text-[12px] text-[#8A8680]">{{ formatTime(record.updated_at) }}</span>
<span class="text-[12px] text-[var(--c-muted)]">{{ formatTime(record.updated_at) }}</span>
</template>
<template v-else-if="column.key === 'actions'">
<a-space>
<a-button size="small" type="link" class="!text-[#a88c6b] !px-1" @click="openDetail(record)">编辑</a-button>
<a-button size="small" type="link" class="!text-[var(--c-primary)] !px-1" @click="openDetail(record)">编辑</a-button>
<a-button size="small" danger type="text" @click="onDelete(record)">删除</a-button>
</a-space>
</template>
@@ -54,8 +54,8 @@
<i class="fa-solid fa-arrow-left mr-1"></i>返回列表
</a-button>
<div class="min-w-0">
<div class="text-sm text-[#2c2c2c] truncate">{{ detail?.name || '迎宾易拉宝' }}</div>
<div class="text-[11px] text-[#8A8680] mt-0.5">改新人姓名与日期 · 拖拽排版 · 导出酒店门口立牌 PNG</div>
<div class="text-sm text-[var(--c-ink)] truncate">{{ detail?.name || '迎宾易拉宝' }}</div>
<div class="text-[11px] text-[var(--c-muted)] mt-0.5">改新人姓名与日期 · 拖拽排版 · 导出酒店门口立牌 PNG</div>
</div>
</div>
<div class="flex gap-2 flex-wrap">
@@ -65,7 +65,7 @@
<a-button
size="small"
type="primary"
class="!bg-[#a88c6b] !border-[#a88c6b]"
class="!bg-[var(--c-primary)] !border-[var(--c-primary)]"
:loading="saving"
@click="saveDetail"
>
@@ -75,7 +75,7 @@
</div>
</div>
<div v-if="detailLoading" class="admin-panel text-center text-[#8A8680] py-16">加载中…</div>
<div v-if="detailLoading" class="admin-panel text-center text-[var(--c-muted)] py-16">加载中…</div>
<div v-else-if="detail" class="hw-editor-grid">
<div class="admin-panel hw-canvas-panel">
@@ -111,7 +111,7 @@
{{ t.content || ' ' }}
</div>
</div>
<div class="text-[10px] text-[#8A8680] mt-2 text-center">点击文字选中并拖拽;点击空白取消选中</div>
<div class="text-[10px] text-[var(--c-muted)] mt-2 text-center">点击文字选中并拖拽;点击空白取消选中</div>
</div>
<div class="admin-panel hw-side-panel">
@@ -156,7 +156,7 @@
</a-button>
</div>
<div v-if="!texts.length" class="text-[11px] text-[#8A8680] mb-3">暂无文字,可切换模板或手动添加</div>
<div v-if="!texts.length" class="text-[11px] text-[var(--c-muted)] mb-3">暂无文字,可切换模板或手动添加</div>
<div
v-for="t in texts"
@@ -166,7 +166,7 @@
@click="selectedId = t.id"
>
<div class="flex justify-between gap-2 mb-2">
<span class="text-[11px] text-[#8A8680] truncate">{{ t.content || '(空)' }}</span>
<span class="text-[11px] text-[var(--c-muted)] truncate">{{ t.content || '(空)' }}</span>
<a-button size="small" danger type="text" class="!px-1" @click.stop="removeText(t.id)">删除</a-button>
</div>
<a-input v-model:value="t.content" placeholder="文字内容" class="mb-2" :maxlength="80" />
@@ -205,7 +205,7 @@
<a-checkbox v-model:checked="t.shadow">阴影</a-checkbox>
<a-checkbox v-model:checked="t.stroke">描边</a-checkbox>
</div>
<div class="text-[10px] text-[#8A8680]">位置 X {{ Math.round(t.x) }}% · Y {{ Math.round(t.y) }}%</div>
<div class="text-[10px] text-[var(--c-muted)]">位置 X {{ Math.round(t.x) }}% · Y {{ Math.round(t.y) }}%</div>
</div>
</a-form>
</div>
@@ -251,7 +251,7 @@
:uploading="uploading"
@pick="pickCreateImage"
/>
<div class="text-[10px] text-[#8A8680] mt-1">创建后改新人姓名、日期,并拖拽排版后导出</div>
<div class="text-[10px] text-[var(--c-muted)] mt-1">创建后改新人姓名、日期,并拖拽排版后导出</div>
</a-form-item>
</a-form>
</a-modal>
@@ -267,7 +267,7 @@
:body-style="{ padding: '12px 16px 8px', maxHeight: 'min(70vh, 640px)', overflow: 'hidden' }"
>
<div class="hw-tpl-modal-body">
<div class="text-[11px] text-[#8A8680] mb-3 sticky top-0 bg-white z-[1] pb-1">
<div class="text-[11px] text-[var(--c-muted)] mb-3 sticky top-0 bg-white z-[1] pb-1">
每个卡片展示文字落点区域,点击即可选用;之后仍可在画布上拖拽微调
</div>
<div v-for="group in templateGroupList" :key="group.label" class="hw-tpl-group">
@@ -885,7 +885,7 @@ defineExpose({ loadList, refresh: loadList })
<style scoped>
.admin-panel {
background: #fff;
border: 1px solid rgba(168, 140, 107, 0.14);
border: 1px solid rgba(var(--c-primary-rgb), 0.14);
border-radius: 16px;
padding: 20px 20px 16px;
box-shadow: 0 8px 28px rgba(88, 68, 42, 0.05);
@@ -901,8 +901,8 @@ defineExpose({ loadList, refresh: loadList })
}
.admin-pill--gold {
color: #8a7048;
background: rgba(168, 140, 107, 0.14);
border-color: rgba(168, 140, 107, 0.28);
background: rgba(var(--c-primary-rgb), 0.14);
border-color: rgba(var(--c-primary-rgb), 0.28);
}
.admin-table :deep(.ant-table-thead > tr > th) {
background: #f7f3ec !important;
@@ -954,7 +954,7 @@ defineExpose({ loadList, refresh: loadList })
pointer-events: none;
}
.hw-stage-empty {
color: #8a8680;
color: var(--c-muted);
font-size: 13px;
line-height: normal;
}
@@ -991,21 +991,21 @@ defineExpose({ loadList, refresh: loadList })
overflow: auto;
}
.hw-text-row {
border: 1px solid rgba(168, 140, 107, 0.2);
border: 1px solid rgba(var(--c-primary-rgb), 0.2);
border-radius: 8px;
padding: 10px;
margin-bottom: 10px;
background: #fdfbf7;
background: var(--c-paper);
cursor: pointer;
}
.hw-text-row.active {
border-color: #a88c6b;
box-shadow: 0 0 0 1px rgba(168, 140, 107, 0.25);
border-color: var(--c-primary);
box-shadow: 0 0 0 1px rgba(var(--c-primary-rgb), 0.25);
}
.hw-color {
width: 100%;
height: 32px;
border: 1px solid rgba(168, 140, 107, 0.35);
border: 1px solid rgba(var(--c-primary-rgb), 0.35);
border-radius: 6px;
padding: 2px;
background: #fff;
@@ -1018,16 +1018,16 @@ defineExpose({ loadList, refresh: loadList })
gap: 10px;
width: 100%;
padding: 8px 10px;
border: 1px solid rgba(168, 140, 107, 0.28);
border: 1px solid rgba(var(--c-primary-rgb), 0.28);
border-radius: 10px;
background: #fdfbf7;
background: var(--c-paper);
text-align: left;
cursor: pointer;
transition: border-color 0.15s, box-shadow 0.15s;
}
.hw-tpl-trigger:hover {
border-color: #a88c6b;
box-shadow: 0 0 0 1px rgba(168, 140, 107, 0.2);
border-color: var(--c-primary);
box-shadow: 0 0 0 1px rgba(var(--c-primary-rgb), 0.2);
}
.hw-tpl-mini {
position: relative;
@@ -1059,12 +1059,12 @@ defineExpose({ loadList, refresh: loadList })
}
.hw-tpl-trigger-title {
font-size: 13px;
color: #2c2c2c;
color: var(--c-ink);
}
.hw-tpl-trigger-desc {
margin-top: 2px;
font-size: 10px;
color: #8a8680;
color: var(--c-muted);
line-height: 1.35;
display: -webkit-box;
-webkit-line-clamp: 2;
@@ -1072,7 +1072,7 @@ defineExpose({ loadList, refresh: loadList })
overflow: hidden;
}
.hw-tpl-trigger-icon {
color: #a88c6b;
color: var(--c-primary);
font-size: 14px;
flex-shrink: 0;
}
@@ -1102,7 +1102,7 @@ defineExpose({ loadList, refresh: loadList })
}
}
.hw-tpl-card {
border: 1px solid rgba(168, 140, 107, 0.2);
border: 1px solid rgba(var(--c-primary-rgb), 0.2);
border-radius: 12px;
padding: 10px;
background: #fff;
@@ -1111,12 +1111,12 @@ defineExpose({ loadList, refresh: loadList })
transition: border-color 0.15s, box-shadow 0.15s, transform 0.12s;
}
.hw-tpl-card:hover {
border-color: #a88c6b;
border-color: var(--c-primary);
transform: translateY(-1px);
}
.hw-tpl-card.active {
border-color: #a88c6b;
box-shadow: 0 0 0 2px rgba(168, 140, 107, 0.22);
border-color: var(--c-primary);
box-shadow: 0 0 0 2px rgba(var(--c-primary-rgb), 0.22);
}
.hw-tpl-preview {
position: relative;
@@ -1161,7 +1161,7 @@ defineExpose({ loadList, refresh: loadList })
}
.hw-tpl-card-label {
font-size: 12px;
color: #2c2c2c;
color: var(--c-ink);
font-weight: 500;
display: flex;
align-items: center;
@@ -1186,7 +1186,7 @@ defineExpose({ loadList, refresh: loadList })
.hw-tpl-card-desc {
margin-top: 2px;
font-size: 10px;
color: #8a8680;
color: var(--c-muted);
line-height: 1.35;
display: -webkit-box;
-webkit-line-clamp: 2;

View File

@@ -1,5 +1,5 @@
<template>
<div class="hb-root">
<div class="hb-root" :data-hb-theme="cfg.posterTheme || 'redgold'">
<div class="hb-bg-decor" aria-hidden="true">
<span class="hb-bg-xi hb-bg-xi-1">囍</span>
<span class="hb-bg-xi hb-bg-xi-2">囍</span>
@@ -168,7 +168,7 @@ import { computed, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import html2canvas from 'html2canvas'
import { getConfig, getPosterConfig } from '@/api/wedding'
import { normalizePosterConfig, posterLayoutMeta, posterSideMeta, resolvePosterLayout, resolvePosterSide } from '@/composables/posterDefaults'
import { normalizePosterConfig, posterLayoutMeta, posterSideMeta, posterThemeMeta, resolvePosterLayout, resolvePosterSide } from '@/composables/posterDefaults'
import { renderFestiveQr } from '@/composables/renderFestiveQr'
import { drawXiSeal } from '@/composables/drawXiSeal'
import { useAudio } from '@/composables/useAudio'
@@ -457,12 +457,13 @@ async function savePosterImage() {
try { await document.fonts?.ready } catch { /* ignore */ }
el.querySelectorAll('canvas.sp-xi-mid, canvas.hb-xi-circle').forEach((c) => {
const isClassic = c.classList.contains('hb-xi-circle')
const theme = posterThemeMeta(cfg.posterTheme)
drawXiSeal(c, {
size: isClassic ? 22 : 28,
fontSize: isClassic ? 11 : 13,
color: '#d4af37',
borderColor: '#d4af37',
fill: isClassic ? 'rgba(155, 0, 0, 0.5)' : 'rgba(0, 0, 0, 0.18)',
color: theme.sealColor,
borderColor: theme.sealColor,
fill: isClassic ? theme.sealFill : 'rgba(0, 0, 0, 0.18)',
})
})
// 按元素实际宽度抬升倍率:目标约 1600px 宽,最高 3x,避免手机导出糊成一片
@@ -471,7 +472,7 @@ async function savePosterImage() {
const canvas = await html2canvas(el, {
useCORS: true,
allowTaint: true,
backgroundColor: '#7a0000',
backgroundColor: posterThemeMeta(cfg.posterTheme).shotBg,
scale,
logging: false,
imageTimeout: 20000,
@@ -601,20 +602,135 @@ onUnmounted(() => {
</script>
<style scoped>
.hb-root {
/* ============================================================
海报风格模板(5 套皮肤):data-hb-theme 切换整组变量
- 面板红分三档 rgb(hi/mid/lo)保留原有明暗层次
- 所有模板(竖版/横版)共用这一组变量
============================================================ */
.hb-root,
.hb-root[data-hb-theme='redgold'] {
--hb-red: #9b0000;
--hb-red-deep: #7a0000;
--hb-red-bright: #c41e3a;
--hb-gold: #d4af37;
--hb-gold-deep: #a8892e;
--hb-gold-soft: #f0d78c;
--hb-cream: #f7e7c3;
--hb-ivory: #f7efe0;
--hb-gold-rgb: 212, 175, 55;
--hb-gold-soft-rgb: 240, 215, 140;
--hb-accent-rgb: 196, 30, 58;
--hb-cream-rgb: 243, 230, 200;
--hb-ivory-rgb: 247, 239, 224;
--hb-panel: #8b0000;
--hb-panel-deep: #5c0000;
--hb-photo-bg: #c23a2b;
--hb-panel-hi-rgb: 145, 5, 5;
--hb-panel-rgb: 116, 0, 0;
--hb-panel-lo-rgb: 85, 0, 0;
--hb-page-hi: #c20808;
--hb-page-mid: #b30000;
}
.hb-root[data-hb-theme='burgundy'] {
--hb-red: #6b1d30;
--hb-red-deep: #4a1220;
--hb-red-bright: #a8465c;
--hb-gold: #d9a05b;
--hb-gold-deep: #a87840;
--hb-gold-soft: #e2b98f;
--hb-cream: #ecd9c2;
--hb-ivory: #f2e8dc;
--hb-gold-rgb: 217, 160, 91;
--hb-gold-soft-rgb: 226, 185, 143;
--hb-accent-rgb: 168, 70, 92;
--hb-cream-rgb: 236, 217, 194;
--hb-ivory-rgb: 242, 232, 220;
--hb-panel: #5a1728;
--hb-panel-deep: #380d18;
--hb-photo-bg: #7d3040;
--hb-panel-hi-rgb: 108, 32, 50;
--hb-panel-rgb: 84, 22, 38;
--hb-panel-lo-rgb: 60, 14, 26;
--hb-page-hi: #7d2438;
--hb-page-mid: #5f1a2a;
}
.hb-root[data-hb-theme='navygold'] {
--hb-red: #1a2f52;
--hb-red-deep: #101f38;
--hb-red-bright: #4a6a9e;
--hb-gold: #d4b060;
--hb-gold-deep: #a3833c;
--hb-gold-soft: #e8cf96;
--hb-cream: #ecdfc0;
--hb-ivory: #f0ead8;
--hb-gold-rgb: 212, 176, 96;
--hb-gold-soft-rgb: 232, 207, 150;
--hb-accent-rgb: 74, 106, 158;
--hb-cream-rgb: 236, 223, 192;
--hb-ivory-rgb: 240, 234, 216;
--hb-panel: #16294a;
--hb-panel-deep: #0b1526;
--hb-photo-bg: #27436d;
--hb-panel-hi-rgb: 30, 52, 88;
--hb-panel-rgb: 20, 38, 66;
--hb-panel-lo-rgb: 12, 24, 44;
--hb-page-hi: #24406b;
--hb-page-mid: #1a3054;
}
.hb-root[data-hb-theme='inkjade'] {
--hb-red: #1d3a32;
--hb-red-deep: #132a24;
--hb-red-bright: #4a8a74;
--hb-gold: #c9b37e;
--hb-gold-deep: #97824f;
--hb-gold-soft: #d8c493;
--hb-cream: #e6dcc0;
--hb-ivory: #eee9d8;
--hb-gold-rgb: 201, 179, 126;
--hb-gold-soft-rgb: 216, 196, 147;
--hb-accent-rgb: 74, 138, 116;
--hb-cream-rgb: 230, 220, 192;
--hb-ivory-rgb: 238, 233, 216;
--hb-panel: #1a342d;
--hb-panel-deep: #0e1e19;
--hb-photo-bg: #2c4f44;
--hb-panel-hi-rgb: 34, 62, 53;
--hb-panel-rgb: 24, 46, 39;
--hb-panel-lo-rgb: 15, 30, 25;
--hb-page-hi: #2a4c40;
--hb-page-mid: #203b32;
}
.hb-root[data-hb-theme='rosewood'] {
--hb-red: #7d2c42;
--hb-red-deep: #5c1f30;
--hb-red-bright: #c05a74;
--hb-gold: #e0b487;
--hb-gold-deep: #b08a5c;
--hb-gold-soft: #f0d3b2;
--hb-cream: #f5e4d0;
--hb-ivory: #f8eee2;
--hb-gold-rgb: 224, 180, 135;
--hb-gold-soft-rgb: 240, 211, 178;
--hb-accent-rgb: 192, 90, 116;
--hb-cream-rgb: 245, 228, 208;
--hb-ivory-rgb: 248, 238, 226;
--hb-panel: #6e2538;
--hb-panel-deep: #451522;
--hb-photo-bg: #94445a;
--hb-panel-hi-rgb: 130, 46, 68;
--hb-panel-rgb: 100, 32, 50;
--hb-panel-lo-rgb: 72, 22, 35;
--hb-page-hi: #963c56;
--hb-page-mid: #762c42;
}
.hb-root {
position: relative;
min-height: 100dvh;
background:
radial-gradient(ellipse at 50% 18%, #c20808 0%, transparent 42%),
radial-gradient(circle at 12% 80%, rgba(212, 175, 55, 0.12), transparent 28%),
radial-gradient(circle at 88% 75%, rgba(212, 175, 55, 0.1), transparent 26%),
radial-gradient(ellipse at 50% 20%, #b30000 0%, var(--hb-red) 45%, var(--hb-red-deep) 100%);
radial-gradient(ellipse at 50% 18%, var(--hb-page-hi) 0%, transparent 42%),
radial-gradient(circle at 12% 80%, rgba(var(--hb-gold-rgb), 0.12), transparent 28%),
radial-gradient(circle at 88% 75%, rgba(var(--hb-gold-rgb), 0.1), transparent 26%),
radial-gradient(ellipse at 50% 20%, var(--hb-page-mid) 0%, var(--hb-red) 45%, var(--hb-red-deep) 100%);
color: var(--hb-gold);
display: flex;
align-items: center;
@@ -667,7 +783,7 @@ onUnmounted(() => {
display: inline-flex;
align-items: center;
gap: 8px;
color: #f0d78c;
color: var(--hb-gold-soft);
font-size: 13px;
letter-spacing: 0.26em;
animation: hb-hint-pulse 1.6s ease-in-out infinite;
@@ -676,8 +792,8 @@ onUnmounted(() => {
width: 6px;
height: 6px;
border-radius: 999px;
background: #f0d78c;
box-shadow: 0 0 8px rgba(240, 215, 140, 0.7);
background: var(--hb-gold-soft);
box-shadow: 0 0 8px rgba(var(--hb-gold-soft-rgb), 0.7);
}
@keyframes hb-hint-pulse {
0%, 100% { opacity: 0.75; }
@@ -691,9 +807,9 @@ onUnmounted(() => {
width: 44px;
height: 44px;
border-radius: 999px;
border: 1.5px solid rgba(212, 175, 55, 0.85);
background: linear-gradient(160deg, rgba(212, 175, 55, 0.42), rgba(122, 0, 0, 0.82));
color: #f0d78c;
border: 1.5px solid rgba(var(--hb-gold-rgb), 0.85);
background: linear-gradient(160deg, rgba(var(--hb-gold-rgb), 0.42), rgba(var(--hb-panel-rgb), 0.82));
color: var(--hb-gold-soft);
display: inline-flex;
align-items: center;
justify-content: center;
@@ -715,9 +831,9 @@ onUnmounted(() => {
}
.hb-save-btn {
appearance: none;
border: 1.5px solid #d4af37;
background: linear-gradient(180deg, rgba(212, 175, 55, 0.45), rgba(155, 0, 0, 0.72));
color: #f0d78c;
border: 1.5px solid var(--hb-gold);
background: linear-gradient(180deg, rgba(var(--hb-gold-rgb), 0.45), rgba(var(--hb-panel-hi-rgb), 0.72));
color: var(--hb-gold-soft);
padding: 10px 16px;
border-radius: 999px;
letter-spacing: 0.2em;
@@ -737,27 +853,27 @@ onUnmounted(() => {
.hb-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.hb-bg-decor { position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
.hb-bg-xi {
position: absolute; color: rgba(240, 215, 140, 0.14); font-weight: 700; line-height: 1;
position: absolute; color: rgba(var(--hb-gold-soft-rgb), 0.14); font-weight: 700; line-height: 1;
user-select: none; animation: hb-pulse-soft 7s ease-in-out infinite;
}
.hb-bg-xi-1 { top: 6%; left: 4%; font-size: 56px; transform: rotate(-12deg); }
.hb-bg-xi-2 { top: 14%; right: 6%; font-size: 42px; transform: rotate(10deg); color: rgba(196, 30, 58, 0.22); animation-delay: 1.2s; }
.hb-bg-xi-2 { top: 14%; right: 6%; font-size: 42px; transform: rotate(10deg); color: rgba(var(--hb-accent-rgb), 0.22); animation-delay: 1.2s; }
.hb-bg-xi-3 { bottom: 18%; left: 8%; font-size: 48px; transform: rotate(8deg); animation-delay: 2s; }
.hb-bg-xi-4 { bottom: 10%; right: 10%; font-size: 64px; transform: rotate(-8deg); color: rgba(196, 30, 58, 0.18); animation-delay: 0.6s; }
.hb-bg-xi-4 { bottom: 10%; right: 10%; font-size: 64px; transform: rotate(-8deg); color: rgba(var(--hb-accent-rgb), 0.18); animation-delay: 0.6s; }
.hb-bg-xi-5 { top: 42%; left: 2%; font-size: 36px; opacity: 0.8; animation-delay: 3s; }
.hb-bg-xi-6 { top: 48%; right: 3%; font-size: 40px; opacity: 0.75; animation-delay: 2.4s; }
.hb-bg-knot { position: absolute; color: rgba(212, 175, 55, 0.38); width: 56px; height: 70px; animation: hb-float 10s ease-in-out infinite; }
.hb-bg-knot { position: absolute; color: rgba(var(--hb-gold-rgb), 0.38); width: 56px; height: 70px; animation: hb-float 10s ease-in-out infinite; }
.hb-bg-knot-1 { top: 7%; left: 12%; width: 52px; height: 66px; }
.hb-bg-knot-2 { top: 20%; right: 10%; width: 46px; height: 58px; color: rgba(240, 215, 140, 0.34); animation-delay: 1.8s; }
.hb-bg-knot-2 { top: 20%; right: 10%; width: 46px; height: 58px; color: rgba(var(--hb-gold-soft-rgb), 0.34); animation-delay: 1.8s; }
.hb-bg-knot-3 { bottom: 18%; left: 8%; width: 50px; height: 64px; animation-delay: 2.6s; }
.hb-bg-knot-4 { bottom: 10%; right: 12%; width: 58px; height: 72px; color: rgba(240, 215, 140, 0.3); animation-delay: 1s; }
.hb-bg-lantern { position: absolute; color: rgba(240, 215, 140, 0.48); width: 42px; height: 64px; animation: hb-swing 5.5s ease-in-out infinite; }
.hb-bg-knot-4 { bottom: 10%; right: 12%; width: 58px; height: 72px; color: rgba(var(--hb-gold-soft-rgb), 0.3); animation-delay: 1s; }
.hb-bg-lantern { position: absolute; color: rgba(var(--hb-gold-soft-rgb), 0.48); width: 42px; height: 64px; animation: hb-swing 5.5s ease-in-out infinite; }
.hb-bg-lantern-1 { top: 3%; left: 20%; }
.hb-bg-lantern-2 { top: 4%; right: 18%; animation-delay: 1.4s; }
.hb-bg-spark {
position: absolute; width: 6px; height: 6px; border-radius: 50%;
background: radial-gradient(circle, var(--hb-gold-soft), transparent 70%);
box-shadow: 0 0 10px rgba(240, 215, 140, 0.55); animation: hb-twinkle 2.8s ease-in-out infinite;
box-shadow: 0 0 10px rgba(var(--hb-gold-soft-rgb), 0.55); animation: hb-twinkle 2.8s ease-in-out infinite;
}
.hb-bg-spark-1 { top: 18%; left: 30%; }
.hb-bg-spark-2 { top: 28%; right: 28%; animation-delay: 0.7s; }
@@ -770,11 +886,11 @@ onUnmounted(() => {
.hb-seal {
width: 88px; height: 88px; border-radius: 50%; border: 3px solid var(--hb-gold); color: var(--hb-gold);
display: flex; align-items: center; justify-content: center; font-size: 42px; font-weight: 700;
animation: hb-seal-pop 1s ease both; box-shadow: 0 0 0 6px rgba(212, 175, 55, 0.15);
animation: hb-seal-pop 1s ease both; box-shadow: 0 0 0 6px rgba(var(--hb-gold-rgb), 0.15);
}
.hb-shimmer {
font-size: 28px; letter-spacing: 0.35em;
background: linear-gradient(90deg, #8a6a1a, var(--hb-gold-soft), #8a6a1a); background-size: 200% 100%;
background: linear-gradient(90deg, var(--hb-gold-deep), var(--hb-gold-soft), var(--hb-gold-deep)); background-size: 200% 100%;
-webkit-background-clip: text; background-clip: text; color: transparent; animation: hb-shimmer 1.2s linear infinite;
}
.hb-fade-dot {
@@ -784,11 +900,11 @@ onUnmounted(() => {
.hb-enter { position: fixed; inset: 0; z-index: 30; pointer-events: none; }
.hb-enter--curtain .hb-enter-panel {
position: absolute; top: 0; bottom: 0; width: 50%;
background: linear-gradient(180deg, #8b0000, #5c0000);
background: linear-gradient(180deg, var(--hb-panel), var(--hb-panel-deep));
transition: transform 0.95s cubic-bezier(0.65, 0, 0.35, 1);
}
.hb-enter--curtain .hb-enter-left { left: 0; border-right: 1px solid rgba(212, 175, 55, 0.35); }
.hb-enter--curtain .hb-enter-right { right: 0; border-left: 1px solid rgba(212, 175, 55, 0.35); }
.hb-enter--curtain .hb-enter-left { left: 0; border-right: 1px solid rgba(var(--hb-gold-rgb), 0.35); }
.hb-enter--curtain .hb-enter-right { right: 0; border-left: 1px solid rgba(var(--hb-gold-rgb), 0.35); }
.hb-enter--curtain.is-done .hb-enter-left { transform: translateX(-105%); }
.hb-enter--curtain.is-done .hb-enter-right { transform: translateX(105%); }
.hb-enter--fadeUp .hb-enter-veil,
@@ -801,11 +917,11 @@ onUnmounted(() => {
.hb-disabled {
position: fixed; inset: 0; z-index: 50; display: flex; flex-direction: column;
align-items: center; justify-content: center; gap: 16px;
background: rgba(80, 0, 0, 0.92); color: var(--hb-cream);
background: rgba(var(--hb-panel-lo-rgb), 0.92); color: var(--hb-cream);
}
.hb-cta-btn {
appearance: none; border: 1px solid var(--hb-gold);
background: linear-gradient(180deg, rgba(212, 175, 55, 0.22), rgba(212, 175, 55, 0.08));
background: linear-gradient(180deg, rgba(var(--hb-gold-rgb), 0.22), rgba(var(--hb-gold-rgb), 0.08));
color: var(--hb-gold-soft); padding: 8px 22px; border-radius: 999px;
letter-spacing: 0.28em; font-size: 13px; cursor: pointer;
}
@@ -832,12 +948,12 @@ onUnmounted(() => {
gap: 0;
padding: 8px 10px;
background:
radial-gradient(circle at 50% 0%, rgba(240, 215, 140, 0.28), transparent 55%),
linear-gradient(180deg, #9b0000, #6e0000);
border: 1.5px solid #d4af37;
radial-gradient(circle at 50% 0%, rgba(var(--hb-gold-soft-rgb), 0.28), transparent 55%),
linear-gradient(180deg, var(--hb-red), var(--hb-red-deep));
border: 1.5px solid var(--hb-gold);
border-radius: 10px;
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35);
color: #f0d78c;
color: var(--hb-gold-soft);
pointer-events: none;
font-family: "Noto Serif SC", "Source Han Serif SC", "Songti SC", "STSong", serif;
}
@@ -886,15 +1002,15 @@ onUnmounted(() => {
[data-hb-capture-qr] .hb-cq-frame {
position: relative;
padding: 8px;
background: linear-gradient(145deg, #fff8e7, #f7e7c3);
border: 2px solid #d4af37;
background: linear-gradient(145deg, #fff8e7, var(--hb-cream));
border: 2px solid var(--hb-gold);
border-radius: 8px;
}
[data-hb-capture-qr] .hb-cq-c {
position: absolute;
font-size: 10px;
font-weight: 700;
color: #c41e3a;
color: var(--hb-red-bright);
line-height: 1;
z-index: 1;
pointer-events: none;
@@ -913,7 +1029,7 @@ onUnmounted(() => {
font-size: 10px;
letter-spacing: 0.12em;
line-height: 1.35;
color: rgba(240, 215, 140, 0.92);
color: rgba(var(--hb-gold-soft-rgb), 0.92);
text-align: center;
white-space: nowrap;
flex-shrink: 0;

View File

@@ -144,7 +144,7 @@
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import BlurText from '@/components/vue-bits/BlurText.vue'
import TextType from '@/components/vue-bits/TextType.vue'
import { heroFocusCss } from '@/composables/posterDefaults'
import { heroFocusCss, posterThemeMeta } from '@/composables/posterDefaults'
import { drawXiSeal } from '@/composables/drawXiSeal'
const props = defineProps({
@@ -170,15 +170,19 @@ const nameRight = computed(() => `${props.cfg.daughterInLawLabel || ''}${props.c
async function paintXiSeal() {
try { await document.fonts?.ready } catch { /* ignore */ }
// canvas 不解析 CSS 变量,从海报主题元数据取印章色
const theme = posterThemeMeta(props.cfg.posterTheme)
drawXiSeal(xiSealRef.value, {
size: 22,
fontSize: 11,
color: '#d4af37',
borderColor: '#d4af37',
fill: 'rgba(155, 0, 0, 0.5)',
color: theme.sealColor,
borderColor: theme.sealColor,
fill: theme.sealFill,
})
}
watch(() => props.cfg.posterTheme, () => nextTick(paintXiSeal))
function later(fn, ms) {
const id = setTimeout(fn, ms)
timers.push(id)
@@ -255,9 +259,9 @@ onBeforeUnmount(() => {
border-radius: 10px;
padding: 8px;
background:
linear-gradient(135deg, rgba(212, 175, 55, 0.18), transparent 40%),
linear-gradient(180deg, rgba(155, 0, 0, 0.4), rgba(90, 0, 0, 0.25));
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35), inset 0 0 0 1px rgba(240, 215, 140, 0.2);
linear-gradient(135deg, rgba(var(--hb-gold-rgb), 0.18), transparent 40%),
linear-gradient(180deg, rgba(var(--hb-panel-hi-rgb), 0.4), rgba(var(--hb-panel-lo-rgb), 0.25));
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35), inset 0 0 0 1px rgba(var(--hb-gold-soft-rgb), 0.2);
}
.hb-frame-ornament {
position: absolute;
@@ -275,13 +279,13 @@ onBeforeUnmount(() => {
.hb-frame-ornament-br { bottom: 2px; right: 2px; transform: scale(-1); }
.hb-inner {
position: relative;
border: 1.5px solid rgba(212, 175, 55, 0.85);
border: 1.5px solid rgba(var(--hb-gold-rgb), 0.85);
border-radius: 6px;
padding: 14px 14px 20px;
background:
linear-gradient(180deg, rgba(130, 0, 0, 0.58), rgba(110, 0, 0, 0.38)),
linear-gradient(180deg, rgba(var(--hb-panel-hi-rgb), 0.58), rgba(var(--hb-panel-rgb), 0.38)),
radial-gradient(circle at 90% 8%, rgba(255, 255, 255, 0.08), transparent 28%),
radial-gradient(circle at 10% 92%, rgba(196, 30, 58, 0.2), transparent 30%);
radial-gradient(circle at 10% 92%, rgba(var(--hb-accent-rgb), 0.2), transparent 30%);
min-height: min(780px, calc(100dvh - 40px));
display: flex;
flex-direction: column;
@@ -292,7 +296,7 @@ onBeforeUnmount(() => {
content: '';
position: absolute;
inset: 5px;
border: 1px solid rgba(240, 215, 140, 0.35);
border: 1px solid rgba(var(--hb-gold-soft-rgb), 0.35);
border-radius: 4px;
pointer-events: none;
z-index: 1;
@@ -304,28 +308,28 @@ onBeforeUnmount(() => {
inset: 10px;
pointer-events: none;
z-index: 1;
border: 1px dashed rgba(212, 175, 55, 0.18);
border: 1px dashed rgba(var(--hb-gold-rgb), 0.18);
border-radius: 2px;
}
.hb-paper-decor { position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
.hb-paper-watermark {
position: absolute; left: 50%; top: 52%; transform: translate(-50%, -50%);
font-size: min(220px, 58vw); font-weight: 700; color: rgba(196, 30, 58, 0.1); line-height: 1;
font-size: min(220px, 58vw); font-weight: 700; color: rgba(var(--hb-accent-rgb), 0.1); line-height: 1;
}
.hb-paper-knot { position: absolute; width: 34px; height: 44px; color: rgba(212, 175, 55, 0.55); }
.hb-paper-knot { position: absolute; width: 34px; height: 44px; color: rgba(var(--hb-gold-rgb), 0.55); }
.hb-paper-knot-tl { top: 10px; left: 10px; }
.hb-paper-knot-tr { top: 10px; right: 10px; transform: scaleX(-1); }
.hb-paper-knot-bl { bottom: 12px; left: 10px; transform: scaleY(-1); }
.hb-paper-knot-br { bottom: 12px; right: 10px; transform: scale(-1); }
.hb-paper-cloud { position: absolute; width: 96px; height: 42px; color: rgba(212, 175, 55, 0.32); }
.hb-paper-cloud { position: absolute; width: 96px; height: 42px; color: rgba(var(--hb-gold-rgb), 0.32); }
.hb-paper-cloud-t { top: 8px; left: 50%; transform: translateX(-50%); }
.hb-paper-cloud-b { bottom: 8px; left: 50%; transform: translateX(-50%) scaleY(-1); color: rgba(196, 30, 58, 0.28); }
.hb-paper-xi { position: absolute; top: 46%; font-size: 22px; font-weight: 700; color: rgba(196, 30, 58, 0.28); }
.hb-paper-cloud-b { bottom: 8px; left: 50%; transform: translateX(-50%) scaleY(-1); color: rgba(var(--hb-accent-rgb), 0.28); }
.hb-paper-xi { position: absolute; top: 46%; font-size: 22px; font-weight: 700; color: rgba(var(--hb-accent-rgb), 0.28); }
.hb-paper-xi-l { left: 8px; transform: rotate(-18deg); }
.hb-paper-xi-r { right: 8px; transform: rotate(18deg); }
.hb-paper-dot {
position: absolute; width: 5px; height: 5px; border-radius: 50%;
background: rgba(212, 175, 55, 0.45); box-shadow: 0 0 0 3px rgba(196, 30, 58, 0.12);
background: rgba(var(--hb-gold-rgb), 0.45); box-shadow: 0 0 0 3px rgba(var(--hb-accent-rgb), 0.12);
}
.hb-paper-dot-1 { top: 22%; left: 14px; }
.hb-paper-dot-2 { top: 22%; right: 14px; }
@@ -333,11 +337,11 @@ onBeforeUnmount(() => {
.hb-paper-dot-4 { bottom: 26%; right: 16px; }
.hb-paper-side {
position: absolute; top: 28%; bottom: 28%; width: 10px;
background: repeating-linear-gradient(180deg, transparent 0 10px, rgba(212, 175, 55, 0.22) 10px 11px, transparent 11px 20px);
background: repeating-linear-gradient(180deg, transparent 0 10px, rgba(var(--hb-gold-rgb), 0.22) 10px 11px, transparent 11px 20px);
opacity: 0.7;
}
.hb-paper-side-l { left: 4px; border-left: 1px solid rgba(212, 175, 55, 0.25); }
.hb-paper-side-r { right: 4px; border-right: 1px solid rgba(212, 175, 55, 0.25); }
.hb-paper-side-l { left: 4px; border-left: 1px solid rgba(var(--hb-gold-rgb), 0.25); }
.hb-paper-side-r { right: 4px; border-right: 1px solid rgba(var(--hb-gold-rgb), 0.25); }
.hb-hero-wrap, .hb-names, .hb-title-wrap, .hb-body, .hb-footer, .hb-cta { position: relative; z-index: 2; }
.hb-hero-wrap {
width: 100%; opacity: 0; transform: translateY(12px);
@@ -351,10 +355,10 @@ onBeforeUnmount(() => {
.hb-hero-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.hb-hero-placeholder {
width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
color: rgba(240, 215, 140, 0.55); letter-spacing: 0.3em; font-size: 13px; background: rgba(0, 0, 0, 0.2);
color: rgba(var(--hb-gold-soft-rgb), 0.55); letter-spacing: 0.3em; font-size: 13px; background: rgba(0, 0, 0, 0.2);
}
.hb-xi {
position: absolute; top: 10%; font-size: 28px; color: #c41e3a; font-weight: 700;
position: absolute; top: 10%; font-size: 28px; color: var(--hb-red-bright); font-weight: 700;
opacity: 0.92; pointer-events: none;
}
.hb-xi-l { left: 8%; }
@@ -374,7 +378,7 @@ onBeforeUnmount(() => {
.hb-name {
flex: 1 1 0;
min-width: 0;
color: #f7efe0 !important;
color: var(--hb-ivory) !important;
font-size: 12px;
letter-spacing: 0.12em;
justify-content: center;
@@ -409,11 +413,11 @@ onBeforeUnmount(() => {
.hb-cursor { color: var(--hb-gold); }
.hb-footer { margin-top: 8px; min-height: 24px; opacity: 0; transition: opacity 0.4s ease; }
.hb-footer.is-show { opacity: 1; }
.hb-footer-text { color: #f7efe0 !important; font-size: 12px; letter-spacing: 0.2em; justify-content: center; }
.hb-footer-text { color: var(--hb-ivory) !important; font-size: 12px; letter-spacing: 0.2em; justify-content: center; }
.hb-cta { margin-top: 16px; opacity: 0; transform: translateY(8px); animation: hb-cta-in 0.5s ease forwards; }
.hb-cta-btn {
appearance: none; border: 1px solid var(--hb-gold);
background: linear-gradient(180deg, rgba(212, 175, 55, 0.22), rgba(212, 175, 55, 0.08));
background: linear-gradient(180deg, rgba(var(--hb-gold-rgb), 0.22), rgba(var(--hb-gold-rgb), 0.08));
color: var(--hb-gold-soft); padding: 8px 22px; border-radius: 999px;
letter-spacing: 0.28em; font-size: 13px; cursor: pointer;
}

View File

@@ -160,7 +160,7 @@
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import BlurText from '@/components/vue-bits/BlurText.vue'
import TextType from '@/components/vue-bits/TextType.vue'
import { heroFocusCss } from '@/composables/posterDefaults'
import { heroFocusCss, posterThemeMeta } from '@/composables/posterDefaults'
import { drawXiSeal } from '@/composables/drawXiSeal'
const props = defineProps({
@@ -203,15 +203,19 @@ const roleRight = computed(() => {
async function paintXiSeal() {
try { await document.fonts?.ready } catch { /* ignore */ }
// canvas 不解析 CSS 变量,从海报主题元数据取印章色
const theme = posterThemeMeta(props.cfg.posterTheme)
drawXiSeal(xiSealRef.value, {
size: 28,
fontSize: 13,
color: '#d4af37',
borderColor: '#d4af37',
color: theme.sealColor,
borderColor: theme.sealColor,
fill: 'rgba(0, 0, 0, 0.18)',
})
}
watch(() => props.cfg.posterTheme, () => nextTick(paintXiSeal))
function later(fn, ms) {
timers.push(setTimeout(fn, ms))
}
@@ -280,29 +284,29 @@ onBeforeUnmount(() => {
.sp-shell-outer {
padding: 1px;
border-radius: 14px;
background: linear-gradient(145deg, #f0d78c, #d4af37 45%, #a8892e);
background: linear-gradient(145deg, var(--hb-gold-soft), var(--hb-gold) 45%, var(--hb-gold-deep));
box-shadow: 0 16px 42px rgba(0, 0, 0, 0.4);
}
.sp-shell-gap {
padding: 4px;
border-radius: 13px;
background: #7a0000;
border: 1px solid rgba(240, 215, 140, 0.28);
background: var(--hb-red-deep);
border: 1px solid rgba(var(--hb-gold-soft-rgb), 0.28);
}
.sp-card {
position: relative;
display: flex;
flex-direction: column;
border: 2px solid #d4af37;
border: 2px solid var(--hb-gold);
border-radius: 10px;
overflow: hidden;
min-height: min(720px, calc(100dvh - 48px));
background: #8b0000;
background: var(--hb-panel);
}
.sp-card-rim {
position: absolute;
inset: 5px;
border: 1px solid rgba(240, 215, 140, 0.45);
border: 1px solid rgba(var(--hb-gold-soft-rgb), 0.45);
border-radius: 7px;
pointer-events: none;
z-index: 6;
@@ -311,7 +315,7 @@ onBeforeUnmount(() => {
position: absolute;
width: 28px;
height: 28px;
color: rgba(240, 215, 140, 0.95);
color: rgba(var(--hb-gold-soft-rgb), 0.95);
z-index: 7;
pointer-events: none;
}
@@ -353,7 +357,7 @@ onBeforeUnmount(() => {
.sp-left {
position: relative;
min-height: 260px;
background: #c23a2b;
background: var(--hb-photo-bg);
display: flex;
flex-direction: column;
}
@@ -369,11 +373,11 @@ onBeforeUnmount(() => {
.sp-photo img { width: 100%; height: 100%; object-fit: cover; display: block; position: absolute; inset: 0; }
.sp-photo-ph {
position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
color: rgba(255, 240, 220, 0.55); letter-spacing: 0.3em;
color: rgba(var(--hb-ivory-rgb), 0.55); letter-spacing: 0.3em;
}
.sp-photo-veil {
position: absolute; inset: 0;
background: linear-gradient(180deg, transparent 40%, rgba(140, 20, 20, 0.55) 100%);
background: linear-gradient(180deg, transparent 40%, rgba(var(--hb-panel-hi-rgb), 0.55) 100%);
pointer-events: none;
}
@@ -405,7 +409,7 @@ onBeforeUnmount(() => {
}
.sp-slogan {
color: #f3e6c8 !important;
color: var(--hb-cream) !important;
font-size: clamp(36px, 8vw, 52px);
letter-spacing: 0.2em;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
@@ -413,7 +417,7 @@ onBeforeUnmount(() => {
}
.sp-subs { display: flex; flex-direction: column; gap: 18px; margin-top: 8px; }
.sp-sub {
color: rgba(243, 230, 200, 0.88) !important;
color: rgba(var(--hb-cream-rgb), 0.88) !important;
font-size: 13px;
letter-spacing: 0.35em;
}
@@ -423,7 +427,7 @@ onBeforeUnmount(() => {
padding: 22px 20px 18px;
background:
radial-gradient(circle at 80% 10%, rgba(255, 255, 255, 0.06), transparent 30%),
linear-gradient(180deg, #9b0000, #7a0000);
linear-gradient(180deg, var(--hb-red), var(--hb-red-deep));
color: var(--hb-gold-soft);
display: flex;
flex-direction: column;
@@ -434,7 +438,7 @@ onBeforeUnmount(() => {
.sp-right-decor { position: absolute; inset: 0; pointer-events: none; }
.sp-wm {
position: absolute; left: 50%; top: 48%; transform: translate(-50%, -50%);
font-size: 180px; font-weight: 700; color: rgba(196, 30, 58, 0.12); line-height: 1;
font-size: 180px; font-weight: 700; color: rgba(var(--hb-accent-rgb), 0.12); line-height: 1;
}
.sp-xi-top {
@@ -445,7 +449,7 @@ onBeforeUnmount(() => {
.sp-xi-top.is-show { opacity: 1; transform: scale(1); }
.sp-greeting {
color: #f7efe0 !important; font-size: 14px; letter-spacing: 0.2em;
color: var(--hb-ivory) !important; font-size: 14px; letter-spacing: 0.2em;
margin-bottom: 12px; justify-content: center; position: relative; z-index: 1;
}
@@ -481,7 +485,7 @@ onBeforeUnmount(() => {
}
.sp-person-l { padding-right: 22px; }
.sp-person-r { padding-left: 22px; }
.sp-role { font-size: 11px; color: rgba(247, 239, 224, 0.7); letter-spacing: 0.3em; }
.sp-role { font-size: 11px; color: rgba(var(--hb-ivory-rgb), 0.7); letter-spacing: 0.3em; }
.sp-name { color: var(--hb-gold-soft) !important; font-size: 18px; letter-spacing: 0.2em; justify-content: center; }
/* canvas 位图印章:不用 CSS transform,避免 html2canvas 错位 */
.sp-xi-mid {
@@ -503,11 +507,11 @@ onBeforeUnmount(() => {
}
.sp-meta.is-show { opacity: 1; }
.sp-meta-title {
font-size: 12px; letter-spacing: 0.35em; color: rgba(247, 239, 224, 0.75); margin-bottom: 4px;
font-size: 12px; letter-spacing: 0.35em; color: rgba(var(--hb-ivory-rgb), 0.75); margin-bottom: 4px;
}
.sp-meta-title.mt { margin-top: 12px; }
.sp-meta-val { font-size: 14px; letter-spacing: 0.12em; color: var(--hb-gold-soft); }
.sp-meta-sub { font-size: 12px; margin-top: 2px; color: rgba(240, 215, 140, 0.75); letter-spacing: 0.15em; }
.sp-meta-sub { font-size: 12px; margin-top: 2px; color: rgba(var(--hb-gold-soft-rgb), 0.75); letter-spacing: 0.15em; }
.sp-love {
position: relative; z-index: 1;
@@ -516,13 +520,13 @@ onBeforeUnmount(() => {
display: flex; align-items: center; gap: 6px;
}
.sp-love.is-show { opacity: 1; }
.sp-love-text { font-size: 22px; color: #f3e6c8; }
.sp-heart { color: #e85d5d; font-size: 14px; }
.sp-love-text { font-size: 22px; color: var(--hb-cream); }
.sp-heart { color: var(--hb-red-bright); font-size: 14px; }
.sp-cta { position: relative; z-index: 1; margin-top: 12px; }
.sp-cta-btn {
appearance: none; border: 1px solid var(--hb-gold);
background: linear-gradient(180deg, rgba(212, 175, 55, 0.22), rgba(212, 175, 55, 0.08));
background: linear-gradient(180deg, rgba(var(--hb-gold-rgb), 0.22), rgba(var(--hb-gold-rgb), 0.08));
color: var(--hb-gold-soft); padding: 7px 18px; border-radius: 999px;
letter-spacing: 0.28em; font-size: 12px; cursor: pointer;
}

File diff suppressed because it is too large Load Diff