2b69686795
- 新增404页面,优化未找到页面体验,避免被搜索引擎索引 - 增加文件代理接口,隐藏真实文件服务器地址,支持文件请求代理 - 实现/article、/case、/product及/page动态路由兼容列表与详情展示 - 添加动态CMS页面兼容入口处理旧式路径,统一路由与SEO设置 - 新增模板1、模板7、模板2、模板3关于我们页面,实现多模板支持 - 模板增强支持CMS单页内容加载及SEO信息动态设置 - 配置环境变量及Git忽略文件规则辅助开发和构建环境管理
124 lines
3.5 KiB
TypeScript
124 lines
3.5 KiB
TypeScript
import { useHead, useRequestURL, useSeoMeta, useRuntimeConfig } from '#app'
|
|
import type { CmsSiteInfo } from '~/types'
|
|
|
|
type SeoInput = {
|
|
title: string
|
|
description?: string
|
|
keywords?: string
|
|
path?: string
|
|
image?: string
|
|
type?: 'website' | 'article' | 'product'
|
|
siteName?: string
|
|
publishedTime?: string
|
|
modifiedTime?: string
|
|
}
|
|
|
|
function getSiteOrigin() {
|
|
if (import.meta.client) return window.location.origin
|
|
try {
|
|
return useRequestURL().origin
|
|
} catch {
|
|
return ''
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 页面 SEO 设置
|
|
* 设置 TDK、Open Graph、Twitter Card、Canonical URL
|
|
*/
|
|
export function usePageSeo(input: SeoInput, site?: CmsSiteInfo | null) {
|
|
const origin = getSiteOrigin()
|
|
const url = input.path && origin ? new URL(input.path, origin).toString() : undefined
|
|
const overrideSiteName = (useRuntimeConfig().public.siteName as string) || ''
|
|
const siteName = input.siteName || overrideSiteName || site?.websiteName || ''
|
|
const description = input.description || site?.comments || site?.content || ''
|
|
const keywords = input.keywords || site?.keywords || ''
|
|
const image = input.image || site?.websiteLogo || ''
|
|
|
|
const fullTitle = siteName ? `${input.title} - ${siteName}` : input.title
|
|
|
|
useSeoMeta({
|
|
title: fullTitle,
|
|
description,
|
|
keywords,
|
|
ogTitle: input.title,
|
|
ogDescription: description,
|
|
ogType: input.type || 'website',
|
|
ogSiteName: siteName,
|
|
...(image ? { ogImage: image } : {}),
|
|
...(url ? { ogUrl: url } : {}),
|
|
twitterCard: 'summary_large_image',
|
|
twitterTitle: input.title,
|
|
twitterDescription: description,
|
|
...(image ? { twitterImage: image } : {}),
|
|
...(input.publishedTime ? { articlePublishedTime: input.publishedTime } : {}),
|
|
...(input.modifiedTime ? { articleModifiedTime: input.modifiedTime } : {})
|
|
})
|
|
|
|
if (url) {
|
|
useHead({
|
|
link: [{ rel: 'canonical', href: url }]
|
|
})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 注入 JSON-LD 结构化数据
|
|
*/
|
|
export function useJsonLd(data: Record<string, unknown> | Record<string, unknown>[]) {
|
|
const items = Array.isArray(data) ? data : [data]
|
|
|
|
useHead({
|
|
script: items.map((item) => ({
|
|
type: 'application/ld+json',
|
|
innerHTML: JSON.stringify(item)
|
|
}))
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 注入企业组织结构化数据
|
|
*/
|
|
export function useOrganizationSeo(site?: CmsSiteInfo | null) {
|
|
if (!site) return
|
|
|
|
const origin = getSiteOrigin()
|
|
const rawPhone = site.phone || ''
|
|
const phone = rawPhone && !rawPhone.includes('*')
|
|
? rawPhone
|
|
: (useRuntimeConfig().public.phone as string) || site.config?.tel || rawPhone || ''
|
|
const email = site.config?.email || site.email || ''
|
|
const address = site.address || site.config?.address || ''
|
|
const logo = site.websiteLogo || ''
|
|
const overrideSiteName = (useRuntimeConfig().public.siteName as string) || ''
|
|
|
|
useJsonLd({
|
|
'@context': 'https://schema.org',
|
|
'@type': 'Organization',
|
|
name: overrideSiteName || site.websiteName || '',
|
|
url: origin,
|
|
...(logo ? { logo: new URL(logo, origin).toString() } : {}),
|
|
...(phone ? { telephone: phone } : {}),
|
|
...(email ? { email: email } : {}),
|
|
...(address ? { address: { '@type': 'PostalAddress', address } } : {})
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 注入面包屑结构化数据
|
|
*/
|
|
export function useBreadcrumbSeo(items: { name: string; url: string }[]) {
|
|
const origin = getSiteOrigin()
|
|
|
|
useJsonLd({
|
|
'@context': 'https://schema.org',
|
|
'@type': 'BreadcrumbList',
|
|
itemListElement: items.map((item, index) => ({
|
|
'@type': 'ListItem',
|
|
position: index + 1,
|
|
name: item.name,
|
|
item: new URL(item.url, origin).toString()
|
|
}))
|
|
})
|
|
}
|