Files
hjc-web/outputs/seo-geo-audit-2026-08-04.md
gxwebsoft 2b69686795 feat(app): 添加多模板关于我们页面及相关路由和404页面
- 新增404页面,优化未找到页面体验,避免被搜索引擎索引
- 增加文件代理接口,隐藏真实文件服务器地址,支持文件请求代理
- 实现/article、/case、/product及/page动态路由兼容列表与详情展示
- 添加动态CMS页面兼容入口处理旧式路径,统一路由与SEO设置
- 新增模板1、模板7、模板2、模板3关于我们页面,实现多模板支持
- 模板增强支持CMS单页内容加载及SEO信息动态设置
- 配置环境变量及Git忽略文件规则辅助开发和构建环境管理
2026-09-08 12:13:44 +08:00

15 KiB
Raw Permalink Blame History

SEO / GEO 优化诊断报告

项目:website-templateNuxt 多租户 CMS 官网模板)
检查日期:2026-08-04
检查范围:Nuxt 配置、页面/模板 SEO、结构化数据、站点地图、robots、图片与性能、GEO 友好度


一、执行摘要

当前项目已具备 基础的 SEO 骨架SSR 渲染、统一的 usePageSeo 工具、动态 robots.txt / sitemap.xml、Organization 结构化数据、旧链接 301 重定向等。

详情页 SEO 几乎空白,这是目前最大的流量损失点:/article/:id/product/:id/case/:id 的标题、描述、OG 信息都没有使用实际内容,导致搜索引擎看到的所有详情页都是统一的栏目名(如“新闻资讯 - 广西恒瑞天诚项目管理有限公司”)。

下方按 P0(立即修复)/ P1(强烈建议)/ P2(持续优化) 分级列出问题与落地建议。


二、已做 SEO 事项(可保留)

事项 位置 状态
全局 SSR,搜索引擎可抓取完整 HTML nuxt.config.ts routeRules
统一页面 SEO 工具(TDK + OG + Twitter + Canonical app/composables/usePageSeo.ts
Organization 结构化数据 useOrganizationSeo
首页 SEO + 组织结构化数据 app/pages/index.vue
CMS 单页 SEOtitle/keywords/description/photo app/pages/page/[id].vue
关于我们页 SEO template-*/pages/About.vue
动态 robots.txt(开发环境屏蔽) server/api/robots.txt.ts
动态 sitemap.xml server/api/sitemap.xml.ts (但范围不足,见 P0
旧链接 301 重定向(/news→/article 等) server/middleware/z-news-detail-redirect.ts
部分图片使用 loading="lazy" 模板 Home
富文本图片响应式处理 app/components/RichText.vue

三、问题清单与优化建议

🔴 P0 — 立即修复(影响搜索收录与排名)

1. 文章/产品/案例详情页没有使用内容标题做 SEO

问题

  • useModuleRoute 只给详情页设置了栏目兜底标题,如“新闻资讯 / 产品中心 / 案例展示”。
  • 所有模板中的 NewsDetail.vueProductDetail.vueCaseDetail.vue 均未调用 usePageSeo
  • 截图中的 /article/396(招标代理)实际标题应该是“招标代理 - 广西恒瑞天诚项目管理有限公司”,但搜索引擎看到的是“新闻资讯 - 广西恒瑞天诚项目管理有限公司”。

影响:大量详情页在搜索结果中标题重复、CTR 低;长尾关键词无法被有效索引。

修复方案:在每个详情组件中,拿到数据后立即调用 usePageSeo,并注入 Article / Product 结构化数据。

示例(app/templates/template-08/pages/NewsDetail.vue

const route = useRoute()
const id = route.params.id as string
const { siteInfo, fetchSiteInfo } = useSite()
await fetchSiteInfo()

const { data: article } = await useFetch<Article | null>(`/api/article/detail?id=${id}`, {
  key: `article-${id}`
})

// 用文章内容覆盖 SEO
usePageSeo({
  title: article.value?.title || '新闻资讯',
  description: stripHtml(article.value?.summary || '').slice(0, 160) || undefined,
  keywords: article.value?.tags?.join(',') || undefined,
  path: route.path, // 注意用 route.path 而非 route.fullPath,避免 ?navId= 进入 canonical
  image: article.value?.image || article.value?.cover || undefined,
  type: 'article',
  publishedTime: article.value?.publishTime || article.value?.createTime,
  modifiedTime: article.value?.updateTime
}, siteInfo.value)

// 注入文章结构化数据
useJsonLd({
  '@context': 'https://schema.org',
  '@type': 'Article',
  headline: article.value?.title,
  description: stripHtml(article.value?.summary || ''),
  image: article.value?.image || article.value?.cover || undefined,
  datePublished: article.value?.publishTime || article.value?.createTime,
  dateModified: article.value?.updateTime,
  author: { '@type': 'Organization', name: siteInfo.value?.websiteName }
})

需同步修改的模板文件

  • app/templates/template-0*/pages/NewsDetail.vue9 套)
  • app/templates/template-0*/pages/ProductDetail.vue9 套)
  • app/templates/template-0*/pages/CaseDetail.vue9 套)

