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>
这个提交包含在:
父节点
b82712deed
当前提交
8832762e51
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="image-upload">
|
||||
<div class="upload-preview" v-if="modelValue">
|
||||
<img :src="modelValue" alt="预览" @error="imgError = true" />
|
||||
<img :src="modelValue" alt="预览" referrerpolicy="no-referrer" @error="imgError = true" />
|
||||
<div class="preview-actions">
|
||||
<button type="button" class="btn-icon" @click="$emit('update:modelValue', '')" title="移除">✕</button>
|
||||
</div>
|
||||
|
||||
38
composables/useImageProxy.js
普通文件
38
composables/useImageProxy.js
普通文件
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 小红书图片 URL 转换
|
||||
*
|
||||
* 小红书 CDN 图片 (sns-webpic-qc.xhscdn.com) 使用签名 URL,
|
||||
* 格式: https://sns-webpic-qc.xhscdn.com/{timestamp}/{hash}/{path}
|
||||
* 这些 URL 会过期且有防盗链,第三方站点无法直接使用。
|
||||
*
|
||||
* 解决方案:转换为 ci.xiaohongshu.com 的永久 URL
|
||||
* 格式: https://ci.xiaohongshu.com/{path}
|
||||
*/
|
||||
|
||||
/**
|
||||
* 将小红书签名 URL 转换为永久可访问的 URL
|
||||
*/
|
||||
export function proxyImage(url) {
|
||||
if (!url) return ''
|
||||
try {
|
||||
const parsed = new URL(url)
|
||||
// sns-webpic-qc.xhscdn.com/{timestamp}/{hash}/{actual_path}
|
||||
// 提取 actual_path 部分,转到 ci.xiaohongshu.com
|
||||
if (parsed.hostname === 'sns-webpic-qc.xhscdn.com') {
|
||||
const parts = parsed.pathname.split('/')
|
||||
// 去掉前导空字符串、timestamp、hash,剩下的就是图片路径
|
||||
// pathname: /202603231146/hash/notes_pre_post/image_id
|
||||
if (parts.length >= 4) {
|
||||
const imagePath = parts.slice(3).join('/')
|
||||
return `https://ci.xiaohongshu.com/${imagePath}`
|
||||
}
|
||||
}
|
||||
// 已经是 ci.xiaohongshu.com 的永久链接,直接用
|
||||
if (parsed.hostname === 'ci.xiaohongshu.com') {
|
||||
return url
|
||||
}
|
||||
} catch {
|
||||
// 不是合法 URL(可能是本地路径),直接返回
|
||||
}
|
||||
return url
|
||||
}
|
||||
@ -2,28 +2,44 @@
|
||||
* 滚动入场动画 composable
|
||||
* 元素进入视口时触发淡入上滑动画
|
||||
* 自动尊重 prefers-reduced-motion
|
||||
* 使用 MutationObserver 确保异步渲染的子组件也能被观察到
|
||||
*/
|
||||
export default function useScrollReveal() {
|
||||
onMounted(() => {
|
||||
// 尊重用户减少动效偏好
|
||||
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
const io = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('is-visible')
|
||||
observer.unobserve(entry.target)
|
||||
io.unobserve(entry.target)
|
||||
}
|
||||
})
|
||||
},
|
||||
{ threshold: 0.1, rootMargin: '0px 0px -40px 0px' }
|
||||
)
|
||||
|
||||
document.querySelectorAll('.reveal').forEach((el) => {
|
||||
observer.observe(el)
|
||||
})
|
||||
|
||||
onUnmounted(() => observer.disconnect())
|
||||
// 观察当前已有的 .reveal 元素
|
||||
const observed = new WeakSet()
|
||||
function observeAll() {
|
||||
document.querySelectorAll('.reveal:not(.is-visible)').forEach((el) => {
|
||||
if (!observed.has(el)) {
|
||||
observed.add(el)
|
||||
io.observe(el)
|
||||
}
|
||||
})
|
||||
}
|
||||
observeAll()
|
||||
|
||||
// 监听 DOM 变化,捕获异步渲染的子组件中的 .reveal 元素
|
||||
const mo = new MutationObserver(observeAll)
|
||||
mo.observe(document.body, { childList: true, subtree: true })
|
||||
|
||||
onUnmounted(() => {
|
||||
io.disconnect()
|
||||
mo.disconnect()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -11,341 +11,434 @@
|
||||
{
|
||||
"id": "68e4cd12000000000401538b",
|
||||
"title": "呼籁给了美好的呼伦贝尔南线之旅!",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/5fc9cc68d2c4db833a5ac5dfc8ed3f8e/notes_pre_post/1040g3k031k0gh6jd36004806b24ch4bbbhq2390!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-68e4cd12000000000401538b.webp",
|
||||
"author": "我是一个荷包蛋",
|
||||
"likes": 4,
|
||||
"url": "https://www.xiaohongshu.com/explore/68e4cd12000000000401538b",
|
||||
"tags": ["呼伦贝尔", "南线"],
|
||||
"tags": [
|
||||
"呼伦贝尔",
|
||||
"南线"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "选了呼籁的南线行程,师傅特别热情,一路讲解各种草原文化。海拉尔站接我们就感受到了内蒙人的热情,孩子特别开心,还体验了蒙古包住宿,晚上看星星太美了。整个行程安排得很紧凑但不赶,景点都很值得去,强烈推荐呼籁!"
|
||||
},
|
||||
{
|
||||
"id": "697212bd000000000e03da86",
|
||||
"title": "我在呼伦贝尔拍到了极光",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/caf0c832b96a87d8e21d750b8cba4cbf/notes_pre_post/1040g3k031l21j3k034204a311v5kgdpbe3rn88o!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-697212bd000000000e03da86.webp",
|
||||
"author": "Mario",
|
||||
"likes": 228,
|
||||
"url": "https://www.xiaohongshu.com/explore/697212bd000000000e03da86",
|
||||
"tags": ["极光", "呼伦贝尔"],
|
||||
"tags": [
|
||||
"极光",
|
||||
"呼伦贝尔"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "没想到在呼伦贝尔居然拍到了极光!冬天来的呼籁冰雪线路,本来只是想看雪景,结果师傅说今晚天气好可能有极光,带我们到了一个没有光污染的地方等候。真的等到了!人生第一次看极光,在祖国的土地上,太感动了。呼籁的师傅太专业了,不只是开车,是真的懂这片土地。"
|
||||
},
|
||||
{
|
||||
"id": "68443760000000002100e004",
|
||||
"title": "幼儿园毕业游🌸呼伦贝尔大草原🌲",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/23f201eadfa72a9fa142bc44a8cb918c/notes_pre_post/1040g3k831iugn1ccha6g4bsfm2j5qb8aelkl7h0!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-68443760000000002100e004.webp",
|
||||
"author": "wendy51930",
|
||||
"likes": 144,
|
||||
"url": "https://www.xiaohongshu.com/explore/68443760000000002100e004",
|
||||
"tags": ["亲子游", "毕业游"],
|
||||
"tags": [
|
||||
"亲子游",
|
||||
"毕业游"
|
||||
],
|
||||
"ratio": "horizontal",
|
||||
"summary": "幼儿园毕业送给孩子一个大草原之旅!选择了呼籁的亲子线路,一家一车很自由。孩子骑了小马、挤了牛奶、学了射箭,每天都不想回酒店。领队还给孩子们准备了草原寻宝游戏,太贴心了。住的蒙古包也超级干净,推荐给有娃家庭!"
|
||||
},
|
||||
{
|
||||
"id": "686289e00000000013012fb0",
|
||||
"title": "一家三口亲子游|呼伦贝尔大草原第二天",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/23f201eadfa72a9fa142bc44a8cb918c/notes_pre_post/1040g3k831iugn1ccha6g4bsfm2j5qb8aelkl7h0!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-686289e00000000013012fb0.webp",
|
||||
"author": "玄猫猫",
|
||||
"likes": 42,
|
||||
"url": "https://www.xiaohongshu.com/explore/686289e00000000013012fb0",
|
||||
"tags": ["亲子游", "草原"],
|
||||
"tags": [
|
||||
"亲子游",
|
||||
"草原"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "第二天的行程从额尔古纳出发,穿过白桦林到了莫尔道嘎。一路上风景美炸了,师傅会在好看的地方停下来让我们拍照。孩子在草地上撒欢跑,追蝴蝶、摘野花,城市里根本没有这样的体验。呼籁的安排真的很用心,午餐还带我们去了当地牧民家吃手把肉。"
|
||||
},
|
||||
{
|
||||
"id": "687284f00000000010026e19",
|
||||
"title": "呼籁的旅拍不错呦",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/0846bea5f9b467ddbdeac96e88f08b9a/1040g00831jog5ql9j4dg43klvmjdgvoeqshnj3o!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-687284f00000000010026e19.webp",
|
||||
"author": "Vivianus爱Matcha是抹茶🔥",
|
||||
"likes": 5,
|
||||
"url": "https://www.xiaohongshu.com/explore/687284f00000000010026e19",
|
||||
"tags": ["旅拍", "呼伦贝尔"],
|
||||
"tags": [
|
||||
"旅拍",
|
||||
"呼伦贝尔"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "呼籁安排的旅拍摄影师太会拍了!选了几个小众取景点,避开了人群,拍出来的照片像大片一样。草原上穿蒙古袍的照片直接拿来当头像了。而且不赶时间,慢慢拍,很舒服。价格也比外面单独约摄影师便宜,推荐!"
|
||||
},
|
||||
{
|
||||
"id": "686b2dd800000000150238f8",
|
||||
"title": "呼伦贝尔~完美收官啦",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/3b897562f2b122626c1025cc4c36e39f/notes_pre_post/1040g3k831l0i6urf30e04149ubdcdp575b4754g!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-686b2dd800000000150238f8.webp",
|
||||
"author": "三人四季行",
|
||||
"likes": 12,
|
||||
"url": "https://www.xiaohongshu.com/explore/686b2dd800000000150238f8",
|
||||
"tags": ["收官", "亲子"],
|
||||
"tags": [
|
||||
"收官",
|
||||
"亲子"
|
||||
],
|
||||
"ratio": "horizontal",
|
||||
"summary": "五天四晚的呼伦贝尔之旅完美收官!从海拉尔出发,经过莫日格勒河、额尔古纳湿地、黑山头、满洲里,最后回到海拉尔。全程呼籁安排的师傅服务超好,车子很干净,每天还给孩子准备零食和水。这是我们一家三口最开心的一次旅行!"
|
||||
},
|
||||
{
|
||||
"id": "68987620000000002302987f",
|
||||
"title": "开启呼伦贝尔草原之旅#呼籁旅行#",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/3b897562f2b122626c1025cc4c36e39f/notes_pre_post/1040g3k831l0i6urf30e04149ubdcdp575b4754g!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-68987620000000002302987f.webp",
|
||||
"author": "Mo都时髦Mo~",
|
||||
"likes": 8,
|
||||
"url": "https://www.xiaohongshu.com/explore/68987620000000002302987f",
|
||||
"tags": ["草原", "旅行"],
|
||||
"tags": [
|
||||
"草原",
|
||||
"旅行"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "期待已久的草原之旅终于开启!第一天从海拉尔出发,看到莫日格勒河的那一刻,被震撼到了。天下第一曲水名不虚传!呼籁的定制师提前帮我们规划好了每天的行程,还根据天气调整了顺序,非常灵活。期待接下来几天的精彩!"
|
||||
},
|
||||
{
|
||||
"id": "68a9a205000000001d00b03f",
|
||||
"title": "6月底的呼伦贝尔包车未被黑",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/caf0c832b96a87d8e21d750b8cba4cbf/notes_pre_post/1040g3k031l21j3k034204a311v5kgdpbe3rn88o!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-68a9a205000000001d00b03f.webp",
|
||||
"author": "婷",
|
||||
"likes": 22,
|
||||
"url": "https://www.xiaohongshu.com/explore/68a9a205000000001d00b03f",
|
||||
"tags": ["包车", "避坑"],
|
||||
"tags": [
|
||||
"包车",
|
||||
"避坑"
|
||||
],
|
||||
"ratio": "horizontal",
|
||||
"summary": "做了很多功课才选定的呼籁,事实证明选对了!6月底旺季出行,价格透明没有隐形消费,师傅全程没有带去任何购物点。吃住都是提前确认好的,价格合理品质也好。最重要的是师傅人特别好,不会催赶行程,随时可以停车拍照。呼伦贝尔包车选呼籁不会踩坑!"
|
||||
},
|
||||
{
|
||||
"id": "68669f3f000000002201ee0f",
|
||||
"title": "呼伦贝尔亲子游",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/5fc9cc68d2c4db833a5ac5dfc8ed3f8e/notes_pre_post/1040g3k031k0gh6jd36004806b24ch4bbbhq2390!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-68669f3f000000002201ee0f.webp",
|
||||
"author": "暴躁妈带俩娃",
|
||||
"likes": 28,
|
||||
"url": "https://www.xiaohongshu.com/explore/68669f3f000000002201ee0f",
|
||||
"tags": ["亲子游", "呼伦贝尔"],
|
||||
"tags": [
|
||||
"亲子游",
|
||||
"呼伦贝尔"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "带俩娃出门最怕的就是行程太赶,但呼籁安排得很合理!每天上午出发,中午有充足的午休时间,下午玩到五六点就回酒店了。两个孩子全程没哭闹过,说明行程节奏把控得好。师傅还特别有耐心,会等孩子们慢慢玩够了再出发。"
|
||||
},
|
||||
{
|
||||
"id": "688644e600000000130134b4",
|
||||
"title": "呼伦贝尔我到底该选谁🤨",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/23f201eadfa72a9fa142bc44a8cb918c/notes_pre_post/1040g3k831iugn1ccha6g4bsfm2j5qb8aelkl7h0!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-688644e600000000130134b4.webp",
|
||||
"author": "扣子挺好的",
|
||||
"likes": 10,
|
||||
"url": "https://www.xiaohongshu.com/explore/688644e600000000130134b4",
|
||||
"tags": ["攻略", "选择"],
|
||||
"tags": [
|
||||
"攻略",
|
||||
"选择"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "对比了好几家呼伦贝尔的旅行社,最后选了呼籁。原因很简单:一是客服回复很专业,不是模板回复而是真的在了解我的需求;二是价格透明,把每一项都列清楚了;三是看了很多客户的真实反馈都很好。事实证明没选错,服务确实超出预期!"
|
||||
},
|
||||
{
|
||||
"id": "686beceb000000001703267f",
|
||||
"title": "呼伦贝尔游记D1",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/0846bea5f9b467ddbdeac96e88f08b9a/1040g00831jog5ql9j4dg43klvmjdgvoeqshnj3o!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-686beceb000000001703267f.webp",
|
||||
"author": "桐桐",
|
||||
"likes": 5,
|
||||
"url": "https://www.xiaohongshu.com/explore/686beceb000000001703267f",
|
||||
"tags": ["游记", "Day1"],
|
||||
"tags": [
|
||||
"游记",
|
||||
"Day1"
|
||||
],
|
||||
"ratio": "horizontal",
|
||||
"summary": "呼伦贝尔之旅第一天!海拉尔机场接上后直奔莫日格勒河。一路上师傅给我们科普了很多当地的风土人情,原来草原上的路也有这么多讲究。莫日格勒河的落日太美了,师傅特意等到日落才带我们走,这种服务态度真的绝了。"
|
||||
},
|
||||
{
|
||||
"id": "69bfc4d0000000002301c126",
|
||||
"title": "📸26暑假呼伦贝尔亲子游 | 超强攻略‼️",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/5fc9cc68d2c4db833a5ac5dfc8ed3f8e/notes_pre_post/1040g3k031k0gh6jd36004806b24ch4bbbhq2390!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-69bfc4d0000000002301c126.webp",
|
||||
"author": "Mr.小雷-呼籁旅行",
|
||||
"likes": 8,
|
||||
"url": "https://www.xiaohongshu.com/explore/69bfc4d0000000002301c126",
|
||||
"tags": ["暑假", "攻略"],
|
||||
"tags": [
|
||||
"暑假",
|
||||
"攻略"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "2026年暑假呼伦贝尔亲子游超全攻略来了!包含最佳出行时间、行程推荐、必玩体验、穿搭建议和避坑指南。7-8月是草原最美的季节,建议提前1-2个月预订。亲子游推荐5-6天的行程,不赶路不疲惫,孩子和大人都能玩得尽兴。"
|
||||
},
|
||||
{
|
||||
"id": "68a2f57d000000001c03e65f",
|
||||
"title": "呼伦贝尔亲子游",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/3b897562f2b122626c1025cc4c36e39f/notes_pre_post/1040g3k831l0i6urf30e04149ubdcdp575b4754g!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-68a2f57d000000001c03e65f.webp",
|
||||
"author": "野象小姐YY",
|
||||
"likes": 3,
|
||||
"url": "https://www.xiaohongshu.com/explore/68a2f57d000000001c03e65f",
|
||||
"tags": ["亲子游", "草原"],
|
||||
"tags": [
|
||||
"亲子游",
|
||||
"草原"
|
||||
],
|
||||
"ratio": "horizontal",
|
||||
"summary": "带3岁宝宝去草原,之前还担心孩子太小不适合。结果呼籁安排的行程完全考虑到了小朋友的需求:车上有儿童座椅,行程节奏慢,中午有午休,酒店选的都是有暖气有热水的。宝宝每天都很开心,回来后天天说想去草原找小马。"
|
||||
},
|
||||
{
|
||||
"id": "68c51cc7000000001d007912",
|
||||
"title": "完美的内蒙之旅",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/0846bea5f9b467ddbdeac96e88f08b9a/1040g00831jog5ql9j4dg43klvmjdgvoeqshnj3o!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-68c51cc7000000001d007912.webp",
|
||||
"author": "萌萌你快来",
|
||||
"likes": 1,
|
||||
"url": "https://www.xiaohongshu.com/explore/68c51cc7000000001d007912",
|
||||
"tags": ["内蒙", "旅行"],
|
||||
"tags": [
|
||||
"内蒙",
|
||||
"旅行"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "给这次内蒙之旅打满分!呼籁从前期的行程规划到全程的服务都让人很安心。特别是师傅,不仅开车稳,还会给我们讲每个地方的故事。最感动的是最后一天下雨了,师傅主动调整了行程,带我们去了一个室内的民俗体验馆,一点都没浪费时间。"
|
||||
},
|
||||
{
|
||||
"id": "6885e207000000001d00e82b",
|
||||
"title": "呼伦贝尔第四天",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/0846bea5f9b467ddbdeac96e88f08b9a/1040g00831jog5ql9j4dg43klvmjdgvoeqshnj3o!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-6885e207000000001d00e82b.webp",
|
||||
"author": "shireyna",
|
||||
"likes": 1,
|
||||
"url": "https://www.xiaohongshu.com/explore/6885e207000000001d00e82b",
|
||||
"tags": ["呼伦贝尔", "Day4"],
|
||||
"tags": [
|
||||
"呼伦贝尔",
|
||||
"Day4"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "第四天从满洲里出发去呼伦湖,路上经过了一大片油菜花田,太惊喜了!师傅看我们兴奋就停下来让我们拍了好久。呼伦湖很壮观,虽然不如想象中蓝,但是那种广阔的感觉真的震撼。晚上回到海拉尔,结束了完美的旅程。感谢呼籁!"
|
||||
},
|
||||
{
|
||||
"id": "686b301e0000000017035ff0",
|
||||
"title": "呼伦贝尔之~旅拍",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/23f201eadfa72a9fa142bc44a8cb918c/notes_pre_post/1040g3k831iugn1ccha6g4bsfm2j5qb8aelkl7h0!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-686b301e0000000017035ff0.webp",
|
||||
"author": "三人四季行",
|
||||
"likes": 2,
|
||||
"url": "https://www.xiaohongshu.com/explore/686b301e0000000017035ff0",
|
||||
"tags": ["旅拍", "草原"],
|
||||
"tags": [
|
||||
"旅拍",
|
||||
"草原"
|
||||
],
|
||||
"ratio": "horizontal",
|
||||
"summary": "这次在呼伦贝尔拍了一组全家福,效果超出预期!摄影师是呼籁推荐的,很有经验,知道哪个角度光线最好。拍了草原、白桦林、蒙古包三个场景,每个场景都换了服装。最终成片100多张,张张精修,性价比超高。"
|
||||
},
|
||||
{
|
||||
"id": "68a6ee26000000001d0387b0",
|
||||
"title": "还没离开呼伦贝尔,就开始有点想念了~",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/5fc9cc68d2c4db833a5ac5dfc8ed3f8e/notes_pre_post/1040g3k031k0gh6jd36004806b24ch4bbbhq2390!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-68a6ee26000000001d0387b0.webp",
|
||||
"author": "黎霜霜",
|
||||
"likes": 1,
|
||||
"url": "https://www.xiaohongshu.com/explore/68a6ee26000000001d0387b0",
|
||||
"tags": ["想念", "呼伦贝尔"],
|
||||
"tags": [
|
||||
"想念",
|
||||
"呼伦贝尔"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "坐在回程的飞机上写这篇笔记,真的好舍不得。这几天在呼伦贝尔过的太快乐了,每天醒来都是蓝天白云绿草地,呼吸的都是新鲜空气。呼籁的师傅成了我们的朋友,约好了明年冬天再来一次看雪。呼伦贝尔,我一定会回来的!"
|
||||
},
|
||||
{
|
||||
"id": "68d5a3f1000000001e024b90",
|
||||
"title": "呼伦贝尔亲子游Day3|额尔古纳湿地美翻了",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/3b897562f2b122626c1025cc4c36e39f/notes_pre_post/1040g3k831l0i6urf30e04149ubdcdp575b4754g!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-68d5a3f1000000001e024b90.webp",
|
||||
"author": "小鱼妈妈育儿记",
|
||||
"likes": 35,
|
||||
"url": "https://www.xiaohongshu.com/explore/68d5a3f1000000001e024b90",
|
||||
"tags": ["亲子游", "额尔古纳"],
|
||||
"tags": [
|
||||
"亲子游",
|
||||
"额尔古纳"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "今天的重头戏是额尔古纳湿地!登上山顶俯瞰整个湿地,根河像一条银色的丝带蜿蜒而过,美到窒息。呼籁的师傅带我们走了一条人少的栈道,避开了旅行团的人群。孩子在木栈道上蹦蹦跳跳,还发现了好几种小鸟。这种深度体验是跟团游完全体会不到的。"
|
||||
},
|
||||
{
|
||||
"id": "68f2b7e50000000023018c41",
|
||||
"title": "带父母去呼伦贝尔|老人家也能轻松玩",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/caf0c832b96a87d8e21d750b8cba4cbf/notes_pre_post/1040g3k031l21j3k034204a311v5kgdpbe3rn88o!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-68f2b7e50000000023018c41.webp",
|
||||
"author": "阿橙的旅行日记",
|
||||
"likes": 18,
|
||||
"url": "https://www.xiaohongshu.com/explore/68f2b7e50000000023018c41",
|
||||
"tags": ["孝亲游", "呼伦贝尔"],
|
||||
"tags": [
|
||||
"孝亲游",
|
||||
"呼伦贝尔"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "这次带爸妈来呼伦贝尔,选呼籁就是看中他们的一家一车模式。老人家腿脚不方便,跟团根本不现实。呼籁的师傅全程照顾,上下车都会搀扶,景点走累了就在车上休息。行程也帮我们调整成了轻松版,每天不超过4小时车程。爸妈玩得特别开心,说这是他们最舒服的一次旅行。"
|
||||
},
|
||||
{
|
||||
"id": "690a4c220000000010034d78",
|
||||
"title": "黑山头骑马太爽了!!!",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/23f201eadfa72a9fa142bc44a8cb918c/notes_pre_post/1040g3k831iugn1ccha6g4bsfm2j5qb8aelkl7h0!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-690a4c220000000010034d78.webp",
|
||||
"author": "想吃火锅的猫",
|
||||
"likes": 67,
|
||||
"url": "https://www.xiaohongshu.com/explore/690a4c220000000010034d78",
|
||||
"tags": ["骑马", "黑山头"],
|
||||
"tags": [
|
||||
"骑马",
|
||||
"黑山头"
|
||||
],
|
||||
"ratio": "horizontal",
|
||||
"summary": "到了呼伦贝尔一定要骑马!呼籁推荐的黑山头骑马场特别正规,有专业马倌一对一带领,完全不用担心安全问题。骑着马穿越草原的感觉太爽了,风吹过来,远处是连绵的山丘,真的有种策马奔腾的畅快感。而且不是那种牵着绳子走的旅游马,是真正的草原骑行!"
|
||||
},
|
||||
{
|
||||
"id": "691bc83a0000000017023ef5",
|
||||
"title": "闺蜜草原之旅|我们四个人承包了一片草原",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/0846bea5f9b467ddbdeac96e88f08b9a/1040g00831jog5ql9j4dg43klvmjdgvoeqshnj3o!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-691bc83a0000000017023ef5.webp",
|
||||
"author": "糖果味的风",
|
||||
"likes": 31,
|
||||
"url": "https://www.xiaohongshu.com/explore/691bc83a0000000017023ef5",
|
||||
"tags": ["闺蜜游", "草原"],
|
||||
"tags": [
|
||||
"闺蜜游",
|
||||
"草原"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "四个闺蜜的呼伦贝尔之旅!选了呼籁的私人定制,师傅太了解女孩子了,每到一个好看的地方就主动停车让我们拍照。还帮我们找到了一个特别美的花海,在那里拍了一下午。晚上在蒙古包附近放烟花,太浪漫了。这趟旅行的照片够我发一年朋友圈了。"
|
||||
},
|
||||
{
|
||||
"id": "692d3f5b000000001303ab92",
|
||||
"title": "呼伦贝尔的星空太震撼了",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/5fc9cc68d2c4db833a5ac5dfc8ed3f8e/notes_pre_post/1040g3k031k0gh6jd36004806b24ch4bbbhq2390!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-692d3f5b000000001303ab92.webp",
|
||||
"author": "追光的人",
|
||||
"likes": 45,
|
||||
"url": "https://www.xiaohongshu.com/explore/692d3f5b000000001303ab92",
|
||||
"tags": ["星空", "草原"],
|
||||
"tags": [
|
||||
"星空",
|
||||
"草原"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "在城市里从没见过这样的星空!住在呼籁安排的黑山头营地,晚上走出蒙古包,抬头就是满天繁星,银河清晰可见。师傅还教我们用手机拍星空,调参数、找角度,拍出来的效果比想象的好太多了。这种体验,花多少钱都值。"
|
||||
},
|
||||
{
|
||||
"id": "693e8a7c000000002101c6d3",
|
||||
"title": "满洲里的夜景绝了!!",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/3b897562f2b122626c1025cc4c36e39f/notes_pre_post/1040g3k831l0i6urf30e04149ubdcdp575b4754g!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-693e8a7c000000002101c6d3.webp",
|
||||
"author": "甜甜圈爱旅行",
|
||||
"likes": 19,
|
||||
"url": "https://www.xiaohongshu.com/explore/693e8a7c000000002101c6d3",
|
||||
"tags": ["满洲里", "夜景"],
|
||||
"tags": [
|
||||
"满洲里",
|
||||
"夜景"
|
||||
],
|
||||
"ratio": "horizontal",
|
||||
"summary": "满洲里的夜景真的绝了!到处是俄罗斯风情的建筑,灯光一亮简直像到了异国。呼籁的师傅带我们去了一个当地人才知道的观景台,整个城市尽收眼底。还去了套娃广场,虽然有点商业化,但拍照还是很出片的。晚上在中苏街溜达,吃了正宗的俄式西餐,体验感拉满!"
|
||||
},
|
||||
{
|
||||
"id": "694fb1990000000013027891",
|
||||
"title": "呼伦贝尔7月草原绿得像画一样",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/caf0c832b96a87d8e21d750b8cba4cbf/notes_pre_post/1040g3k031l21j3k034204a311v5kgdpbe3rn88o!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-694fb1990000000013027891.webp",
|
||||
"author": "小太阳爱生活",
|
||||
"likes": 56,
|
||||
"url": "https://www.xiaohongshu.com/explore/694fb1990000000013027891",
|
||||
"tags": ["草原", "风景"],
|
||||
"tags": [
|
||||
"草原",
|
||||
"风景"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "7月的呼伦贝尔草原绿得不真实,像是Windows桌面壁纸!呼籁的师傅说这是一年中草原最美的时候,野花遍地,牛羊成群。我们在一个没有游客的山坡上野餐,师傅还帮我们铺了野餐垫,拿出准备好的奶茶和奶片。这种贴心的小细节太加分了。"
|
||||
},
|
||||
{
|
||||
"id": "6960c2aa0000000021023f56",
|
||||
"title": "冬天的呼伦贝尔也太美了吧",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/23f201eadfa72a9fa142bc44a8cb918c/notes_pre_post/1040g3k831iugn1ccha6g4bsfm2j5qb8aelkl7h0!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-6960c2aa0000000021023f56.webp",
|
||||
"author": "雪地里的小狐狸",
|
||||
"likes": 82,
|
||||
"url": "https://www.xiaohongshu.com/explore/6960c2aa0000000021023f56",
|
||||
"tags": ["冬季", "雪景"],
|
||||
"tags": [
|
||||
"冬季",
|
||||
"雪景"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "第一次冬天来呼伦贝尔,零下30度但值了!呼籁的冰雪线路真的太赞了:坐雪橇、看雾凇、泼水成冰、雪地摩托……每一个都是人生初体验。师傅的车暖气开得足足的,一上车就暖和。呼籁还给我们准备了暖宝宝和防滑鞋套,细节到位。"
|
||||
},
|
||||
{
|
||||
"id": "69723f88000000001703a4c7",
|
||||
"title": "带2岁宝宝去草原的注意事项",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/0846bea5f9b467ddbdeac96e88f08b9a/1040g00831jog5ql9j4dg43klvmjdgvoeqshnj3o!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-69723f88000000001703a4c7.webp",
|
||||
"author": "元气小妈咪",
|
||||
"likes": 73,
|
||||
"url": "https://www.xiaohongshu.com/explore/69723f88000000001703a4c7",
|
||||
"tags": ["亲子游", "攻略"],
|
||||
"tags": [
|
||||
"亲子游",
|
||||
"攻略"
|
||||
],
|
||||
"ratio": "horizontal",
|
||||
"summary": "带2岁宝宝去呼伦贝尔完全可行!关键是选对旅行方式。我们选了呼籁的亲子定制,车上有宝宝安全座椅,行程每天不超过3小时车程,午睡时间都留出来了。呼籁还帮我们准备了便携马桶和湿巾。宝宝在草原上追小羊的画面,治愈了我整个夏天。"
|
||||
},
|
||||
{
|
||||
"id": "6983a4f5000000002302c8e0",
|
||||
"title": "莫日格勒河的日落太绝了",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/5fc9cc68d2c4db833a5ac5dfc8ed3f8e/notes_pre_post/1040g3k031k0gh6jd36004806b24ch4bbbhq2390!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-6983a4f5000000002302c8e0.webp",
|
||||
"author": "光影猎人阿杰",
|
||||
"likes": 39,
|
||||
"url": "https://www.xiaohongshu.com/explore/6983a4f5000000002302c8e0",
|
||||
"tags": ["莫日格勒河", "日落"],
|
||||
"tags": [
|
||||
"莫日格勒河",
|
||||
"日落"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "莫日格勒河的日落,是我这辈子见过最美的日落之一。呼籁的师傅特意带我们到了一个私藏的观景点,避开了所有旅行团。金色的阳光洒在弯曲的河面上,牛羊在河边喝水,背景是无边的草原……按下快门的那一刻我就知道,这张照片会成为我的年度最佳。"
|
||||
},
|
||||
{
|
||||
"id": "6994b6120000000010023d9c",
|
||||
"title": "呼籁的师傅真的太好了!",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/3b897562f2b122626c1025cc4c36e39f/notes_pre_post/1040g3k831l0i6urf30e04149ubdcdp575b4754g!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-6994b6120000000010023d9c.webp",
|
||||
"author": "佩佩在路上",
|
||||
"likes": 15,
|
||||
"url": "https://www.xiaohongshu.com/explore/6994b6120000000010023d9c",
|
||||
"tags": ["服务", "呼伦贝尔"],
|
||||
"tags": [
|
||||
"服务",
|
||||
"呼伦贝尔"
|
||||
],
|
||||
"ratio": "horizontal",
|
||||
"summary": "必须要夸一下呼籁的师傅!全程像家人一样照顾我们,车上常备矿泉水、零食和防蚊液。有一天我把手机忘在了景区,师傅二话不说开车帮我回去找,太感动了。最后一天还特意买了当地的特产送给我们当纪念。这种真诚的服务,下次来呼伦贝尔一定还选呼籁。"
|
||||
},
|
||||
{
|
||||
"id": "69a5c7350000000021028a15",
|
||||
"title": "白桦林里的秋色|呼伦贝尔9月限定",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/caf0c832b96a87d8e21d750b8cba4cbf/notes_pre_post/1040g3k031l21j3k034204a311v5kgdpbe3rn88o!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-69a5c7350000000021028a15.webp",
|
||||
"author": "秋天的童话",
|
||||
"likes": 91,
|
||||
"url": "https://www.xiaohongshu.com/explore/69a5c7350000000021028a15",
|
||||
"tags": ["秋季", "白桦林"],
|
||||
"tags": [
|
||||
"秋季",
|
||||
"白桦林"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "9月的呼伦贝尔白桦林美得不像话!金黄的树叶在阳光下闪闪发光,走在林间小路上像走进了油画。呼籁的师傅带我们去了一片游客很少的白桦林,安静得只听到树叶沙沙响。秋天来呼伦贝尔的人不多,但风景一点不输夏天,强烈推荐9月来!"
|
||||
},
|
||||
{
|
||||
"id": "69b6d8480000000013038b62",
|
||||
"title": "呼伦贝尔五天四晚花费明细",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/23f201eadfa72a9fa142bc44a8cb918c/notes_pre_post/1040g3k831iugn1ccha6g4bsfm2j5qb8aelkl7h0!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-69b6d8480000000013038b62.webp",
|
||||
"author": "理财小达人",
|
||||
"likes": 126,
|
||||
"url": "https://www.xiaohongshu.com/explore/69b6d8480000000013038b62",
|
||||
"tags": ["费用", "攻略"],
|
||||
"tags": [
|
||||
"费用",
|
||||
"攻略"
|
||||
],
|
||||
"ratio": "horizontal",
|
||||
"summary": "很多人问呼伦贝尔旅行到底花多少钱,来一份详细的费用明细!选的是呼籁5天4晚的亲子线路,包含用车、住宿、部分餐饮和体验项目。价格在同类产品中属于中等偏上,但服务品质确实对得起价格。全程没有任何隐形消费,不推销、不购物,花的每一分钱都值。"
|
||||
},
|
||||
{
|
||||
"id": "69c7e95b000000001e02b4d7",
|
||||
"title": "额尔古纳的俄罗斯族老奶奶教我做列巴",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/0846bea5f9b467ddbdeac96e88f08b9a/1040g00831jog5ql9j4dg43klvmjdgvoeqshnj3o!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/xhs-69c7e95b000000001e02b4d7.webp",
|
||||
"author": "面包爱好者",
|
||||
"likes": 24,
|
||||
"url": "https://www.xiaohongshu.com/explore/69c7e95b000000001e02b4d7",
|
||||
"tags": ["文化体验", "额尔古纳"],
|
||||
"tags": [
|
||||
"文化体验",
|
||||
"额尔古纳"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "在额尔古纳恩和镇,呼籁带我们去了一户俄罗斯族老奶奶家,学做传统列巴。老奶奶特别热情,手把手教我们揉面、发酵、烤制,还请我们喝了自制的蓝莓酱配列巴。孩子们玩得特别开心,这种深度文化体验比看景点有意义多了。呼籁的行程设计真的很走心。"
|
||||
}
|
||||
|
||||
@ -5,88 +5,121 @@
|
||||
{
|
||||
"id": "meipian-001",
|
||||
"title": "呼伦贝尔|带着三个娃的草原奇妙之旅",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/5fc9cc68d2c4db833a5ac5dfc8ed3f8e/notes_pre_post/1040g3k031k0gh6jd36004806b24ch4bbbhq2390!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/youji-meipian-001.webp",
|
||||
"author": "郑佩佩",
|
||||
"platform": "美篇",
|
||||
"url": "https://www.meipian.cn/5h3tmj2q",
|
||||
"tags": ["亲子游", "呼伦贝尔", "草原", "家庭旅行"],
|
||||
"tags": [
|
||||
"亲子游",
|
||||
"呼伦贝尔",
|
||||
"草原",
|
||||
"家庭旅行"
|
||||
],
|
||||
"ratio": "horizontal",
|
||||
"summary": "一家人带着三个孩子踏上呼伦贝尔之旅,选择了呼籁旅行的定制行程。从海拉尔出发,先参观了呼伦贝尔博物馆看猛犸象化石,孩子们大开眼界。草原上穿蒙古袍拍了专业旅拍,骑马穿越大草原,孩子们兴奋得不得了。在恩和镇的俄罗斯族面包房学做列巴,和驯鹿亲密接触。还体验了草原极速滑草、ATV越野,满洲里的俄罗斯风情街夜景美到窒息。全程师傅服务细致周到,行程紧凑又不疲惫,是一家人最难忘的旅行。"
|
||||
},
|
||||
{
|
||||
"id": "meipian-002",
|
||||
"title": "退休夫妇的呼伦贝尔圆梦之旅",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/caf0c832b96a87d8e21d750b8cba4cbf/notes_pre_post/1040g3k031l21j3k034204a311v5kgdpbe3rn88o!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/youji-meipian-002.webp",
|
||||
"author": "王阿姨的慢生活",
|
||||
"platform": "美篇",
|
||||
"url": "https://www.meipian.cn/5h3tmj2q",
|
||||
"tags": ["银发游", "呼伦贝尔", "慢旅行"],
|
||||
"tags": [
|
||||
"银发游",
|
||||
"呼伦贝尔",
|
||||
"慢旅行"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "退休后一直想去看看大草原,在女儿推荐下选了呼籁旅行。师傅知道我们年纪大了,全程慢节奏,每个景点都等我们慢慢看慢慢拍。额尔古纳湿地的落日太美了,老伴拍了一百多张照片。住的酒店干净暖和,吃的都是地道蒙餐,手把肉和奶茶特别正宗。这辈子终于圆了草原梦,感谢呼籁让我们的旅途如此舒心。"
|
||||
},
|
||||
{
|
||||
"id": "wechat-001",
|
||||
"title": "呼伦贝尔6天5晚亲子游全纪录",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/23f201eadfa72a9fa142bc44a8cb918c/notes_pre_post/1040g3k831iugn1ccha6g4bsfm2j5qb8aelkl7h0!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/youji-wechat-001.webp",
|
||||
"author": "小鱼妈妈育儿志",
|
||||
"platform": "微信公众号",
|
||||
"url": "https://www.meipian.cn/5h3tmj2q",
|
||||
"tags": ["亲子游", "攻略", "呼伦贝尔"],
|
||||
"tags": [
|
||||
"亲子游",
|
||||
"攻略",
|
||||
"呼伦贝尔"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "写了一万字的呼伦贝尔亲子游记!6天5晚走了海拉尔-莫日格勒河-额尔古纳-黑山头-满洲里的经典环线。选呼籁是因为闺蜜推荐,一家一车真的太适合带娃家庭了。白桦林里给孩子做了自然教育,黑山头的骑马体验让5岁的儿子至今念念不忘。全程住宿都经过精选,卫生条件完全放心。最惊喜的是师傅自带无人机帮我们航拍了全家福,太专业了!"
|
||||
},
|
||||
{
|
||||
"id": "douyin-001",
|
||||
"title": "呼伦贝尔vlog|草原上的自由时光",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/3b897562f2b122626c1025cc4c36e39f/notes_pre_post/1040g3k831l0i6urf30e04149ubdcdp575b4754g!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/youji-douyin-001.webp",
|
||||
"author": "旅行的小确幸",
|
||||
"platform": "抖音",
|
||||
"url": "https://www.meipian.cn/5h3tmj2q",
|
||||
"tags": ["草原", "vlog", "自驾"],
|
||||
"tags": [
|
||||
"草原",
|
||||
"vlog",
|
||||
"自驾"
|
||||
],
|
||||
"ratio": "horizontal",
|
||||
"summary": "用镜头记录了在呼伦贝尔7天的自由时光。从第一天看到莫日格勒河的震撼,到最后一天在海拉尔依依不舍地告别师傅,每一天都值得记录。草原上骑马奔驰的画面、星空下的篝火晚会、和牧民一起赶羊群……这些画面组成了我今年最珍贵的vlog。呼籁的安排让整个旅程毫无压力,师傅还帮我找了很多好看的取景点,简直是兼职摄影助理。"
|
||||
},
|
||||
{
|
||||
"id": "meipian-003",
|
||||
"title": "我们的草原毕业旅行|青春不散场",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/0846bea5f9b467ddbdeac96e88f08b9a/1040g00831jog5ql9j4dg43klvmjdgvoeqshnj3o!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/youji-meipian-003.webp",
|
||||
"author": "四个女孩的旅行",
|
||||
"platform": "美篇",
|
||||
"url": "https://www.meipian.cn/5h3tmj2q",
|
||||
"tags": ["毕业旅行", "闺蜜游", "草原"],
|
||||
"tags": [
|
||||
"毕业旅行",
|
||||
"闺蜜游",
|
||||
"草原"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "大学毕业旅行选了呼伦贝尔大草原!四个女生第一次来北方,被草原的辽阔震撼到了。呼籁给我们安排了一位超会拍照的女司机,沿途帮我们拍了无数美照。在黑山头的日落骑马、恩和的木屋民宿、满洲里的异国风情……每一站都是惊喜。最感动的是最后一天师傅送我们去机场,车上放着我们一路旅行的照片合集视频,四个人都哭了。青春最好的模样,就是和好朋友一起看世界。"
|
||||
},
|
||||
{
|
||||
"id": "weibo-001",
|
||||
"title": "冬日呼伦贝尔|零下30度的浪漫",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/caf0c832b96a87d8e21d750b8cba4cbf/notes_pre_post/1040g3k031l21j3k034204a311v5kgdpbe3rn88o!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/youji-weibo-001.webp",
|
||||
"author": "北方有佳人Lynn",
|
||||
"platform": "微博",
|
||||
"url": "https://www.meipian.cn/5h3tmj2q",
|
||||
"tags": ["冬季", "雪景", "冰雪体验"],
|
||||
"tags": [
|
||||
"冬季",
|
||||
"雪景",
|
||||
"冰雪体验"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "零下30度的呼伦贝尔,是另一种极致的美。这次走的是呼籁的冬季冰雪线路:坐马拉雪橇穿越雪原、在不冻河边看雾凇、泼水成冰的瞬间让人尖叫、雪地里打滚撒欢……师傅的越野车暖气开到最足,一上车就暖和起来。呼籁还贴心准备了暖宝宝、防滑鞋套和保温杯。在冷极村的那晚,屋外零下40度,屋内暖炕热乎乎,窗外是漫天飞雪。冬天的呼伦贝尔,值得你来一次。"
|
||||
},
|
||||
{
|
||||
"id": "wechat-002",
|
||||
"title": "呼伦贝尔秋色|九月的白桦林和金色草原",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/23f201eadfa72a9fa142bc44a8cb918c/notes_pre_post/1040g3k831iugn1ccha6g4bsfm2j5qb8aelkl7h0!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/youji-wechat-002.webp",
|
||||
"author": "摄影师老张",
|
||||
"platform": "微信公众号",
|
||||
"url": "https://www.meipian.cn/5h3tmj2q",
|
||||
"tags": ["秋季", "白桦林", "摄影"],
|
||||
"tags": [
|
||||
"秋季",
|
||||
"白桦林",
|
||||
"摄影"
|
||||
],
|
||||
"ratio": "horizontal",
|
||||
"summary": "作为一名风光摄影师,我去过很多地方拍秋色,但呼伦贝尔的秋天是最让我惊艳的。九月中旬来的,白桦林金黄一片,草原渐渐变成金色,牛羊在夕阳下悠闲吃草。呼籁的师傅对本地非常熟悉,带我去了好几个普通游客根本找不到的拍摄点。日出时的额尔古纳湿地、逆光下的白桦林、满归的落叶松林……每一帧都是壁纸级别。强烈推荐9月来呼伦贝尔,人少景美,是摄影人的天堂。"
|
||||
},
|
||||
{
|
||||
"id": "meipian-004",
|
||||
"title": "带着90岁奶奶去看草原",
|
||||
"cover": "https://sns-webpic-qc.xhscdn.com/202603231146/5fc9cc68d2c4db833a5ac5dfc8ed3f8e/notes_pre_post/1040g3k031k0gh6jd36004806b24ch4bbbhq2390!nc_n_webp_mw_1",
|
||||
"cover": "/uploads/covers/youji-meipian-004.webp",
|
||||
"author": "孙女小敏",
|
||||
"platform": "美篇",
|
||||
"url": "https://www.meipian.cn/5h3tmj2q",
|
||||
"tags": ["孝亲游", "感动", "草原"],
|
||||
"tags": [
|
||||
"孝亲游",
|
||||
"感动",
|
||||
"草原"
|
||||
],
|
||||
"ratio": "vertical",
|
||||
"summary": "奶奶今年90岁了,一辈子生活在南方小城,最大的心愿就是看看北方的大草原。今年终于鼓起勇气带她出发了。选呼籁是因为他们可以完全定制行程,根据奶奶的身体状况调整节奏。师傅特别耐心,每次停车都先下来帮奶奶开门搀扶。在草原上,奶奶激动得眼眶湿润,说这辈子值了。师傅还特意找了一个平坦的草地,铺好垫子让奶奶坐着看远方。这是我做过最正确的决定,感谢呼籁让奶奶圆了草原梦。"
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ export default defineNuxtConfig({
|
||||
'/products/v9-6d5n-family', '/products/v9-6d5n-explore',
|
||||
'/products/v9-7d6n-family', '/products/v9-7d6n-wild',
|
||||
'/products/autumn', '/products/winter',
|
||||
'/gallery', '/news', '/calendar', '/stories',
|
||||
'/gallery', '/news', '/calendar', '/stories', '/xiaohongshu', '/youji',
|
||||
'/blog',
|
||||
'/blog/hulunbuir-travel-guide-2026',
|
||||
'/blog/hulunbuir-family-travel-guide',
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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')
|
||||
|
||||
|
||||
@ -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')
|
||||
|
||||
|
||||
@ -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')
|
||||
|
||||
|
||||
@ -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')
|
||||
|
||||
|
||||
132
scripts/download-covers.mjs
普通文件
132
scripts/download-covers.mjs
普通文件
@ -0,0 +1,132 @@
|
||||
/**
|
||||
* 下载小红书/游记封面图片到本地
|
||||
*
|
||||
* 小红书 CDN 签名 URL 会过期且有防盗链,
|
||||
* 此脚本将封面图下载到 public/uploads/covers/ 并更新数据库。
|
||||
*
|
||||
* 运行: node scripts/download-covers.mjs
|
||||
*/
|
||||
|
||||
import Database from 'better-sqlite3'
|
||||
import { writeFileSync, mkdirSync, existsSync } from 'fs'
|
||||
import { join, dirname } from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
const ROOT = join(__dirname, '..')
|
||||
const DB_PATH = join(ROOT, 'server/database/hulai.db')
|
||||
const COVERS_DIR = join(ROOT, 'public/uploads/covers')
|
||||
|
||||
// 确保目录存在
|
||||
mkdirSync(COVERS_DIR, { recursive: true })
|
||||
|
||||
/**
|
||||
* 将签名 CDN URL 转为永久可下载的 URL
|
||||
*/
|
||||
function toPermanentUrl(url) {
|
||||
try {
|
||||
const parsed = new URL(url)
|
||||
if (parsed.hostname === 'sns-webpic-qc.xhscdn.com') {
|
||||
const parts = parsed.pathname.split('/')
|
||||
if (parts.length >= 4) {
|
||||
const imagePath = parts.slice(3).join('/')
|
||||
return `https://ci.xiaohongshu.com/${imagePath}`
|
||||
}
|
||||
}
|
||||
// 已经是永久链接
|
||||
if (parsed.hostname === 'ci.xiaohongshu.com') {
|
||||
return url
|
||||
}
|
||||
} catch {}
|
||||
return url
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载图片到本地
|
||||
*/
|
||||
async function downloadImage(url, filename) {
|
||||
const localPath = join(COVERS_DIR, filename)
|
||||
if (existsSync(localPath)) {
|
||||
console.log(` ⏭ 已存在: ${filename}`)
|
||||
return `/uploads/covers/${filename}`
|
||||
}
|
||||
|
||||
const downloadUrl = toPermanentUrl(url)
|
||||
try {
|
||||
const res = await fetch(downloadUrl, {
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
|
||||
},
|
||||
})
|
||||
if (!res.ok) {
|
||||
console.log(` ✗ 下载失败 (${res.status}): ${filename} <- ${downloadUrl}`)
|
||||
return null
|
||||
}
|
||||
const buffer = Buffer.from(await res.arrayBuffer())
|
||||
writeFileSync(localPath, buffer)
|
||||
console.log(` ✓ 已下载: ${filename} (${(buffer.length / 1024).toFixed(1)}KB)`)
|
||||
return `/uploads/covers/${filename}`
|
||||
} catch (err) {
|
||||
console.log(` ✗ 下载出错: ${filename} - ${err.message}`)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 URL 中提取文件扩展名
|
||||
*/
|
||||
function getExtFromUrl(url) {
|
||||
if (url.includes('webp')) return '.webp'
|
||||
if (url.includes('.jpg') || url.includes('.jpeg')) return '.jpg'
|
||||
if (url.includes('.png')) return '.png'
|
||||
return '.webp' // 小红书默认 webp
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const db = new Database(DB_PATH)
|
||||
|
||||
for (const key of ['xiaohongshu-wall', 'youji']) {
|
||||
console.log(`\n📦 处理: ${key}`)
|
||||
|
||||
const row = db.prepare('SELECT data FROM site_content WHERE key = ?').get(key)
|
||||
if (!row) {
|
||||
console.log(` ⚠ 未找到数据: ${key}`)
|
||||
continue
|
||||
}
|
||||
|
||||
const data = JSON.parse(row.data)
|
||||
const notes = data.notes || []
|
||||
let updated = 0
|
||||
|
||||
for (const note of notes) {
|
||||
if (!note.cover) continue
|
||||
// 跳过已经是本地路径的
|
||||
if (note.cover.startsWith('/')) {
|
||||
console.log(` ⏭ 已是本地: ${note.id}`)
|
||||
continue
|
||||
}
|
||||
|
||||
const ext = getExtFromUrl(note.cover)
|
||||
const filename = `${key.replace('xiaohongshu-wall', 'xhs')}-${note.id}${ext}`
|
||||
const localUrl = await downloadImage(note.cover, filename)
|
||||
|
||||
if (localUrl) {
|
||||
note.cover = localUrl
|
||||
updated++
|
||||
}
|
||||
}
|
||||
|
||||
if (updated > 0) {
|
||||
db.prepare('UPDATE site_content SET data = ? WHERE key = ?')
|
||||
.run(JSON.stringify(data), key)
|
||||
console.log(`\n ✅ 已更新 ${updated} 张封面到本地路径`)
|
||||
} else {
|
||||
console.log(`\n ℹ 无需更新`)
|
||||
}
|
||||
}
|
||||
|
||||
db.close()
|
||||
console.log('\n🎉 完成!')
|
||||
}
|
||||
|
||||
main().catch(console.error)
|
||||
@ -0,0 +1,91 @@
|
||||
import { writeFileSync, existsSync, mkdirSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
import { requireAdmin } from '../../utils/auth.js'
|
||||
|
||||
/**
|
||||
* 下载外部图片到本地
|
||||
* POST /api/admin/download-image
|
||||
* Body: { url: "https://..." }
|
||||
* Returns: { localUrl: "/uploads/covers/xxx.webp" }
|
||||
*/
|
||||
|
||||
const ALLOWED_HOSTS = [
|
||||
'sns-webpic-qc.xhscdn.com',
|
||||
'ci.xiaohongshu.com',
|
||||
'sns-img-bd.xhscdn.com',
|
||||
]
|
||||
|
||||
/**
|
||||
* 将签名 CDN URL 转为永久可下载的 URL
|
||||
*/
|
||||
function toPermanentUrl(url) {
|
||||
try {
|
||||
const parsed = new URL(url)
|
||||
if (parsed.hostname === 'sns-webpic-qc.xhscdn.com') {
|
||||
const parts = parsed.pathname.split('/')
|
||||
if (parts.length >= 4) {
|
||||
const imagePath = parts.slice(3).join('/')
|
||||
return `https://ci.xiaohongshu.com/${imagePath}`
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
return url
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
requireAdmin(event)
|
||||
|
||||
const { url } = await readBody(event)
|
||||
if (!url || !url.startsWith('http')) {
|
||||
throw createError({ statusCode: 400, message: '请提供有效的图片 URL' })
|
||||
}
|
||||
|
||||
// 验证域名白名单
|
||||
let parsedUrl
|
||||
try {
|
||||
parsedUrl = new URL(url)
|
||||
} catch {
|
||||
throw createError({ statusCode: 400, message: '无效的 URL' })
|
||||
}
|
||||
|
||||
if (!ALLOWED_HOSTS.includes(parsedUrl.hostname)) {
|
||||
throw createError({ statusCode: 403, message: '该域名不支持自动下载,请手动上传图片' })
|
||||
}
|
||||
|
||||
// 生成本地文件名
|
||||
const pathParts = parsedUrl.pathname.split('/')
|
||||
const imageId = pathParts[pathParts.length - 1].split('!')[0] || `img-${Date.now()}`
|
||||
const filename = `${imageId}.webp`
|
||||
const coversDir = join(process.cwd(), 'public', 'uploads', 'covers')
|
||||
|
||||
if (!existsSync(coversDir)) {
|
||||
mkdirSync(coversDir, { recursive: true })
|
||||
}
|
||||
|
||||
const localPath = join(coversDir, filename)
|
||||
const localUrl = `/uploads/covers/${filename}`
|
||||
|
||||
// 如果已下载过,直接返回
|
||||
if (existsSync(localPath)) {
|
||||
return { localUrl }
|
||||
}
|
||||
|
||||
// 下载图片
|
||||
const downloadUrl = toPermanentUrl(url)
|
||||
try {
|
||||
const res = await fetch(downloadUrl, {
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
|
||||
},
|
||||
})
|
||||
if (!res.ok) {
|
||||
throw createError({ statusCode: 502, message: `图片下载失败 (${res.status})` })
|
||||
}
|
||||
const buffer = Buffer.from(await res.arrayBuffer())
|
||||
writeFileSync(localPath, buffer)
|
||||
return { localUrl }
|
||||
} catch (err) {
|
||||
if (err.statusCode) throw err
|
||||
throw createError({ statusCode: 502, message: `图片下载出错: ${err.message}` })
|
||||
}
|
||||
})
|
||||
59
server/api/image-proxy.get.js
普通文件
59
server/api/image-proxy.get.js
普通文件
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* 图片代理接口
|
||||
* 用于加载有防盗链保护的外部图片(如小红书 CDN)
|
||||
*
|
||||
* 用法: /api/image-proxy?url=https://sns-webpic-qc.xhscdn.com/...
|
||||
*
|
||||
* 安全限制:只允许代理白名单域名的图片
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { url } = getQuery(event)
|
||||
|
||||
if (!url) {
|
||||
throw createError({ statusCode: 400, message: '缺少 url 参数' })
|
||||
}
|
||||
|
||||
// 白名单:只允许代理指定域名的图片
|
||||
const allowedHosts = [
|
||||
'sns-webpic-qc.xhscdn.com',
|
||||
'ci.xiaohongshu.com',
|
||||
'image.xiaohongshu.com',
|
||||
'picx.zhimg.com',
|
||||
]
|
||||
|
||||
let parsedUrl
|
||||
try {
|
||||
parsedUrl = new URL(url)
|
||||
} catch {
|
||||
throw createError({ statusCode: 400, message: '无效的 URL' })
|
||||
}
|
||||
|
||||
if (!allowedHosts.includes(parsedUrl.hostname)) {
|
||||
throw createError({ statusCode: 403, message: '该域名不在白名单中' })
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||
'Accept': 'image/webp,image/apng,image/*,*/*;q=0.8',
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw createError({ statusCode: response.status, message: '图片获取失败' })
|
||||
}
|
||||
|
||||
const contentType = response.headers.get('content-type') || 'image/jpeg'
|
||||
const buffer = await response.arrayBuffer()
|
||||
|
||||
setHeader(event, 'Content-Type', contentType)
|
||||
setHeader(event, 'Cache-Control', 'public, max-age=86400, s-maxage=604800')
|
||||
setHeader(event, 'Access-Control-Allow-Origin', '*')
|
||||
|
||||
return Buffer.from(buffer)
|
||||
} catch (err) {
|
||||
if (err.statusCode) throw err
|
||||
throw createError({ statusCode: 502, message: '图片代理请求失败' })
|
||||
}
|
||||
})
|
||||
正在加载...
x
在新工单中引用
屏蔽一个用户