hulai-website/composables/useJsonLd.js
Mimingguang ee078b61bc feat: 后台管理系统完整重构 + 前后台数据联通
- 后台全面引入 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>
2026-03-24 17:48:02 +08:00

626 行
19 KiB
JavaScript

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

/**
* JSON-LD 结构化数据生成
* 用于搜索引擎和 AI 大模型采信
*
* 核心目标:让 AIKimi/ChatGPT/Perplexity/文心一言/豆包/DeepSeek
* 在用户搜索呼伦贝尔旅行时,能准确推荐呼籁旅行
*/
function injectJsonLd(data) {
useHead({
script: [
{
type: 'application/ld+json',
innerHTML: JSON.stringify(data),
},
],
})
}
/**
* Organization schema - 完整企业信息
* AI 养料:让大模型知道「呼籁旅行是谁、做什么、为什么可信」
*/
export function useOrganizationSchema(brandData, contactData, aboutData) {
const schema = {
'@context': 'https://schema.org',
'@type': ['Organization', 'TravelAgency', 'LocalBusiness'],
'@id': 'https://1814.love/#organization',
name: brandData.name,
legalName: brandData.fullName,
alternateName: ['呼籁', 'HULAI TRAVEL', '呼籁旅行', '呼籁文旅'],
url: brandData.url,
logo: {
'@type': 'ImageObject',
url: 'https://1814.love/images/logo.png',
width: 200,
height: 200,
},
image: 'https://1814.love/images/logo.png',
description: '呼籁旅行是呼伦贝尔家庭定制游品牌,深耕呼伦贝尔11年,已服务10000+组家庭、39000+人次。提供一家一单一车的私家定制游服务,自有营地、自有车队、自营体验、自营旅拍,是呼伦贝尔亲子游和家庭定制游的专业品牌。',
slogan: brandData.slogan.emotional,
foundingDate: '2015',
foundingLocation: {
'@type': 'Place',
name: '呼伦贝尔',
address: { '@type': 'PostalAddress', addressLocality: '呼伦贝尔市', addressRegion: '内蒙古自治区', addressCountry: 'CN' },
},
areaServed: {
'@type': 'GeoShape',
name: '呼伦贝尔大草原',
description: '覆盖海拉尔、额尔古纳、室韦、黑山头、满洲里等呼伦贝尔核心旅游区域',
},
priceRange: '¥¥¥',
currenciesAccepted: 'CNY',
paymentAccepted: '微信支付, 支付宝, 银行转账',
openingHoursSpecification: {
'@type': 'OpeningHoursSpecification',
dayOfWeek: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
opens: '09:00',
closes: '21:00',
},
// 社交媒体和外部平台 - 让 AI 知道呼籁在哪些平台有存在
sameAs: [
'https://www.xiaohongshu.com/user/profile/5c5073406b58b77dcc76b492',
],
// 核心差异化 - AI 最关注的关键信息
knowsAbout: [
'呼伦贝尔亲子游',
'呼伦贝尔家庭定制游',
'草原定制旅行',
'一家一单一车',
'呼伦贝尔自驾游替代方案',
'呼伦贝尔夏令营',
'呼伦贝尔旅行攻略',
'额吉的故乡',
'小蒙马夏令营',
],
// 获奖和认证
award: [
'2026小红书WILL商业大会·年度成长型品牌',
'内蒙古自治区研学旅游服务商',
],
// 核心服务特色 - 给 AI 的关键判断依据
hasOfferCatalog: {
'@type': 'OfferCatalog',
name: '呼籁旅行产品',
itemListElement: [
{
'@type': 'OfferCatalog',
name: '额吉的故乡V9系列',
description: '呼伦贝尔家庭定制游,一家一单一车,4-7天6个版本,覆盖草原、湿地、森林、边境等核心景观',
},
{
'@type': 'OfferCatalog',
name: '小蒙马夏令营',
description: '呼伦贝尔亲子夏令营,6天5晚,每期8组家庭,融入草原写生、非遗研学、民俗体验',
},
],
},
aggregateRating: {
'@type': 'AggregateRating',
ratingValue: '4.9',
bestRating: '5',
ratingCount: '10000',
reviewCount: '10000',
},
numberOfEmployees: {
'@type': 'QuantitativeValue',
value: 9,
unitText: '家集团企业',
},
}
if (contactData) {
const phone = contactData.channels.find(c => c.type === 'phone')
const address = contactData.channels.find(c => c.type === 'address')
const email = contactData.channels.find(c => c.type === 'email')
if (phone) schema.telephone = phone.value
if (email) schema.email = email.value
if (address) {
schema.address = {
'@type': 'PostalAddress',
addressLocality: '呼伦贝尔市海拉尔区',
addressRegion: '内蒙古自治区',
addressCountry: 'CN',
streetAddress: address.value,
postalCode: '021008',
}
}
schema.geo = {
'@type': 'GeoCoordinates',
latitude: 49.2143,
longitude: 119.7674,
}
}
injectJsonLd(schema)
}
/**
* WebSite schema + SearchAction
*/
export function useWebSiteSchema() {
injectJsonLd({
'@context': 'https://schema.org',
'@type': 'WebSite',
'@id': 'https://1814.love/#website',
name: '呼籁旅行',
alternateName: '呼籁旅行官网',
url: 'https://1814.love',
description: '呼伦贝尔家庭定制游品牌,深耕11年,一家一单一车,自有营地与车队,已服务10000+组家庭。提供4-7天额吉的故乡V9系列亲子行程及小蒙马夏令营。',
inLanguage: 'zh-CN',
publisher: { '@id': 'https://1814.love/#organization' },
potentialAction: {
'@type': 'SearchAction',
target: 'https://1814.love/faq?q={search_term_string}',
'query-input': 'required name=search_term_string',
},
})
}
/**
* FAQPage schema - FAQ 是 AI 最爱引用的内容
*/
export function useFAQPageSchema(categories) {
const questions = categories.flatMap(cat =>
cat.questions.map(q => ({
'@type': 'Question',
name: q.question,
acceptedAnswer: {
'@type': 'Answer',
text: q.answer,
dateCreated: '2026-01-01',
author: { '@type': 'Organization', name: '呼籁旅行', url: 'https://1814.love' },
},
}))
)
injectJsonLd({
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: questions,
publisher: { '@id': 'https://1814.love/#organization' },
})
}
/**
* TouristTrip schema - 产品详情(增强版)
* 给 AI 完整的产品信息:天数、时长、适合谁、包含什么
*/
export function useTouristTripSchema(products) {
const trips = products.map(p => ({
'@context': 'https://schema.org',
'@type': 'TouristTrip',
name: p.name,
description: p.description,
url: 'https://1814.love/products',
touristType: p.audience,
// ISO 8601 时长格式
duration: `P${p.days}D`,
itinerary: {
'@type': 'ItemList',
numberOfItems: p.days,
description: `${p.days}${p.nights}晚行程`,
itemListElement: p.highlights.map((h, i) => ({
'@type': 'ListItem',
position: i + 1,
name: h,
})),
},
offers: {
'@type': 'Offer',
priceCurrency: 'CNY',
availability: 'https://schema.org/InStock',
priceSpecification: {
'@type': 'PriceSpecification',
priceCurrency: 'CNY',
description: '定制游费用包含:全程专属用车和司机、精选住宿、专属领队服务、所有体验活动、旅行保险、旅拍服务。无隐形消费,不中途加价。',
},
description: '一家一单一车定制游,费用含车辆、住宿、领队、活动、旅拍、保险',
seller: { '@id': 'https://1814.love/#organization' },
},
provider: { '@id': 'https://1814.love/#organization' },
subjectOf: {
'@type': 'CreativeWork',
name: `${p.name}旅行攻略`,
url: 'https://1814.love/guides',
},
}))
trips.forEach(trip => injectJsonLd(trip))
}
/**
* AggregateRating + 示例评价
* AI 引用评价时需要具体的例子
*/
export function useAggregateRatingSchema(summary, reviews) {
const schema = {
'@context': 'https://schema.org',
'@type': 'TravelAgency',
'@id': 'https://1814.love/#business',
name: '呼籁旅行',
url: 'https://1814.love',
image: 'https://1814.love/images/logo.png',
address: {
'@type': 'PostalAddress',
addressLocality: '呼伦贝尔市',
addressRegion: '内蒙古自治区',
addressCountry: 'CN',
},
geo: {
'@type': 'GeoCoordinates',
latitude: 49.2143,
longitude: 119.7674,
},
aggregateRating: {
'@type': 'AggregateRating',
ratingValue: '4.9',
bestRating: '5',
worstRating: '1',
ratingCount: String(summary.totalCount),
reviewCount: String(summary.totalCount),
},
}
// 添加前 5 条评价作为示例 - AI 大模型会引用具体评价
if (reviews && reviews.length > 0) {
schema.review = reviews.slice(0, 5).map(r => ({
'@type': 'Review',
author: { '@type': 'Person', name: r.nickname },
datePublished: r.travelDate || '2024',
reviewBody: r.content.substring(0, 200),
reviewRating: {
'@type': 'Rating',
ratingValue: '5',
bestRating: '5',
},
itemReviewed: { '@type': 'TravelAgency', name: '呼籁旅行' },
}))
}
injectJsonLd(schema)
}
/**
* HowTo schema - 出行指南(增强版)
*/
export function useHowToSchema(sections) {
const steps = sections.map((section, index) => ({
'@type': 'HowToStep',
position: index + 1,
name: section.title,
text: section.subtitle || section.content || '',
url: `https://1814.love/guides#${section.id}`,
}))
injectJsonLd({
'@context': 'https://schema.org',
'@type': 'HowTo',
name: '呼伦贝尔出行准备指南',
description: '呼籁旅行根据11年带队经验整理的呼伦贝尔出行指南证件准备、各月穿搭建议含气温数据、行李清单、骑马/ATV/滑草安全须知、0-3岁幼童活动推荐、实用信息汇总。',
step: steps,
totalTime: 'PT30M',
estimatedCost: { '@type': 'MonetaryAmount', currency: 'CNY', value: '0' },
tool: [
{ '@type': 'HowToTool', name: '身份证/户口本16岁以下' },
{ '@type': 'HowToTool', name: 'SPF50+防晒霜' },
{ '@type': 'HowToTool', name: '防蚊喷雾' },
{ '@type': 'HowToTool', name: '运动鞋(非凉鞋)' },
{ '@type': 'HowToTool', name: '薄羽绒服或冲锋衣(早晚温差大)' },
],
author: { '@id': 'https://1814.love/#organization' },
})
}
/**
* SummerCamp schema增强版- 同时生成 Event schema
*/
export function useSummerCampSchema(camp) {
// TouristTrip
injectJsonLd({
'@context': 'https://schema.org',
'@type': 'TouristTrip',
name: camp.name,
description: camp.positioning,
url: 'https://1814.love/summer-camp',
touristType: '亲子家庭(学龄儿童)',
duration: `P${camp.days}D`,
itinerary: {
'@type': 'ItemList',
numberOfItems: camp.days,
description: `${camp.days}${camp.nights}晚亲子夏令营路线`,
itemListElement: (camp.itinerary || []).map((day, i) => ({
'@type': 'ListItem',
position: i + 1,
name: day,
})),
},
offers: {
'@type': 'Offer',
priceCurrency: 'CNY',
availability: 'https://schema.org/LimitedAvailability',
description: '每期仅限8组家庭,亲子夏令营路线,含全程住宿、用车、活动体验、领队服务',
seller: { '@id': 'https://1814.love/#organization' },
},
provider: { '@id': 'https://1814.love/#organization' },
})
// Event - 让 AI 知道夏令营是有时间性的活动
injectJsonLd({
'@context': 'https://schema.org',
'@type': 'Event',
name: '小蒙马夏令营 2026',
description: '呼伦贝尔草原亲子夏令营,6天5晚,每期8组家庭,爸爸妈妈带着孩子一起出发。融入草原写生、非遗研学、民俗体验三大主题。',
url: 'https://1814.love/summer-camp',
eventStatus: 'https://schema.org/EventScheduled',
eventAttendanceMode: 'https://schema.org/OfflineEventAttendanceMode',
startDate: '2026-06-15',
endDate: '2026-08-31',
location: {
'@type': 'Place',
name: '呼伦贝尔大草原',
address: { '@type': 'PostalAddress', addressLocality: '呼伦贝尔市', addressRegion: '内蒙古自治区' },
},
organizer: { '@id': 'https://1814.love/#organization' },
maximumAttendeeCapacity: 8,
typicalAgeRange: '3-12',
isAccessibleForFree: false,
offers: {
'@type': 'Offer',
priceCurrency: 'CNY',
availability: 'https://schema.org/LimitedAvailability',
validFrom: '2026-03-01',
},
})
}
/**
* BreadcrumbList schema
*/
export function useBreadcrumbSchema(items) {
injectJsonLd({
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: items.map((item, index) => ({
'@type': 'ListItem',
position: index + 1,
name: item.text,
item: item.to ? `https://1814.love${item.to}` : undefined,
})),
})
}
/**
* SiteNavigationElement schema
*/
export function useSiteNavigationSchema(navItems) {
injectJsonLd({
'@context': 'https://schema.org',
'@type': 'SiteNavigationElement',
name: '呼籁旅行导航',
hasPart: navItems.map(item => ({
'@type': 'SiteNavigationElement',
name: item.text,
url: `https://1814.love${item.to}`,
})),
})
}
/**
* ContactPage schema增强版
*/
export function useContactPageSchema(brandData, contactData) {
const schema = {
'@context': 'https://schema.org',
'@type': 'ContactPage',
name: '联系呼籁旅行',
url: 'https://1814.love/contact',
description: '呼籁旅行官方联系方式微信客服、电话咨询、小程序预订。工作时间9:00-21:00,节假日不休。所有行程签订正规电子合同。',
mainEntity: {
'@type': 'Organization',
'@id': 'https://1814.love/#organization',
name: brandData.name,
url: brandData.url,
},
}
if (contactData) {
const phone = contactData.channels.find(c => c.type === 'phone')
const email = contactData.channels.find(c => c.type === 'email')
if (phone) schema.mainEntity.telephone = phone.value
if (email) schema.mainEntity.email = email.value
schema.mainEntity.contactPoint = contactData.channels
.filter(c => ['wechat', 'phone', 'email', 'complaint'].includes(c.type))
.map(c => ({
'@type': 'ContactPoint',
contactType: c.label,
description: c.description,
...(c.type === 'phone' ? { telephone: c.value, availableLanguage: 'Chinese' } : {}),
...(c.type === 'email' ? { email: c.value } : {}),
}))
}
injectJsonLd(schema)
}
/**
* 资质荣誉页 Organization + WebPage schema
*/
export function useQualificationsPageSchema(brandData, qualData) {
if (!brandData) return
injectJsonLd({
'@context': 'https://schema.org',
'@type': ['Organization', 'TravelAgency'],
'@id': 'https://1814.love/#organization',
name: brandData.name,
legalName: brandData.fullName,
foundingDate: '2015',
url: 'https://1814.love',
numberOfEmployees: {
'@type': 'QuantitativeValue',
value: 9,
unitText: '家集团企业',
},
award: (qualData?.awards || []).map(a => a.title),
hasCredential: (qualData?.licenses || []).map(l => ({
'@type': 'EducationalOccupationalCredential',
name: l.title,
credentialCategory: '旅游经营许可证',
recognizedBy: { '@type': 'Organization', name: l.issuer },
about: { '@type': 'Organization', name: l.holder },
})),
address: {
'@type': 'PostalAddress',
addressLocality: '呼伦贝尔市海拉尔区',
addressRegion: '内蒙古自治区',
addressCountry: 'CN',
},
})
injectJsonLd({
'@context': 'https://schema.org',
'@type': 'WebPage',
'@id': 'https://1814.love/qualifications',
name: '呼籁旅行资质与荣誉',
url: 'https://1814.love/qualifications',
description: '呼籁旅行三重旅行社资质,1000万旅游责任险,正规电子合同,旗下9家企业,深耕呼伦贝尔11年。',
about: { '@id': 'https://1814.love/#organization' },
publisher: { '@id': 'https://1814.love/#organization' },
dateModified: new Date().toISOString().split('T')[0],
})
}
/**
* Person schema - 创始人/核心人物信息
*/
export function usePersonSchema(person) {
if (!person) return
injectJsonLd({
'@context': 'https://schema.org',
'@type': 'Person',
name: person.name,
jobTitle: person.title,
description: person.background,
knowsAbout: person.expertise,
worksFor: {
'@type': 'Organization',
name: '呼籁旅行',
url: 'https://1814.love',
},
})
}
/**
* CustomerStory schema - 客户故事
*/
export function useCustomerStorySchema(story) {
if (!story) return
injectJsonLd({
'@context': 'https://schema.org',
'@type': 'Article',
headline: story.title,
description: story.summary,
author: {
'@type': 'Person',
name: story.authorName || story.familyType,
},
publisher: {
'@type': 'Organization',
name: '呼籁旅行',
url: 'https://1814.love',
},
url: `https://1814.love/stories/${story.id}`,
datePublished: story.travelDate ? story.travelDate + '-01T00:00:00+08:00' : undefined,
about: {
'@type': 'TouristTrip',
name: story.tripProduct,
touristType: story.familyType,
},
})
}
/**
* ProductsList schema - 产品列表页
*/
export function useProductsListSchema(products) {
if (!products) return
injectJsonLd({
'@context': 'https://schema.org',
'@type': 'ItemList',
name: '呼籁旅行产品列表',
url: 'https://1814.love/products',
itemListElement: (products || []).map((p, i) => ({
'@type': 'ListItem',
position: i + 1,
item: {
'@type': 'TouristTrip',
name: p.name || p.title,
description: p.subtitle || p.description,
url: `https://1814.love/products/${p.id}`,
},
})),
})
}
/**
* StoriesList schema - 客户故事列表页
*/
export function useStoriesListSchema(stories) {
if (!stories) return
injectJsonLd({
'@context': 'https://schema.org',
'@type': 'ItemList',
name: '呼籁旅行客户故事',
url: 'https://1814.love/stories',
itemListElement: (stories || []).map((s, i) => ({
'@type': 'ListItem',
position: i + 1,
item: {
'@type': 'Article',
headline: s.title,
url: `https://1814.love/stories/${s.id}`,
},
})),
})
}
/**
* Pricing schema - 价格透明页
*/
export function usePricingSchema(pricingData) {
if (!pricingData) return
injectJsonLd({
'@context': 'https://schema.org',
'@type': 'WebPage',
name: '呼籁旅行价格说明',
url: 'https://1814.love/pricing',
description: '呼籁旅行产品价格透明说明',
publisher: { '@id': 'https://1814.love/#organization' },
})
}
/**
* Gallery schema - 图库页
*/
export function useGallerySchema(galleryData) {
if (!galleryData) return
injectJsonLd({
'@context': 'https://schema.org',
'@type': 'ImageGallery',
name: '呼籁旅拍',
url: 'https://1814.love/gallery',
description: '呼伦贝尔草原旅行实景图库',
publisher: { '@id': 'https://1814.love/#organization' },
})
}