如果 9 套模板结构差异大,可先在 useModuleRoute 中统一注入一份“最小可用 SEO”(标题用详情接口数据),再逐步让各模板补充更精细的 OG 图和结构化数据。


2. Sitemap 只包含单页,未覆盖文章/产品/案例详情和分页

问题server/api/sitemap.xml.ts 只拉取 cms-website/pageAll(即 CMS 单页 slug),未包含:

  • 文章详情页 /article/{id}
  • 产品详情页 /product/{id}
  • 案例详情页 /case/{id}
  • 栏目列表页 /article/article/{navId}/product/case

影响:搜索引擎依赖 sitemap 发现新内容,缺失会导致收录速度慢、遗漏详情页。

修复方案:扩展 sitemap.xml.ts,增加 article/product/case 列表接口调用,并拆分 sitemapindex(如果 URL 数量 > 50,000 或体积 > 50MB)。

最小可用实现(伪代码)

// server/api/sitemap.xml.ts
const [pages, articles, products, cases] = await Promise.all([
  fetchPages(),
  fetchList('/cms/cms-article/page', { limit: 1000 }),
  fetchList('/cms/cms-product/page', { limit: 1000 }),
  fetchList('/cms/cms-case/page', { limit: 1000 })
])

const staticUrls = [
  { loc: '/', priority: '1.0', changefreq: 'daily' },
  { loc: '/article', priority: '0.9', changefreq: 'daily' },
  { loc: '/product', priority: '0.9', changefreq: 'daily' },
  { loc: '/case', priority: '0.9', changefreq: 'daily' }
]

const articleUrls = articles.map(a => ({
  loc: `/article/${a.id || a.articleId}`,
  priority: '0.7',
  changefreq: 'weekly',
  lastmod: a.updateTime || a.publishTime
}))
// ... product/case 同理

注意事项

  • 只输出 status === 0(已发布)的内容。
  • 列表接口若支持 limit,需分页拉取全部;若总量大,建议生成 sitemapindex.xml + 多个子 sitemap。
  • 列表页分页(/article?page=2)可不必写入 sitemap,让搜索引擎通过页面内链发现即可。

3. Canonical URL 可能包含查询参数,导致重复内容

问题:多处使用 route.fullPath 作为 path 传给 usePageSeo

  • useModuleRoutepath: route.fullPath
  • app/pages/page/[id].vuepath: route.fullPath
  • app/pages/[slug].vuepath: /${slug}(这个 OK

当 URL 带 ?navId=xxx?keywords=xxx?page=2 时,canonical 会带上这些参数,造成同一内容多个 canonical。

修复方案:统一使用干净路径route.path)作为 canonical,必要时把分页参数也排除。

// 建议增加一个干净 canonical 工具
function getCanonicalPath(route: RouteLocationNormalizedLoaded) {
  // 只保留 path,去掉 navId/keywords/page 等查询参数
  return route.path
}

对于列表分页,如果希望保留 ?page=2 的 canonical,可单独处理;否则建议所有列表页的 canonical 都不带查询参数。


