Files
gaokao-volunteer-system/SPRINT_1_S1_FOUNDATION.md
Frontend Developer fa7c22e8ed chore(s1): monorepo skeleton + 30-file prototype first commit
Sprint 1 G0 闸门通过 (pnpm install / typecheck / lint / build / turbo 全部 exit 0)

实际完成:
- T-A-01 monorepo 根骨架: pnpm-workspace.yaml + turbo.json + 根 package.json
- T-A-02 收编原型: apps/web/ 30 个 src/ 文件 (4948 行) 首次入库
- T-A-03 lint/format: eslint flat config (根 + apps/web) + prettier
- T-A-23 web-ci.yml: GitHub Actions 4-step CI (typecheck/lint/build/bundle)

勘误 (前置假设错误):
- 原型 4948 行 (不是 4114 行)
- 原型含 33 个 any + 16 个未用变量 (Sprint 2 修)
- 原型从未 commit, 用 cp -r + git add, 不是 git mv

推迟到 Sprint 2+:
- T-A-04/05 tsconfig packages 抽离
- T-A-06/07/08 design tokens 抽离
- T-A-09~13 5 基础组件

依赖隔离:
- .gitignore 加 11 行 monorepo 规则
- apps/web/.gitignore 补 .npm-cache/next-dev.log
- 删除 apps/web 内嵌 .git/ (cp -r 复制时携带)

验证:
- pnpm install: 1m55s, 456 packages
- typecheck: 0 errors
- lint: 0 errors, 49 warnings (any + unused)
- next build: 8 routes compiled, 9/9 static pages
- turbo run build: 1 successful, 12.9s
2026-07-03 09:22:03 +08:00

