162 lines
11 KiB
Markdown
162 lines
11 KiB
Markdown
# 前端
|
||
|
||
> AI摘要:衍水集团营销人员工资管理系统的前端。Vue 3 + TypeScript + Vite + Element Plus + Pinia。关键文件 `src/api/request.ts`(统一请求封装)、`src/components/layout/BasicLayout.vue`(应用布局与导航)、`src/router/index.ts`(路由守卫)、`src/store/user.ts`(会话状态)。已实现登录链路(2026-10 视觉方向重做:点阵底/涟漪/天光/居中卡片)、系统管理页面、岗位角色与计提参数配置页、Bento 仪表盘首页;视觉体系与设计预览见 `docs/design/视觉方向预览.html`。
|
||
|
||
## 职责
|
||
|
||
- 登录页与图形验证码交互
|
||
- 会话状态管理与路由守卫
|
||
- 岗位角色管理页(模板绑定)、计提参数配置页(分组目录/批次提交/版本对比/导出/计算预览)
|
||
- 首页 Bento 仪表盘(M1 空态设计;业务上线后接入真实统计,图表用 ECharts)
|
||
- 后续:基础数据、业务数据、绩效核算、报表等业务页面
|
||
|
||
## 技术栈与版本约束
|
||
|
||
| 组件 | 版本 | 说明 |
|
||
| --- | --- | --- |
|
||
| 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 # 认证接口封装与类型
|
||
│ ├── users.ts / roles.ts / logs.ts # 系统管理接口封装
|
||
│ └── types.ts # 分页等公共类型
|
||
├── assets/styles/ # 全局样式与品牌 token(:root 的 --ys-*)
|
||
├── components/
|
||
│ ├── layout/ # BasicLayout(侧边栏/顶栏/面包屑)、AppMenu
|
||
│ └── table/ # StatusTag(0/1 状态标签)、AppPagination
|
||
├── composables/
|
||
│ ├── useAppMenu.ts # 菜单从路由表派生(meta.title/group/order/requiresAdmin)
|
||
│ ├── useConfirmAction.ts # 确认框→执行→提示→刷新 动作流(启停/删除共用)
|
||
│ └── usePagedList.ts # 分页列表状态(请求序号防竞态、删除后页码回退)
|
||
├── router/index.ts # 路由表与全局守卫(四层,含 requiresAdmin)
|
||
├── store/
|
||
│ ├── index.ts # Pinia 实例
|
||
│ └── user.ts # 会话状态:token、用户信息、登录/登出
|
||
├── utils/ # storage/format/validate/constants/error(handledError)
|
||
└── views/
|
||
├── login/ # 登录页
|
||
├── dashboard/ # 首页(登录链路验证页)
|
||
└── system/ # 系统管理:users(用户)/ roles(角色)/ logs(日志查询)
|
||
```
|
||
|
||
## 启动
|
||
|
||
```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` | 正文 |
|
||
|
||
**字体**:品牌名用**宋体**(衬线,拟正式、拟账册),正文用系统无衬线,数字与验证码用等宽。
|
||
**界面不引入网络字体**——内网环境字体加载不可靠,靠字重、字距与留白做层次更稳妥。新页面请沿用这套字号节奏。
|
||
|
||
**落地方式**:品牌色已提升为全局 CSS 变量(`src/assets/styles/index.css` 的 `:root`,`--ys-*` 前缀),登录页与布局、系统管理页面共用;全局还把 `--el-color-primary` 指向赭金,Element Plus 组件默认走品牌色。登录页 scoped 里的同名局部变量只是全局 token 的别名。
|
||
|
||
**签名元素**:品牌面板的水纹层理线,越往下越密(如潜向深处),载入时用 `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/` 下两个脚本用 Playwright 驱动真实浏览器,公共工具抽在 `e2e/common.py`(登录动作、从 Redis 取验证码答案、提示等待、失败断言与控制台错误收集):
|
||
|
||
- `login_flow.py`(8 项):路由守卫、验证码渲染、失败后自动刷新、登录跳转、用户信息展示、刷新恢复会话、登出、登出后令牌失效
|
||
- `system_management.py`(13 项):菜单导航、用户增删改查(含重名负例)、重置密码、启用/禁用、角色保护与 CRUD、操作日志留痕与详情懒加载脱敏、登录日志、普通用户菜单隐藏与路由守卫
|
||
|
||
```bash
|
||
# 前置:MySQL/Redis 容器、后端、前端 dev server 均在运行
|
||
python e2e/login_flow.py
|
||
# 管理员密码经环境变量传入(不写死在源码里)
|
||
E2E_ADMIN_PASSWORD=<开发库admin密码> python e2e/system_management.py
|
||
```
|
||
|
||
需要 Python `playwright` 包与 chromium(`python -m playwright install chromium`)。
|
||
脚本自建的测试数据(`e2e_` 前缀)在正常流程内清理;中途失败会残留在开发库。
|
||
|
||
## 常见问题
|
||
|
||
**登录后刷新页面被踢回登录页** —— 检查 `/api/v1/auth/me` 是否可达;路由守卫靠它恢复会话。
|
||
|
||
**验证码图片不显示** —— 多为代理未生效。访问 `http://localhost:5173/api/health` 应返回统一信封格式 JSON。
|
||
|
||
**`pnpm install` 报 peer 依赖错误** —— 确认 `@vue/devtools-api` 在 `dependencies` 中(pinia 4 的必需 peer)。
|
||
|
||
## 端到端测试(2026-10-13 追加 · M1 参数体系)
|
||
|
||
| 脚本 | 覆盖 | 结果 |
|
||
| --- | --- | --- |
|
||
| `e2e/login_flow.py` | 登录链路 8 项 | ✓ 全通过(登录页视觉重做后回归) |
|
||
| `e2e/system_management.py` | 系统管理 13 项 | ✓ 全通过(布局白化后回归) |
|
||
| `e2e/param_management.py` | 计提参数 7 步:目录分组 → 修改入待提交 → 批次提交(立即生效)→ 目录反映新值 → 版本记录 → 恢复种子值 → 计算预览金标准(27 天 / 9.7‰ / 145.5 元) | ✓ 全通过(首跑) |
|
||
|
||
运行前置:容器 + 后端(8000)+ 前端 dev(5173);`E2E_ADMIN_PASSWORD` 传管理员密码。
|
||
|
||
## 端到端测试(2026-10-13 追加 · M2 基础数据与业务数据)
|
||
|
||
| 脚本 | 覆盖 | 结果 |
|
||
| --- | --- | --- |
|
||
| `e2e/base_data.py` | 基础数据 13 步:菜单两组 → 人员建(经理/业务员指派)→ 编辑抽屉加辐射区域回显 → 删除保护(400 引导改离职)→ 产品建 + 重名 400 → 客户建 → 归属变更(确认文案含「不回溯历史」)→ 留痕抽屉可见 → 控制台无 JS 错误 | ✓ 全通过(首跑) |
|
||
| `e2e/business_data.py` | 业务全链路 16 步:基础建数 → 业务建(双类别明细)→ 编辑汇总重算(65000→115000)→ 详情抽屉(时间线/毛利润卡片)→ 归属调整 → 发货(SH 编号)→ 回款级联指定批次 → 回款天数「6 天」→ 未指定批次文案 → 状态流转已完成 → 终态禁发货 → 评级计算器出 A 级 → 清理留档 | ✓ 全通过(修复单价小数位/删除保护后) |
|
||
|
||
运行前置同上;测试数据 `e2e_` 前缀,正常流程内清理(业务含发货/回款时按设计不可删,留档说明)。
|
||
**期间修复的缺陷**:后端删除保护两处漏检(客户被业务引用/人员被业务归属引用 → 500)、
|
||
单价列 `DECIMAL(14,4)` 被两位小数校验拒收(前后端同源)、`wait_toast` 超时丢失诊断信息。
|