Files
TurboHR/docs/20260809-优化.md
freedakgmail a2e9ba55c2 feat: 20260809 系统优化 - 全部28项问题修复(P0×6+P1×16+P2×6)
P0: 福利批量参保/离职证明下载防乱码/考勤模板合并Sheet/补卡修改/附件在线查看删除
P1: 分页pageSize修复/离职导出筛选/撤回删除草稿/加班费自动计算/考勤加班汇总/证据链异常详情/制度催办/模板导入Word/社保封顶保底/校验字段提示/职务字段/社保费用明细/弹窗防误关/身份证查重/证明员工下拉/培训批量
P2: 离职流程去重/社保基数覆盖输入/薪税入口改名/添加员工引导/绩效模板清理
2026-08-09 11:59:02 +08:00

40 KiB
Raw Permalink Blame History

20260809 优化需求清单

基于用户反馈整理,共 28 项问题,按模块和优先级分类。

代码审查更新2026-08-09 完成全量代码核查,补充实际代码定位和确认结果。


一、员工福利模块

问题1:福利方案创建后无法添加享受人员,批量参保无人员数据

模块:员工福利
优先级P0
状态:待验证

现状描述: 创建好福利方案后,无法增加享受福利的人员,批量参保时无人员数据可选。

代码核查结果 功能实际已实现。EmployeeBenefits.tsx 中有完整的批量参保功能:

  • 点击福利方案卡片可展开参保人员列表(EmployeeBenefits.tsx:228
  • 「批量参保」按钮打开 Modal,加载花名册在职员工列表(:232
  • 支持全选/勾选员工,设置生效月份,提交参保(:395-456
  • rosterApi.list 查询 pageSize: 200 条员工数据(:78

潜在问题rosterData 查询仅在 showEnrollModal 为 true 时启用(enabled: showEnrollModal),如果员工超过200人则无法全部加载。建议改用不分页的 allLite 接口。

涉及文件

  • frontend/src/pages/EmployeeBenefits.tsx 福利方案和批量参保
  • frontend/src/lib/api-services.ts benefitApi 定义
  • backend/src/routes/benefits.routes.ts

优化方案

  1. 批量参保的员工列表改用 allLite 接口,避免200条限制
  2. 增加按部门筛选功能
  3. 验证实际运行时员工列表是否正常加载

二、全局通用问题

问题2:多个模块中每页条数选择无反应

模块:全局(花名册、薪税、考勤等多个列表页)
优先级P1
状态:待验证

现状描述: 多个模块列表页底部的「每页条数」选择器点击后无反应,无法切换每页显示条数。

代码核查结果 usePageSize hookfrontend/src/hooks/usePageSize.ts:1-18)通过 localStorage 持久化,并通过 page-size-changed 自定义事件实现跨页面响应。Pagination 组件(frontend/src/components/ui/Pagination.tsx:44-53)在 onPageSizeChange 时触发回调。

疑似问题:多个列表页在 onPageSizeChange 回调中仅调用 setPage(1) 但未显式传递新的 pageSize 值。例如 Evidence.tsx:132

onPageSizeChange={() => setPage(1)}

由于 usePageSize hook 返回的 pageSize 是全局状态,变更后自动触发 queryKey 变化,理论上应该能工作。需实际运行验证事件监听是否在所有页面正确触发重渲染。

涉及文件

  • frontend/src/hooks/usePageSize.ts:1-18 全局 pageSize 状态管理
  • frontend/src/lib/pageSize.ts:1-21 getPageSize/setPageSize 工具函数
  • frontend/src/components/ui/Pagination.tsx:44-53 分页组件
  • frontend/src/pages/Settings.tsx:15-245 全局设置页
  • frontend/src/pages/AuditLog.tsx:118-246 使用示例
  • frontend/src/pages/Evidence.tsx:127-133 疑似问题点

优化方案

  1. 验证 usePageSizepage-size-changed 事件是否在所有页面正确触发
  2. 确保所有列表页 onPageSizeChange 回调中 setPage(1) 后 queryKey 包含 pageSize
  3. 全局统一分页组件,确保所有列表页行为一致

三、离职管理模块

问题3:离职证明下载内容为乱码

模块:离职管理
优先级P0
状态:待修复

现状描述: 离职管理中下载的离职证明文件内容是一团乱码,无法正常阅读。

问题分析

  • work-process.service.ts 生成的 .doc 文件为纯文本格式,Word 打开时可能出现编码问题
  • 文件下载时 Content-Type 和编码声明可能不正确
  • 前端下载方式可能未正确处理二进制流

涉及文件

  • backend/src/services/work-process.service.ts:246-290 .doc 文件生成
  • backend/src/routes/work-process.routes.ts 下载接口
  • frontend/src/pages/WorkProcess.tsx 下载逻辑
  • frontend/src/pages/Termination.tsx:330-400 离职管理页面

优化方案

  1. 在生成的 .doc 内容头部添加 BOM 标记(\uFEFF),确保 Word 正确识别 UTF-8 编码
  2. 后端下载接口设置正确的 Content-Type: application/msword; charset=utf-8
  3. 前端下载时使用 Blob 并指定编码
  4. 考虑生成 HTML 格式的 Word 文件(带 xmlns:o 命名空间),确保格式正确

问题4:离职管理导出数据缺少筛选条件

模块:离职管理
优先级P1
状态:待优化

现状描述: 离职管理导出数据时一次性导出全部数据,无法按时间范围等条件筛选导出。

问题分析

  • 导出接口未接收前端筛选参数,直接查询全部离职记录
  • 前端导出按钮未传递当前筛选条件

涉及文件

  • backend/src/routes/export.routes.ts 导出接口(含 terminations 导出)
  • backend/src/routes/termination.routes.ts 离职路由
  • frontend/src/pages/Termination.tsx:330-400 导出按钮
  • frontend/src/lib/api-services.ts:613-696 terminationApi 定义

优化方案

  1. 导出接口增加 dateFromdateTodepartmentstatus 等查询参数
  2. 前端导出时携带当前筛选条件
  3. 增加导出确认弹窗,显示筛选范围和预计条数

问题5:已提交的离职数据无法撤回,已撤回的无用数据无法删除

模块:离职管理
优先级P1
状态:待修复

现状描述: 离职管理中已提交的数据无法撤回操作,已撤回的无用数据无法删除清理。

代码核查结果

  • 后端 termination.service.ts:256-334revokeTermination 方法,路由 termination.routes.ts:81-115DELETE /:id/revoke 端点
  • 前端 api-services.ts:613-696revoke 方法定义
  • 但前端 Termination.tsx 页面未暴露撤回和删除草稿的按钮——UI 缺少对应操作入口
  • 后端有 cancelTerminationtermination.service.ts:729-954)和 getDrafts 方法

涉及文件

  • backend/src/services/termination.service.ts:256-334 revokeTermination
  • backend/src/services/termination.service.ts:729-954 cancelTermination, getDrafts
  • backend/src/routes/termination.routes.ts:81-115 撤回路由
  • backend/src/routes/termination.routes.ts:128-234 草稿管理路由
  • frontend/src/lib/api-services.ts:613-696 terminationApi.revoke/cancel
  • frontend/src/pages/Termination.tsx:330-400 缺少撤回/删除按钮

优化方案

  1. 前端 Termination.tsx 为已提交但未完成的离职流程增加「撤回」按钮
  2. 已撤回的草稿数据允许删除,增加二次确认
  3. 已完成离职的记录保留不可删除(合规要求)

问题6:用工办理中离职/解聘与离职管理模块重复

模块:用工办理 / 离职管理
优先级P2
状态:待优化

现状描述: 用工办理中有员工离职、解聘功能,同时还有独立的离职管理模块,功能重复,显得混乱。

代码核查结果

  • WorkProcess.tsx:37-40 包含 TERMINATE(合同终止)、RESCIND(合同解除)、LEAVING_CERT(离职证明)等流程类型
  • Termination.tsx 是独立的离职管理页面,含草稿管理、审批、执行等完整流程
  • 两个入口功能确实重叠

涉及文件

  • frontend/src/pages/WorkProcess.tsx:37-40 流程类型定义
  • frontend/src/pages/Termination.tsx:330-400 离职管理页面
  • frontend/src/components/layout/SidebarNav.tsx

优化方案

  1. 用工办理中保留「入职办理」「转正」「调岗」等入职相关流程
  2. 离职、解聘相关流程统一归入「离职管理」模块
  3. 侧边栏菜单分组明确:用工办理(入职类)→ 离职管理(离职类)

四、考勤管理模块

问题7:考勤导入模板包含无关Sheet,且加班/违纪/考勤三个Sheet需合并

模块:考勤管理
优先级P0
状态:待优化

现状描述: 导入考勤的模板包含「员工信息」和「劳动合同」两个无关 Sheet,只录入考勤信息无法导入。加班记录、违纪记录、考勤记录三个 Sheet 录入同一人员时需重复粘贴姓名与身份证号,应合并。

代码核查结果

  • backend/src/routes/import.routes.ts:494-692 模板下载接口生成包含:员工信息、劳动合同、考勤记录、加班记录、违纪记录等多个 Sheet
  • gen_import_sample.py:50-73 Python 脚本也生成了包含多余 Sheet 的示例文件
  • 导入接口 import.routes.ts 处理 考勤记录加班记录违纪记录薪资调整社保变动公积金变动 等多个 Sheet
  • 确认:模板确实包含无关的员工信息和劳动合同 Sheet

涉及文件

  • backend/src/routes/import.routes.ts:494-692 模板下载和导入处理
  • frontend/src/pages/Attendance.tsx:525-610 前端导入弹窗
  • gen_import_sample.py:50-73 示例文件生成脚本

优化方案

  1. 考勤导入模板只保留考勤相关 Sheet,移除员工信息和劳动合同 Sheet
  2. 将考勤记录、加班记录合并为一个 Sheet,用列区分(日期、班次、签到时间、签退时间、加班时长等)
  3. 违纪记录因字段差异较大,可保留独立 Sheet 或独立导入入口
  4. 每项业务(考勤、加班、违纪)提供独立的专用模板下载

问题8:补卡无法修改未打卡状态,签到签退时间显示有问题

模块:考勤管理 - 每日出勤
优先级P0
状态:待修复

现状描述: 考勤排班中每日出勤页面,操作补卡时无法修改未打卡状态,且签到与签退的时间显示有异常。

代码核查结果

  • backend/src/services/attendance.service.ts:264-360manualCorrectAttendance 方法支持更新考勤记录,可设置签到/签退时间和状态
  • backend/src/routes/attendance.routes.ts:210-233POST /manual-correct 端点
  • frontend/src/pages/Attendance.tsx:970-1174 的 DailyTab 有补卡弹窗和按钮
  • frontend/src/lib/api-services.ts:247-297manualCorrect API 调用
  • 考勤状态常量定义在 Attendance.tsx:25-33NORMAL/LATE/EARLY_LEAVE/ABSENT/LEAVE/BUSINESS_TRIP/UNREGISTERED
  • 需确认:补卡弹窗是否限制了状态选项(未覆盖 UNREGISTERED→其他状态的修正),以及时间格式化是否有时区问题

涉及文件

  • frontend/src/pages/Attendance.tsx:25-33 状态常量定义
  • frontend/src/pages/Attendance.tsx:970-1174 DailyTab 补卡弹窗
  • frontend/src/lib/api-services.ts:247-297 attendanceApi.manualCorrect
  • backend/src/services/attendance.service.ts:264-360 manualCorrectAttendance
  • backend/src/routes/attendance.routes.ts:210-233 补卡路由

优化方案

  1. 补卡弹窗允许修改所有考勤状态(包括未打卡→已打卡/请假/出差等)
  2. 检查时间字段的时区处理,确保显示本地时间
  3. 签到签退时间统一格式化为 HH:mm 格式

问题9:加班费计算与考勤不关联,需重复导入

模块:考勤管理 / 薪税管理
优先级P1
状态:待优化

现状描述: 加班费计算时需要再导入一遍考勤数据,与考勤管理模块的数据不关联。

问题分析

  • 加班费计算模块可能独立于考勤管理,未从已有的考勤记录中读取加班时长
  • 考勤管理中的加班数据未传递到薪税计算的加班费环节

涉及文件

  • frontend/src/pages/money/ 加班费相关组件
  • backend/src/routes/payroll2.routes.ts 加班费计算逻辑
  • backend/src/routes/attendance.routes.ts 考勤数据查询
  • backend/src/routes/import.routes.ts 考勤导入(含加班记录 Sheet

优化方案

  1. 加班费计算改为从考勤管理模块读取已确认的加班记录
  2. 薪税批次创建时自动拉取当月考勤加班数据,无需重复导入
  3. 保留手动导入作为备选方案

问题10:个人考勤记录添加后加班汇总不显示

模块:考勤管理
优先级P1
状态:待修复

现状描述: 个人考勤记录添加时手动填写了加班时长,但加班汇总中不显示条数,不清楚加班汇总关联的是哪里。

问题分析

  • 加班汇总可能统计的是考勤导入的加班数据,而非手动添加的加班时长
  • 加班汇总的数据源与个人考勤记录的加班字段未关联

涉及文件

  • frontend/src/pages/Attendance.tsx:970-1174 加班汇总和考勤记录
  • backend/src/routes/attendance.routes.ts 加班统计接口

优化方案

  1. 加班汇总统计应包含手动添加的考勤记录中的加班时长
  2. 加班汇总增加数据来源标识(导入/手动添加)
  3. 明确加班汇总与考勤记录的关联关系,UI 上增加说明

五、证据链模块

问题11:验证全部完整性功能简陋,无法定位异常

模块:证据链
优先级P1
状态:待优化

现状描述: 证据链中「验证全部完整性」功能验证后显示异常,但无法告知哪部分异常,下方提醒也无法跳转操作。

代码核查结果

  • frontend/src/pages/Evidence.tsx:31-37 调用 evidenceApi.verifyAll(),返回结果仅显示 totalvalidinvalid 三个数字(:60-75
  • 无详细异常项列表,无跳转操作
  • frontend/src/lib/api-services.ts:700-707 evidenceApi 定义了 listverifyAll 方法
  • frontend/src/pages/roster/EvidenceChain.tsx:1-155 是员工个人维度的仲裁证据链,展示证据列表、风险提醒和导出功能

涉及文件

  • frontend/src/pages/Evidence.tsx:31-75 验证全部完整性功能
  • frontend/src/lib/api-services.ts:700-707 evidenceApi 定义
  • frontend/src/pages/roster/EvidenceChain.tsx:1-155 员工个人证据链
  • backend/src/routes/roster.routes.ts 证据链验证接口

优化方案

  1. 验证接口返回详细的检查项列表(每项:名称、状态、异常描述)
  2. 前端展示验证结果明细,异常项高亮显示
  3. 每个异常项增加「去处理」跳转按钮,跳转到对应模块

六、规章制度管理

问题12:规章制度签收缺少催办和未签收人员查看

模块:规章制度
优先级P1
状态:待优化

现状描述: 规章制度向员工公示后,签收只显示签收人数和占比,无法查看具体未签收人员,也无法催办。

代码核查结果

  • frontend/src/pages/Policies.tsx:249-285ReadStats 组件,展示签收百分比和未签收人数
  • 已签收人员列表可展开查看(:278-285),显示姓名、部门、签收时间
  • 缺少催办通知功能——无催办按钮
  • 未签收人员列表未展示——仅显示未签收人数(:273-277),未列出具体人员
  • frontend/src/lib/api-services.ts:674-696 policiesApi.readStats 返回 readCounttotalunreadCountrecords
  • 员工端 frontend/src/pages/portal/MyPolicies.tsx:36-46 有阅读确认 mutation 和待签收数量统计

涉及文件

  • frontend/src/pages/Policies.tsx:110-120 签收进度条
  • frontend/src/pages/Policies.tsx:246-285 ReadStats 组件
  • frontend/src/lib/api-services.ts:674-696 policiesApi 定义
  • frontend/src/pages/portal/MyPolicies.tsx:30-50 员工端阅读确认
  • backend/src/routes/regulations.routes.ts

优化方案

  1. 签收统计增加「查看明细」按钮,展开已签收/未签收人员列表
  2. 未签收人员列表支持「一键催办」,发送通知提醒员工签收
  3. 显示每位员工的签收状态和时间

七、文本模板模块

问题13:新建模板不支持导入文档,现有方式易造成格式混乱

模块:文本模板
优先级P1
状态:待优化

现状描述: 文本模板新建时只能手动输入内容,无法通过导入 Word 文档创建,现有方式容易造成格式混乱,需要保留导入文档的原始格式。

代码核查结果

  • frontend/src/pages/Templates.tsx:303-571EnterpriseTemplates 组件中,新建模板仅支持 textarea 手动输入内容(:493-498
  • 模板内容使用 {{变量名}} 占位符,支持变量替换渲染
  • 系统模板支持下载 Word.doc 格式),通过 fetch 请求 /templates/:id/download
  • frontend/src/lib/api-services.ts:814-839 templatesApi 无文档导入接口
  • 确认:无文档上传入口,不支持导入 .docx 文件

涉及文件

  • frontend/src/pages/Templates.tsx:1-571 模板管理页面(系统模板+企业模板)
  • frontend/src/lib/api-services.ts:814-839 templatesApi 定义
  • backend/src/routes/templates.routes.ts

优化方案

  1. 新建模板增加「导入文档」入口,支持上传 .docx 文件
  2. 后端使用 mammoth 或类似库解析 Word 文档,保留段落、表格等结构
  3. 导入后转为 HTML 存储模板内容,前端预览时保留格式
  4. 保留现有手动创建方式作为备选

八、花名册模块

问题14:录入工资后社保基数自动取工资数,选择参保地后未自动封上下限

模块:花名册
优先级P1
状态:待优化

现状描述: 花名册单独录入员工时,社保基数自动取工资数可以,但如果选择参保地,计算时未能自动封上下限。

代码核查结果

  • frontend/src/pages/roster/modals.tsx:680-681 社保基数默认取月工资:value={form.socialInsBase || form.monthlySalary}
  • socialInsuranceApi.cities() 已获取城市列表(modals.tsx:493-498
  • socialInsuranceApi.calculate(base, city) 可计算社保费用(api-services.ts:510-512
  • 确认:未根据参保城市查询基数上下限进行封顶/封底处理
  • frontend/src/pages/roster/BasicInfo.tsx:74 显示社保基数,编辑时为普通输入框(:344-345

涉及文件

  • frontend/src/pages/roster/modals.tsx:486-767 AddEmployeeModal 社保基数填充
  • frontend/src/pages/roster/modals.tsx:235-484 RehireModal 社保基数填充
  • frontend/src/pages/roster/BasicInfo.tsx:60-120 编辑表单
  • frontend/src/lib/api-services.ts:510-520 socialInsuranceApi

优化方案

  1. 选择参保地后,自动查询该城市的社保基数上下限
  2. 社保基数 = min(max(工资数, 下限), 上限)
  3. 如果工资数在上下限范围内,直接取工资数;否则显示封顶/封底后的值并提示

问题15:社保基数手动修改时原有数据不能直接覆盖

模块:花名册
优先级P2
状态:待修复

现状描述: 社保基数自动取工资后实际不是社保基数时需要手动修改,但修改时原有数据不能删除,必须用鼠标点击选中后再修改,影响录入效率。

代码核查结果

  • frontend/src/pages/roster/modals.tsx:681 使用 value={form.socialInsBase || form.monthlySalary},当 socialInsBase 为空时回退到 monthlySalary
  • 用户清空输入框时 socialInsBase 变为空字符串,又回退到 monthlySalary,无法真正清空
  • 缺少 onFocus={(e) => e.target.select()} 聚焦全选功能
  • BasicInfo.tsx:344-345 编辑模式下的社保基数输入框为普通 Input,无自动回退问题

涉及文件

  • frontend/src/pages/roster/modals.tsx:680-681 AddEmployeeModal 社保基数输入框
  • frontend/src/pages/roster/modals.tsx:397-398 RehireModal 社保基数输入框
  • frontend/src/pages/roster/BasicInfo.tsx:344-345 编辑表单社保基数输入框

优化方案

  1. 社保基数输入框改为受控组件,自动填充后用户可直接输入覆盖
  2. 输入框获得焦点时自动全选当前值,方便直接覆盖
  3. 增加 onFocus={(e) => e.target.select()} 实现聚焦全选

问题16:录入校验失败未指明具体字段

模块:花名册
优先级P1
状态:待优化

现状描述: 录入员工时可能是手机号录入有问题,但系统只提示「校验失败」,不指出哪个字段校验失败。

代码核查结果

  • backend/src/schemas/contract.schema.ts:3-27 createEmployeeSchema 定义了字段级 Zod 校验规则,如 phone: z.string().regex(/^1[3-9]\d{9}$/)
  • 前端 modals.tsx:648-650 错误处理仅显示通用消息:{error.response?.data?.error?.message || '操作失败'}
  • 未解析 Zod 返回的字段级错误信息并在对应字段下方显示
  • backend/src/middleware/errorHandler.ts:27-31 P2002 唯一约束错误返回通用"数据已存在,请勿重复操作"

涉及文件

  • backend/src/schemas/contract.schema.ts:1-72 Zod 校验 schema 定义
  • backend/src/middleware/errorHandler.ts:27-31 错误处理中间件
  • backend/src/routes/employee.routes.ts:99-126 创建/更新员工路由
  • frontend/src/pages/roster/modals.tsx:648-650 AddEmployeeModal 错误提示
  • frontend/src/pages/roster/BasicInfo.tsx:82-119 编辑表单错误处理

优化方案

  1. 后端校验失败时返回具体字段名和错误原因(如 {"field": "phone", "message": "手机号格式不正确"}
  2. 前端解析错误信息,在对应字段下方显示红色提示
  3. toast 提示中包含具体字段名

问题17:花名册员工详情中薪税入口意义不明

模块:花名册
优先级P2
状态:待优化

现状描述: 花名册员工个人详情中的小标识第二个点进去直接进入薪税模块(批次发薪),不理解放在员工个人这里的意义,应该是与此员工有关的个人薪资关联。

代码核查结果

  • frontend/src/pages/roster/EmployeeProfile.tsx:66payslip tab 展示 PayslipSocialInfo,显示该员工的工资条和社保记录
  • EmployeeProfileShell.tsx:12-17 员工 profile 类型定义包含 position 字段
  • 需确认是否有跳转到薪税批次列表页的入口

涉及文件

  • frontend/src/pages/roster/EmployeeProfile.tsx:60-71 tab 定义
  • frontend/src/pages/roster/EmployeeProfileShell.tsx:12-17 profile 类型
  • frontend/src/pages/roster/BasicInfo.tsx 快捷入口

优化方案

  1. 改为跳转到该员工的个人薪资历史记录页面
  2. 或在员工详情中增加「薪资历史」标签页,展示该员工所有批次的工资条

问题18:花名册列表有职务列,但录入时无职务字段

模块:花名册
优先级P1
状态:待修复

现状描述: 花名册主页显示有职务这一栏,但单独录入员工时却没有职务这一项。

代码核查结果(确认)

  • 花名册列表 Roster.tsx:29position 列(职务),:500 有表头,:560 有数据渲染
  • AddEmployeeModalmodals.tsx:486-767)表单中position 字段
  • BasicInfo.tsx 编辑表单中也position 字段
  • createEmployeeSchemacontract.schema.ts:3-27)中position 字段
  • updateEmployeeSchemacontract.schema.ts:29-51)中也position 字段
  • 后端 createEmployeecontract.service.ts:193-272)中也未设置 position 字段
  • 但后端查询时 select 包含 positionemployee.routes.ts:56,82),说明数据库有此字段
  • EmployeeProfileShell.tsx:15 类型定义包含 position:147-149 显示 position

涉及文件

  • frontend/src/pages/Roster.tsx:29,500,560 列表显示职务列
  • frontend/src/pages/roster/modals.tsx:486-767 AddEmployeeModal 缺少 position 字段
  • frontend/src/pages/roster/BasicInfo.tsx:60-120 编辑表单 缺少 position 字段
  • backend/src/schemas/contract.schema.ts:3-51 缺少 position 字段
  • backend/src/services/contract.service.ts:193-272 createEmployee 未设置 position
  • backend/src/routes/employee.routes.ts:56,82 查询时 select 包含 position
  • frontend/src/pages/roster/EmployeeProfileShell.tsx:15,147-149 profile 显示 position

优化方案

  1. AddEmployeeModalBasicInfo 编辑表单增加「职务」字段
  2. createEmployeeSchemaupdateEmployeeSchema 增加 position: z.string().max(50).optional()
  3. createEmployeeupdateEmployee 服务中设置 position 字段

问题19:花名册中社保费用计算与社保模块不一致

模块:花名册 / 社保管理
优先级P1
状态:待修复

现状描述: 花名册里员工个人计算的社保费用与社保模块中不一致。社保模块里已修改了养老医保基数不一致,但花名册里计算还是保持一致。

代码核查结果

  • BasicInfo.tsx:325-330 显示社保缴费基数和公积金缴费基数,使用统一基数
  • BasicInfo.tsx:364-368 未设置基数时显示警告提示
  • contract.service.ts:205-206 创建员工时 socialInsBasehousingFundBase 均默认取 salaryNum
  • api-services.ts:510-512 socialInsuranceApi.calculate(base, city) 使用统一 base 计算
  • 确认:花名册使用统一基数,未读取社保模块中按险种分别配置的基数

涉及文件

  • frontend/src/pages/roster/BasicInfo.tsx:320-370 社保费用显示和编辑
  • backend/src/services/contract.service.ts:205-206 创建员工时社保基数设置
  • frontend/src/lib/api-services.ts:510-520 socialInsuranceApi
  • backend/src/routes/social.routes.ts 社保配置查询

优化方案

  1. 花名册社保费用计算改为读取社保模块中各险种的独立基数和比例
  2. 养老保险用养老基数、医疗保险用医疗基数,分别计算后汇总
  3. 确保两个模块的计算逻辑统一

问题20:合同附件PDF/Word不支持在线查看,且无法删除传错的附件

模块:花名册 - 劳动合同
优先级P0
状态:待修复

现状描述: 劳务合同附件上传了 PDF 后不可以查看,显示没有插件;Word 也不支持在线查看,只有图片格式可以查看。且附件上传之后传错了无法删除,没有删除按钮。

代码核查结果

  • ContractInfo.tsx:391-455 附件预览弹窗实现:
    • 图片<img> 在线预览 :432
    • PDF<embed> 在线预览 :434)——已支持,非完全缺失
    • Word/其他:显示"此文件格式不支持在线预览",提供下载 :436-449
  • 附件上传支持格式:.pdf, .jpg, .jpeg, .png, .heic, .gif, .bmp, .webp, .doc, .docx, .xls, .xlsx, .tiff, .tif:36,106
  • 新建合同时的附件可删除(:258
  • 已保存合同的附件无删除按钮——只有下载按钮(:335-358)和补充上传按钮(:363
  • 附件以 base64 data URL 存储在 attachmentUrl 字段中,预览时转为 blob URL

涉及文件

  • frontend/src/pages/roster/ContractInfo.tsx:16-70 附件上传逻辑
  • frontend/src/pages/roster/ContractInfo.tsx:258 新建时删除附件按钮
  • frontend/src/pages/roster/ContractInfo.tsx:310-370 已保存合同附件展示(无删除)
  • frontend/src/pages/roster/ContractInfo.tsx:391-455 附件预览弹窗

优化方案

  1. Word 预览:使用 mammoth.js 转换为 HTML 在线预览,或提示下载查看
  2. 已保存合同的附件增加删除按钮,删除时二次确认
  3. 后端增加附件删除接口,更新 attachmentUrl 字段

问题21:用工办理与花名册添加员工功能重复

模块:花名册 / 用工办理
优先级P2
状态:待优化

现状描述: 花名册可以添加员工,用工办理也可以录入员工,两个模块添加员工有什么区别不清楚。如果都可以添加没有必要,最好固定在一个模块。

代码核查结果

  • Roster.tsxAddEmployeeModalmodals.tsx:486-767)直接创建员工
  • WorkProcess.tsx:56-66HIRE 流程类型也创建员工,字段为 namedepartmentidCardNumber 等 text 输入
  • WorkProcess.tsx:67-70ONBOARD 流程使用 employee-select 选择已有员工
  • 两个入口都调用 createEmployee,写入同一张表

涉及文件

  • frontend/src/pages/Roster.tsx:70-90 花名册状态和模态框
  • frontend/src/pages/roster/modals.tsx:486-767 AddEmployeeModal
  • frontend/src/pages/WorkProcess.tsx:56-70 HIRE/ONBOARD 流程定义
  • backend/src/routes/employee.routes.ts:99-126 创建员工路由
  • backend/src/services/contract.service.ts:193-272 createEmployee

优化方案

  1. 统一员工添加入口为「用工办理 → 入职办理」,包含完整入职流程
  2. 花名册保留「查看」和「编辑」功能,移除独立添加入口
  3. 或在花名册添加员工时引导跳转到用工办理的入职流程

问题22:用工办理录入中途切换窗口丢失已填信息

模块:用工办理
优先级P1
状态:待修复

现状描述: 在用工办理里录入员工,录到身份证号处,点开别的文件想粘贴一下,再回去,刚才录入的页面就退出了,需要重新打开重新录前面的信息。

代码核查结果

  • frontend/src/components/ui/Modal.tsx:36 遮罩层 onClick={onClose}——点击遮罩层会关闭弹窗
  • closeOnOverlayClick={false} 配置选项
  • 表单数据未持久化到 sessionStorage
  • AddEmployeeModalmodals.tsx:641)使用了 useUnsavedChanges(isDirty) 但仅提示,不阻止关闭
  • WorkProcess.tsx:204-205 录入弹窗也使用 div + onClick={onClose} 模式

涉及文件

  • frontend/src/components/ui/Modal.tsx:33-56 Modal 组件(遮罩层 onClick={onClose}
  • frontend/src/pages/roster/modals.tsx:641-643 AddEmployeeModal useUnsavedChanges
  • frontend/src/pages/WorkProcess.tsx:204-205 录入弹窗

优化方案

  1. 弹窗设置为 closeOnOverlayClick={false},禁止点击遮罩层关闭
  2. 表单数据持久化到 sessionStorage,重新打开时恢复
  3. 关闭前增加「确认关闭?未保存的数据将丢失」提示

问题23:用工办理未按身份证号查重

模块:用工办理
优先级P1
状态:待修复

现状描述: 在花名册录入一个人,在用工办理里录入了一个人但没录入身份证号,不显示重复,不知道是否用身份证查重。

代码核查结果

  • createEmployeecontract.service.ts:193-272无查重逻辑——直接创建
  • 数据库依赖 idCardHash 唯一约束,重复时抛出 P2002 错误
  • errorHandler.ts:27-31 P2002 错误返回通用"数据已存在,请勿重复操作"消息
  • 前端 WorkProcess.tsxHIRE 流程类型使用 text 类型字段(namedepartment 等),employee-select
  • INCOME_CERTLEAVING_CERT 也使用 text 类型手动输入员工信息(:107-114, :129-134
  • import.routes.ts:330-333 导入时有身份证号查重,返回字段级错误信息

涉及文件

  • backend/src/services/contract.service.ts:193-272 createEmployee(无查重)
  • backend/src/middleware/errorHandler.ts:27-31 P2002 错误处理
  • frontend/src/pages/WorkProcess.tsx:56-66 HIRE 流程字段定义
  • frontend/src/pages/WorkProcess.tsx:107-114,129-134 证明开具字段(手动输入)
  • backend/src/routes/import.routes.ts:330-333 导入查重(有字段级错误)

优化方案

  1. 用工办理录入时根据姓名+手机号或身份证号查重
  2. 身份证号为空时用姓名+手机号组合查重
  3. 发现重复时提示「该员工已存在,是否查看/跳转」

九、证明开具模块

问题24:收入证明等应支持员工下拉选择,直接拉取数据

模块:用工办理 - 证明开具
优先级P1
状态:待优化

现状描述: 开具收入证明或其他证明时,需要手动粘贴员工信息,应该有员工下拉选项直接拉取数据,避免开具非本公司员工的证明。

代码核查结果

  • INCOME_CERTWorkProcess.tsx:107-114)字段为手动输入:employeeNametext)、idCardNumbertext)、positiontext)、monthlyIncometext
  • LEAVING_CERT:129-134)同样为手动输入
  • 未使用 employee-select 类型,不关联花名册
  • 但批量开具证明弹窗(:537-590)已有员工多选列表——单条开具时却无下拉选择
  • WorkProcess.tsx:746-809EmployeeSelect 组件实现,支持搜索和选择员工
  • WorkProcess.tsx:298-306 批量提交时从员工数据自动填充 employeeNameidCardNumberposition

