PayrollManagementSystem/frontend
liuwenxin 678db0499d fix(frontend): 分页失败回滚到实际显示页
records 只在请求成功时更新,而分页器高亮绑的是 pageNum,失败不回滚就会
出现「高亮第 1 页、列表是第 2 页的数据」的永久错位,用户来回点才恢复。
- 用 shownPageNum / shownPageSize 记录实际显示的页,失败时 restoreShownPage
  退回该页
- 回滚本身也要防竞态:只有本次请求仍是最新请求(seq === requestSeq)才回滚,
  否则连点时旧请求失败会把用户刚点出的新页码覆盖掉,表现为页码自己跳回去
- 页码与条数在 await 之前取快照,避免等待期间被别的点击改动后记错「显示的页」

对外接口不变(load / search / onPageChange / onSizeChange / reloadAfterDelete),
现有调用方无需改动。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-23 16:22:19 +08:00
..
e2e test(e2e): 二次分配流程断言改为取实际方案并补审批内容校验 2026-10-21 14:25:39 +08:00
src fix(frontend): 分页失败回滚到实际显示页 2026-10-23 16:22:19 +08:00
.env.development chore: 初始化项目结构与登录鉴权模块 2026-10-09 18:46:36 +08:00
.env.production chore: 初始化项目结构与登录鉴权模块 2026-10-09 18:46:36 +08:00
env.d.ts chore: 初始化项目结构与登录鉴权模块 2026-10-09 18:46:36 +08:00
index.html chore: 初始化项目结构与登录鉴权模块 2026-10-09 18:46:36 +08:00
package.json feat(frontend): 菜单图标与视觉设计系统落地 2026-10-22 19:45:54 +08:00
pnpm-lock.yaml feat(frontend): 菜单图标与视觉设计系统落地 2026-10-22 19:45:54 +08:00
pnpm-workspace.yaml chore: 初始化项目结构与登录鉴权模块 2026-10-09 18:46:36 +08:00
README.md feat(frontend): 视觉规范 v4 全站落地与实测修复 2026-10-23 09:58:48 +08:00
tsconfig.json chore: 初始化项目结构与登录鉴权模块 2026-10-09 18:46:36 +08:00
vite.config.ts chore: 初始化项目结构与登录鉴权模块 2026-10-09 18:46:36 +08:00

前端

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

目录结构

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(日志查询)

启动

# 前置:后端已在 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 与后端签发方式。

视觉设计

设计立意:公司名「衍水」是水,而中文「流水账」字面就是流水之账——系统的全部工作正是记录回款怎么流、绩效怎么算。视觉方向由此而生:中性灰白的水面纸感 + 靛蓝点缀,安静、克制,长时间核数不累眼。完整规范见 docs/design/视觉方向预览.html(2026-10-12 起为 v3「质感版」;2026-10-22 升 v4「同步版」——设计稿与已交付的月结 / 审批 / 报表 / 工作台对齐,色板、字体与组件基调未变)。

配色(靛蓝只作重点点缀——主按钮 / 链接 / 焦点 / 当前菜单,大面积用中性灰白)

名 值 用途
靛蓝 #2e4ba8 主按钮、链接、焦点、当前菜单(配白字约 7.5:1)
深靛 #223a85 主色深阶(按压态)
淡靛 #edf1fa 选中底色、浅色强调底
纸白 #ffffff 卡片与面板底色
雾灰 #f4f5f8 页面底色
墨 #1c2333 正文
墨灰 #5b6577 / #9aa3b2 次级文本 / 辅助文本

字体:品牌名用思源黑体(Noto Sans SC / Source Han Sans SC,开源可商用),正文用同一无衬线栈,数字与验证码用等宽。禁止引入微软雅黑、宋体等需商业授权的字体——CSS 字体栈、设计稿、导出物一律只用开源或免费商用字体(如阿里巴巴普惠体、HarmonyOS Sans)。 界面不引入网络字体——内网环境字体加载不可靠,靠字重、字距与留白做层次更稳妥。新页面请沿用这套字号节奏。

质感来源(而非装饰):表面 1–2% 明度差的微渐变、半透明描边、近距 + 远距分层阴影、内高光 / 内阴影、极微噪点(内联 SVG)、精确排版(字距 / 等宽数字 / 行高节律)。

