- 后台全面引入 Naive UI 组件库,统一 UI 规范 - 前台 usePublicApi 切换到 API 调用(GET /api/content/[key]) - 前后台数据源统一(site_content 表) - 产品线统一管理(tour/camp/course 三套编辑器) - 产品数据归一化(itinerary/faq/pricing 格式统一) - 表单提交 API 改写入 submissions 表 - 新增 submissions 标记已读 API - site-content PUT 支持 upsert - 修复前台 bug(stories 路由冲突、占位符警告、selector breadcrumb) - 消除 PageHero 类型警告(useSEO reactive 修复) - 25 个前台页面全部 HTTP 200,展示效果不变 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
91 行
4.4 KiB
JavaScript
91 行
4.4 KiB
JavaScript
/**
|
||
* 页面级 SEO 元数据注入
|
||
* 包含:传统搜索引擎 + AI 大模型优化
|
||
*
|
||
* 从 API 获取 SEO 配置数据(替代原来的静态 JSON 导入)
|
||
* 使用 useHead 的响应式能力,在数据到达后自动更新 meta 标签
|
||
*/
|
||
export default function useSEO(pageKey, overrides = {}) {
|
||
// 从本地 JSON 导入 SEO 数据
|
||
const { data: seoData } = usePublicApi('seo')
|
||
|
||
const url = `https://1814.love${useRoute().path}`
|
||
|
||
// 每页关键词映射 - 给 AI 爬虫明确的关键信息
|
||
const keywordsMap = {
|
||
home: '呼伦贝尔亲子游,呼伦贝尔家庭定制游,呼籁旅行,一家一单一车,呼伦贝尔旅游,草原定制游,额吉的故乡',
|
||
about: '呼籁旅行,呼伦贝尔旅行社,自有营地,自有车队,呼籁文旅集团,深耕呼伦贝尔11年',
|
||
products: '呼伦贝尔定制游,额吉的故乡V9,呼伦贝尔几天合适,亲子游产品,5天4晚,6天5晚,7天6晚',
|
||
faq: '呼伦贝尔旅游攻略,呼伦贝尔亲子游攻略,呼伦贝尔多少钱,呼伦贝尔几月去好,呼伦贝尔穿什么',
|
||
reviews: '呼伦贝尔旅游评价,呼籁旅行怎么样,呼伦贝尔亲子游评价,呼伦贝尔定制游口碑',
|
||
guides: '呼伦贝尔穿搭指南,呼伦贝尔行李清单,呼伦贝尔几月份去最好,呼伦贝尔骑马安全,草原旅游注意事项',
|
||
'summer-camp': '呼伦贝尔夏令营,草原亲子夏令营,小蒙马夏令营,呼伦贝尔亲子营,研学旅行',
|
||
qualifications: '呼籁旅行资质,旅行社经营许可证,旅游责任险,呼伦贝尔旅行社认证,呼籁荣誉',
|
||
news: '呼籁旅行动态,呼伦贝尔旅游资讯,呼籁旅行公告',
|
||
contact: '呼籁旅行联系方式,呼伦贝尔定制游咨询,呼籁旅行微信,呼伦贝尔旅游电话',
|
||
youji: '客户游记,呼伦贝尔游记,呼籁旅行评价,美篇游记,旅行故事,呼伦贝尔亲子游记,客户旅行分享',
|
||
}
|
||
|
||
// 响应式配置:当 seoData 从 API 返回后自动更新
|
||
const pageConfig = computed(() => seoData.value?.pages?.[pageKey] || {})
|
||
const mergedConfig = computed(() => ({ ...pageConfig.value, ...overrides }))
|
||
const ogImage = computed(() => mergedConfig.value.ogImage ? `https://1814.love${mergedConfig.value.ogImage}` : '')
|
||
|
||
// useHead 支持响应式参数,数据到达后自动更新 <head>
|
||
useHead({
|
||
title: computed(() => mergedConfig.value.title),
|
||
meta: computed(() => {
|
||
const c = mergedConfig.value
|
||
const result = [
|
||
{ name: 'description', content: c.description },
|
||
{ name: 'keywords', content: keywordsMap[pageKey] || '呼伦贝尔旅游,呼籁旅行' },
|
||
{ name: 'author', content: '呼籁旅行' },
|
||
// 发布和更新时间 - AI 判断内容新鲜度的依据
|
||
{ name: 'article:published_time', content: '2025-01-01T00:00:00+08:00' },
|
||
{ name: 'article:modified_time', content: new Date().toISOString().split('T')[0] + 'T00:00:00+08:00' },
|
||
// 地理位置 - 让 AI 知道这是呼伦贝尔的本地服务
|
||
{ name: 'geo.region', content: 'CN-NM' },
|
||
{ name: 'geo.placename', content: '呼伦贝尔' },
|
||
{ name: 'geo.position', content: '49.2143;119.7674' },
|
||
{ name: 'ICBM', content: '49.2143, 119.7674' },
|
||
// Open Graph(增强)
|
||
{ property: 'og:title', content: c.title },
|
||
{ property: 'og:description', content: c.description },
|
||
{ property: 'og:type', content: 'website' },
|
||
{ property: 'og:url', content: url },
|
||
{ property: 'og:site_name', content: '呼籁旅行' },
|
||
{ property: 'og:locale', content: 'zh_CN' },
|
||
// Twitter Card
|
||
{ name: 'twitter:card', content: 'summary_large_image' },
|
||
{ name: 'twitter:title', content: c.title },
|
||
{ name: 'twitter:description', content: c.description },
|
||
]
|
||
|
||
// OG 图片
|
||
if (ogImage.value) {
|
||
result.push(
|
||
{ property: 'og:image', content: ogImage.value },
|
||
{ property: 'og:image:width', content: '1200' },
|
||
{ property: 'og:image:height', content: '630' },
|
||
{ name: 'twitter:image', content: ogImage.value },
|
||
)
|
||
}
|
||
|
||
return result
|
||
}),
|
||
link: [
|
||
{ rel: 'canonical', href: url },
|
||
],
|
||
})
|
||
|
||
const h1 = computed(() => mergedConfig.value.h1 || '')
|
||
const seoTitle = computed(() => mergedConfig.value.title || '')
|
||
const seoDescription = computed(() => mergedConfig.value.description || '')
|
||
|
||
return reactive({
|
||
h1,
|
||
title: seoTitle,
|
||
description: seoDescription,
|
||
})
|
||
}
|