fix: 升级了一些视觉
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user