落地方式:品牌色已提升为全局 CSS 变量(src/assets/styles/index.css 的 :root,--ys-* 前缀),登录页、布局与各业务页面共用;全局把 --el-color-primary 指向靛蓝,并按 Element Plus 公式预计算了 light-3/5/7/8/9 与 dark-2 色阶(不用 color-mix(),避免依赖较新浏览器特性)。旧 token 名(深水青 / 金色系列)在 :root 中保留映射,存量页面迁移完成后移除。

语义三色(2026-10-22 落地):成功 #1e9e6a / 警示 #c98a1b / 危险 #c94f4f,已覆盖 --el-color-success/warning/danger/error 及其 -rgb、light-3/5/7/8/9、dark-2 全阶。Element Plus 默认的绿黄红饱和度过高,放在灰底卡片上会跳色。-rgb 变体必须一并覆盖——组件库的半透明底色用 rgba(var(--el-color-success-rgb), .1) 取色,漏改会出现「文字是新的、底色还是旧的」。色阶按 Element Plus 公式预计算,不用 color-mix()。

组件基调(同样写在 src/assets/styles/index.css,页面不要再重复):按钮高 34px / 圆角 7px / 字号 13px,主按钮是 #3a58bc → #2e4ba8 微渐变 + 内高光 + 悬停上抬 1px 并加深阴影;输入框高 34px,焦点为靛蓝描边加 3px 柔光外环;表头 12px/500/字距 0.04em + 极浅渐变底,单元格内距 12px 16px、行高 1.5,行悬停 120ms;分页需 background 模式,当前页与主按钮同材质;el-tag 压到 11px 方角。动效统一 120–180ms,只做颜色、阴影与 1px 位移,并尊重 prefers-reduced-motion。

表格行高必须是 1.5(全局 .el-table .cell):Element Plus 默认给单元格 23px 行高(≈1.77 倍),加上 12px 上下内距,单行数据行会到 51px——一屏少看两行。收到 1.5 后单行数据行 44px、纯文本行 44px,与设计稿「12px 内距 + 13px 字」对齐;多行单元格仍按内容撑开。

页面版式:只用全局类(新页面不再各写一套)

类 用途
.page-head / .page-title / .page-desc / .waterline 页面头:18px 标题 + 一句 12px 描述 + 水线(每页一条,不重复)
.card / .card-head / .card-title / .card-body / .card-foot 内容卡片;卡脚右对齐,承载分页或合计条
.filter-bar / .filter-bar__actions 筛选条(横向 flex);按钮组靠右
.toolbar / .toolbar-note 主操作行 / 右侧提示文字
.section-title 区块小标题(13px/600)
.cell-code / .cell-num / .cell-muted 表格列语义:等宽编号 / 数字右对齐 + 等宽数字 / 弱化文本
.ys-pill + --ok --wait --lock --reject 状态胶囊(浅底 + 6px 圆点),四档语义与月结生命周期一致
.ys-rise 入场上浮动画(卡片默认已带)

.cell-num 有两种用法:直接挂在 td 上,或包一层 <span class="cell-num"> 处理「一行里既文本又数字」。给后者单独撑满一行是必要的——行内元素的 text-align 无效;但不能给 .cell-num 统一加 display,那会把 td 从 table-cell 变成 inline-block,表格列宽算法随即失效。

表单页版式(公司信息页为样板)

设置类表单页不要把表单缩在左上角一小块——内容区再宽也只占三分之一,页面看着像没做完。views/system/settings/index.vue 是基准:

做法 口径
网格 左列 minmax(0, 1fr) + 右列固定 480px(实景预览),align-items: start;≤1200px 折成单列
控件行 主控件与说明同一行(.field-control):控件 ≤420px,说明跟在后面 20px;≥1600px 说明改右对齐贴卡片右缘,整行铺满而不是缩在左上角
实景预览 右侧「登录页预览」按真实登录页 1:1 复刻(控件 38px 高、品牌名 17px / 0.34em 字距、logo ≤56×36、三项输入含验证码位、登录按钮、版权行),不是缩比示意——控件高度对不上就等于没有所见即所得
生效范围 独立一张卡列出这份设置会出现在哪(登录页品牌位与版权行 / Excel 导出页眉 / 打印稿页眉),用户不用存完再去别处找
预览态 预览卡 aria-hidden + 标注「示意,非交互」,避免被当成可点的登录框