4. 详情页没有面包屑结构化数据

问题useBreadcrumbSeo 已定义但未被任何页面使用。

影响:Google 搜索结果可能无法展示面包屑导航,降低 SERP 丰富度。

修复方案:在详情页和列表页注入面包屑:

useBreadcrumbSeo([
  { name: '首页', url: '/' },
  { name: article.value?.categoryName || '新闻资讯', url: '/article' },
  { name: article.value?.title || '详情', url: route.path }
])

🟡 P1 — 强烈建议(提升 CTR 与收录质量)

5. 缺少 <html lang="zh-CN">

问题nuxt.config.tsapp.head 未设置 htmlAttrs.lang

影响:搜索引擎无法准确判断页面语言;屏幕阅读器体验受损。

修复方案

app: {
  head: {
    htmlAttrs: { lang: 'zh-CN' },
    // ...
  }
}

6. 404 页面没有 noindex

问题app/pages/404.vue 没有设置 robots: noindex

修复方案

useSeoMeta({
  robots: 'noindex, follow',
  title: '页面未找到'
})

7. 标题模板未统一站点名后缀

问题nuxt.config.tstitleTemplate: '%s' 只是原样输出,各页面需自己拼 - 站点名。目前 usePageSeo 会自动拼接,但兜底标题(如 app/pages/[slug].vue 中的 slug)可能出现没有站点名的情况。

建议:把全局 titleTemplate 改为 ' %s - {{siteName}}',或在 usePageSeo 中统一处理。当前 usePageSeo 已经拼接,可作为单一事实源。


8. 图片懒加载与 CLS 优化不足

问题

  • 多数模板图片没有 loading="lazy"
  • 所有图片缺少 width / heightaspect-ratio,导致 Cumulative Layout ShiftCLS)。
  • 没有 decoding="async"、没有响应式 srcset/sizes
  • 装饰性/无意义图片没有 alt=""

修复方案

<img
  :src="fileUrl(item.cover)"
  :alt="item.productName || ''"
  loading="lazy"
  decoding="async"
  width="800"
  height="450"
  class="w-full h-full object-cover"
>

对于 CMS 富文本中的图片,可在 RichText.vue 中统一注入 loading="lazy" decoding="async"


问题usePageSeo 在没有 input.image 时会 fallback 到 site.websiteLogo。站点 Logo 通常是 PNG/SVG,尺寸可能不符合 OG 推荐(1200×630)。

建议

  • 列表页/首页配置一张专门的 OG 封面图(可在 CMS siteInfo 中增加 ogImage 字段)。
  • 详情页优先使用内容封面图,并确保图片可被公开访问、尺寸合适。

10. 分页缺少 rel="prev/next" 或规范处理

问题:文章/产品列表页使用按钮分页,没有 <a> 标签链接,也没有 rel="prev/next"

影响:搜索引擎可能无法顺畅抓取深层列表页。

建议

  • 分页按钮使用 <NuxtLink> 而不是 <button @click>,使列表页 URL 变为可抓取的链接。
  • 在列表页 head 中注入:
if (currentPage.value > 1) {
  useHead({
    link: [
      { rel: 'prev', href: `/article${currentPage.value === 2 ? '' : '?page=' + (currentPage.value - 1)}` }
    ]
  })
}
if (currentPage.value < totalPages.value) {
  useHead({
    link: [
      { rel: 'next', href: `/article?page=${currentPage.value + 1}` }
    ]
  })
}

🟢 P2 — 持续优化(GEO 与长期竞争力)

11. GEO:内容结构不利于 AI 引擎引用

问题

  • 没有 FAQ / HowTo 结构化数据。
  • 富文本内容层级不清晰,AI 摘要时难以提取要点。
  • 服务/产品页面多为纯列表,缺少“问题-答案”式内容块。

建议

  • 在“关于我们”、“服务范围”等页面增加 FAQ 区块,并注入 FAQPage 结构化数据:
