Day4(Web 端桌面/移动适配 + 样式复现 + 云端资讯接入)
今日目标
根据 design 设计稿,对照 apps/web/src 现有代码,完成以下三件事并沉淀为可复盘记录:
- 设计并落地各页面桌面端/移动端
- 复现设计稿核心视觉样式
- 接入云端 API,获取最新 AI 资讯
Step 1:页面设计拆解与端形态确认(已完成)
1.1 设计稿范围确认
已逐一核对以下 4 组页面设计稿(桌面 + 移动):
design/Portal/desktop.htmldesign/Portal/mobile.htmldesign/Todos/desktop.htmldesign/Todos/mobile.htmldesign/AiNews/desktop.htmldesign/AiNews/mobile.htmldesign/Info/desktop.htmldesign/Info/mobile.html
1.2 Web 路由与页面映射确认
已确认 apps/web/src/App.tsx 中页面路由与设计稿主题对应关系:
/-> 门户页(Portal)/todos-> 待办页(Todos)/ai-news-> 资讯页(AiNews)/web-> 个人信息页(Info/Profile)
对应实现文件:
apps/web/src/pages/PortalPage.tsxapps/web/src/pages/TodoPage.tsxapps/web/src/pages/AiNewsPage.tsxapps/web/src/pages/DashboardPage.tsx
1.3 双端布局策略确认
统一采用响应式分支:
- 移动端:
lg:hidden - 桌面端:
hidden lg:block
并在桌面端通过 DesktopShellLayout.tsx 统一左侧导航与头部骨架,保证 Todos / AiNews / Profile 三页视觉一致性。
Step 2:样式复现与组件对齐(已完成)
2.1 门户页样式复现
在 PortalPage.tsx 已复现设计稿核心结构与视觉:
- 固定半透明顶部导航 + 毛玻璃效果
- Hero 区域渐变标题与双 CTA
- 桌面端大卡片 mockup + 移动端 mockup
- Features 模块桌面网格/移动卡片双布局
- 品牌色体系以 teal/emerald 为主,保持与设计稿一致
2.2 业务内页(Todos / AiNews / Profile)样式复现
已对齐设计稿中的关键风格特征:
- 左侧导航(桌面)与底部 Tab(移动)
- 卡片圆角、边框层级、阴影强弱
- 标题层级、信息密度、状态色(teal/amber/rose/slate)
- 资讯转待办标识(“来自 AI 资讯”)
- 顶部渐变背景装饰与列表滚动区分层
涉及文件:
apps/web/src/pages/TodoPage.tsxapps/web/src/pages/AiNewsPage.tsxapps/web/src/pages/DashboardPage.tsxapps/web/src/pages/components/DesktopShellLayout.tsxapps/web/src/pages/components/NavIcons.tsx
2.3 状态与交互细节复现
已覆盖设计稿强调的主要交互状态:
- loading / error / empty 状态文案
- 按钮禁用态(如“已加入待办”)
- hover / active / transition 反馈
- 编辑态与普通态切换(待办页)
Step 3:接入云端 API 获取最新资讯(已完成)
3.1 API Client 接入
在 apps/web/src/lib/api.ts 中通过 @aitodos/api-sdk 创建客户端:
createApiClient(baseUrl)baseUrl来源:VITE_API_BASE_URL- 默认回退:
http://localhost:3000/api
3.2 资讯 Store 能力
在 apps/web/src/store/news.store.ts 已落地三类能力:
fetchNews:读取资讯列表(api.listAiNews())refreshNewsFromCloud:从云端抓取最新资讯(api.refreshAiNews())addToTodo:资讯一键加入待办(api.addAiNewsToTodo(newsId, userId))
并包含基础状态管理:items / loading / error。
3.3 页面触发与用户反馈
在 apps/web/src/pages/AiNewsPage.tsx 已完成接入与交互:
- 页面初次加载自动执行
fetchNews() - 点击刷新按钮触发
refreshNewsFromCloud() - 刷新成功/失败均有文案反馈(
actionMessage) - 支持“加入待办”并跳转待办页,携带提示消息
结论:云端资讯获取链路已打通,具备“拉取最新资讯 -> 展示 -> 转待办”的完整闭环。
今日产出总结
- 已完成设计稿到 React 页面的桌面/移动双端映射
- 已完成核心视觉样式复现(门户 + 三个业务页)
- 已完成云端资讯 API 的接入与页面联动
- 当前可支持个人博客向外说明:Day4 聚焦于“UI 复刻 + 响应式 + 云端资讯能力闭环”
Day5(Admin 管理系统:设计落地到开发步骤)
今日目标
基于 docs/Todo/day5.md 与 design/Admin/*.html 设计稿,沉淀一份可直接执行的开发步骤,确保:
- 管理后台四大模块可路由访问(Dashboard / Users / AI News / System)
- UI 与设计稿一致(桌面端侧边栏 + 移动端底部导航)
- 包含关键样板代码、配置说明、核心实现要点
Step 1:项目骨架与路由(先跑起来)
1.1 页面与路由清单
建议页面文件:
apps/admin/src/pages/AdminDashboardPage.tsxapps/admin/src/pages/AdminUsersPage.tsxapps/admin/src/pages/AdminAiNewsPage.tsxapps/admin/src/pages/AdminSystemPage.tsx
建议路由:
/admin/dashboard/admin/users/admin/ai-news/admin/system
1.2 路由样板代码(React Router)
// apps/admin/src/App.tsx
import { Navigate, Route, Routes } from 'react-router-dom'
import { AdminShellLayout } from './components/admin/AdminShellLayout'
import { AdminDashboardPage } from './pages/AdminDashboardPage'
import { AdminUsersPage } from './pages/AdminUsersPage'
import { AdminAiNewsPage } from './pages/AdminAiNewsPage'
import { AdminSystemPage } from './pages/AdminSystemPage'
export default function App() {
return (
<Routes>
<Route path="/admin" element={<AdminShellLayout />}>
<Route index element={<Navigate to="dashboard" replace />} />
<Route path="dashboard" element={<AdminDashboardPage />} />
<Route path="users" element={<AdminUsersPage />} />
<Route path="ai-news" element={<AdminAiNewsPage />} />
<Route path="system" element={<AdminSystemPage />} />
</Route>
</Routes>
)
}
关键说明:
- 用嵌套路由把公共壳层(Header / SideNav / BottomNav)收敛到一个布局组件。
index -> dashboard保证进入后台就有默认页面。
Step 2:AdminShellLayout(响应式导航一次做对)
2.1 设计约束(根据最新稿)
- 桌面端(
lg及以上):左侧SideNav+ 顶部TopBar - 移动端(
lg以下):简化Header+ 底部BottomTabBar - 仪表盘不提供全局搜索
2.2 样板代码(布局骨架)
// apps/admin/src/components/admin/AdminShellLayout.tsx
import { NavLink, Outlet } from 'react-router-dom'
const tabs = [
{ to: '/admin/dashboard', label: '仪表盘' },
{ to: '/admin/users', label: '用户' },
{ to: '/admin/ai-news', label: '资讯' },
{ to: '/admin/system', label: '系统' },
]
export function AdminShellLayout() {
return (
<div className="bg-slate-50 min-h-screen text-slate-800">
<header className="lg:hidden fixed top-0 inset-x-0 h-16 bg-white/90 backdrop-blur border-b border-slate-200 z-50" />
<div className="flex pt-16 lg:pt-0 pb-24 lg:pb-0 min-h-screen">
<aside className="hidden lg:flex w-64 shrink-0 border-r border-slate-200 bg-white fixed inset-y-0" />
<div className="flex-1 lg:pl-64 flex flex-col">
<header className="hidden lg:flex h-16 border-b border-slate-200 bg-white/80 backdrop-blur items-center px-8">
<p className="text-sm text-slate-400 italic">管理员仪表盘 - 数据监控中</p>
</header>
<main className="flex-1 p-4 lg:p-8">
<Outlet />
</main>
</div>
</div>
<nav className="lg:hidden fixed bottom-0 inset-x-0 h-[68px] bg-white/95 border-t border-slate-200 z-50 flex items-center justify-around">
{tabs.map((tab) => (
<NavLink key={tab.to} to={tab.to} className="text-xs">
{tab.label}
</NavLink>
))}
</nav>
</div>
)
}
关键说明:
pb-24防止移动端内容被底部导航遮挡。- 导航激活态建议统一用
NavLink的isActive控制品牌色。
Step 3:基础配置(Tailwind / 主题 / API)
3.1 Tailwind 品牌色(与设计稿一致)
// tailwind.config.ts
export default {
theme: {
extend: {
colors: {
brand: {
50: '#f0fdfa',
100: '#ccfbf1',
200: '#99f6e4',
500: '#14b8a6',
600: '#0d9488',
},
},
fontFamily: {
sans: ['Inter', 'sans-serif'],
},
},
},
}
3.2 API SDK 统一入口
// apps/admin/src/lib/api.ts
import {
createApiClient } from '@aitodos/api-sdk'
export const api = createApiClient(
import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000/api'
)
关键说明:
- 后台不要散落
fetch,统一走 SDK,便于后续鉴权、拦截、错误处理。 - 建议按领域分组 SDK 方法:
statsApi/userAdminApi/aiNewsApi/systemApi。
Step 4:Dashboard(先完成“可看”)
4.1 数据结构建议
type Overview = {
totalUsers: number
addedUserCount: number
totalAiNewsCount: number
newsToTodoRate: number
}
4.2 store 样板(Zustand)
// apps/admin/src/store/adminDashboard.store.ts
import {
create } from 'zustand'
import {
api } from '../lib/api'
type State = {
overview: Overview | null
loading: boolean
error: string | null
fetchOverview: () => Promise<void>
}
export const useAdminDashboardStore = create<State>((set) => ({
overview: null,
loading: false,
error: null,
fetchOverview: async () => {
set({
loading: true, error: null })
try {
const data = await api.statsApi.getOverview()
set({
overview: data, loading: false })
} catch {
set({
error: '加载概览数据失败', loading: false })
}
},
}))
关键说明:
- 先打通 KPI + 趋势图,再补活跃热力块和来源分布。
- 页面必须有
loading/empty/error三态。
Step 5:Users(可筛选 + 可变更)
5.1 接口动作
GET /admin/usersPATCH /admin/users/:id/rolePATCH /admin/users/:id/status
5.2 关键交互清单
- 筛选:角色 / 状态 / 关键词
- 操作:改角色、启停账号
- 反馈:二次确认 + Toast
样板交互代码(伪代码):
await updateUserRole(userId, role)
toast.success('角色更新成功')
await fetchUsers()
Step 6:AI News(同步链路)
6.1 接口动作
GET /ai-newsPOST /ai-news/refresh
6.2 页面实现要点
- 顶部“立即同步抓取”按钮
- 列表展示来源、抓取时间、转化人数
- 同步中禁用按钮 + 状态提示条
样板代码(刷新按钮):
<button disabled={refreshing} onClick={refreshNews}>
{refreshing ? '同步中...' : '立即同步抓取'}
</button>
Step 7:System(合并日志/设置/管理员信息)
7.1 接口动作(建议)
GET /admin/system/profileGET /admin/system/logsPATCH /admin/system/settingsPOST /admin/system/cache/clear
页面包含三块:
- 管理员信息卡
- 最近操作日志
- 全局设置(推送时间、开放注册、缓存清理)
关键说明:
- “缓存清理”属于危险操作,必须二次确认。
- 系统设置建议单独 API 分组(
systemApi)。
Step 8:执行清单(开发顺序)
- [ ] 搭建 Admin 路由与壳层布局
- [ ] 完成桌面侧边栏与移动底部导航切换
- [ ] 移除 TopBar 搜索并统一头部样式
- [ ] 接入 Dashboard 概览与趋势数据
- [ ] 完成 Users 筛选与角色/状态变更
- [ ] 接入 AI News 列表与手动同步
- [ ] 完成 System 三大模块(信息/日志/设置)
- [ ] 全页面补齐 loading/empty/error 三态
- [ ] 自测桌面/移动端布局不遮挡、不跳动
今日结论
Day5 的核心不是“把页面画出来”,而是把 Admin 的实现路径标准化:
- 先壳层后页面
- 先数据骨架后细节交互
- 先闭环后增强
按本文步骤执行,可直接进入 apps/admin 的 Step2 开发阶段。