You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

4.0 KiB

设计:个人主页文章详情页导出 Markdown

日期:2026-04-23
状态:已定稿(待你最终确认后进入实现计划)

1. 背景与目标

当前个人主页文章详情页支持阅读与评论,但缺少“导出原始 Markdown”能力。
本期目标是在公开文章详情页新增导出能力,满足“访客可直接下载 .md 文件”的需求。

2. 本期确认范围

  • 导出格式:仅 Markdown (.md)
  • 入口位置:个人主页文章详情页(/@[publicSlug]/posts/[postSlug])。
  • 权限:所有访客可导出(无需登录)。
  • 导出内容:仅正文 bodyMarkdown
  • 图片处理:导出时将站内相对图片链接转换为绝对 URL。

3. 非目标

  • 不导出 PDF / HTML。
  • 不附带 Front Matter 元数据。
  • 不打包图片文件到本地,仅处理 Markdown 内图片链接文本。
  • 不新增后端导出 API(本期前端本地导出)。

4. 技术方案

采用前端本地导出(Blob + download):

  1. 在详情页数据加载完成后显示 导出 .md 按钮。
  2. 点击按钮时读取当前已加载的 data.bodyMarkdown
  3. 仅对 Markdown 图片语法 ![](...) 中的链接做归一化:
    • 若为站内相对路径(如 /static/media/...),替换为 window.location.origin + path
    • 其他链接类型保持不变。
  4. 生成 Blob(['...'], { type: 'text/markdown;charset=utf-8' })
  5. 通过临时 <a download> 触发下载,文件名按规则生成。

该方案不新增接口,改动范围小,符合“仅导出正文、访客可用、快速交付”的目标。

5. URL 归一化规则

5.1 处理对象

  • 仅处理 Markdown 图片语法:![](...)
  • 普通链接语法 [](...) 不处理。

5.2 保持不变

  • http://...https://...
  • //...(协议相对)
  • data:image...

5.3 转换条件

  • / 开头的站内相对路径(重点覆盖 /static/media/...)。

5.4 基地址来源

  • 使用 window.location.origin 作为导出时站点基地址。

5.5 示例

  • 输入:![](/static/media/u1/a.webp)
  • 当前访问域名:https://example.com
  • 输出:![](https://example.com/static/media/u1/a.webp)

6. 交互与错误处理

  • 按钮位置:详情页顶部操作区,与“返回主页 / 编辑”同级。
  • 显示与可用性:
    • pending 状态禁用按钮。
    • error && !data 时不显示按钮。
    • data 就绪后访客可点击导出。
  • 成功反馈:导出完成后给轻量 Toast(如“已导出 Markdown”)。
  • 失败反馈:捕获异常并提示“导出失败,请稍后重试”,不影响页面其他功能。
  • 空正文:允许导出空 .md 文件。

7. 文件命名规则

  • 优先:${postSlug}.md
  • 回退:post-${id}.md

确保命名稳定、可预测,便于用户归档。

8. 验收标准

8.1 功能验收

  • 公开文章详情页可见 导出 .md 按钮,未登录访客可使用。
  • 点击后下载文件名符合规则。
  • 导出正文与 bodyMarkdown 一致(仅图片链接归一化除外)。

8.2 图片链接验收

  • ![](/static/media/xxx.png) 转换为当前域名下绝对 URL。
  • 已是绝对地址、协议相对地址、data URI 的图片链接保持不变。
  • 普通链接 [](...) 不被误改。

8.3 状态与异常

  • 加载中按钮禁用;加载失败场景不显示导出按钮。
  • 成功/失败提示可见且文案清晰。
  • 异常不导致页面崩溃。

8.4 回归检查

  • 原有“返回主页”“编辑(作者可见)”“评论”功能不受影响。
  • 移动端与桌面端按钮布局保持可用。

9. 实现建议(供下一步计划使用)

  • 修改页面:app/pages/@[publicSlug]/posts/[postSlug].vue
  • 可抽离工具函数(可选):app/utils/markdown-export.ts
    • normalizeMarkdownImageUrls(markdown: string, origin: string): string
    • downloadMarkdownFile(filename: string, content: string): void

后续进入 implementation plan 时,可根据当前代码风格决定“内联实现”或“工具函数抽离”。