fix: 修复首页空白、小红书/游记封面图、定制页二维码

1. 首页大面积空白:useScrollReveal 添加 MutationObserver,
   确保异步子组件的 .reveal 元素也能触发入场动画

2. 小红书/游记封面图不显示:
   - 小红书 CDN 签名链接过期+防盗链,全部下载到本地
   - 新增 download-covers.mjs 脚本批量下载封面
   - 新增 useImageProxy composable 转换外部 URL
   - 页面 img 标签添加 referrerpolicy="no-referrer"
   - 管理端保存时自动下载外部封面图到本地
   - 种子数据更新为本地路径

3. 定制页微信号文字替换为二维码图片

4. prerender 路由添加 /xiaohongshu 和 /youji

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
这个提交包含在:
刘涛
2026-03-25 15:37:58 +08:00
共同撰写人 Claude Opus 4.6
父节点 b82712deed
当前提交 8832762e51
共修改 16 个文件,包含 599 行新增和 106 行删除
+11
查看文件
@@ -126,6 +126,17 @@ function addNote() {
async function save() {
saving.value = true
try {
// 自动下载外部封面图到本地
for (const note of data.value.notes) {
if (note.cover && note.cover.startsWith('http')) {
try {
const res = await adminFetch('/api/admin/download-image', { method: 'POST', body: { url: note.cover } })
note.cover = res.localUrl
} catch {
// 下载失败保留原 URL,不阻断保存
}
}
}
await adminFetch('/api/admin/site-content', { method: 'PUT', body: { key: 'xiaohongshu-wall', data: data.value } })
message.success('已保存')
} catch {
+11
查看文件
@@ -113,6 +113,17 @@ function addNote() {
async function save() {
saving.value = true
try {
// 自动下载外部封面图到本地
for (const note of data.value.notes) {
if (note.cover && note.cover.startsWith('http')) {
try {
const res = await adminFetch('/api/admin/download-image', { method: 'POST', body: { url: note.cover } })
note.cover = res.localUrl
} catch {
// 下载失败保留原 URL,不阻断保存
}
}
}
await adminFetch('/api/admin/site-content', { method: 'PUT', body: { key: 'youji', data: data.value } })
message.success('已保存')
} catch {
+16 -12
查看文件
@@ -46,7 +46,7 @@
</p>
<div class="success-wechat">
<span class="wechat-label">也可主动添加定制师微信</span>
<span class="wechat-id">{{ customize.contact.wechat }}</span>
<img src="/images/contact/qrcode-wechat.jpg" alt="定制师微信二维码" class="wechat-qrcode wechat-qrcode--success" />
</div>
<p class="success-tip">{{ customize.contact.tip }}</p>
<button class="btn-reset" @click="resetForm">重新填写</button>
@@ -249,8 +249,8 @@
<div class="aside-card aside-wechat">
<h3 class="aside-title">不想填表?</h3>
<p class="aside-desc">直接添加定制师微信,随时咨询:</p>
<div class="wechat-id-display">{{ customize.contact.wechat }}</div>
<p class="aside-desc">扫码添加定制师微信,随时咨询:</p>
<img src="/images/contact/qrcode-wechat.jpg" alt="定制师微信二维码" class="wechat-qrcode" />
<p class="wechat-tip">{{ customize.contact.tip }}</p>
</div>
</aside>
@@ -974,16 +974,20 @@ select,
border-color: transparent;
}
.wechat-id-display {
font-size: @font-size-lg;
font-weight: 700;
color: @color-primary;
padding: @space-sm @space-md;
background: @color-bg-white;
.wechat-qrcode {
display: block;
width: 180px;
height: 180px;
object-fit: contain;
margin: @space-sm auto;
border-radius: @border-radius-md;
text-align: center;
margin-bottom: @space-sm;
letter-spacing: 1px;
border: 1px solid @color-border;
background: @color-bg-white;
&--success {
width: 160px;
height: 160px;
}
}
.wechat-tip {
+3 -2
查看文件
@@ -14,7 +14,7 @@
<main class="detail-main">
<!-- 封面图 -->
<div class="detail-cover" :class="{ 'detail-cover--wide': note.ratio === 'horizontal' }">
<img :src="note.cover" :alt="note.title" />
<img :src="proxyImage(note.cover)" :alt="note.title" referrerpolicy="no-referrer" />
</div>
<!-- 笔记正文预览 -->
@@ -126,7 +126,7 @@
:class="{ 'sidebar-note--active': n.id === note.id }"
>
<NuxtLink :to="`/xiaohongshu/${n.id}`" class="sidebar-note__link">
<img :src="n.cover" :alt="n.title" class="sidebar-note__thumb" />
<img :src="proxyImage(n.cover)" :alt="n.title" class="sidebar-note__thumb" referrerpolicy="no-referrer" />
<div class="sidebar-note__info">
<span class="sidebar-note__title">{{ n.title }}</span>
<span class="sidebar-note__author">@{{ n.author }}</span>
@@ -163,6 +163,7 @@
<script setup>
import { useBreadcrumbSchema } from '~/composables/useJsonLd'
import { proxyImage } from '~/composables/useImageProxy'
const { data: wallData } = await usePublicApi('xiaohongshu-wall')
+3 -1
查看文件
@@ -102,9 +102,10 @@
>
<div class="note-card__cover" :class="{ 'note-card__cover--wide': note.ratio === 'horizontal' }">
<img
:src="note.cover"
:src="proxyImage(note.cover)"
:alt="note.title"
loading="lazy"
referrerpolicy="no-referrer"
@error="handleImageError"
/>
<div class="note-card__overlay">
@@ -160,6 +161,7 @@
<script setup>
import { useBreadcrumbSchema } from '~/composables/useJsonLd'
import { proxyImage } from '~/composables/useImageProxy'
const { data: wallData } = await usePublicApi('xiaohongshu-wall')
+3 -2
查看文件
@@ -13,7 +13,7 @@
<main class="detail-main">
<!-- 封面 -->
<div class="detail-cover" :class="{ 'detail-cover--wide': note.ratio === 'horizontal' }">
<img :src="note.cover" :alt="note.title" />
<img :src="proxyImage(note.cover)" :alt="note.title" referrerpolicy="no-referrer" />
</div>
<!-- 正文 -->
@@ -97,7 +97,7 @@
:class="{ 'sidebar-note--active': n.id === note.id }"
>
<NuxtLink :to="`/youji/${n.id}`" class="sidebar-note__link">
<img :src="n.cover" :alt="n.title" class="sidebar-note__thumb" />
<img :src="proxyImage(n.cover)" :alt="n.title" class="sidebar-note__thumb" referrerpolicy="no-referrer" />
<div class="sidebar-note__info">
<span class="sidebar-note__title">{{ n.title }}</span>
<span class="sidebar-note__meta">
@@ -143,6 +143,7 @@
<script setup>
import { useBreadcrumbSchema } from '~/composables/useJsonLd'
import { proxyImage } from '~/composables/useImageProxy'
const { data: youjiData } = await usePublicApi('youji')
+2 -1
查看文件
@@ -93,7 +93,7 @@
class="note-card"
>
<div class="note-card__cover" :class="{ 'note-card__cover--wide': note.ratio === 'horizontal' }">
<img :src="note.cover" :alt="note.title" loading="lazy" @error="handleImageError" />
<img :src="proxyImage(note.cover)" :alt="note.title" loading="lazy" referrerpolicy="no-referrer" @error="handleImageError" />
<div class="note-card__overlay">
<span class="note-card__read">查看详情</span>
</div>
@@ -136,6 +136,7 @@
<script setup>
import { useBreadcrumbSchema } from '~/composables/useJsonLd'
import { proxyImage } from '~/composables/useImageProxy'
const { data: youjiData } = await usePublicApi('youji')