feat(sprint-2): V10 选项 B 完整切换 + G1 闸门通过

框架切换:Next.js 16 → Vite 5 + React 19 + React Router 7
状态管理:7 手写 hook → 4 Zustand slice
数据获取:直接 fetch → TanStack Query 5 (15 hooks)
表单:3-step 状态机 → RHF 7 + Zod
测试:0 → 25 单测 + 20 e2e (4 浏览器)
Bundle:192 KB gzip (目标 < 300 KB)
Lint:49 warning → 0 warning
Type:33 any → 0 any

S2-T-01 切 Vite 5 + React 19 框架
S2-T-02 Zustand 4 slice 替代 7 手写 hook
S2-T-03 TanStack Query 5 + 15 hooks
S2-T-04 Vitest + RTL + MSW + Chromatic 配置
S2-T-05 OpenAPI Codegen + Zod schema 复用
S2-T-06 RHF 7 + Zod 重写 FormCard
S2-T-07 Playwright 视觉基线 4 浏览器 5 spec
S2-T-08 Vite build + bundle < 300KB gzip

G1 闸门 (全部通过):
- typecheck: 0 error
- lint: 0 error 0 warning
- test (Vitest): 25/25 passed
- test:e2e (Playwright): 20/20 passed
- build (Vite): 326 modules 4.14s
- codegen:check: OpenAPI types 非占位

Sprint 2 收口报告:SPRINT_2_CLOSEOUT_2026-07-03.md
This commit is contained in:
Frontend Developer
2026-07-03 15:51:03 +08:00
parent 7892625ed2
commit e8b8ad079e
107 changed files with 13903 additions and 6652 deletions

View File

@@ -1,69 +1,129 @@
name: Web CI (Sprint 1+)
name: web-ci
on:
push:
branches: [main, develop]
paths:
- 'apps/web/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'turbo.json'
pull_request:
paths:
- 'apps/web/**'
- 'packages/**'
- 'pnpm-workspace.yaml'
- 'package.json'
- 'pnpm-lock.yaml'
- 'turbo.json'
- 'package.json'
- 'eslint.config.mjs'
- '.github/workflows/web-ci.yml'
push:
branches: [main, feat/**]
paths:
- 'apps/web/**'
# 同分支/同次 push 的旧运行自动取消
concurrency:
group: web-ci-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
build:
name: pnpm build / typecheck / lint
ci:
runs-on: ubuntu-latest
timeout-minutes: 12
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.33.0
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: TypeScript typecheck
run: pnpm turbo run typecheck --filter=@gaokao/web
- name: TypeScript typecheck (G1 闸门: 0 any)
run: pnpm typecheck
- name: ESLint
run: pnpm turbo run lint --filter=@gaokao/web
- name: ESLint (G1 闸门: 0 warning)
run: pnpm lint
- name: Build (Next.js)
run: pnpm turbo run build --filter=@gaokao/web
- name: Vitest (单元测试)
run: pnpm test
- name: Comment bundle size
if: github.event_name == 'pull_request'
run: |
SIZE=$(du -sb apps/web/.next 2>/dev/null | cut -f1 || echo "0")
SIZE_KB=$((SIZE / 1024))
echo "## Web bundle size" >> $GITHUB_STEP_SUMMARY
echo "- **Total .next/**: ${SIZE_KB} KB" >> $GITHUB_STEP_SUMMARY
if [ "$SIZE_KB" -gt 204800 ]; then
echo "::warning::Bundle size > 200MB, please check"
fi
- name: Build
run: pnpm build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: web-dist
path: apps/web/dist
retention-days: 7
e2e:
runs-on: ubuntu-latest
needs: ci
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Install Playwright browsers
run: pnpm --filter @gaokao/web exec playwright install --with-deps
- name: Build for preview
run: pnpm --filter @gaokao/web build
- name: Run Playwright e2e tests
run: pnpm --filter @gaokao/web test:e2e
- name: Upload Playwright report
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: apps/web/playwright-report
retention-days: 7
chromatic:
runs-on: ubuntu-latest
needs: ci
timeout-minutes: 15
if: github.event_name == 'push' || github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm --filter @gaokao/web build
- name: Publish to Chromatic
uses: chromaui/action@v1
with:
projectToken: ${{ secrets.CHROMATIC_TOKEN }}
workingDir: apps/web
buildScriptName: build
exitZeroOnChanges: true