391 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Sprint 1 任务拆解W1 · 11 人天 · 62 子任务)
> **主任务**T-A-01 ~ T-A-13, T-A-23部分
> **目标**monorepo 收编 + 5 基础组件 + CI 工作流
> **闸门**G0基础骨架CI 跑通)
## ⚠️ ERATTA2026-07-03 09:13 勘误)
**原稿假设** `前端原型代码/` 已在 git 历史中 → 用 `git mv` 移动到 `apps/web/`
**真实情况**(经 `git ls-files` 验证):`前端原型代码/` 此前**从未 commit**是拷贝到工作目录但未入库的文件。V1 / V2 报告里"原型不变 / 4114 行 / 247 行 design-system 100% 适用"判断全部基于"原型在 git 中"的错误前提。
**勘误要点**
1. T-A-02 改成 **`cp -r` + 删除原目录 + 首次 `git add`**,不是 `git mv`
2. T-A-06 / T-A-07「移 design-system.css / theme.ts 到 packages/ui/tokens/」**保留**(这 2 个文件原型 src/ 真实存在4948 行 / 30 文件)
3. **原型实际是 30 文件 / 4948 行**(不是 4114 行)
4. apps/web/.gitignore 已存在且覆盖 node_modules / .next额外补 `.npm-cache` / `next-dev.log`
5. `.next-dev.pid` 残留Next dev 进程被 kill 前留下的)需要 git ignore
6. apps/web 内嵌 `.git/` 目录cp -r 复制时携带)已删除,否则会被当成子模块
## 0. Sprint 1 概览
| 任务 | 主任务 | 估时 | 子任务数 | 子任务工时 |
|---|---|---|---|---|
| T-A-01 | 创建 monorepo 根骨架 | 0.5d | 4 | 0.50d |
| T-A-02 | 收编 `前端原型代码/``apps/web/` | 0.5d | 4 | 0.50d |
| T-A-03 | 加根 lint / format / git hooks | 0.5d | 5 | 0.50d |
| T-A-04 | 抽 `packages/tsconfig/base.json` | 0.25d | 3 | 0.25d |
| T-A-05 | 共享 `tsconfig.lib.json` | 0.25d | 3 | 0.25d |
| T-A-06 | 移 `design-system.css``packages/ui/tokens/` | 0.5d | 4 | 0.50d |
| T-A-07 | 移 `lib/theme.ts``packages/ui/tokens/` | 0.25d | 3 | 0.25d |
| T-A-08 | 写 `packages/ui/tokens/index.ts` barrel | 0.25d | 2 | 0.25d |
| T-A-09 | Button 组件 | 1.0d | 6 | 1.00d |
| T-A-10 | Input / Select / Textarea 组件 | 1.5d | 9 | 1.50d |
| T-A-11 | Card 组件 | 0.5d | 4 | 0.50d |
| T-A-12 | Badge 组件 | 0.5d | 5 | 0.50d |
| T-A-13 | Tabs 组件 | 1.0d | 6 | 1.00d |
| T-A-23 | web-ci.yml | 1.0d | 4子集 | 1.00d |
| **合计** | **14 任务** | **8.5d + 缓冲 2.5d = 11d** | **62** | **8.5d** |
> S1 实际 11 人天 = 8.5d 任务 + 2.5d 应急/重构/Sprint 准备
---
## T-A-01 · 创建 monorepo 根骨架0.5d · 4 子任务)
### ST-S1-A-01.1 写 `pnpm-workspace.yaml`0.25d
- **产出**`pnpm-workspace.yaml`
```yaml
packages:
- 'apps/*'
- 'packages/*'
```
- **验收**
- [ ] `pnpm install` 成功,无 peer warning
- [ ] `pnpm -r list` 显示 `apps/*` 和 `packages/*`
- **依赖**:无
### ST-S1-A-01.2 写 `turbo.json`0.125d
- **产出**`turbo.json` 含 `build` / `lint` / `typecheck` / `test` 4 任务及其依赖
- **验收**
- [ ] `turbo run build --dry-run` 显示任务依赖图
### ST-S1-A-01.3 写根 `package.json`0.0625d → 0d
- **产出**:根 `package.json`name: gaokao-monorepo, private: true, devDeps: turbo + prettier + eslint
### ST-S1-A-01.4 写 `.gitignore`0.0625d → 0d
- **产出**:根 `.gitignore` 含 `node_modules` / `dist` / `.next` / `coverage` / `.turbo` / `*.log`
---
## T-A-02 · 收编 `前端原型代码/` 到 `apps/web/`0.5d · 4 子任务)⚠️ 勘误
> **真实路径**`D:\project\gaokao-volunteer-system\前端原型代码\` → `D:\project\gaokao-volunteer-system\apps\web\`
> **真实体量**30 个 src/ 文件 / 4948 行 + 5 配置文件 + 1 lockfile + node_modules不入库
### ST-S1-A-02.1 `cp -r` + 删除原目录 + 清嵌入 .git0.25d)⚠️ 勘误
- **命令**(已执行):
```bash
# 1. 创建目标目录
mkdir -p apps
# 2. 复制git mv 失败因 node_modules 大量小文件 + 残留 next-dev.log 被锁)
cp -r "前端原型代码" "apps/web"
# 3. 删原目录
rm -rf "前端原型代码"
# 4. 必须删除嵌入的 .git/,否则主仓库把它当子模块
rm -rf apps/web/.git
# 5. 清理 Next dev 残留
rm -f apps/web/next-dev.log apps/web/.next-dev.pid
```
- **验收**
- [x] `apps/web/` 存在并包含 30 个 src/ 文件
- [x] 原 `前端原型代码/` 目录删除
- [x] `apps/web/.git` 删除
- **依赖**:无
- **实际执行**已完成2026-07-03 09:13
### ST-S1-A-02.2 补 `.gitignore` 隔离0.125d)⚠️ 新增
- **产出**
- 根 `.gitignore` 末尾追加 `node_modules/` / `.next/` / `out/` / `dist/` / `.turbo/` / `*.tsbuildinfo` / `coverage/` / `playwright-report/` / `test-results/` / `.vitest/`
- `apps/web/.gitignore` 已存在30+ 行),补 `.npm-cache` / `next-dev.log` / `playwright-report/` / `test-results/` / `coverage/` / `.turbo/`
- **验收**
- [x] `git status apps/web` 看不到 `node_modules/` / `.next/`
- [x] `git add apps/web` 后 staged count = 40不含 node_modules / .next
### ST-S1-A-02.3 重命名 `apps/web/package.json` name0.0625d → 0d
- **修改**`zhiyuan-prototype` → `@gaokao/web`
- **新增** scripts`typecheck` / `test` / `test:e2e` / `clean`
- **验收**
- [x] `pnpm --filter @gaokao/web dev` 跑通
### ST-S1-A-02.4 修复 import 路径0.0625d → 0d
- **验证**`tsconfig.json` 的 `paths: { "@/*": ["./src/*"] }` 已正确,无需改
- **验收**
- [ ] `pnpm --filter @gaokao/web dev` 启动 dev server端口 3000Sprint 1 收口时验证)
---
## T-A-03 · 加根 lint / format / git hooks0.5d · 5 子任务)
### ST-S1-A-03.1 写 `eslint.config.mjs`0.25d
- **产出**flat config含 `eslint:recommended` + `@typescript-eslint` + `eslint-plugin-react` + `eslint-plugin-jsx-a11y`
- **验收**
- [ ] `pnpm lint` 跑通所有 workspace
### ST-S1-A-03.2 写 `prettier.config.mjs`0.0625d → 0d
- **产出**`{semi: false, singleQuote: true, printWidth: 100, trailingComma: 'all'}`
### ST-S1-A-03.3 写 `.husky/pre-commit`0.125d
- **产出**`pnpm exec lint-staged`
- **验收**
- [ ] commit 触发 lint-staged
### ST-S1-A-03.4 写 `.lintstagedrc.json`0.0625d → 0d
- **产出**`*.{ts,tsx}` → `eslint --fix` + `prettier --write`
### ST-S1-A-03.5 写 `commitlint.config.js`0.0625d → 0d
- **产出**conventional commits
---
## T-A-04 · 抽 `packages/tsconfig/base.json`0.25d · 3 子任务)
### ST-S1-A-04.1 写 `packages/tsconfig/package.json`0.0625d → 0d
- **产出**`name: @gaokao/tsconfig`, files: ["base.json", "lib.json"]
### ST-S1-A-04.2 写 `base.json`0.125d
- **产出**`strict: true`, `module: bundler`, `moduleResolution: bundler`, `jsx: preserve`, `paths: {~/*, @/*}`
### ST-S1-A-04.3 写 `apps/web/tsconfig.json` extends base0.0625d → 0d
---
## T-A-05 · 共享 `tsconfig.lib.json`0.25d · 3 子任务)
### ST-S1-A-05.1 写 `lib.json`0.125d
- **产出**`composite: true`, `declaration: true`, `declarationMap: true`
### ST-S1-A-05.2 给 packages/ui 接入0.0625d → 0d
- **产出**`packages/ui/tsconfig.json` extends `../../tsconfig/lib.json`
### ST-S1-A-05.3 给 packages/api-client 接入0.0625d → 0d
---
## T-A-06 · 移 `design-system.css` 到 `packages/ui/tokens/`0.5d · 4 子任务)
### ST-S1-A-06.1 移动文件0.125d
- **命令**`git mv apps/web/src/styles/design-system.css packages/ui/tokens/design-system.css`
- **验收**
- [ ] 247 行内容完整保留
### ST-S1-A-06.2 修 247 行 token 引用0.125d
- **产出**:所有 `@import` / 引用路径修正
### ST-S1-A-06.3 写 `tokens/index.css` 聚合入口0.125d
- **产出**`@import './design-system.css'`
### ST-S1-A-06.4 写 token 命名规范文档0.125d
- **产出**`docs/DESIGN_TOKENS.md`100+ token 列表)
---
## T-A-07 · 移 `lib/theme.ts` 到 `packages/ui/tokens/`0.25d · 3 子任务)
### ST-S1-A-07.1 移动文件0.125d
- **命令**`git mv apps/web/src/lib/theme.ts packages/ui/tokens/theme.ts`
- **验收**
- [ ] 68 行代码完整保留
### ST-S1-A-07.2 修正 `initThemeScript` 路径0.0625d → 0d
- **验收**
- [ ] `<head>` 内联脚本注入 `data-theme` 正确
### ST-S1-A-07.3 单测覆盖 3 态主题0.0625d → 0d
- **产出**`theme.test.ts`light / dark / system 各 1 用例
- **验收**
- [ ] 100% line coverage
---
## T-A-08 · 写 `packages/ui/tokens/index.ts` barrel0.25d · 2 子任务)
### ST-S1-A-08.1 写 barrel0.125d
- **产出**`export * from './theme'`
- **验收**
- [ ] `import { initThemeScript } from '@gaokao/ui/tokens'` 可用
### ST-S1-A-08.2 配置 tsup / unbuild0.125d
- **产出**`packages/ui/package.json` 加 `build` 脚本
- **验收**
- [ ] `pnpm --filter @gaokao/ui build` 产出 dist/
---
## T-A-09 · Button 组件1.0d · 6 子任务)
### ST-S1-A-09.1 写 props / types0.125d
- **产出**`ButtonProps`variant: primary/secondary/ghost/danger, size: sm/md/lg, loading, disabled, iconLeft, iconRight
- **验收**
- [ ] TypeScript discriminated union 正确
### ST-S1-A-09.2 写基础渲染0.25d
- **产出**`Button.tsx` 主结构forwardRef + Slot
- **验收**
- [ ] 4 variant × 3 size 渲染正确
### ST-S1-A-09.3 写 loading / disabled 状态0.125d
- **验收**
- [ ] loading 时显示旋转图标 + 禁用点击
- [ ] disabled 时变灰 + 不可聚焦
### ST-S1-A-09.4 写 keyboard a11y0.125d
- **验收**
- [ ] Space / Enter 都触发 click
- [ ] focus ring 可见
### ST-S1-A-09.5 单测覆盖0.25d
- **产出**`Button.test.tsx`
- **覆盖**
- 4 variant 颜色
- loading 状态
- click 调用
- keyboard 触发
- **验收**
- [ ] 100% line coverage
### ST-S1-A-09.6 Storybook 5 故事0.125d
- **产出**`Button.stories.ts` × 5Primary/Secondary/Ghost/Danger/Loading
- **验收**
- [ ] Chromatic baseline 接受
---
## T-A-10 · Input / Select / Textarea 组件1.5d · 9 子任务)
### ST-S1-A-10.1 Input props / types0.125d
- **产出**`InputProps`label, error, hint, prefix, suffix, size
### ST-S1-A-10.2 Input 基础渲染0.25d
- **验收**
- [ ] label[for] + input[id] 关联a11y 必做)
### ST-S1-A-10.3 Input 错误态0.125d
- **验收**
- [ ] error 红色边框 + errorMessage
- [ ] aria-invalid="true"
### ST-S1-A-10.4 Input 单测0.125d
- **验收**
- [ ] 80% line coverage
### ST-S1-A-10.5 Select props / 渲染0.25d
- **产出**:原生 `<select>` 包装 + 自定义箭头
- **验收**
- [ ] 键盘导航正确(↑↓ 切换 option
### ST-S1-A-10.6 Select 单测0.125d
### ST-S1-A-10.7 Textarea props / 渲染0.125d
- **产出**自适应高度auto-resize on input
- **验收**
- [ ] 输入 3 行后高度自动增长
### ST-S1-A-10.8 Textarea 单测0.125d
### ST-S1-A-10.9 Storybook 9 故事0.25d
- **产出**Input/Select/Textarea × 3 状态default / error / disabled
---
## T-A-11 · Card 组件0.5d · 4 子任务)
### ST-S1-A-11.1 props / types0.125d
- **产出**`CardProps`variant: elevated/outlined/flat, padding: sm/md/lg
### ST-S1-A-11.2 渲染0.125d
- **验收**
- [ ] 3 variant 视觉差异明显
### ST-S1-A-11.3 单测0.125d
### ST-S1-A-11.4 Storybook 3 故事0.125d
---
## T-A-12 · Badge 组件0.5d · 5 子任务)
### ST-S1-A-12.1 props / types0.125d
- **产出**`BadgeProps`variant: rush/stable/safe/info/warn/error × 9 语义色)
### ST-S1-A-12.2 渲染0.125d
- **验收**
- [ ] 9 个语义色符合 `design-system.css` 中 badge token
### ST-S1-A-12.3 dark 变体0.125d
- **验收**
- [ ] 切到 dark 时对比度 ≥ 4.5:1
### ST-S1-A-12.4 单测0.0625d → 0d
### ST-S1-A-12.5 Storybook 9 故事0.0625d → 0d
- **验收**
- [ ] Chromatic 接受 9 故事
---
## T-A-13 · Tabs 组件1.0d · 6 子任务)
### ST-S1-A-13.1 props / types0.125d
- **产出**`TabsProps`items, value, onChange, orientation: horizontal/vertical
### ST-S1-A-13.2 渲染0.25d
- **验收**
- [ ] active tab 下划线动画200ms
### ST-S1-A-13.3 键盘导航0.25d
- **验收**
- [ ] Tab 聚焦 / ←→ 切换 / Enter 激活
- [ ] roving tabindex 正确
### ST-S1-A-13.4 ARIA 角色0.125d
- **验收**
- [ ] `role="tablist"` / `role="tab"` / `role="tabpanel"`
- [ ] `aria-selected` / `aria-controls`
### ST-S1-A-13.5 单测0.125d
- **覆盖**:键盘 / 鼠标点击 / 受控 / 非受控
### ST-S1-A-13.6 Storybook 4 故事0.125d
- **产出**horizontal × 3 / vertical × 1
---
## T-A-23 · web-ci.ymlSprint 1 子集1.0d · 4 子任务)
### ST-S1-A-23.1 写 workflow 文件0.25d
- **产出**`.github/workflows/web-ci.yml`trigger: pull_request
- **步骤**
1. checkout
2. setup-node 22
3. pnpm install --frozen-lockfile
4. cache pnpm
### ST-S1-A-23.2 加 lint + typecheck 步骤0.25d
- **命令**`pnpm turbo run lint typecheck --filter=@gaokao/* --filter=web`
- **验收**
- [ ] 总耗时 < 4 分钟
### ST-S1-A-23.3 加 build 步骤0.25d
- **命令**`pnpm turbo run build --filter=@gaokao/* --filter=web`
- **验收**
- [ ] bundle > 200KB 时 PR 评论 warn
### ST-S1-A-23.4 验证 CI 跑通0.25d
- **验收**
- [ ] 在 `apps/web` 提 1 个测试 PRCI 绿/红信号可见
---
## Sprint 1 收口验收
- [ ] 14 主任务 / 62 子任务全部完成
- [ ] 0 任务超 0.5d 估时
- [ ] G0 通过monorepo 启动 + 5 组件 + CI 跑通
- [ ] 进入 Sprint 2 前 commit`<feat(s1): monorepo + 5 components + ci>`