状态与流程表达(2026-10-23 实测补)

  • 月结生命周期条四段圆点按阶段着色:草稿中性 / 待审批琥珀 / 已审定靛蓝 / 已驳回砖红。颜色之外每段仍有文字,不靠颜色单独表意。
  • 流程条这类分段控件旁要挂一行操作提示(如「点状态只看该阶段,再点一次取消」)。没有提示时,未选中态的四段读起来像四颗孤立的小按钮,不像一条流程。
  • 卡片内空态(工作台 .b-empty-mini)在卡片剩余高度内居中,不要全挤在顶部留一整块空白。

菜单图标(2026-10-22 落地,M1 遗留项):路由 meta.icon 只写语义 key,图标实现集中在 src/components/layout/menuIcons.ts 的注册表(@element-plus/icons-vue 具名导入,可摇树)。换图标库或换风格只改注册表,路由表不动。视觉口径沿用设计稿:16px、透明度 0.7(激活项 1)、右间距 9px;分组标题不加图标,只有菜单项有。新增页面必须补 meta.icon,否则菜单项只有文字。注册表是 UTF-8 文件——批量转码脚本会把中文注释转成乱码(2026-10-23 已在该文件踩过一次,已修回),改这个文件不要过任何编码转换。

签名元素:渐隐靛蓝水线——各页面标题下的细线(56px,左实右虚)与登录页品牌名下的短线(40px),「衍水」之水的提纯,全站唯一的装饰性元素;登录页右上角另有涟漪同心圆与顶部靛蓝天光作背景(低透明度)。与早期深色面板 + 水纹层理同一意象,品牌连续而非推倒重来。

打印视图(views/reports/print/):纸面产物与屏幕稿不同源——独立顶层路由、不挂侧栏顶栏,白灰画布上一张定宽白稿纸,表格 11px 细实线、表头浅底左对齐(数值列表头右对齐)、章节编号、三栏签字(制表 / 复核 / 审批),无圆角无阴影。屏幕上的操作条带 .no-print,打印时整块消失。超出打印行数上限时只打前 N 行并在稿面明确提示,不静默截断。

构图:登录页品牌内容(名称 / 标语 / 水线)垂直居中聚成一块,配一层柔和径向光晕作落点。 早期版本用 space-between 把品牌与标语顶到两端,中间空出大半屏,两处都成了孤岛——超宽屏会把这种疏离放大得更明显。内容块要限宽(max-width),否则宽屏下文字会被拉散。

写代码时的五条约定

  • 覆盖 Element Plus 主题请改 CSS 变量(--el-button-bg-color 等),不要直接写 background-color —— 后者与 .el-button--primary 同权重,会受样式注入顺序影响而不稳定
  • 中文可在任意字间断行,关键文案的断行位置要显式控制(如主标语拆成两个 <span>),交给浏览器自动换行会出现单字独占一行的难看结果
  • 新页面需响应式到移动端、保留键盘焦点可见、并尊重 prefers-reduced-motion
  • 确认框统一加 customClass: 'ys-confirm-box',不要再传 type: 'warning' —— Element Plus 的默认 warning 图标会渲染在正文左侧独立成列(多行正文时图标悬在文字块中间),与 v3 靛蓝体系也冲突。全局样式(src/assets/styles/index.css)已把图标改为 18px 描边样式并放在标题行左侧,正文与标题文字对齐
  • 页面 scoped 里只写本页特有的东西:表格 / 按钮 / 输入框 / 表头 / 分页的皮肤已经全局覆盖,页面再写一遍与组件库同权重,会受样式注入顺序影响而不稳定;结构用全局类(见上一节表格),样式只补本页独有的块(如展开区、抽屉内的键值表)
  • 状态 / 结果列的固定宽度要按胶囊内容给足:.cell 左右各 16px 内距(共 32px),2 字胶囊约 55px、3 字约 67px、4 字约 79px,宽度不够时 Element Plus 会在胶囊右侧画一个省略号(看起来像多了个小点)。口径:2 字列 ≥96px(如 启用/禁用、成功/失败)、3 字列 ≥108px(如 已核算/已替代/执行中)、4 字列 ≥120px(如 以物抵顶)

验证码图片

