项目从单体结构重构为 pnpm monorepo (shared/backend/frontend), 新增 YouTube、Instagram、Twitter/X、哔哩哔哩、微博 5 个平台适配器, 包含完整的单元测试和 E2E 测试覆盖。 - 完成 T-031~T-044: 5 个适配器实现、注册、配置和测试 - 重构前后端分离: Hono 后端 + Next.js 前端 - 151 个单元测试 + 21 个 Mock E2E + 25 个真实 E2E - 适配器基于真实 TikHub API 响应结构实现 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
62 KiB
62 KiB
Muse Creative Hotspots — UI 设计文档
文档信息
| 项目 | 内容 |
|---|---|
| 版本 | v1.0 |
| 创建日期 | 2026-03-02 |
| 来源文档 | DevelopmentPlan.md, FeatureSummary.md, PRD.md |
1. 设计概述
1.1 设计原则
| 原则 | 说明 |
|---|---|
| 简约现代 | 参考 Notion/Linear 风格,大量留白,信息层次清晰 |
| 内容优先 | 卡片封面图和数据指标是核心,UI 元素不喧宾夺主 |
| 平台可识别 | 平台图标采用官方配色,用户可快速辨别内容来源 |
| 状态完备 | 每个页面覆盖默认、加载中、空状态、错误四种状态 |
| 响应式 | 桌面端优先,CSS Grid 自适应屏幕宽度 |
1.2 页面总览
| 页面ID | 页面名称 | 路由 | 描述 | 对应功能 | 优先级 |
|---|---|---|---|---|---|
| P-001 | 首页 | / |
热点内容卡片信息流,含平台 Tab、排序、刷新 | F-001, F-002, F-003, F-005, F-006 | P0 |
| P-002 | 详情页 | /detail/[platform]/[id] |
单条内容完整信息展示 | F-004, F-007 | P0 |
| P-003 | 收藏夹 | /favorites |
已收藏内容的网格展示与管理 | F-007, F-008, F-009 | P0 |
| P-004 | 设置页 | /settings |
API Key 配置、刷新间隔设置 | F-010, F-011 | P0 |
1.3 页面导航图
┌──────────────────┐
│ P-001 │
│ 首页 │
│ (默认入口页面) │
└───────┬──────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
┌────────────────┐ ┌────────────────┐ ┌────────────────┐
│ P-002 │ │ P-003 │ │ P-004 │
│ 详情页 │ │ 收藏夹 │ │ 设置页 │
│ 点击卡片进入 │ │ Header导航进入 │ │ Header ⚙ 进入 │
└────────┬───────┘ └────────┬───────┘ └────────────────┘
│ │
│ ┌─────────────┘
│ │ 点击收藏卡片
▼ ▼
┌────────────────┐
│ P-002 │
│ 详情页(复用) │
└────────────────┘
导航说明:
- Header 常驻所有页面,提供全局导航(Logo 回首页、收藏夹入口、设置入口)
- 首页 → 详情页:点击任意内容卡片
- 首页 → 收藏夹:点击 Header 收藏入口
- 首页 → 设置页:点击 Header ⚙ 图标
- 收藏夹 → 详情页:点击收藏的内容卡片
- 详情页 → 返回上一页:浏览器 Back / 返回按钮
2. 页面设计
2.1 P-001: 首页
页面信息
| 属性 | 值 |
|---|---|
| 页面ID | P-001 |
| 路由 | / |
| 对应功能 | F-001, F-002, F-003, F-005, F-006 |
| 入口 | 应用默认页面;Header Logo 点击 |
| 出口 | P-002(点击卡片)、P-003(Header 收藏)、P-004(Header 设置) |
页面布局 — ASCII 原型图
┌──────────────────────────────────────────────────────────────────────────┐
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ Header │ │
│ │ │ │
│ │ Muse [全部] [抖音] [TikTok] [小红书] [♡ 收藏] [⚙] │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
├──────────────────────────────────────────────────────────────────────────┤
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ Toolbar │ │
│ │ │ │
│ │ 排序: [▼ 播放量] [↓ 降序] [🔄 刷新] 上次: 10:30 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ │ 封面图 │ │ │ │ 封面图 │ │ │ │ 封面图 │ │ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │ │
│ │ │ │ │ │ │ │
│ │ 📱 抖音 │ │ 🎵 TikTok │ │ 📕 小红书 │ │
│ │ 标题文字截断展 │ │ Title text tr.. │ │ 标题文字截断展 │ │
│ │ 示最多两行... │ │ uncated to two.. │ │ 示最多两行... │ │
│ │ │ │ │ │ │ │
│ │ 👤 作者昵称 │ │ 👤 Author Name │ │ 👤 作者昵称 │ │
│ │ ▶1.2M ❤5.3K │ │ ▶800K ❤3.1K │ │ ❤2.1K 💬156 │ │
│ │ 💬 203 [♡] │ │ 💬 150 [♡] │ │ [♡] │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │ │
│ │ │ 封面图 │ │ │ │ 封面图 │ │ │ │ 封面图 │ │ │
│ │ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │ │
│ │ 📱 抖音 │ │ 📕 小红书 │ │ 🎵 TikTok │ │
│ │ 标题文字... │ │ 标题文字... │ │ Title text... │ │
│ │ 👤 作者 ▶ ❤ 💬 │ │ 👤 作者 ❤ 💬 │ │ 👤 Author ▶ ❤ │ │
│ │ [♡] │ │ [♡] │ │ [♡] │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ ... 更多卡片 ... │
│ │
└──────────────────────────────────────────────────────────────────────────┘
组件清单
| 组件ID | 组件名称 | 类型 | 说明 | 交互 |
|---|---|---|---|---|
| C-001 | Header | 导航栏 | 顶部固定,含 Logo、平台 Tab、收藏入口、设置入口 | Logo 回首页;Tab 切换平台 |
| C-002 | PlatformTabs | Tab 栏 | Header 内嵌,展示"全部"+各平台 Tab | 点击切换平台,触发数据重新获取 |
| C-003 | SortToolbar | 工具栏 | 排序选择器 + 排序方向 + 刷新按钮 + 上次刷新时间 | 选择排序字段/方向;点击刷新 |
| C-004 | ContentCard | 卡片 | 单条内容:封面图、平台标识、标题、作者、数据指标、收藏按钮 | 点击进入详情页;点击 ♡ 收藏 |
| C-005 | ContentGrid | 网格容器 | CSS Grid 响应式网格 repeat(auto-fill, minmax(280px, 1fr)) |
- |
| C-006 | CardSkeleton | 骨架屏 | 数据加载时的占位动画 | - |
| C-008 | FavoriteButton | 收藏按钮 | 卡片右下角,♡ 空心/♥ 实心切换 | 点击切换收藏状态 |
交互说明
| 触发 | 动作 | 结果 |
|---|---|---|
| 点击平台 Tab | 切换 queryKey | 重新获取对应平台数据,"全部"聚合所有平台 |
| 选择排序字段/方向 | 前端内存排序(useMemo) | 卡片网格重新排列,不请求 API |
| 点击刷新按钮 | invalidateQueries + 重置自动刷新计时器 | 按钮显示 loading 旋转,完成后更新"上次刷新"时间 |
| 点击卡片(非收藏按钮区域) | 路由跳转 | 进入 /detail/[platform]/[id] 详情页 |
| 点击卡片收藏按钮 ♡ | Zustand addFavorite/removeFavorite | ♡ ↔ ♥ 状态切换,数据持久化到 localStorage |
| 自动定时刷新 | refetchInterval 触发 | 静默刷新数据,更新"上次刷新"时间 |
| 页面不可见(切换 Tab) | 暂停自动刷新 | 节省 API 调用 |
页面状态
| 状态 | 说明 | 展示 |
|---|---|---|
| 默认 | 数据加载完成 | 卡片网格正常展示 |
| 加载中 | 首次加载或切换平台 | 骨架屏(CardSkeleton x N) |
| 刷新中 | 手动/自动刷新 | 刷新按钮旋转,保留当前卡片 |
| 空状态 | 平台无数据返回 | 空状态组件 + "暂无热点内容" |
| 错误 — API Key 未配置 | 未设置 API Key | 引导提示 + "去配置" 按钮跳转设置页 |
| 错误 — 请求失败 | 网络/服务端错误 | 错误提示 + "重试" 按钮 |
| 错误 — 频率超限 | 429 响应 | 提示"请求过于频繁,请稍后重试" |
加载态原型
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │░░░░░░░░░░░░░│ │ │ │░░░░░░░░░░░░░│ │ │ │░░░░░░░░░░░░░│ │
│ │░░░░░░░░░░░░░│ │ │ │░░░░░░░░░░░░░│ │ │ │░░░░░░░░░░░░░│ │
│ │░░░░░░░░░░░░░│ │ │ │░░░░░░░░░░░░░│ │ │ │░░░░░░░░░░░░░│ │
│ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │
│ ░░░░░░ │ │ ░░░░░░ │ │ ░░░░░░ │
│ ░░░░░░░░░░░░░░ │ │ ░░░░░░░░░░░░░░ │ │ ░░░░░░░░░░░░░░ │
│ ░░░░░░░░░░ │ │ ░░░░░░░░░░ │ │ ░░░░░░░░░░ │
│ ░░░░ ░░░░ ░░░░ │ │ ░░░░ ░░░░ ░░░░ │ │ ░░░░ ░░░░ ░░░░ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
空状态原型
┌──────────────────────────────────────────────────────────┐
│ │
│ ┌──────────┐ │
│ │ 📭 │ │
│ └──────────┘ │
│ │
│ 暂无热点内容 │
│ 当前平台暂时没有热门内容 │
│ │
│ [🔄 刷新试试] │
│ │
└──────────────────────────────────────────────────────────┘
API Key 未配置引导原型
┌──────────────────────────────────────────────────────────┐
│ │
│ ┌──────────┐ │
│ │ 🔑 │ │
│ └──────────┘ │
│ │
│ 请先配置 API Key │
│ 需要配置 TikHub API Key 才能获取内容 │
│ │
│ [⚙ 前往设置] │
│ │
└──────────────────────────────────────────────────────────┘
2.2 P-002: 详情页
页面信息
| 属性 | 值 |
|---|---|
| 页面ID | P-002 |
| 路由 | /detail/[platform]/[id] |
| 对应功能 | F-004, F-007 |
| 入口 | P-001(点击卡片)、P-003(点击收藏卡片) |
| 出口 | 返回上一页;外部跳转(查看原文) |
页面布局 — ASCII 原型图
┌──────────────────────────────────────────────────────────────────────────┐
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ Header(同首页 C-001) │ │
│ └────────────────────────────────────────────────────────────────────┘ │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ [← 返回] [♡ 收藏] │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ┌──────────────────────────────────────────┐ ┌──────────────┐ │ │
│ │ │ │ │ │ │ │
│ │ │ │ │ 平台信息 │ │ │
│ │ │ │ │ │ │ │
│ │ │ 封面图 │ │ 📱 抖音 │ │ │
│ │ │ (大图展示) │ │ │ │ │
│ │ │ │ │ 发布时间 │ │ │
│ │ │ │ │ 2026-03-01 │ │ │
│ │ │ │ │ 14:30 │ │ │
│ │ │ │ │ │ │ │
│ │ └──────────────────────────────────────────┘ └──────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ 内容标题(完整展示,不截断) │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ 作者信息 │ │ │
│ │ │ ┌────┐ │ │ │
│ │ │ │头像│ 作者昵称 │ │ │
│ │ │ └────┘ │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ 数据指标面板 │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │
│ │ │ │ ▶ 播放量 │ │ ❤ 点赞 │ │ 💬 评论 │ │ ↗ 分享 │ │ │ │
│ │ │ │ 1,234,567│ │ 53,210 │ │ 2,031 │ │ 8,456 │ │ │ │
│ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ 标签 │ │ │
│ │ │ [#热门话题] [#创意] [#内容创作] │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ [🔗 查看原文] │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────┘
组件清单
| 组件ID | 组件名称 | 类型 | 说明 | 交互 |
|---|---|---|---|---|
| C-001 | Header | 导航栏 | 复用全局 Header | 同首页 |
| C-007 | DetailPanel | 信息面板 | 封面大图 + 标题 + 作者 + 数据指标 + 标签 | - |
| C-008 | FavoriteButton | 收藏按钮 | 右上角,大号 ♡/♥ | 点击切换收藏 |
交互说明
| 触发 | 动作 | 结果 |
|---|---|---|
| 点击 [← 返回] | 路由 back | 返回上一页面(首页或收藏夹) |
| 点击 [♡ 收藏] | Zustand toggle | 收藏状态切换 |
| 点击 [🔗 查看原文] | window.open | 新窗口打开原平台页面 |
| 封面图加载失败 | 显示占位图 | 灰色占位 + 平台图标 |
页面状态
| 状态 | 说明 | 展示 |
|---|---|---|
| 默认 | 详情数据加载完成 | 完整信息面板 |
| 加载中 | 请求详情 API | 骨架屏(大图区域 + 文字行) |
| 错误 | 详情加载失败 | 错误提示 + "重试" + "返回首页" 按钮 |
错误态原型
┌────────────────────────────────────────────────────────────┐
│ [← 返回] │
│ │
│ │
│ ┌──────────┐ │
│ │ ⚠️ │ │
│ └──────────┘ │
│ │
│ 内容加载失败 │
│ 请检查网络连接或稍后重试 │
│ │
│ [🔄 重试] [🏠 返回首页] │
│ │
│ │
└────────────────────────────────────────────────────────────┘
加载态原型
┌────────────────────────────────────────────────────────────┐
│ [← 返回] │
│ │
│ ┌──────────────────────────────────┐ ┌──────────────┐ │
│ │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ │░░░░░░░░░░░░░░│ │
│ │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ │░░░░░░░░░░░░░░│ │
│ │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ │░░░░░░░░░░░░░░│ │
│ │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ └──────────────┘ │
│ └──────────────────────────────────┘ │
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
│ ░░░░ ░░░░░░░░░░░ │
│ ░░░░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ │
│ ░░░░░░░░░ ░░░░░░ ░░░░░░░ │
└────────────────────────────────────────────────────────────┘
2.3 P-003: 收藏夹页面
页面信息
| 属性 | 值 |
|---|---|
| 页面ID | P-003 |
| 路由 | /favorites |
| 对应功能 | F-007, F-008, F-009 |
| 入口 | Header 收藏入口 |
| 出口 | P-002(点击卡片);Header 导航至其他页面 |
页面布局 — ASCII 原型图
┌──────────────────────────────────────────────────────────────────────────┐
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ Header(同首页 C-001) │ │
│ └────────────────────────────────────────────────────────────────────┘ │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 我的收藏 共 12 条 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │ │
│ │ │ 封面图 │ │ │ │ 封面图 │ │ │ │ 封面图 │ │ │
│ │ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │ │
│ │ 📱 抖音 │ │ 🎵 TikTok │ │ 📕 小红书 │ │
│ │ 标题文字... │ │ Title text... │ │ 标题文字... │ │
│ │ 👤 作者 │ │ 👤 Author │ │ 👤 作者 │ │
│ │ ▶1.2M ❤5.3K │ │ ▶800K ❤3.1K │ │ ❤2.1K 💬156 │ │
│ │ [♥] │ │ [♥] │ │ [♥] │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ ┌─────────────┐ │ │ ┌─────────────┐ │ │
│ │ │ 封面图 │ │ │ │ 封面图 │ │ │
│ │ └─────────────┘ │ │ └─────────────┘ │ │
│ │ ... │ │ ... │ │
│ │ [♥] │ │ [♥] │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────┘
组件清单
| 组件ID | 组件名称 | 类型 | 说明 | 交互 |
|---|---|---|---|---|
| C-001 | Header | 导航栏 | 复用全局 Header | 同首页 |
| C-005 | ContentGrid | 网格容器 | 复用首页网格布局 | - |
| C-004 | ContentCard | 卡片 | 复用首页卡片,收藏按钮为实心 ♥ | 点击进入详情;点击 ♥ 取消收藏 |
交互说明
| 触发 | 动作 | 结果 |
|---|---|---|
| 点击卡片 | 路由跳转 | 进入 /detail/[platform]/[id] |
| 点击 ♥ 取消收藏 | Zustand removeFavorite | 卡片从列表移除 |
页面状态
| 状态 | 说明 | 展示 |
|---|---|---|
| 默认 | 有收藏内容 | 卡片网格展示 |
| 空状态 | 无收藏内容 | 空状态引导 |
空状态原型
┌──────────────────────────────────────────────────────────┐
│ │
│ ┌──────────┐ │
│ │ ♡ │ │
│ └──────────┘ │
│ │
│ 还没有收藏内容 │
│ 浏览热点内容时,点击 ♡ 收藏感兴趣的内容 │
│ │
│ [去浏览热点] │
│ │
└──────────────────────────────────────────────────────────┘
2.4 P-004: 设置页面
页面信息
| 属性 | 值 |
|---|---|
| 页面ID | P-004 |
| 路由 | /settings |
| 对应功能 | F-010, F-011 |
| 入口 | Header ⚙ 图标 |
| 出口 | Header 导航至其他页面 |
页面布局 — ASCII 原型图
┌──────────────────────────────────────────────────────────────────────────┐
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ Header(同首页 C-001) │ │
│ └────────────────────────────────────────────────────────────────────┘ │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 设置 │ │
│ │ │ │
│ │ ─────────────────────────────────────────────── │ │
│ │ │ │
│ │ API 配置 │ │
│ │ │ │
│ │ TikHub API Key │ │
│ │ ┌──────────────────────────────────────────┐ │ │
│ │ │ sk-xxxxxxxxxxxxxxxxxxxx │ │ │
│ │ └──────────────────────────────────────────┘ │ │
│ │ API Key 保存在服务端,不会暴露到浏览器。 │ │
│ │ 优先使用 .env.local 中的预配置值。 │ │
│ │ │ │
│ │ [保存 API Key] │ │
│ │ │ │
│ │ ─────────────────────────────────────────────── │ │
│ │ │ │
│ │ 刷新设置 │ │
│ │ │ │
│ │ 自动刷新间隔 │ │
│ │ ┌──────────────────────────────────────────┐ │ │
│ │ │ 30 分钟 ▼ │ │ │
│ │ └──────────────────────────────────────────┘ │ │
│ │ 可选: 5 / 10 / 15 / 30 / 60 分钟 │ │
│ │ 最低 5 分钟,防止 API 成本过高。 │ │
│ │ │ │
│ │ ─────────────────────────────────────────────── │ │
│ │ │ │
│ │ 关于 │ │
│ │ │ │
│ │ Muse Creative Hotspots v1.0 │ │
│ │ 数据来源: TikHub API (api.tikhub.io) │ │
│ │ │ │
│ └──────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────┘
组件清单
| 组件ID | 组件名称 | 类型 | 说明 | 交互 |
|---|---|---|---|---|
| C-001 | Header | 导航栏 | 复用全局 Header | 同首页 |
| C-011 | ApiKeyInput | 输入框 | password 类型输入框 + 保存按钮 | 输入 Key → 点击保存 |
| C-012 | IntervalSelect | 下拉选择 | 刷新间隔选择器,选项: 5/10/15/30/60 分钟 | 选择后立即生效 |
交互说明
| 触发 | 动作 | 结果 |
|---|---|---|
| 输入 API Key + 点击保存 | POST /api/settings | 保存到服务端内存变量;成功/失败 Toast 提示 |
| 选择刷新间隔 | Zustand setRefreshInterval | 立即更新 TanStack Query refetchInterval |
| API Key 为空点击保存 | 前端校验 | 输入框红色边框 + "请输入 API Key" 提示 |
页面状态
| 状态 | 说明 | 展示 |
|---|---|---|
| 默认 | 正常展示设置表单 | 当前 Key(掩码)+ 当前间隔 |
| 保存中 | API Key 保存请求中 | 保存按钮 loading |
| 保存成功 | 保存完成 | Toast "API Key 已保存" |
| 保存失败 | 保存请求失败 | Toast "保存失败,请重试" |
3. 用户流程
3.1 内容浏览主流程
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 打开应用 │ ──▶ │ 浏览首页 │ ──▶ │ 筛选/排序 │ ──▶ │ 查看详情 │
│ │ │ 卡片信息流│ │ 切换平台 │ │ 完整信息 │
│ P-001 │ │ P-001 │ │ P-001 │ │ P-002 │
└──────────┘ └────┬─────┘ └──────────┘ └────┬─────┘
│ │
│ API Key 未配置 │
▼ ▼
┌──────────┐ ┌──────────┐
│ 配置Key │ │ 查看原文 │
│ P-004 │ │ (外部跳转)│
└──────────┘ └──────────┘
流程步骤
| 步骤 | 页面 | 用户操作 | 系统响应 |
|---|---|---|---|
| 1 | P-001 | 打开应用 | 自动获取默认平台(全部)的热点内容 |
| 2 | P-001 | 浏览卡片信息流 | 展示卡片网格(封面图+标题+数据) |
| 3 | P-001 | 点击平台 Tab 切换 | 重新获取对应平台数据,更新卡片网格 |
| 4 | P-001 | 选择排序方式 | 前端内存排序,卡片重新排列 |
| 5 | P-001 | 点击感兴趣的卡片 | 路由跳转到详情页 |
| 6 | P-002 | 查看完整信息 | 展示大图+标题+数据面板+标签 |
| 7 | P-002 | 点击"查看原文" | 新窗口打开原平台页面 |
3.2 收藏管理流程
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 浏览内容 │ ──▶ │ 点击收藏 │ ──▶ │ 打开收藏夹│ ──▶ │ 查看详情 │
│ P-001 │ │ ♡ → ♥ │ │ P-003 │ │ P-002 │
└──────────┘ └──────────┘ └──────────┘ └────┬─────┘
│ │
│ 也可在详情页收藏 │
▼ │
┌──────────┐ │
│ 详情页收藏│ │
│ P-002 │ ◀────────────────────────────┘
└──────────┘
流程步骤
| 步骤 | 页面 | 用户操作 | 系统响应 |
|---|---|---|---|
| 1 | P-001/P-002 | 点击卡片收藏按钮 ♡ | ♡ → ♥ 切换,存入 localStorage |
| 2 | P-003 | 点击 Header 收藏入口 | 展示所有已收藏内容的卡片网格 |
| 3 | P-003 | 点击卡片 | 跳转详情页 |
| 4 | P-003 | 点击 ♥ 取消收藏 | 卡片从收藏列表移除 |
3.3 设置配置流程
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 点击 ⚙ │ ──▶ │ 输入Key │ ──▶ │ 保存Key │ ──▶ │ 设置间隔 │
│ P-001 │ │ P-004 │ │ P-004 │ │ P-004 │
└──────────┘ └──────────┘ └────┬─────┘ └────┬─────┘
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ 保存成功 │ │ 立即生效 │
│ Toast提示 │ │ 更新定时器│
└──────────┘ └──────────┘
流程步骤
| 步骤 | 页面 | 用户操作 | 系统响应 |
|---|---|---|---|
| 1 | P-001 | 点击 Header ⚙ 图标 | 跳转设置页 |
| 2 | P-004 | 输入 TikHub API Key | 输入框显示内容(password 掩码) |
| 3 | P-004 | 点击"保存 API Key" | POST /api/settings,成功后 Toast 提示 |
| 4 | P-004 | 选择刷新间隔(如 15 分钟) | Zustand 更新,TanStack Query refetchInterval 立即变更 |
| 5 | P-001 | 返回首页 | 使用新 Key 获取数据,按新间隔自动刷新 |
3.4 数据刷新流程
┌─────────────────────────────────────┐
│ 刷新触发 │
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ 自动刷新 │ │ 手动刷新 │ │
│ │ 定时器 │ │ 点击 🔄 │ │
│ └────┬─────┘ └────┬─────┘ │
│ │ │ │
│ └────────┬─────────┘ │
│ ▼ │
│ ┌──────────┐ │
│ │ 获取数据 │ │
│ │ F-001 │ │
│ └────┬─────┘ │
│ │ │
│ ┌────┴────┐ │
│ ▼ ▼ │
│ ┌────────┐ ┌────────┐ │
│ │ 成功 │ │ 失败 │ │
│ └───┬────┘ └───┬────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ │
│ │ 更新卡片 │ │ 保留旧数据│ │
│ │ 更新时间 │ │ 错误提示 │ │
│ └──────────┘ └──────────┘ │
│ │
└─────────────────────────────────────┘
4. 组件规范
4.1 全局组件
C-001: Header 导航栏
┌──────────────────────────────────────────────────────────────────────┐
│ │
│ Muse [全部] [抖音] [TikTok] [小红书] [♡ 收藏] [⚙] │
│ ─────── │
│ (当前Tab下划线高亮) │
│ │
└──────────────────────────────────────────────────────────────────────┘
说明:
- 左侧: Logo 文字 "Muse",点击回首页
- 中部: 平台 Tab 栏(仅首页展示),当前选中 Tab 有下划线高亮
- 右侧: 收藏入口 + 设置图标
- 固定在页面顶部 (sticky top)
- 背景: 白色,底部 1px 边框线
C-001 变体: 非首页 Header(详情页、收藏夹、设置页使用)
┌──────────────────────────────────────────────────────────────────────┐
│ │
│ Muse [♡ 收藏] [⚙] │
│ │
└──────────────────────────────────────────────────────────────────────┘
说明:
- 平台 Tab 栏在非首页隐藏,Logo 与右侧图标之间保持留白
- 其余元素位置不变(Logo 左对齐,图标右对齐)
- 适用于: P-002 详情页、P-003 收藏夹、P-004 设置页
C-002: PlatformTabs 平台 Tab 栏
默认态: [全部] [抖音] [TikTok] [小红书]
─────
(选中态: 下划线 + 文字加粗)
各 Tab 含平台图标:
全部: 🌐 全部
抖音: 📱 抖音 (品牌色: #000000)
TikTok: 🎵 TikTok (品牌色: #00F2EA)
小红书: 📕 小红书 (品牌色: #FF2442)
Tab 切换时,下划线平滑滑动过渡
C-003: SortToolbar 工具栏
┌──────────────────────────────────────────────────────────────────────┐
│ │
│ 排序: [▼ 播放量] [↓ 降序] [🔄 刷新] 上次: 10:30│
│ ────────── ──────── ──────── │
│ 下拉选择 切换按钮 图标按钮 │
│ │
└──────────────────────────────────────────────────────────────────────┘
排序字段选项:
- 播放量 (play_count)
- 点赞数 (like_count)
- 评论数 (comment_count)
- 发布时间 (publish_time)
排序方向:
- ↓ 降序 (desc) — 默认
- ↑ 升序 (asc)
刷新按钮状态:
- 默认: 🔄 图标
- 刷新中: 旋转动画
- 防抖: 2 秒内重复点击忽略
4.2 业务组件
C-004: ContentCard 内容卡片
┌─────────────────────┐
│ ┌─────────────────┐ │
│ │ │ │ ← 封面图区域 (aspect-ratio: 4/3 或 3/4)
│ │ Cover Image │ │ 加载失败 → 灰色占位 + 平台图标
│ │ │ │ 使用 Next.js Image + loading="lazy"
│ └─────────────────┘ │
│ │
│ 📱 抖音 │ ← 平台图标 + 平台名 (品牌色小标签)
│ │
│ 标题文字截断展示最 │ ← 标题 (最多 2 行, line-clamp-2)
│ 多两行省略号... │
│ │
│ ┌──┐ │
│ │头│ 作者昵称 │ ← 作者头像 (24px 圆形) + 昵称
│ └──┘ │
│ │
│ ▶ 1.2M ❤ 5.3K │ ← 数据指标 (数字缩写: K/M)
│ 💬 203 [♡] │ ← 评论数 + 收藏按钮
│ │
└─────────────────────┘
宽度: minmax(280px, 1fr)
圆角: 8px (rounded-lg)
阴影: hover 时提升阴影
边框: 1px solid border色
过渡: hover 时 translateY(-2px)
收藏按钮 [♡]:
未收藏: ♡ 空心,灰色
已收藏: ♥ 实心,红色
点击区域: 足够大 (44x44px),防止误触卡片跳转
指标展示规则: <!-- NEW -->
指标值为空(undefined)时隐藏该指标项,而非展示 0。
与 DevelopmentPlan ContentItem 类型中 play_count?: number(可选字段)一致。
Hover 行为: <!-- NEW -->
MVP 阶段: hover 仅做视觉反馈(阴影提升 + translateY(-2px))。
v1.1 迭代: 考虑增加 hover tooltip/popover 展示更多信息(如完整标题、分享数)。
C-006: CardSkeleton 骨架屏
┌─────────────────────┐
│ ┌─────────────────┐ │
│ │░░░░░░░░░░░░░░░░░│ │ ← 封面占位 (pulse 动画)
│ │░░░░░░░░░░░░░░░░░│ │
│ │░░░░░░░░░░░░░░░░░│ │
│ └─────────────────┘ │
│ ░░░░░░░ │ ← 平台标签占位
│ ░░░░░░░░░░░░░░░░░░ │ ← 标题行占位
│ ░░░░░░░░░░░░░ │
│ ░░ ░░░░░░░░░ │ ← 作者占位
│ ░░░░ ░░░░ ░░░░ │ ← 数据指标占位
└─────────────────────┘
动画: Tailwind animate-pulse
颜色: bg-slate-200
C-008: FavoriteButton 收藏按钮
未收藏态: ♡ (text-slate-400, hover: text-red-400)
已收藏态: ♥ (text-red-500)
过渡动画: 点击时 scale 弹跳效果 (scale-110 → scale-100)
点击区域: 44px x 44px (无障碍最小触摸区域)
阻止冒泡: e.stopPropagation() 防止触发卡片跳转
C-009: EmptyState 空状态
┌──────────────────────────────────────┐
│ │
│ ┌──────────┐ │
│ │ {icon} │ │
│ └──────────┘ │
│ │
│ {主要文案} │ ← text-slate-800, 16px, medium
│ {辅助文案} │ ← text-slate-500, 14px, regular
│ │
│ [{action button}] │ ← 可选操作按钮
│ │
└──────────────────────────────────────┘
Props:
icon: ReactNode (图标)
title: string (主要文案)
description: string (辅助文案)
action?: { label: string, onClick: () => void }
C-013: Toast 通知
成功态 (右上角弹出):
┌──────────────────────────────┐
│ ✅ API Key 已保存 │
└──────────────────────────────┘
失败态 (右上角弹出):
┌──────────────────────────────┐
│ ❌ 保存失败,请重试 │
└──────────────────────────────┘
规范:
- 使用 shadcn/ui Toast 组件
- 位置: 页面右上角 (top-right)
- 自动消失时间: 3 秒
- 成功态: 绿色左边框 (border-l-4 border-green-500)
- 失败态: 红色左边框 (border-l-4 border-red-500)
- 支持手动关闭 (点击 × 按钮)
- 背景: white,阴影: shadow-lg
- 进入动画: slide-in-from-right
- 退出动画: fade-out
C-010: ErrorState 错误状态
┌──────────────────────────────────────┐
│ │
│ ┌──────────┐ │
│ │ ⚠️ │ │
│ └──────────┘ │
│ │
│ {错误描述} │ ← text-red-600
│ │
│ [🔄 重试] │ ← 主按钮
│ │
└──────────────────────────────────────┘
5. 设计规范
5.1 色彩规范
| 用途 | 色值 | Tailwind Class | 示例 |
|---|---|---|---|
| 主色 | #2563EB | blue-600 |
主按钮、链接、选中态 |
| 主色悬停 | #1D4ED8 | blue-700 |
按钮 hover |
| 成功 | #16A34A | green-600 |
保存成功提示 |
| 警告 | #D97706 | amber-600 |
频率限制提示 |
| 错误 | #DC2626 | red-600 |
错误提示、必填校验 |
| 收藏红 | #EF4444 | red-500 |
♥ 已收藏状态 |
| 文字主色 | #1E293B | slate-800 |
标题、正文 |
| 文字次色 | #64748B | slate-500 |
描述、辅助信息 |
| 文字弱色 | #94A3B8 | slate-400 |
占位文字、禁用态 |
| 背景色 | #FFFFFF | white |
页面背景 |
| 表面色 | #F8FAFC | slate-50 |
卡片背景、工具栏 |
| 边框色 | #E2E8F0 | slate-200 |
卡片边框、分割线 |
平台品牌色
| 平台 | 色值 | 用途 |
|---|---|---|
| 抖音 | #000000 | Tab 标签、平台图标背景 |
| TikTok | #00F2EA | Tab 标签、平台图标背景 |
| 小红书 | #FF2442 | Tab 标签、平台图标背景 |
5.2 字体规范
| 用途 | 字号 | 字重 | Tailwind Class |
|---|---|---|---|
| 页面标题 | 24px | Bold (700) | text-2xl font-bold |
| 区域标题 | 20px | Semibold (600) | text-xl font-semibold |
| 卡片标题 | 14px | Medium (500) | text-sm font-medium |
| 正文 | 14px | Regular (400) | text-sm |
| 数据指标 | 12px | Medium (500) | text-xs font-medium |
| 辅助文字 | 12px | Regular (400) | text-xs |
- 字体族: 系统默认字体栈 (Inter, system-ui, sans-serif)
5.3 间距规范
| 间距 | 值 | Tailwind | 用途 |
|---|---|---|---|
| xs | 4px | 1 |
图标与文字间距 |
| sm | 8px | 2 |
卡片内元素间距 |
| md | 12px | 3 |
卡片内部 padding |
| lg | 16px | 4 |
组件间距、网格 gap |
| xl | 24px | 6 |
区域间距 |
| 2xl | 32px | 8 |
页面 padding |
5.4 圆角规范
| 元素 | 圆角 | Tailwind |
|---|---|---|
| 卡片 | 8px | rounded-lg |
| 按钮 | 6px | rounded-md |
| 输入框 | 6px | rounded-md |
| 头像 | 50% | rounded-full |
| 平台标签 | 4px | rounded |
5.5 阴影规范
| 场景 | 阴影 | Tailwind |
|---|---|---|
| 卡片默认 | 0 1px 2px rgba(0,0,0,0.05) | shadow-sm |
| 卡片悬停 | 0 4px 6px rgba(0,0,0,0.1) | shadow-md |
| Header | 0 1px 3px rgba(0,0,0,0.1) | shadow-sm |
| 弹窗/Toast | 0 10px 15px rgba(0,0,0,0.1) | shadow-lg |
5.6 响应式断点
| 断点 | 宽度 | Tailwind | 布局说明 |
|---|---|---|---|
| Mobile | < 640px | sm: |
单栏,卡片全宽 |
| Tablet | 640px - 1024px | md: / lg: |
2 列卡片网格 |
| Desktop | 1024px - 1280px | xl: |
3 列卡片网格 |
| Wide | > 1280px | 2xl: |
4-5 列卡片网格 |
网格响应式规则:
CSS Grid: grid-template-columns: repeat(auto-fill, minmax(280px, 1fr))
< 640px: 1 列 (卡片宽度 100%)
640-960: 2 列
960-1240: 3 列
1240-1520: 4 列
> 1520: 5 列
6. 页面与功能映射
| 功能ID | 功能名称 | 所在页面 | 主要组件 |
|---|---|---|---|
| F-001 | 内容获取 | P-001 | TanStack Query (数据层) |
| F-002 | 卡片信息流展示 | P-001 | C-005 ContentGrid + C-004 ContentCard |
| F-003 | 内容筛选与排序 | P-001 | C-002 PlatformTabs + C-003 SortToolbar |
| F-004 | 内容详情页 | P-002 | C-007 DetailPanel |
| F-005 | 自动定时刷新 | P-001 | C-003 SortToolbar (时间显示) |
| F-006 | 手动刷新 | P-001 | C-003 SortToolbar (刷新按钮) |
| F-007 | 内容收藏 | P-001, P-002 | C-008 FavoriteButton |
| F-008 | 收藏夹管理 | P-003 | C-005 ContentGrid + C-004 ContentCard |
| F-009 | 收藏数据持久化 | P-003 | Zustand persist (数据层) |
| F-010 | API Key 配置 | P-004 | C-011 ApiKeyInput |
| F-011 | 刷新间隔设置 | P-004 | C-012 IntervalSelect |
| F-014 | API 请求代理 | - | 后端 API Route (无 UI) |
| F-015 | 统一数据模型 | - | TypeScript 类型 (无 UI) |
| F-016 | 平台适配器 | - | 后端适配器 (无 UI) |