涉及文件

  • frontend/src/pages/WorkProcess.tsx:107-114 INCOME_CERT 字段定义(手动输入)
  • frontend/src/pages/WorkProcess.tsx:129-134 LEAVING_CERT 字段定义(手动输入)
  • frontend/src/pages/WorkProcess.tsx:537-590 批量开具证明弹窗(有员工选择)
  • frontend/src/pages/WorkProcess.tsx:746-809 EmployeeSelect 组件
  • frontend/src/pages/WorkProcess.tsx:298-306 批量提交自动填充字段

优化方案

  1. 证明开具表单增加员工下拉选择器,支持姓名/手机号搜索
  2. 选择员工后自动填充身份证号、入职日期、职务、月收入等字段
  3. 只允许选择本公司在职员工

十、培训记录模块

问题25:培训记录只能选择单个员工,不支持批量/按部门

模块:培训记录
优先级P1
状态:待优化

现状描述: 添加培训记录只能选择一个员工,但实际培训可能是好几个员工一起,也可能是一个部门甚至整个公司。

代码核查结果

  • TrainingRecords.tsx:211-220 员工选择为 <Select> 单选下拉框,employees.map 渲染选项
  • AttendanceOvertimeInfo.tsx:79-81 中的培训记录新增也为单选
  • 不支持多选或按部门批量选择
  • 表单字段:employeeIdtrainingDatetopiccontenttrainerdurationremark

