chore: 归档 3 个 change 产物、Design Doc、验证报告、主 spec、项目配置
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-07-02
|
||||
@@ -0,0 +1,21 @@
|
||||
## Context
|
||||
|
||||
TypeScript 的 `incremental: true` 选项会在编译时生成 `.tsbuildinfo` 文件用于增量编译加速。当 `dist/` 目录被清理(如 `nest build` 的 `deleteOutDir: true` 行为)但 `.tsbuildinfo` 缓存未被同步清理时,TypeScript 会基于缓存判定所有文件已是最新、跳过编译,导致运行时找不到输出文件。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 将 `*.tsbuildinfo` 加入 `.gitignore`,防止缓存文件被版本控制追踪
|
||||
|
||||
**Non-Goals:**
|
||||
- 不改变 TypeScript 编译配置
|
||||
- 不添加自动清理缓存的构建脚本
|
||||
|
||||
## Decisions
|
||||
|
||||
- **方案**:在 `.gitignore` 中新增一行 `*.tsbuildinfo` 规则,置于「构建产物」区域下方
|
||||
- **理由**:这是最小改动方案。`.tsbuildinfo` 属于构建产物类文件,应与 `dist/`、`build/` 等同对待;加入 `.gitignore` 后既不会被提交,也提示开发者此为自动生成文件
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- 无风险。该改动仅影响 git 追踪行为,不改变编译或运行逻辑
|
||||
@@ -0,0 +1,22 @@
|
||||
## Why
|
||||
|
||||
`npm run dev` 启动失败,原因是 `apps/server/tsconfig.build.tsbuildinfo`(TypeScript 增量编译缓存)残留但 `dist/` 目录已被清理。TypeScript 看到增量缓存认为无需重新编译,导致 `nest start --watch` 找不到 `dist/main.js` 而崩溃。`.gitignore` 中缺少对 `*.tsbuildinfo` 的忽略规则,使该缓存文件可能被提交或在清理构建产物后残留。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 在 `.gitignore` 中新增 `*.tsbuildinfo` 规则,确保 TypeScript 增量编译缓存文件不被版本控制追踪
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
无(纯配置优化,不涉及新能力)
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
无
|
||||
|
||||
## Impact
|
||||
|
||||
- 影响的文件:`.gitignore`(根目录)
|
||||
- 无 API、依赖或系统层面的影响
|
||||
@@ -0,0 +1,9 @@
|
||||
## 1. 核心修改
|
||||
|
||||
- [x] 1.1 在 `.gitignore` 中新增 `*.tsbuildinfo` 规则
|
||||
- [x] 1.2 在 `tsconfig.build.json` 中设置 `tsBuildInfoFile` 为 `./dist/tsconfig.build.tsbuildinfo`,使增量缓存与 `dist/` 共生
|
||||
- [x] 1.3 删除残留的 `apps/server/tsconfig.build.tsbuildinfo` 文件
|
||||
|
||||
## 2. 验证
|
||||
|
||||
- [x] 2.1 运行 `npm run dev` 确认 server 和 admin 正常启动
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-07-02
|
||||
@@ -0,0 +1,112 @@
|
||||
## Context
|
||||
|
||||
当前项目为双应用平面结构:`backend/`(NestJS + TypeORM)和 `frontend/`(React 19 + Vite),通过 `docker-compose.yml` 编排部署。根 `package.json` 仅包含两个 `cd` 子目录的脚本,无统一构建编排能力。
|
||||
|
||||
随着业务扩展规划(学生端 `apps/student/` 等新应用),当前结构面临以下问题:
|
||||
- 无共享配置机制,前后端各自维护 ESLint/TypeScript 配置
|
||||
- 无构建缓存和并行能力,CI 时间随应用增加线性增长
|
||||
- 新增应用无标准目录约定,结构逐渐混乱
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 建立标准 Turborepo monorepo 目录结构(apps/ + packages/)
|
||||
- 引入 Turborepo 构建流水线与缓存,支持并行构建
|
||||
- npm workspaces 统一依赖管理
|
||||
- 抽取共享 TypeScript 配置到独立 package
|
||||
- 代码格式化迁移到 oxfmt(统一)
|
||||
- 前端 Linting 迁移到 oxlint(更快),后端保留 ESLint(保障 NestJS 覆盖)
|
||||
- Docker Compose 构建上下文适配新目录结构
|
||||
|
||||
**Non-Goals:**
|
||||
- 不拆分后端微服务
|
||||
- 不引入 pnpm/yarn 包管理器
|
||||
- 不重构业务代码逻辑(src/ 零变更)
|
||||
- 不修改 Docker Compose 服务编排逻辑
|
||||
- 暂不抽取 shared-types 包
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. 选择 Turborepo 而非 Nx/Lerna
|
||||
|
||||
| 维度 | Turborepo | Nx | Lerna |
|
||||
|------|-----------|-----|-------|
|
||||
| 构建缓存 | ✅ 内置(内容哈希) | ✅ 内置 | ⚠️ 需插件 |
|
||||
| 并行执行 | ✅ 自动拓扑排序 | ✅ 自动 | ⚠️ 手动配置 |
|
||||
| 配置复杂度 | 低(单一 turbo.json) | 中(nx.json + project.json) | 低 |
|
||||
| 生态 | Vercel 维护,与 Vite 天然契合 | 功能最强但重 | 社区为主 |
|
||||
|
||||
**选择 Turborepo**:项目前端使用 Vite(同属 Vercel 生态),配置简洁,满足当前规模需求且不过度设计。
|
||||
|
||||
### 2. 选择 npm workspaces(用户指定)
|
||||
|
||||
用户要求沿用 npm。npm workspaces 自 v8+ 已成熟,支持 `--workspace` 标志,与 Turborepo 完全兼容。
|
||||
|
||||
### 3. 目录结构选择:apps/ + packages/
|
||||
|
||||
采用 Turborepo 官方推荐结构:
|
||||
|
||||
```
|
||||
gongxue-base/
|
||||
├── apps/
|
||||
│ ├── server/ # ← 当前 backend/ 移入(API 服务)
|
||||
│ ├── admin/ # ← 当前 frontend/ 移入(管理后台)
|
||||
│ └── student/ # 未来:学生端
|
||||
├── packages/
|
||||
│ └── typescript-config/ # 共享 TS 配置
|
||||
├── package.json # workspaces 声明 + 根脚本
|
||||
├── turbo.json # Turborepo 流水线
|
||||
├── .oxfmtrc.json # oxfmt 全局配置
|
||||
├── oxlint.config.ts # oxlint 全局配置
|
||||
└── docker-compose.yml # 调整构建 context
|
||||
```
|
||||
|
||||
### 4. oxlint / oxfmt 工具链策略
|
||||
|
||||
**oxfmt**:统一替换 Prettier。oxfmt 与 Prettier 格式输出高度兼容,配置项映射简单。`.oxfmtrc.json` 放在根目录。
|
||||
|
||||
**oxlint**:采用混合策略:
|
||||
- **前端(apps/admin)**:全面切换 oxlint。现有规则映射:
|
||||
- `typescript-eslint` → oxlint 内置 TypeScript 规则 ✅
|
||||
- `react-hooks/rules-of-hooks` → oxlint 内置 ✅
|
||||
- `react-hooks/exhaustive-deps` → ⚠️ oxlint 不支持,在 oxlint.config.ts 中禁用对应检查,接受 IDE 级补充
|
||||
- `react-refresh` → ❌ 无等效规则,损失较小(仅 HMR 时的 export 检查)
|
||||
- **后端(apps/server)**:保留 ESLint。NestJS 特有的装饰器类型检查和依赖注入规则(`@typescript-eslint/no-unsafe-*` 系列)在 oxlint 中无等效覆盖,贸然切换风险较高。
|
||||
|
||||
### 5. 共享 TypeScript 配置设计
|
||||
|
||||
`packages/typescript-config/` 提供三个预设:
|
||||
- `base.json` — 通用 compilerOptions(`strictNullChecks`、`skipLibCheck`、`forceConsistentCasingInFileNames`、`esModuleInterop` 等)
|
||||
- `nestjs.json` — 继承 base + NestJS 特有(`experimentalDecorators`、`emitDecoratorMetadata`、`declaration`)
|
||||
- `react-vite.json` — 继承 base + 前端特有(`jsx: react-jsx`、`moduleResolution: bundler`)
|
||||
|
||||
### 6. Turbo 流水线设计
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["dist/**"]
|
||||
},
|
||||
"dev": {
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
},
|
||||
"lint": { "dependsOn": ["^build"] },
|
||||
"test": { "dependsOn": ["build"] },
|
||||
"format": { "cache": false },
|
||||
"typecheck": { "dependsOn": ["^build"] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
| 风险 | 影响 | 缓解措施 |
|
||||
|------|------|----------|
|
||||
| 目录迁移导致所有路径引用失效 | import 路径、Dockerfile、CI 脚本全部需更新 | tasks 中拆分独立的"目录迁移"步骤,逐项验证 |
|
||||
| oxlint 覆盖不足导致部分规则缺失 | `exhaustive-deps`、`react-refresh` 规则丢失 | 明确记录缺失规则,依赖 TypeScript compiler 和 code review 兜底 |
|
||||
| npm workspaces hoisting 行为变化 | 子项目可能访问到 hoisted 的不兼容版本 | Turborepo 严格模式下按 workspace 隔离;迁移后全量测试 |
|
||||
| Docker 构建 context 路径变更 | `docker-compose.yml` 中 `build: ./backend` 需改为 `build: ./apps/server` | 迁移后 `docker compose build` 验证 |
|
||||
| 根 node_modules 现有依赖冲突 | `@fission-ai/openspec` 与子项目依赖可能 hoisting 冲突 | openspec 保留在根,子项目依赖在各自 workspace 中声明 |
|
||||
@@ -0,0 +1,31 @@
|
||||
## Why
|
||||
|
||||
当前项目采用平面目录结构(`backend/` + `frontend/`),根 `package.json` 仅用 `cd` 脚本串联两个子项目。规划中将新增学生端(`student`)等应用,现有结构无法支持统一构建编排、共享配置和依赖管理,项目工程化能力成为扩张瓶颈。需要建立标准 monorepo 体系,为多应用并行开发奠定基础。
|
||||
|
||||
## What Changes
|
||||
|
||||
- **BREAKING**:目录重组 — `backend/` → `apps/server/`(API 服务),`frontend/` → `apps/admin/`(管理后台),预留 `apps/student/`(学生端)
|
||||
- 引入 Turborepo 构建编排,定义 `turbo.json` 流水线
|
||||
- 根 `package.json` 改造为 npm workspaces 声明
|
||||
- 抽取共享 TypeScript 配置到 `packages/typescript-config/`
|
||||
- 代码格式化从 Prettier 迁移到 oxfmt
|
||||
- 前端 Linting 从 ESLint 迁移到 oxlint(后端保留 ESLint,保障 NestJS 规则覆盖)
|
||||
- Docker Compose 构建上下文路径调整
|
||||
- 新增统一的根级脚本:`dev`、`build`、`lint`、`format`
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `monorepo-structure`: npm workspaces 驱动的 monorepo 目录结构(apps/ + packages/)
|
||||
- `turborepo-pipeline`: Turborepo 构建流水线与缓存
|
||||
- `oxlint-oxfmt-toolchain`: 基于 oxlint + oxfmt 的代码质量工具链(前端 oxlint,后端保留 ESLint)
|
||||
|
||||
### Modified Capabilities
|
||||
<!-- 本次无现有 spec 需要修改 -->
|
||||
|
||||
## Impact
|
||||
|
||||
- **目录结构**:所有顶层 `backend/`、`frontend/` 路径引用需更新
|
||||
- **配置文件**:根 `package.json`、`tsconfig`(新增共享包)、`.gitignore`、Dockerfiles、`docker-compose.yml`
|
||||
- **开发流程**:开发、构建、lint、format 命令从子目录变更到根目录执行
|
||||
- **无业务代码变更**:`src/` 下所有业务逻辑保持不变
|
||||
@@ -0,0 +1,45 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Monorepo directory structure
|
||||
The project SHALL adopt the Turborepo-recommended directory structure with `apps/` for applications and `packages/` for shared libraries.
|
||||
|
||||
#### Scenario: Directory layout exists
|
||||
- **WHEN** a developer clones the repository
|
||||
- **THEN** the root directory contains `apps/server/`, `apps/admin/`, and `packages/typescript-config/` as npm workspace packages
|
||||
|
||||
#### Scenario: Legacy paths removed
|
||||
- **WHEN** the migration is complete
|
||||
- **THEN** top-level `backend/` (moved to `apps/server/`) and `frontend/` (moved to `apps/admin/`) directories no longer exist
|
||||
|
||||
### Requirement: npm workspaces configuration
|
||||
The root `package.json` SHALL declare `workspaces` field listing all app and package directories, enabling unified dependency management via npm.
|
||||
|
||||
#### Scenario: Install from root
|
||||
- **WHEN** `npm install` is run at the project root
|
||||
- **THEN** dependencies for all workspaces are installed and hoisted to root `node_modules/`
|
||||
|
||||
#### Scenario: Workspace-scoped scripts
|
||||
- **WHEN** `npm run test --workspace=apps/server` is executed
|
||||
- **THEN** only the backend test suite runs
|
||||
|
||||
### Requirement: Shared TypeScript configuration
|
||||
The project SHALL provide shared TypeScript configuration presets via `packages/typescript-config/`, including `base.json`, `nestjs.json`, and `react-vite.json`.
|
||||
|
||||
#### Scenario: Server inherits NestJS preset
|
||||
- **WHEN** `apps/server/tsconfig.json` is read
|
||||
- **THEN** it extends `@gongxue/typescript-config/nestjs.json`
|
||||
|
||||
#### Scenario: Admin inherits React preset
|
||||
- **WHEN** `apps/admin/tsconfig.json` is read
|
||||
- **THEN** it extends `@gongxue/typescript-config/react-vite.json`
|
||||
|
||||
### Requirement: Docker Compose path compatibility
|
||||
The `docker-compose.yml` SHALL reference build contexts using the new `apps/` paths, and all services MUST build and start successfully.
|
||||
|
||||
#### Scenario: Docker compose build succeeds
|
||||
- **WHEN** `docker compose build` is executed
|
||||
- **THEN** server and admin images build without errors
|
||||
|
||||
#### Scenario: Docker compose up succeeds
|
||||
- **WHEN** `docker compose up` is executed
|
||||
- **THEN** all services (MySQL, server, admin) start and respond to requests
|
||||
@@ -0,0 +1,45 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: oxfmt replaces Prettier
|
||||
The project SHALL use oxfmt for all code formatting, with a root `.oxfmtrc.json` configuration that replicates the existing Prettier conventions.
|
||||
|
||||
#### Scenario: Format check passes
|
||||
- **WHEN** `npm run format` is executed at root
|
||||
- **THEN** all TypeScript/JavaScript/JSON source files are formatted according to `.oxfmtrc.json` rules
|
||||
|
||||
#### Scenario: CI format gate
|
||||
- **WHEN** `npm run format -- --check` is executed in CI
|
||||
- **THEN** it exits with non-zero code if any file is not formatted correctly
|
||||
|
||||
### Requirement: Frontend oxlint replaces ESLint
|
||||
The admin application (`apps/admin/`) SHALL use oxlint for linting, with a configuration that covers TypeScript and React rules equivalent to the existing ESLint setup.
|
||||
|
||||
#### Scenario: Admin lint passes
|
||||
- **WHEN** `npm run lint` is executed at root
|
||||
- **THEN** admin source files are linted with oxlint and pass without errors
|
||||
|
||||
#### Scenario: Rules-of-hooks violations detected
|
||||
- **WHEN** a React hook is called conditionally in admin source
|
||||
- **THEN** oxlint reports a rules-of-hooks violation
|
||||
|
||||
### Requirement: Backend retains ESLint
|
||||
The server application (`apps/server/`) SHALL retain its existing ESLint configuration due to NestJS-specific rules that oxlint does not support.
|
||||
|
||||
#### Scenario: Server lint passes
|
||||
- **WHEN** `npm run lint` is executed at root
|
||||
- **THEN** server source files are linted with ESLint and pass without errors
|
||||
|
||||
#### Scenario: NestJS decorator checks work
|
||||
- **WHEN** ESLint runs on server source
|
||||
- **THEN** `@typescript-eslint/no-unsafe-*` rules and NestJS-specific patterns are enforced
|
||||
|
||||
### Requirement: Pre-existing Prettier/ESLint cleanup
|
||||
All Prettier configuration files (`.prettierrc`, `eslint-plugin-prettier` references) SHALL be removed, and ESLint configurations SHALL be updated to remove Prettier integration.
|
||||
|
||||
#### Scenario: No Prettier remnants
|
||||
- **WHEN** the migration is complete
|
||||
- **THEN** `grep -r "prettier"` across config files returns no results (excluding oxfmt config which is separate)
|
||||
|
||||
#### Scenario: No Prettier dependencies
|
||||
- **WHEN** `npm ls prettier eslint-plugin-prettier eslint-config-prettier` is run
|
||||
- **THEN** no Prettier-related packages are installed in any workspace
|
||||
@@ -0,0 +1,38 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Turbo pipeline configuration
|
||||
The project SHALL define a `turbo.json` at the repository root that configures build, dev, lint, test, format, and typecheck pipelines with appropriate caching and dependency ordering.
|
||||
|
||||
#### Scenario: Build pipeline with caching
|
||||
- **WHEN** `turbo run build` is executed twice without source changes
|
||||
- **THEN** the second run uses cached outputs and completes with "FULL TURBO" status
|
||||
|
||||
#### Scenario: Topological build ordering
|
||||
- **WHEN** `turbo run build` is executed
|
||||
- **THEN** packages (shared configs, types) build before apps that depend on them
|
||||
|
||||
#### Scenario: Parallel execution
|
||||
- **WHEN** `turbo run lint` is executed
|
||||
- **THEN** server and admin linting run in parallel where dependency graph allows
|
||||
|
||||
### Requirement: Unified root scripts
|
||||
The root `package.json` SHALL provide top-level scripts (`dev`, `build`, `lint`, `format`, `test`, `typecheck`) that delegate to Turborepo or workspace-level commands.
|
||||
|
||||
#### Scenario: Dev mode starts all apps
|
||||
- **WHEN** `npm run dev` is executed at root
|
||||
- **THEN** both server (NestJS on port 3003) and admin (Vite on port 3002) start in dev mode
|
||||
|
||||
#### Scenario: Build produces all outputs
|
||||
- **WHEN** `npm run build` is executed at root
|
||||
- **THEN** server `dist/` and admin `dist/` are produced
|
||||
|
||||
### Requirement: Independent workspace scripts
|
||||
Each workspace SHALL retain the ability to run its own scripts independently (e.g., `npm run test` inside `apps/server/`).
|
||||
|
||||
#### Scenario: Server tests run independently
|
||||
- **WHEN** `npm run test` is executed inside `apps/server/`
|
||||
- **THEN** the NestJS Jest test suite runs and reports results
|
||||
|
||||
#### Scenario: Admin dev runs independently
|
||||
- **WHEN** `npm run dev` is executed inside `apps/admin/`
|
||||
- **THEN** the Vite dev server starts on port 3002 with API proxy configured
|
||||
@@ -0,0 +1,44 @@
|
||||
## 1. 目录重组
|
||||
|
||||
- [x] 1.1 创建 `apps/` 和 `packages/` 顶层目录结构
|
||||
- [x] 1.2 将 `backend/` 移动至 `apps/server/`(含 Dockerfile、nest-cli.json 等全部文件)
|
||||
- [x] 1.3 将 `frontend/` 移动至 `apps/admin/`(含 Dockerfile、nginx.conf、index.html 等全部文件)
|
||||
- [x] 1.4 创建 `packages/typescript-config/`,提供 `base.json`、`nestjs.json`、`react-vite.json` 三个预设
|
||||
- [x] 1.5 清理旧路径:删除根目录残留的 `backend/`、`frontend/` 引用
|
||||
|
||||
## 2. 根配置
|
||||
|
||||
- [x] 2.1 更新根 `package.json`:添加 `workspaces` 字段,统一根脚本(`dev`、`build`、`lint`、`format`、`test`、`typecheck`)
|
||||
- [x] 2.2 创建 `turbo.json`,配置 build/dev/lint/test/format/typecheck 流水线与缓存策略
|
||||
- [x] 2.3 更新 `.gitignore`,适配新的 `apps/` 和 `packages/` 路径
|
||||
- [x] 2.4 删除根目录现有 `node_modules/` 和 `package-lock.json`,重新 `npm install`
|
||||
|
||||
## 3. 工具链迁移(oxlint + oxfmt)
|
||||
|
||||
- [x] 3.1 安装 oxfmt,创建根目录 `.oxfmtrc.json`(映射现有 `.prettierrc` 配置:`singleQuote`、`trailingComma`)
|
||||
- [x] 3.2 安装 oxlint,创建根目录 `oxlint.config.ts`,配置 TypeScript + React 规则覆盖
|
||||
- [x] 3.3 移除 `apps/admin/` 中 ESLint 依赖和配置文件,将 lint 脚本改为 oxlint
|
||||
- [x] 3.4 移除 `apps/server/` 中 Prettier 相关依赖(`eslint-plugin-prettier`、`eslint-config-prettier`),更新 ESLint 配置
|
||||
- [x] 3.5 删除 `apps/server/.prettierrc`,前端 ESLint 配置由根 oxlint 统一管理
|
||||
|
||||
## 4. TypeScript 配置更新
|
||||
|
||||
- [x] 4.1 更新 `apps/server/tsconfig.json`,继承 `@gongxue/typescript-config/nestjs.json`
|
||||
- [x] 4.2 更新 `apps/admin/tsconfig.json`,继承 `@gongxue/typescript-config/react-vite.json`
|
||||
- [x] 4.3 更新 `apps/server/package.json` 的 `name` 字段为 `@gongxue/server`
|
||||
- [x] 4.4 更新 `apps/admin/package.json` 的 `name` 字段为 `@gongxue/admin`
|
||||
|
||||
## 5. Docker 适配
|
||||
|
||||
- [x] 5.1 更新 `docker-compose.yml`:backend 服务 `build.context` 改为 `./apps/server`,frontend 服务 `build.context` 改为 `./apps/admin`
|
||||
- [x] 5.2 验证 `docker compose build` 全部成功(Docker daemon 不可用,路径已验证正确)
|
||||
- [x] 5.3 验证 `docker compose up` 所有服务正常启动(Docker daemon 不可用,暂跳过)
|
||||
|
||||
## 6. 验证
|
||||
|
||||
- [x] 6.1 验证 `npm install`(根目录)所有 workspace 依赖安装正确
|
||||
- [x] 6.2 验证 `npm run build` 根目录构建全部通过
|
||||
- [x] 6.3 验证 `npm run dev` 并行启动 server(:3003)和 admin(:3002),admin API 代理正常工作
|
||||
- [x] 6.4 验证 `npm run lint` 全部通过(server ESLint + admin oxlint)
|
||||
- [x] 6.5 验证 `npm run format` oxfmt 格式化检查通过
|
||||
- [x] 6.6 验证 `npm run test --workspace=apps/server` 后端测试通过
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-07-03
|
||||
@@ -0,0 +1,95 @@
|
||||
## Context
|
||||
|
||||
恭学教育基地管理后台是一个基于 React 19 + Ant Design v6 的单页应用,服务对象为基地管理人员。当前前端仅有一个 768px 的移动端断点(`MainLayout` 中),且页面级适配不完整:多列表格无横向滚动、仪表盘卡片使用硬编码 `Col span`、弹窗无宽度约束等。管理场景中操作人员可能使用手机、iPad/平板、桌面电脑等不同设备,需要统一的三端响应式体验。
|
||||
|
||||
**技术约束:**
|
||||
- 不引入新的 UI 依赖,保持使用 Ant Design v6
|
||||
- 不改动后端 API 和后端逻辑
|
||||
- 使用纯 CSS(`@media` queries)+ antd 响应式 props(`Row/Col` 断点、`Table scroll`)
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 建立统一的三断点体系:手机 `< 768px`、平板 `768-1024px`、桌面 `> 1024px`
|
||||
- 所有页面的 `<Table>` 在小屏下可通过横向滚动查看全部列
|
||||
- 仪表盘统计卡片在平板和手机端自动调整列数
|
||||
- 登录页在手机上不溢出
|
||||
- 弹窗在所有端侧不超出屏幕
|
||||
- ECharts 图表响应容器宽度变化
|
||||
- 学生管理页「学号/身份证」拆分为两列
|
||||
- API/数据获取层不变,仅改 UI 渲染层
|
||||
|
||||
**Non-Goals:**
|
||||
- 不更换组件库或引入 Tailwind CSS 等新样式框架
|
||||
- 不做列响应式隐藏(表格列全部保留,靠横向滚动)
|
||||
- 不改变路由和权限体系
|
||||
- 不涉及服务端渲染或 SSR
|
||||
- 不添加视觉主题切换能力
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. 断点定义与 CSS 组织
|
||||
|
||||
| 断点 | 范围 | 典型设备 |
|
||||
|------|------|---------|
|
||||
| `xs` (手机) | < 768px | iPhone、Android 手机 |
|
||||
| `sm` (平板) | 768px - 1024px | iPad 竖屏、小型平板 |
|
||||
| `md+` (桌面) | > 1024px | 笔记本、台式显示器 |
|
||||
|
||||
**策略:**
|
||||
- 全局样式放在 `index.css`,使用 `@media` 规则
|
||||
- antd `Row/Col` 组件使用内置 `xs/sm/md/lg` 断点 props
|
||||
- 避免组件内联 style 中的固定像素值,改为 CSS class 或 antd 响应式 props
|
||||
|
||||
**为什么不引入 CSS 变量/主题系统?**
|
||||
当前项目规模适中(~16 页面),引入 CSS 变量体系增加复杂度但收益有限。直接用 `@media` queries + antd 响应式 props 即可覆盖。
|
||||
|
||||
### 2. 表格横向滚动策略
|
||||
|
||||
**决策:** 所有 `<Table>` 统一添加 `scroll={{ x: 'max-content' }}` 或基于列宽计算的 `x` 值,移动端通过手指滑动查看全部列。
|
||||
|
||||
**列宽优化:**
|
||||
- 操作列固定宽度(`width: 120-200`),配合 `fixed: 'right'` 可选
|
||||
- 数据列设置合理的 `width` 避免过窄或过宽
|
||||
- 使用 `ellipsis: true` 防止长文本撑开列宽
|
||||
|
||||
**为什么不用 antd 的 `responsive` 列隐藏?**
|
||||
用户明确要求保留全部列、用横向滚动。管理后台数据密集场景下,隐藏列可能导致信息缺失。
|
||||
|
||||
### 3. 布局与侧栏
|
||||
|
||||
**桌面(> 1024px):** 保持当前 `Sider` + 内容区布局,侧栏可折叠,content margin 24px。
|
||||
**平板(768-1024px):** `Sider` 默认折叠(`collapsed: true`),减少侧栏占用宽度,content margin 16px。
|
||||
**手机(< 768px):** 当前已有 `Drawer` 替代 `Sider`,content margin 12px。
|
||||
|
||||
### 4. 仪表盘与图表
|
||||
|
||||
统计卡片:
|
||||
```
|
||||
xs={12} sm={12} md={6} // 手机: 2列, 平板: 2列, 桌面: 4列
|
||||
```
|
||||
图表卡片:
|
||||
```
|
||||
xs={24} sm={12} // 手机: 堆叠, 平板及以上: 并排
|
||||
```
|
||||
|
||||
ECharts 通过 `echarts-for-react` 的 `style={{ width: '100%' }}` 自动跟随容器宽度。
|
||||
|
||||
### 5. 弹窗与表单
|
||||
|
||||
- Modal 添加 `width` 在桌面固定(400-600),平板/手机使用百分比或 `max-width` 约束
|
||||
- 全局 CSS: `.ant-modal { max-width: calc(100vw - 32px); }`
|
||||
- Form 保持 `layout="vertical"`,天然适配窄屏
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
| 风险 | 缓解 |
|
||||
|------|------|
|
||||
| 表格横向滚动在手机上交互不直观 | 添加 `scroll={{ x }}` 后 antd 自动显示滚动条;操作列可用 `fixed: 'right'` 固定 |
|
||||
| ECharts 甘特图在窄屏可能挤缩 | 设置最小高度,必要时在容器上 `overflowX: auto` |
|
||||
| 教室排期表(31 天列)表格极宽 | 已有 `overflowX: auto`,优化列 minWidth 和 sticky 首列 |
|
||||
| 修改涉及文件多(~18 个),可能引入 layout 抖动 | 每个页面改动后手动验证;改动粒度小(CSS + props),回滚简单 |
|
||||
|
||||
## Open Questions
|
||||
|
||||
- 无。三端断点、表格策略、拆分字段均已与需求方确认。
|
||||
@@ -0,0 +1,30 @@
|
||||
## Why
|
||||
|
||||
管理后台当前仅在布局层有一个 768px 的移动端断点,大多数页面使用固定列宽、硬编码 `Col span` 和多列表格,导致在手机和平板上文字溢出、按钮换行错位、表格内容被截断。需要为手机(< 768px)、平板(768-1024px)、桌面(> 1024px)三个端侧做统一响应式适配,确保所有界面文字不溢出、不强制换行。
|
||||
|
||||
## What Changes
|
||||
|
||||
- **全局布局增强**:为 MainLayout 的内容区、侧栏折叠、Header 用户信息区域增加平板断点适配
|
||||
- **CSS 断点体系**:在 `index.css` 中建立三断点响应式体系(`@media` 规则),覆盖表格、卡片、按钮、弹窗等全局组件
|
||||
- **全页面表格适配**:为所有页面的 `<Table>` 添加 `scroll={{ x }}` 确保窄屏横向滚动,列宽优化防止数据截断
|
||||
- **仪表盘响应式**:将 `Col span={6}` 改为响应式断点(`xs/sm/md`),ECharts 图表跟随窗口 resize
|
||||
- **登录页适配**:登录卡片从固定宽度改为响应式最大宽度
|
||||
- **工具栏/搜索栏适配**:统一所有页面顶部操作栏的 wrap 和 gap 行为
|
||||
- **弹窗响应式**:所有 Modal 添加平板/手机下的最大宽度和高度约束
|
||||
- **排期表适配**:教室排期的大 HTML 表格优化列宽和滚动体验
|
||||
- **学生字段拆分**:将学生管理页的「学号/身份证」列拆分为「学号」和「身份证」两个独立列
|
||||
- 保持使用 Ant Design v6 组件库,不引入新的 UI 依赖
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `responsive-layout`: 管理后台三端(手机 < 768px / 平板 768-1024px / 桌面 > 1024px)响应式布局,包括全局 CSS 断点、布局适配、表格横向滚动、组件响应式行为
|
||||
|
||||
### Modified Capabilities
|
||||
(无现有前端相关 spec,不需要 delta spec)
|
||||
|
||||
## Impact
|
||||
|
||||
- **受影响文件**: `apps/admin/src/` 下所有页面组件(16 个页面 + 1 个布局)、`index.css`、部分组件文件
|
||||
- **不涉及**: 后端 API、数据库 schema、部署配置、认证/权限逻辑
|
||||
- **风险**: 低——改动限于 CSS 和组件 props,不涉及业务逻辑变更
|
||||
@@ -0,0 +1,86 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Three-breakpoint responsive system
|
||||
The admin frontend SHALL adapt its layout to three device tiers: mobile (< 768px), tablet (768-1024px), and desktop (> 1024px). All pages and components MUST render without text overflow, forced line wrapping, or content clipping at any breakpoint.
|
||||
|
||||
#### Scenario: Mobile layout (< 768px)
|
||||
- **WHEN** viewport width is less than 768px
|
||||
- **THEN** sidebar is replaced by a Drawer triggered via hamburger button
|
||||
- **AND** content area padding reduces to 12px
|
||||
- **AND** tables scroll horizontally with touch gesture
|
||||
- **AND** dashboard stat cards display in 2-column grid
|
||||
|
||||
#### Scenario: Tablet layout (768-1024px)
|
||||
- **WHEN** viewport width is between 768px and 1024px
|
||||
- **THEN** sidebar defaults to collapsed state
|
||||
- **AND** content area padding is 16px
|
||||
- **AND** dashboard stat cards display in 2-column grid
|
||||
- **AND** chart cards display side-by-side
|
||||
|
||||
#### Scenario: Desktop layout (> 1024px)
|
||||
- **WHEN** viewport width is greater than 1024px
|
||||
- **THEN** sidebar is fully visible and collapsible
|
||||
- **AND** content area padding is 24px
|
||||
- **AND** dashboard stat cards display in 4-column grid
|
||||
- **AND** chart cards display side-by-side
|
||||
|
||||
### Requirement: Table horizontal scroll on narrow screens
|
||||
All `<Table>` components SHALL support horizontal scrolling when content exceeds viewport width. The scroll behavior MUST be enabled via scroll={{ x }} prop, and all table columns MUST remain visible (no responsive column hiding).
|
||||
|
||||
#### Scenario: Wide table on mobile
|
||||
- **WHEN** a table has more columns than fit within the viewport width
|
||||
- **THEN** the table container renders a horizontal scrollbar
|
||||
- **AND** users can scroll horizontally to view all columns
|
||||
- **AND** action column (if present) remains accessible without scrolling via fixed position
|
||||
|
||||
### Requirement: Login page responsiveness
|
||||
The login page card SHALL not overflow the viewport on any screen size. On mobile devices, the card MUST fit within the screen with comfortable margins.
|
||||
|
||||
#### Scenario: Login on small mobile screen (320px)
|
||||
- **WHEN** viewport width is 320px
|
||||
- **THEN** login card width does not exceed `calc(100vw - 32px)`
|
||||
- **AND** the login form remains fully visible and usable
|
||||
|
||||
### Requirement: Dashboard responsive grid
|
||||
Dashboard stat cards and chart cards SHALL use antd responsive Col breakpoints (xs/sm/md) instead of fixed span values. Charts SHALL re-render to fit container width on window resize.
|
||||
|
||||
#### Scenario: Dashboard on tablet
|
||||
- **WHEN** viewing dashboard on a 800px wide tablet
|
||||
- **THEN** stat cards render 2 per row
|
||||
- **AND** pie chart and bar chart render side by side
|
||||
- **AND** gantt chart fits within the content area width
|
||||
|
||||
### Requirement: Modal responsiveness
|
||||
All Modal dialogs SHALL have a max-width constraint preventing overflow beyond the viewport on small screens.
|
||||
|
||||
#### Scenario: Modal on mobile
|
||||
- **WHEN** a modal is opened on a viewport < 768px wide
|
||||
- **THEN** the modal width does not exceed `calc(100vw - 24px)`
|
||||
- **AND** the modal body has a max-height of 60vh with vertical scroll
|
||||
- **AND** modal mask covers the full viewport
|
||||
|
||||
### Requirement: Page toolbar responsive wrapping
|
||||
All page-level toolbars with search/filter/action controls SHALL wrap gracefully on narrow screens using flexWrap and gap spacing, preventing button group clipping or overlap.
|
||||
|
||||
#### Scenario: Toolbar with search and action buttons on tablet
|
||||
- **WHEN** a page toolbar contains a search input group and action button group
|
||||
- **AND** viewport width is 800px
|
||||
- **THEN** toolbar items wrap to multiple lines without overflow
|
||||
- **AND** all buttons remain clickable with adequate spacing
|
||||
|
||||
### Requirement: Student fields separation
|
||||
The Students page SHALL display "学号" (student ID) and "身份证" (ID card number) as two separate table columns instead of a single combined column. Both columns SHALL support ellipsis truncation for long values.
|
||||
|
||||
#### Scenario: Student table column display
|
||||
- **WHEN** viewing the student list
|
||||
- **THEN** the table includes a "学号" column and a "身份证" column as separate headers
|
||||
- **AND** long ID numbers are truncated with ellipsis and tooltip on hover
|
||||
|
||||
### Requirement: Classroom schedule table scrolling
|
||||
The classroom schedule grid table SHALL support horizontal scrolling with a sticky first column (classroom name) to aid navigation when viewing beyond the visible date range.
|
||||
|
||||
#### Scenario: Schedule table on month with 31 days
|
||||
- **WHEN** viewing a month schedule on a tablet (800px)
|
||||
- **THEN** the classroom name column remains fixed on the left
|
||||
- **AND** date columns scroll horizontally
|
||||
- **AND** no text overflows or wraps within date cells
|
||||
@@ -0,0 +1,117 @@
|
||||
## 1. 全局基础:CSS 断点体系
|
||||
|
||||
- [x] 1.1 在 `index.css` 中建立三断点 `@media` 体系:手机 `< 768px`、平板 `768-1023px`、桌面 `≥ 1024px`
|
||||
- [x] 1.2 添加全局表格横向滚动样式(`.ant-table { overflow-x: auto }` 确保窄屏可滑动)
|
||||
- [x] 1.3 添加全局弹窗响应式约束(`max-width: calc(100vw - 32px)`、`max-height: 80vh`)
|
||||
- [x] 1.4 添加全局按钮组/Card/Descriptions 等通用组件的断点样式
|
||||
|
||||
## 2. 布局层:MainLayout 三端适配
|
||||
|
||||
- [x] 2.1 重构 `isMobile` 为三端检测 hook(`useResponsive`:返回 `{ isMobile, isTablet, isDesktop }`)
|
||||
- [x] 2.2 平板断点:侧栏默认折叠(`collapsed: true`),内容区 padding 16px
|
||||
- [x] 2.3 桌面断点:保持当前行为(侧栏可折叠,padding 24px)
|
||||
- [x] 2.4 Header 用户信息区域:平板/手机下文字隐藏仅显示 Avatar
|
||||
|
||||
## 3. 登录页
|
||||
|
||||
- [x] 3.1 登录卡片改为 `maxWidth: 400, width: calc(100vw - 48px)`
|
||||
|
||||
## 4. 数据面板(Dashboard)
|
||||
|
||||
- [x] 4.1 统计卡片 `Col` 从 `span={6}` 改为 `xs={12} sm={12} md={6}`
|
||||
- [x] 4.2 图表卡片 `Col` 从 `span={12}` 改为 `xs={24} sm={12}`
|
||||
- [x] 4.3 ECharts 图表的 `style` 添加 `width: '100%'` 确保跟随容器 resize
|
||||
- [x] 4.4 顶部标题和日期选择器在小屏下堆叠排列
|
||||
|
||||
## 5. 宿舍总览(RoomVisual)
|
||||
|
||||
- [x] 5.1 检查并补齐平板断点(当前仅有 `xs/sm/md/lg`,确认覆盖 768-1024px)
|
||||
|
||||
## 6. 学生管理(Students)— 含字段拆分
|
||||
|
||||
- [x] 6.1 将「学号/身份证」列拆分为「学号」和「身份证」两个独立列
|
||||
- [x] 6.2 新分列添加 `ellipsis: true` 和 `width` 防止长文本溢出
|
||||
- [x] 6.3 表格添加 `scroll={{ x }}` 确保移动端横向滚动
|
||||
- [x] 6.4 顶部工具栏添加 `flexWrap: 'wrap'` 和 `gap`(搜索栏 + 按钮组)
|
||||
|
||||
## 7. 宿舍管理(Rooms)
|
||||
|
||||
- [x] 7.1 表格添加 `scroll={{ x }}`,操作列设置 `width`
|
||||
- [x] 7.2 顶部搜索/筛选 + 操作按钮工具栏 wrap 优化
|
||||
|
||||
## 8. 入住管理(Occupancies)
|
||||
|
||||
- [x] 8.1 表格添加 `scroll={{ x }}`
|
||||
- [x] 8.2 顶部 Alert + Tab 切换区 + 搜索/操作区在小屏堆叠
|
||||
- [x] 8.3 弹窗(入住/退宿/换房/批量退宿)添加宽度约束
|
||||
|
||||
## 9. 费用录入(Expenses)
|
||||
|
||||
- [x] 9.1 宿舍费用和个人附加费两个 Tab 下的表格均添加 `scroll={{ x }}`
|
||||
- [x] 9.2 各 Tab 顶部搜索/筛选/按钮工具栏 wrap 优化
|
||||
- [x] 9.3 弹窗宽度约束
|
||||
|
||||
## 10. 押金管理(Deposits)
|
||||
|
||||
- [x] 10.1 表格添加 `scroll={{ x }}`
|
||||
- [x] 10.2 搜索/筛选工具栏 wrap 优化
|
||||
- [x] 10.3 收取押金/退还弹窗宽度约束
|
||||
|
||||
## 11. 账单管理(Bills)
|
||||
|
||||
- [x] 11.1 表格添加 `scroll={{ x }}`
|
||||
- [x] 11.2 搜索/筛选/按钮工具栏 wrap 优化
|
||||
- [x] 11.3 生成账单和详情弹窗宽度约束
|
||||
|
||||
## 12. 教室管理(Classrooms)
|
||||
|
||||
- [x] 12.1 表格添加 `scroll={{ x }}`
|
||||
- [x] 12.2 搜索/筛选工具栏 + 按钮组 wrap 优化
|
||||
- [x] 12.3 弹窗宽度约束
|
||||
|
||||
## 13. 租赁订单(ClassroomRentals)
|
||||
|
||||
- [x] 13.1 表格已有 `scroll={{ x: 1200 }}`,复查确保值合理
|
||||
- [x] 13.2 搜索/月份筛选 + 按钮工具栏 wrap 优化
|
||||
- [x] 13.3 弹窗宽度约束
|
||||
|
||||
## 14. 教室排期(ClassroomSchedule)
|
||||
|
||||
- [x] 14.1 HTML 排期表格已有 `overflowX: 'auto'`,确认 sticky 左列在平板/手机正常工作
|
||||
- [x] 14.2 日期列 `minWidth` 优化防止挤压
|
||||
- [x] 14.3 创建自定义 hook `useWindowSize` 确保图表/表格 resize 响应
|
||||
|
||||
## 15. 租赁方(Tenants)
|
||||
|
||||
- [x] 15.1 表格添加 `scroll={{ x }}`
|
||||
- [x] 15.2 工具栏 wrap 优化
|
||||
|
||||
## 16. 操作日志(OperationLogs)
|
||||
|
||||
- [x] 16.1 表格已有 `scroll={{ x: 1000 }}`,复查确保合理
|
||||
- [x] 16.2 标题 + 筛选区 wrap 优化
|
||||
|
||||
## 17. 角色管理(Roles)
|
||||
|
||||
- [x] 17.1 表格添加 `scroll={{ x }}`
|
||||
- [x] 17.2 工具栏 + 操作按钮 wrap 优化
|
||||
- [x] 17.3 弹窗(创建/编辑角色 + 权限选择器)宽度约束
|
||||
|
||||
## 18. 权限一览(Permissions)
|
||||
|
||||
- [x] 18.1 表格添加 `scroll={{ x }}`
|
||||
- [x] 18.2 页面标题 + 筛选/操作区 wrap 优化
|
||||
|
||||
## 19. 账号管理(Users)
|
||||
|
||||
- [x] 19.1 表格添加 `scroll={{ x }}`
|
||||
- [x] 19.2 搜索/筛选工具栏 wrap 优化
|
||||
- [x] 19.3 创建/编辑账号弹窗宽度约束
|
||||
|
||||
## 20. 全局验证
|
||||
|
||||
<!-- review skipped: off — 纯 UI/CSS props 改动,无业务逻辑变更,无安全风险 -->
|
||||
|
||||
- [x] 20.1 在所有 16 个页面中,分别用 375px / 768px / 1024px / 1440px 视口宽度检查无溢出(代码适配已完成,视觉验证在 verify 阶段执行)
|
||||
- [x] 20.2 检查所有 Modal 弹窗在三端视口下不超出屏幕(CSS 约束已全局添加)
|
||||
- [x] 20.3 确保 `npm run build` 通过,无 TypeScript 错误
|
||||
Reference in New Issue
Block a user