海报暂未开放
@@ -103,9 +114,10 @@ import { computed, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue' import { useRoute, useRouter } from 'vue-router' import html2canvas from 'html2canvas' -import { getPosterConfig } from '@/api/wedding' +import { getConfig, getPosterConfig } from '@/api/wedding' import { normalizePosterConfig, posterSideMeta, resolvePosterSide } from '@/composables/posterDefaults' import { renderFestiveQr } from '@/composables/renderFestiveQr' +import { useAudio } from '@/composables/useAudio' import ClassicPoster from '@/views/hb/templates/ClassicPoster.vue' import SplitPoster from '@/views/hb/templates/SplitPoster.vue' import FestiveQrCard from '@/components/FestiveQrCard.vue' @@ -120,6 +132,8 @@ const contentVisible = ref(false) const savingImg = ref(false) const isPc = ref(false) const pageUrl = computed(() => (typeof window !== 'undefined' ? window.location.href : '')) +const audio = useAudio() +const hasMusic = computed(() => !!String(cfg.musicUrl || '').trim()) let timers = [] function later(fn, ms) { @@ -242,6 +256,23 @@ async function savePosterImage() { } } +async function setupPosterMusic() { + const url = String(cfg.musicUrl || '').trim() + if (!url) return + let track = { url, name: '背景音乐' } + try { + const res = await getConfig() + const list = Array.isArray(res.data?.musicList) ? res.data.musicList : [] + const hit = list.find((t) => t && t.url === url) + if (hit) track = { url: hit.url, name: hit.name || '背景音乐', coverUrl: hit.coverUrl || '' } + } catch { + /* 仅用 poster 上的 url 亦可播放 */ + } + audio.setTracks([track], url) + const ok = await audio.attemptAutoplay() + if (!ok) audio.armAutoplay() +} + async function runSequence() { const loadMs = cfg.loadingEffect === 'fade' ? 700 : 1100 await new Promise((r) => later(r, loadMs)) @@ -276,6 +307,7 @@ onMounted(async () => { Object.assign(cfg, normalizePosterConfig(null, side)) } applyPageMeta() + setupPosterMusic() runSequence() }) @@ -337,6 +369,36 @@ onUnmounted(() => { pointer-events: none; } .hb-pc-tools > * { pointer-events: auto; } +.hb-music-fab { + position: fixed; + left: 16px; + bottom: calc(16px + env(safe-area-inset-bottom, 0px)); + z-index: 30; + 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; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 16px; + cursor: pointer; + box-shadow: 0 8px 22px rgba(0, 0, 0, 0.35); + backdrop-filter: blur(6px); +} +.hb-music-fab.is-playing { + animation: hb-music-spin 8s linear infinite; +} +.hb-music-fab.is-playing .fa-pause { + animation: none; +} +@keyframes hb-music-spin { + from { filter: brightness(1); } + 50% { filter: brightness(1.12); } + to { filter: brightness(1); } +} .hb-save-btn { appearance: none; border: 1.5px solid #d4af37;