涉及文件

  • frontend/src/pages/roster/TrainingRecords.tsx:200-267 培训记录表单(单选员工)
  • frontend/src/pages/roster/AttendanceOvertimeInfo.tsx:10,79-81 考勤/培训合并组件
  • backend/src/routes/employee.routes.ts 培训记录接口

优化方案

  1. 员工选择改为多选模式,支持按部门筛选勾选
  2. 增加「按部门添加」和「全公司添加」快捷选项
  3. 批量创建培训记录,每人选一条,共享培训主题/日期/讲师等信息

十一、绩效考核模块

问题26:绩效考核模块过于片面,应支持导入公司自定义考核表

模块:绩效考核
优先级P2
状态:待优化

现状描述: 绩效考核模块只有简单的得分/等级/评语,每个公司考核类别、评分等差别比较大,现有功能几乎没法用。应支持导入本公司绩效考核表,再进行个人绩效考核统计。

代码核查结果

  • PerformanceInfo.tsx:14 表单仅包含:periodperiodType(月度/季度/年度)、scoregradeA/B/C/D)、result(优秀/合格/需改进/不胜任)、summaryimprovementPlanrevieweremployeeAck
  • 得分自动计算等级和结果(:29-39
  • 无自定义考核维度、权重、指标
  • 不支持导入 Excel 考核表

涉及文件

  • frontend/src/pages/roster/PerformanceInfo.tsx:1-118 绩效考核完整组件
  • backend/src/routes/employee.routes.ts 绩效记录接口
  • backend/prisma/schema.prisma PerformanceRecord 模型

优化方案

  1. 增加「绩效模板」管理,支持定义考核维度、权重、评分标准
  2. 支持导入 Excel 考核表作为模板
  3. 绩效考核时按模板填写各维度得分,系统按权重计算总分
  4. 保留现有简单模式作为默认,自定义模板作为高级功能

十二、考勤导入流程

问题27:考勤模板 Sheet 过多,导入后无法确认数据

模块:考勤管理
优先级P0
状态:待优化

现状描述: 考勤管理下载模板时模板包含太多无关 Sheet,需要都删除后再导入。而且导入后提示导入成功,但找不到从哪里确认数据。

代码核查结果

  • 与问题7相关,import.routes.ts:494-692 模板包含多个无关 Sheet
  • Attendance.tsx:525-610 导入弹窗显示导入结果(成功数、跳过数、错误),但无跳转到考勤确认页面的链接
  • 导入成功后仅 toast 提示,无自动跳转

涉及文件

  • backend/src/routes/import.routes.ts:494-692 模板下载
  • frontend/src/pages/Attendance.tsx:525-610 前端导入弹窗
  • frontend/src/pages/attendance/AttendanceConfirm.tsx 考勤确认页面

优化方案

  1. 模板精简为单个考勤 Sheet(与问题7统一处理)
  2. 导入成功后 toast 提示中增加「点击查看」跳转链接
  3. 导入成功后自动跳转到考勤确认页面

十三、加班费计算

问题28:加班费计算需重复导入考勤数据

模块:薪税管理 / 考勤管理
优先级P1
状态:待优化

现状描述: 加班费计算跟考勤不关联,到加班费计算时还需要再导入一遍考勤。

问题分析

  • 与问题9相同,加班费计算模块独立于考勤管理
  • 考勤管理中已确认的加班数据未传递到薪税计算

涉及文件

  • frontend/src/pages/money/ 加班费相关
  • backend/src/routes/payroll2.routes.ts
  • backend/src/routes/import.routes.ts 考勤导入(含加班记录 Sheet
  • backend/src/routes/attendance.routes.ts 考勤数据查询

优化方案

  1. 与问题9统一处理:加班费从考勤管理读取已确认的加班记录
  2. 薪税批次创建时自动拉取当月加班数据

优先级汇总

优先级 编号 问题
P0 1 福利方案无法添加人员
P0 3 离职证明下载乱码
P0 7 考勤导入模板无关Sheet+合并
P0 8 补卡无法修改状态+时间显示异常
P0 20 合同附件PDF/Word不支持查看+无法删除
P0 27 考勤模板Sheet过多+导入后无法确认
P1 2 每页条数选择无反应
P1 4 离职导出缺少筛选条件
P1 5 已提交离职无法撤回+已撤回无法删除
P1 9 加班费与考勤不关联
P1 10 加班汇总不显示手动添加的加班
P1 11 证据链验证无法定位异常
P1 12 规章制度签收缺少催办和明细
P1 13 文本模板不支持导入文档
P1 14 社保基数选择参保地后未封上下限
P1 16 校验失败未指明具体字段
P1 17 员工详情薪税入口意义不明
P1 18 录入时缺少职务字段
P1 19 花名册社保计算与社保模块不一致
P1 22 用工办理录入中途切换窗口丢失数据
P1 23 用工办理未按身份证号查重
P1 24 证明开具不支持员工下拉选择
P1 25 培训记录不支持批量选择员工
P1 28 加班费需重复导入考勤
P2 6 用工办理与离职管理功能重复
P2 15 社保基数修改不能直接覆盖
P2 21 花名册与用工办理添加员工重复
P2 26 绩效考核模块过于片面