后端自绘渲染(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、操作日志留痕与详情懒加载脱敏、登录日志、普通用户菜单隐藏与路由守卫
# 前置: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-17 追加 · M3-core ④ 核算与月结)

页面 说明
/calc/ledger 绩效台账(新组「绩效工资」order17) 月/版本筛选、行展开计算快照、重算(原因对话框,管理员/财务)
/settlements 月度结算(order18) 生成快照(月份输入)→ 状态筛选 → 提交审批(开关关=自动通过锁定)→ 详情抽屉(按人三口径明细/试用期说明/审批单号)

路由组「绩效工资」;api 封装 src/api/{calc,settlements}.ts。type-check 与 build 通过(2026-10-17)。

端到端测试(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 超时丢失诊断信息。

端到端测试(2026-10-16 追加 · M3 增量批次)

脚本 覆盖 结果
e2e/settings_flow.py 公司信息 4 步:进设置页改公司名(随机名防撞)→ 保存 → 退出登录 → 登录页品牌位与页脚即时显示新名(public 端点)→ finally 还原默认名(防污染其它 e2e 的品牌断言) ✓ 全通过(修复 public 缓存缺陷后)
e2e/secondary_allocation_flow.py 二次分配 6 步:路由直达业务管理 → 建客户 → 建 2 名在职营销员 → 新增业务录两行参与人(30%+40%)→ 编辑回显 2 行 + 合计条「余额 30.00%(归属待业务确认)」 → 删除清理 ✓ 全通过(修复 logo 无关的三处 e2e 选择器后)

运行前置同上(需 E2E_ADMIN_PASSWORD)。期间修复的缺陷:public 端点 300s 响应缓存让登录页拿旧公司名(改 fetch cache: no-store + 时间戳绕过磁盘缓存);业务弹窗分配表与产品明细共用 items-table 类导致选择器错位(分配表加专属 alloc-table 类)。 回归:既有五脚本(登录 8 / 系统管理 13 / 基础数据 12 / 业务 16 / 参数 7 步)在 allowedRoles 路由扩展与品牌名动态化后全部复跑通过。

前端页面(2026-10-19 追加 · M4 ⑤ 报表统计)

页面 说明
/reports/personal-detail 个人绩效明细(新组「报表统计」order19) 人员/月份/客户类型/产品类别/岗位筛选;需规 2.4.1 全部列;行展开懒加载台账快照;页脚全筛选合计(注明非当前页);导出/打印
/reports/department-summary 部门绩效汇总(order20) 月份必填;全公司分成独立卡片(单经理 5% × 计入人数 = 合计,每位有聚合行的经理各计一次);团队表(基数/团队分成/全公司分成/占比/调整/净合计/月结冻结额),净合计与月结聚合行同口径,比例列头随在线参数变化;展开成员 → 跨页下钻个人明细(人员与月份带入筛选);未分配桶(按经理筛选时为空)
/reports/business-stats 业务统计(order21) 关键字/客户/归属人/状态/业务日期;发货额/回款额/绩效(累计口径提示);展开下钻回款与台账明细
/reports/payment-stats 回款统计(order22) 月份/回款日期/客户类型/产品类别;8 档区间表(空桶 0,档位由后端下发);展开桶下钻分页明细;客户类型/产品类别对比表
/reports/print/:code 报表打印(顶层路由,menu:false) 不挂 BasicLayout(打印不带侧栏/顶栏);页眉公司信息走 public 端点;循环翻页取全量(以接口 total 判定超过 2000 行并提示截断);部门汇总按「团队分成 → 全公司 5% → 对账 → 成员明细 → 未分配」五块渲染;@media print A4 横版 + 每页表头;签字栏;PDF 由浏览器「另存为」覆盖
  • 路由组「报表统计」四页 allowedRoles: [admin, finance, leader](前端菜单 + 后端 require_roles 双层;admin 须显式列入)。
  • api 封装 src/api/reports.ts(类型 + 查询 + exportReport 裸 axios blob;产品类别标签额外覆盖 mixed)。
  • 权限:营销后勤「授权范围内」待业务权限清单 #6,当前不开放报表。
  • type-check 与 build 通过(2026-10-19)。e2e report_flow.py 已补 12 步:登录 → API 造数 → 四表筛选/导出下载 → 部门下钻筛选生效 → 回款多桶同时展开互不串行 → 打印视图 → 打印截断提示 → 部门汇总打印五块结构;需 E2E_ADMIN_PASSWORD 且前后端在跑。
  • e2e 实跑(2026-10-20):login_flow 8/8、system_management 13/13、role_home_flow 4/4、report_flow 12/12、calc_settlement_flow 8/8 全过。report_flow 的回款统计改为展开目标桶后按本次业务编号定位明细行,不依赖桶累计值,脚本可重复运行(历史 e2e 数据累计不再造成假失败)。

端到端测试(2026-10-19 追加 · M3 台账与月结)

脚本 覆盖 结果
e2e/calc_settlement_flow.py 绩效核算与月度结算 8 步:登录进台账页 → API 预置(客户/人员/业务/发货/回款/核算,随机远未来月份防碰撞)→ 按归属月筛选见 v1 行 → 重算对话框(原因必填)→ v2 生效 → 版本三态筛选(仅历史 / 仅当前 / 清空=全部)→ 生成月结草稿 → 提交审批(开关显式关闭=自动通过锁定)→ 详情抽屉(按人明细/净工资/锁定状态) ✓ 全通过(2026-10-19 首跑,修复下述缺陷后)

运行前置同上(需 E2E_ADMIN_PASSWORD);数据 e2e_ 前缀 + 随机远未来月份(209x),核算/月结按设计不可删,行数据留档开发库。 首跑即抓出三处真实前端缺陷(M3 台账/月结两页自 2026-10-17 交付起一直「暂无数据」): ① src/api/{calc,settlements,approvals}.ts 误用默认导出 request(axios 实例,返回 AxiosResponse 未解包),列表 list 恒为 undefined → 三页永远「暂无数据」;改为具名导入 { request }(信封解包版)。 ② 三页均无 onMounted 初始加载 → 进页不取数;补 onMounted(() => void load())。 ③ 三页 AppPagination 用 v-model:page-num + @change(组件无该事件、也未实现 update:*)→ 翻页失效;统一为 :page-num/:page-size + @page-change/@size-change(usePagedList 的 onPageChange/onSizeChange)。 注意:e2e 与 pytest tests/integration 不可并发——conftest 每用例整体清空开发库,会清掉 e2e 造的数据造成假失败(本次排查耗时即源于此,两次假失败的报错均为「台账行消失 / 401 登录失效」)。

前端页面(2026-10-19 追加 · M4 ⑧ 工作台)

/dashboard 由 Bento 占位升级为工作台四区块(一次聚合请求 /api/v1/dashboard):

区块 展示
经营绩效总览 KPI 四卡(本月绩效/应付/核算人数/团队数 + 月结状态);近 6 个月绩效折线 + 回款柱状(ECharts)
待办事项 待审批(跳审批管理)、回款未指定批次(跳回款管理)、T+ 同步失败(未启用显式占位)
风险预警 回款逾期(danger)/ 审批超时(warning),点击跳处理页;总开关关闭时显示参数提示
最近动态 操作日志时间线,按模块本地筛选
  • ECharts 5.6 按需引入:echarts/core + BarChart/LineChart/Grid/Tooltip + SVGRenderer(与设计预览一致),只注册用到的模块。
  • 字体红线:图表 textStyle.fontFamily 必须显式设为思源黑体栈——ECharts 在 Windows 默认微软雅黑(字体商用合规要求)。
  • 权限:路由 allowedRoles: [admin, finance, leader],与后端 require_roles 双层;营销后勤待权限清单 #6。
  • 普通用户落地页:守卫不再硬编码回 /dashboard,统一走 src/router/access.ts 的 firstAccessiblePath(工作台 → 业务数据 → 回款),普通用户登录/刷新/直连受限页均落 /business/businesses,不会无限重定向;登录回跳也经 canAccessPath 校验。菜单与面包屑复用同一份准入判定。
  • 预警徽标用后端 warnings.total(全部命中数),items 仅为展示上限内的子集。
  • package.json 新增依赖 echarts@5.6.0(MIT);type-check 与 build 通过(2026-10-19)。
  • e2e e2e/role_home_flow.py:普通用户登录落地、菜单隐藏、刷新保持、直连 /dashboard 与报表页回退;需 E2E_ADMIN_PASSWORD。