PayrollManagementSystem/frontend/README.md
liuwenxin 273ebf4657 chore: 初始化项目结构与登录鉴权模块
后端 FastAPI 六层结构(api/services/repositories/models/schemas/core),
统一响应信封、全局异常、日志脱敏;6 张表经 Alembic 迁移建库,SQL 留存
db/migration;登录鉴权含图形验证码(Redis GETDEL 防重放)、JWT、登录限流、
登出黑名单(TTL=令牌剩余有效期)、登录日志。

前端 Vue3 + TS + Element Plus 骨架,含登录页、路由守卫、请求封装与 Vite 代理。

工程配置:uv 锁定 Python 3.12、ruff 检查与格式化、editorconfig、gitattributes。

测试 184 项(单元 155 / 集成 29)全绿,浏览器端到端 8 项通过。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-09 18:46:36 +08:00

123 lines
7.1 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.

# 前端
> AI摘要:衍水集团营销人员工资管理系统的前端。Vue 3 + TypeScript + Vite + Element Plus + Pinia。关键文件 `src/api/request.ts`(统一请求封装)、`src/router/index.ts`(路由守卫)、`src/store/user.ts`(会话状态)、`src/views/login/index.vue`(登录页)。当前完成登录链路,业务模块页面待接入。
## 职责
- 登录页与图形验证码交互
- 会话状态管理与路由守卫
- 后续:基础数据、业务数据、绩效核算、报表等业务页面
## 技术栈与版本约束
| 组件 | 版本 | 说明 |
| --- | --- | --- |
| Vue | 3.5.x | |
| Vue Router | **5.x** | 新大版本;对 vite/pinia 的 peer 均为 optional |
| Pinia | **4.x** | **`@vue/devtools-api` 是其非可选 peer**,必须显式安装,否则装不上或运行时报警 |
| Element Plus | 2.14.x | 全量引入,产物约 1MB(gzip 347KB);后续可改按需引入 |
| Vite | 8.x | |
| TypeScript | **~5.9.3** | **不可用 7.x** —— TS 7 是 Go 重写的原生编译器,而 vue-tsc 的 peer 只写 `>=5.0.0` 拦不住,兼容性未经验证 |
| 包管理 | pnpm | |
## 目录结构
```text
src/
├── api/
│ ├── request.ts # axios 实例、拦截器、统一信封解包
│ └── auth.ts # 认证接口封装与类型
├── assets/styles/ # 全局样式
├── components/ # 通用组件(待补充)
├── composables/ # 组合式函数(待补充)
├── router/index.ts # 路由表与全局守卫
├── store/
│ ├── index.ts # Pinia 实例
│ └── user.ts # 会话状态:token、用户信息、登录/登出
├── utils/storage.ts # localStorage 读写
└── views/
├── login/ # 登录页
└── dashboard/ # 首页(登录链路验证页)
```
## 启动
```bash
# 前置:后端已在 127.0.0.1:8000 运行
pnpm install
pnpm dev # http://localhost:5173
pnpm type-check # vue-tsc 类型检查
pnpm build # 类型检查 + 生产构建
```
## 几个关键约定
**开发期用 Vite 代理,不开 CORS**
`/api` 被代理到 `http://127.0.0.1:8000`。用 `127.0.0.1` 而非 `localhost` 是因为 Node 17+ 对 `localhost` 优先解析 IPv6 的 `::1`,而 uvicorn 默认只监听 IPv4。
反向地,dev server 自身显式绑定 `host: '127.0.0.1'` —— 不指定时 Vite 只监听 `[::1]`,会出现「`localhost:5173` 能开、`127.0.0.1:5173` 连不上」的困惑。此配置让两种写法都可用。
代理成立后 `/api/docs`(Swagger)也能从 5173 端口打开,联调期只需一个域名。
**401 只表示「登录态失效」**
登录接口自身的失败返回 **400/403**,绝不会是 401。因此前端拦截器可以放心地把 401 一律当作「清 token + 跳登录页」处理,不会出现「用户输错一次密码就被弹回登录页」的循环。
并发 401 用模块级标志去重,避免同时弹出多条提示并反复跳转。
**登录失败后必须刷新验证码**
后端用 Redis `GETDEL` 原子消费验证码,**任何一次提交后该验证码都已失效**。登录页在 `catch` 分支里调用了 `refreshCaptcha()` —— 漏掉这一步,用户第二次提交必然报「验证码错误」,且极难自查。
**令牌存 localStorage**
实现最简,代价是 XSS 可窃取。缓解手段是 JWT 有效期上限 2 小时,以及后端支持 `token_version` 提版即时失效。若后续要求更高,可改为 httpOnly Cookie,届时只需改 `src/utils/storage.ts` 与后端签发方式。
## 视觉设计
**设计立意**:公司名「衍水」是水,而中文「流水账」字面就是**流水之账**——系统的全部工作正是记录回款怎么流、绩效怎么算。视觉方向由此而生:深水面板 + 流动的层理线,即公司名的意象,也是业务的隐喻。
**配色**(金色分两档是有意的:亮金只用于深色底、深金只用于浅色底,两处对比度都达标)
| 名 | 值 | 用途 |
| --- | --- | --- |
| 深渊 | `#061A22` | 品牌面板底色 |
| 沉碧 | `#0F3646` | 面板渐变、水纹线 |
| 青金 | `#C9A227` | **深色底上**的细线与边框 |
| 赭金 | `#8F6520` | **浅色底上**的主按钮(配白字约 6.2:1) |
| 素纸 | `#FAFAF7` | 表单侧底色 |
| 墨 | `#16262D` | 正文 |
**字体**:品牌名用**宋体**(衬线,拟正式、拟账册),正文用系统无衬线,数字与验证码用等宽。
**界面不引入网络字体**——内网环境字体加载不可靠,靠字重、字距与留白做层次更稳妥。新页面请沿用这套字号节奏。
**签名元素**:品牌面板的水纹层理线,越往下越密(如潜向深处),载入时用 `stroke-dashoffset` 逐条画出——**一个统一的入场动作,而不是散落的特效**。
**构图**:品牌内容(名称 / 金线 / 系统名 / 主标语 / 环节行)**垂直居中聚成一块**,配一层柔和径向光晕作落点。
早期版本用 `space-between` 把品牌与标语顶到两端,中间空出大半屏,两处都成了孤岛——超宽屏会把这种疏离放大得更明显。**内容块要限宽**(`max-width`),否则宽屏下文字会被拉散。
### 写代码时的三条约定
- 覆盖 Element Plus 主题请改 **CSS 变量**(`--el-button-bg-color` 等),不要直接写 `background-color` —— 后者与 `.el-button--primary` 同权重,会受样式注入顺序影响而不稳定
- 中文可在任意字间断行,**关键文案的断行位置要显式控制**(如主标语拆成两个 `<span>`),交给浏览器自动换行会出现单字独占一行的难看结果
- 新页面需响应式到移动端、保留键盘焦点可见、并尊重 `prefers-reduced-motion`
## 验证码图片
后端自绘渲染(`backend/app/core/captcha_image.py`),**不再使用 `captcha` 库的默认输出**——那是随机彩色字符 + 重噪点,与界面视觉体系冲突。自绘后字符为墨蓝、底为素纸、干扰线与噪点取自同一套配色。
识别难度由**逐字随机旋转(±14°)+ 垂直抖动(±2px)+ 正弦干扰曲线 + 双层噪点**共同保证,与原实现相当;笔画加了 1px 描边——本系统用户是营销人员,屏幕与年龄跨度大,验证码过难会直接变成支持负担。
## 端到端测试
`e2e/login_flow.py` 用 Playwright 驱动真实浏览器,覆盖 8 项:路由守卫、验证码渲染、失败后自动刷新、登录跳转、用户信息展示、刷新恢复会话、登出、登出后令牌失效。
```bash
# 前置:MySQL/Redis 容器、后端、前端 dev server 均在运行
python e2e/login_flow.py
```
需要 Python `playwright` 包与 chromium(`python -m playwright install chromium`)。
## 常见问题
**登录后刷新页面被踢回登录页** —— 检查 `/api/v1/auth/me` 是否可达;路由守卫靠它恢复会话。
**验证码图片不显示** —— 多为代理未生效。访问 `http://localhost:5173/api/health` 应返回统一信封格式 JSON。
**`pnpm install` 报 peer 依赖错误** —— 确认 `@vue/devtools-api` 在 `dependencies` 中(pinia 4 的必需 peer)。