useJsonLd({
  '@context': 'https://schema.org',
  '@type': 'FAQPage',
  mainEntity: [
    {
      '@type': 'Question',
      name: '招标代理服务包含哪些内容?',
      acceptedAnswer: {
        '@type': 'Answer',
        text: '包括设计招标、监理招标、施工招标、材料及设备招标、政府采购招标等全流程代理服务。'
      }
    }
  ]
})
  • 产品/服务详情页使用清晰的 H2/H3 小标题,每个小节回答一个具体问题。
  • 在内容中自然覆盖“是什么 / 怎么做 / 多少钱 / 有什么优势”等 AI 常见引用角度。

12. 内部链接优化

问题

  • “返回新闻列表”链接使用 ?navId=,canonical 处理不好时会造成参数化 URL 泛滥。
  • 列表页卡片链接使用 /article/${id},但没有在正文中增加相关内容推荐链接。

建议

  • 详情页底部增加“相关文章/产品/案例”模块,提升内链密度与页面权重流动。
  • 面包屑使用干净路径链接。

13. 站点速度 / Core Web Vitals

问题

  • 没有图片 CDN 压缩或 WebP/AVIF 格式适配。
  • 没有预连接 DNS / 预加载关键资源。
  • 第三方字体(如有)可能阻塞渲染。

建议

  • nuxt.config.ts 增加:
app: {
  head: {
    link: [
      { rel: 'preconnect', href: 'https://server.websoft.top' },
      { rel: 'dns-prefetch', href: 'https://server.websoft.top' }
    ]
  }
}
  • 对 CMS 图片 URL 增加压缩参数(如 OSS 图片处理 ?x-oss-process=image/resize,w_1200)。
  • 评估引入 @nuxt/image 模块,自动实现懒加载、响应式、格式降级。

14. 搜索功能与搜索关键词页面

问题:搜索结果页(?keywords=xxx)可能被搜索引擎索引,造成低质量页面。

建议:在搜索结果页或无结果页注入 noindex

if (route.query.keywords) {
  useSeoMeta({ robots: 'noindex, follow' })
}

四、推荐落地顺序

顺序 任务 预期收益
1 为所有详情页注入 usePageSeo + Article/Product 结构化数据 解决标题重复问题,提升长尾词收录
2 扩展 sitemap.xml 覆盖 article/product/case 加速收录,减少遗漏
3 Canonical 统一使用 route.path,排除查询参数 避免重复内容,集中权重
4 注入面包屑结构化数据 提升 SERP 展示丰富度
5 设置 html lang="zh-CN"、404 noindex、OG 图片优化 完善基础 SEO 细节
6 图片懒加载、width/height、CLS 优化 提升 Core Web Vitals
7 FAQ / HowTo 结构化数据 + 内容结构化 提升 GEO/AI 引用概率

五、关键文件清单

文件 当前作用 建议改动
app/composables/usePageSeo.ts 统一 SEO 工具 保持现状,可被详情页复用
app/composables/useModuleRoute.ts 模块路由 + 基础 SEO 列表页 SEO 已 OK;详情页改为用 route.path
app/templates/template-0*/pages/NewsDetail.vue 文章详情 增加 usePageSeo + useJsonLd
app/templates/template-0*/pages/ProductDetail.vue 产品详情 增加 usePageSeo + useJsonLd
app/templates/template-0*/pages/CaseDetail.vue 案例详情 增加 usePageSeo + useJsonLd
server/api/sitemap.xml.ts 动态站点地图 扩展 article/product/case 列表
server/api/robots.txt.ts robots.txt 保持现状
nuxt.config.ts 全局配置 增加 htmlAttrs.lang、preconnect
app/pages/404.vue 404 页面 增加 noindex
app/components/RichText.vue 富文本渲染 统一为图片注入 lazy/decoding

六、结论

项目已经有不错的 SEO 基础架构,但 详情页 SEO 是当前最大的短板。修复后,预计:

  • 详情页长尾关键词收录量显著提升;
  • 搜索结果标题/描述点击率提升;
  • AI 搜索引擎(GEO)引用站点内容的概率提高。

建议优先完成 P0 四项(详情页 SEO、Sitemap、Canonical、面包屑),再逐步推进图片性能与 GEO 结构化数据。