P0: 福利批量参保/离职证明下载防乱码/考勤模板合并Sheet/补卡修改/附件在线查看删除 P1: 分页pageSize修复/离职导出筛选/撤回删除草稿/加班费自动计算/考勤加班汇总/证据链异常详情/制度催办/模板导入Word/社保封顶保底/校验字段提示/职务字段/社保费用明细/弹窗防误关/身份证查重/证明员工下拉/培训批量 P2: 离职流程去重/社保基数覆盖输入/薪税入口改名/添加员工引导/绩效模板清理
40 KiB
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.tsbenefitApi 定义backend/src/routes/benefits.routes.ts
优化方案:
- 批量参保的员工列表改用
allLite接口,避免200条限制 - 增加按部门筛选功能
- 验证实际运行时员工列表是否正常加载
二、全局通用问题
问题2:多个模块中每页条数选择无反应
模块:全局(花名册、薪税、考勤等多个列表页)
优先级:P1
状态:待验证
现状描述: 多个模块列表页底部的「每页条数」选择器点击后无反应,无法切换每页显示条数。
代码核查结果:
usePageSize hook(frontend/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-21getPageSize/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疑似问题点
优化方案:
- 验证
usePageSize的page-size-changed事件是否在所有页面正确触发 - 确保所有列表页
onPageSizeChange回调中setPage(1)后 queryKey 包含pageSize - 全局统一分页组件,确保所有列表页行为一致
三、离职管理模块
问题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离职管理页面
优化方案:
- 在生成的
.doc内容头部添加 BOM 标记(\uFEFF),确保 Word 正确识别 UTF-8 编码 - 后端下载接口设置正确的
Content-Type: application/msword; charset=utf-8 - 前端下载时使用 Blob 并指定编码
- 考虑生成 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-696terminationApi 定义
优化方案:
- 导出接口增加
dateFrom、dateTo、department、status等查询参数 - 前端导出时携带当前筛选条件
- 增加导出确认弹窗,显示筛选范围和预计条数
问题5:已提交的离职数据无法撤回,已撤回的无用数据无法删除
模块:离职管理
优先级:P1
状态:待修复
现状描述: 离职管理中已提交的数据无法撤回操作,已撤回的无用数据无法删除清理。
代码核查结果:
- 后端
termination.service.ts:256-334有revokeTermination方法,路由termination.routes.ts:81-115有DELETE /:id/revoke端点 - 前端
api-services.ts:613-696有revoke方法定义 - 但前端
Termination.tsx页面未暴露撤回和删除草稿的按钮——UI 缺少对应操作入口 - 后端有
cancelTermination(termination.service.ts:729-954)和getDrafts方法
涉及文件:
backend/src/services/termination.service.ts:256-334revokeTerminationbackend/src/services/termination.service.ts:729-954cancelTermination, getDraftsbackend/src/routes/termination.routes.ts:81-115撤回路由backend/src/routes/termination.routes.ts:128-234草稿管理路由frontend/src/lib/api-services.ts:613-696terminationApi.revoke/cancelfrontend/src/pages/Termination.tsx:330-400缺少撤回/删除按钮
优化方案:
- 前端
Termination.tsx为已提交但未完成的离职流程增加「撤回」按钮 - 已撤回的草稿数据允许删除,增加二次确认
- 已完成离职的记录保留不可删除(合规要求)
问题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
优化方案:
- 用工办理中保留「入职办理」「转正」「调岗」等入职相关流程
- 离职、解聘相关流程统一归入「离职管理」模块
- 侧边栏菜单分组明确:用工办理(入职类)→ 离职管理(离职类)
四、考勤管理模块
问题7:考勤导入模板包含无关Sheet,且加班/违纪/考勤三个Sheet需合并
模块:考勤管理
优先级:P0
状态:待优化
现状描述: 导入考勤的模板包含「员工信息」和「劳动合同」两个无关 Sheet,只录入考勤信息无法导入。加班记录、违纪记录、考勤记录三个 Sheet 录入同一人员时需重复粘贴姓名与身份证号,应合并。
代码核查结果:
backend/src/routes/import.routes.ts:494-692模板下载接口生成包含:员工信息、劳动合同、考勤记录、加班记录、违纪记录等多个 Sheetgen_import_sample.py:50-73Python 脚本也生成了包含多余 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示例文件生成脚本
优化方案:
- 考勤导入模板只保留考勤相关 Sheet,移除员工信息和劳动合同 Sheet
- 将考勤记录、加班记录合并为一个 Sheet,用列区分(日期、班次、签到时间、签退时间、加班时长等)
- 违纪记录因字段差异较大,可保留独立 Sheet 或独立导入入口
- 每项业务(考勤、加班、违纪)提供独立的专用模板下载
问题8:补卡无法修改未打卡状态,签到签退时间显示有问题
模块:考勤管理 - 每日出勤
优先级:P0
状态:待修复
现状描述: 考勤排班中每日出勤页面,操作补卡时无法修改未打卡状态,且签到与签退的时间显示有异常。
代码核查结果:
backend/src/services/attendance.service.ts:264-360的manualCorrectAttendance方法支持更新考勤记录,可设置签到/签退时间和状态backend/src/routes/attendance.routes.ts:210-233有POST /manual-correct端点frontend/src/pages/Attendance.tsx:970-1174的 DailyTab 有补卡弹窗和按钮frontend/src/lib/api-services.ts:247-297有manualCorrectAPI 调用- 考勤状态常量定义在
Attendance.tsx:25-33:NORMAL/LATE/EARLY_LEAVE/ABSENT/LEAVE/BUSINESS_TRIP/UNREGISTERED - 需确认:补卡弹窗是否限制了状态选项(未覆盖 UNREGISTERED→其他状态的修正),以及时间格式化是否有时区问题
涉及文件:
frontend/src/pages/Attendance.tsx:25-33状态常量定义frontend/src/pages/Attendance.tsx:970-1174DailyTab 补卡弹窗frontend/src/lib/api-services.ts:247-297attendanceApi.manualCorrectbackend/src/services/attendance.service.ts:264-360manualCorrectAttendancebackend/src/routes/attendance.routes.ts:210-233补卡路由
优化方案:
- 补卡弹窗允许修改所有考勤状态(包括未打卡→已打卡/请假/出差等)
- 检查时间字段的时区处理,确保显示本地时间
- 签到签退时间统一格式化为
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)
优化方案:
- 加班费计算改为从考勤管理模块读取已确认的加班记录
- 薪税批次创建时自动拉取当月考勤加班数据,无需重复导入
- 保留手动导入作为备选方案
问题10:个人考勤记录添加后加班汇总不显示
模块:考勤管理
优先级:P1
状态:待修复
现状描述: 个人考勤记录添加时手动填写了加班时长,但加班汇总中不显示条数,不清楚加班汇总关联的是哪里。
问题分析:
- 加班汇总可能统计的是考勤导入的加班数据,而非手动添加的加班时长
- 加班汇总的数据源与个人考勤记录的加班字段未关联
涉及文件:
frontend/src/pages/Attendance.tsx:970-1174加班汇总和考勤记录backend/src/routes/attendance.routes.ts加班统计接口
优化方案:
- 加班汇总统计应包含手动添加的考勤记录中的加班时长
- 加班汇总增加数据来源标识(导入/手动添加)
- 明确加班汇总与考勤记录的关联关系,UI 上增加说明
五、证据链模块
问题11:验证全部完整性功能简陋,无法定位异常
模块:证据链
优先级:P1
状态:待优化
现状描述: 证据链中「验证全部完整性」功能验证后显示异常,但无法告知哪部分异常,下方提醒也无法跳转操作。
代码核查结果:
frontend/src/pages/Evidence.tsx:31-37调用evidenceApi.verifyAll(),返回结果仅显示total、valid、invalid三个数字(:60-75)- 无详细异常项列表,无跳转操作
frontend/src/lib/api-services.ts:700-707evidenceApi定义了list和verifyAll方法frontend/src/pages/roster/EvidenceChain.tsx:1-155是员工个人维度的仲裁证据链,展示证据列表、风险提醒和导出功能
涉及文件:
frontend/src/pages/Evidence.tsx:31-75验证全部完整性功能frontend/src/lib/api-services.ts:700-707evidenceApi 定义frontend/src/pages/roster/EvidenceChain.tsx:1-155员工个人证据链backend/src/routes/roster.routes.ts证据链验证接口
优化方案:
- 验证接口返回详细的检查项列表(每项:名称、状态、异常描述)
- 前端展示验证结果明细,异常项高亮显示
- 每个异常项增加「去处理」跳转按钮,跳转到对应模块
六、规章制度管理
问题12:规章制度签收缺少催办和未签收人员查看
模块:规章制度
优先级:P1
状态:待优化
现状描述: 规章制度向员工公示后,签收只显示签收人数和占比,无法查看具体未签收人员,也无法催办。
代码核查结果:
frontend/src/pages/Policies.tsx:249-285有ReadStats组件,展示签收百分比和未签收人数- 已签收人员列表可展开查看(
:278-285),显示姓名、部门、签收时间 - 缺少催办通知功能——无催办按钮
- 未签收人员列表未展示——仅显示未签收人数(
:273-277),未列出具体人员 frontend/src/lib/api-services.ts:674-696policiesApi.readStats返回readCount、total、unreadCount、records- 员工端
frontend/src/pages/portal/MyPolicies.tsx:36-46有阅读确认 mutation 和待签收数量统计
涉及文件:
frontend/src/pages/Policies.tsx:110-120签收进度条frontend/src/pages/Policies.tsx:246-285ReadStats 组件frontend/src/lib/api-services.ts:674-696policiesApi 定义frontend/src/pages/portal/MyPolicies.tsx:30-50员工端阅读确认backend/src/routes/regulations.routes.ts
优化方案:
- 签收统计增加「查看明细」按钮,展开已签收/未签收人员列表
- 未签收人员列表支持「一键催办」,发送通知提醒员工签收
- 显示每位员工的签收状态和时间
七、文本模板模块
问题13:新建模板不支持导入文档,现有方式易造成格式混乱
模块:文本模板
优先级:P1
状态:待优化
现状描述: 文本模板新建时只能手动输入内容,无法通过导入 Word 文档创建,现有方式容易造成格式混乱,需要保留导入文档的原始格式。
代码核查结果:
frontend/src/pages/Templates.tsx:303-571的EnterpriseTemplates组件中,新建模板仅支持textarea手动输入内容(:493-498)- 模板内容使用
{{变量名}}占位符,支持变量替换渲染 - 系统模板支持下载 Word(
.doc格式),通过fetch请求/templates/:id/download frontend/src/lib/api-services.ts:814-839templatesApi无文档导入接口- 确认:无文档上传入口,不支持导入
.docx文件
涉及文件:
frontend/src/pages/Templates.tsx:1-571模板管理页面(系统模板+企业模板)frontend/src/lib/api-services.ts:814-839templatesApi 定义backend/src/routes/templates.routes.ts
优化方案:
- 新建模板增加「导入文档」入口,支持上传
.docx文件 - 后端使用
mammoth或类似库解析 Word 文档,保留段落、表格等结构 - 导入后转为 HTML 存储模板内容,前端预览时保留格式
- 保留现有手动创建方式作为备选
八、花名册模块
问题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-767AddEmployeeModal 社保基数填充frontend/src/pages/roster/modals.tsx:235-484RehireModal 社保基数填充frontend/src/pages/roster/BasicInfo.tsx:60-120编辑表单frontend/src/lib/api-services.ts:510-520socialInsuranceApi
优化方案:
- 选择参保地后,自动查询该城市的社保基数上下限
- 社保基数 = min(max(工资数, 下限), 上限)
- 如果工资数在上下限范围内,直接取工资数;否则显示封顶/封底后的值并提示
问题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-681AddEmployeeModal 社保基数输入框frontend/src/pages/roster/modals.tsx:397-398RehireModal 社保基数输入框frontend/src/pages/roster/BasicInfo.tsx:344-345编辑表单社保基数输入框
优化方案:
- 社保基数输入框改为受控组件,自动填充后用户可直接输入覆盖
- 输入框获得焦点时自动全选当前值,方便直接覆盖
- 增加
onFocus={(e) => e.target.select()}实现聚焦全选
问题16:录入校验失败未指明具体字段
模块:花名册
优先级:P1
状态:待优化
现状描述: 录入员工时可能是手机号录入有问题,但系统只提示「校验失败」,不指出哪个字段校验失败。
代码核查结果:
backend/src/schemas/contract.schema.ts:3-27createEmployeeSchema定义了字段级 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-31P2002 唯一约束错误返回通用"数据已存在,请勿重复操作"
涉及文件:
backend/src/schemas/contract.schema.ts:1-72Zod 校验 schema 定义backend/src/middleware/errorHandler.ts:27-31错误处理中间件backend/src/routes/employee.routes.ts:99-126创建/更新员工路由frontend/src/pages/roster/modals.tsx:648-650AddEmployeeModal 错误提示frontend/src/pages/roster/BasicInfo.tsx:82-119编辑表单错误处理
优化方案:
- 后端校验失败时返回具体字段名和错误原因(如
{"field": "phone", "message": "手机号格式不正确"}) - 前端解析错误信息,在对应字段下方显示红色提示
- toast 提示中包含具体字段名
问题17:花名册员工详情中薪税入口意义不明
模块:花名册
优先级:P2
状态:待优化
现状描述: 花名册员工个人详情中的小标识第二个点进去直接进入薪税模块(批次发薪),不理解放在员工个人这里的意义,应该是与此员工有关的个人薪资关联。
代码核查结果:
frontend/src/pages/roster/EmployeeProfile.tsx:66中paysliptab 展示PayslipSocialInfo,显示该员工的工资条和社保记录EmployeeProfileShell.tsx:12-17员工 profile 类型定义包含position字段- 需确认是否有跳转到薪税批次列表页的入口
涉及文件:
frontend/src/pages/roster/EmployeeProfile.tsx:60-71tab 定义frontend/src/pages/roster/EmployeeProfileShell.tsx:12-17profile 类型frontend/src/pages/roster/BasicInfo.tsx快捷入口
优化方案:
- 改为跳转到该员工的个人薪资历史记录页面
- 或在员工详情中增加「薪资历史」标签页,展示该员工所有批次的工资条
问题18:花名册列表有职务列,但录入时无职务字段
模块:花名册
优先级:P1
状态:待修复
现状描述: 花名册主页显示有职务这一栏,但单独录入员工时却没有职务这一项。
代码核查结果(确认):
- 花名册列表
Roster.tsx:29有position列(职务),:500有表头,:560有数据渲染 AddEmployeeModal(modals.tsx:486-767)表单中无position字段BasicInfo.tsx编辑表单中也无position字段createEmployeeSchema(contract.schema.ts:3-27)中无position字段updateEmployeeSchema(contract.schema.ts:29-51)中也无position字段- 后端
createEmployee(contract.service.ts:193-272)中也未设置position字段 - 但后端查询时 select 包含
position(employee.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-767AddEmployeeModal 缺少 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-272createEmployee 未设置 positionbackend/src/routes/employee.routes.ts:56,82查询时 select 包含 positionfrontend/src/pages/roster/EmployeeProfileShell.tsx:15,147-149profile 显示 position
优化方案:
AddEmployeeModal和BasicInfo编辑表单增加「职务」字段createEmployeeSchema和updateEmployeeSchema增加position: z.string().max(50).optional()createEmployee和updateEmployee服务中设置position字段
问题19:花名册中社保费用计算与社保模块不一致
模块:花名册 / 社保管理
优先级:P1
状态:待修复
现状描述: 花名册里员工个人计算的社保费用与社保模块中不一致。社保模块里已修改了养老医保基数不一致,但花名册里计算还是保持一致。
代码核查结果:
BasicInfo.tsx:325-330显示社保缴费基数和公积金缴费基数,使用统一基数BasicInfo.tsx:364-368未设置基数时显示警告提示contract.service.ts:205-206创建员工时socialInsBase和housingFundBase均默认取salaryNumapi-services.ts:510-512socialInsuranceApi.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-520socialInsuranceApibackend/src/routes/social.routes.ts社保配置查询
优化方案:
- 花名册社保费用计算改为读取社保模块中各险种的独立基数和比例
- 养老保险用养老基数、医疗保险用医疗基数,分别计算后汇总
- 确保两个模块的计算逻辑统一
问题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附件预览弹窗
优化方案:
- Word 预览:使用
mammoth.js转换为 HTML 在线预览,或提示下载查看 - 已保存合同的附件增加删除按钮,删除时二次确认
- 后端增加附件删除接口,更新
attachmentUrl字段
问题21:用工办理与花名册添加员工功能重复
模块:花名册 / 用工办理
优先级:P2
状态:待优化
现状描述: 花名册可以添加员工,用工办理也可以录入员工,两个模块添加员工有什么区别不清楚。如果都可以添加没有必要,最好固定在一个模块。
代码核查结果:
Roster.tsx有AddEmployeeModal(modals.tsx:486-767)直接创建员工WorkProcess.tsx:56-66的HIRE流程类型也创建员工,字段为name、department、idCardNumber等 text 输入WorkProcess.tsx:67-70的ONBOARD流程使用employee-select选择已有员工- 两个入口都调用
createEmployee,写入同一张表
涉及文件:
frontend/src/pages/Roster.tsx:70-90花名册状态和模态框frontend/src/pages/roster/modals.tsx:486-767AddEmployeeModalfrontend/src/pages/WorkProcess.tsx:56-70HIRE/ONBOARD 流程定义backend/src/routes/employee.routes.ts:99-126创建员工路由backend/src/services/contract.service.ts:193-272createEmployee
优化方案:
- 统一员工添加入口为「用工办理 → 入职办理」,包含完整入职流程
- 花名册保留「查看」和「编辑」功能,移除独立添加入口
- 或在花名册添加员工时引导跳转到用工办理的入职流程
问题22:用工办理录入中途切换窗口丢失已填信息
模块:用工办理
优先级:P1
状态:待修复
现状描述: 在用工办理里录入员工,录到身份证号处,点开别的文件想粘贴一下,再回去,刚才录入的页面就退出了,需要重新打开重新录前面的信息。
代码核查结果:
frontend/src/components/ui/Modal.tsx:36遮罩层onClick={onClose}——点击遮罩层会关闭弹窗- 无
closeOnOverlayClick={false}配置选项 - 表单数据未持久化到
sessionStorage AddEmployeeModal(modals.tsx:641)使用了useUnsavedChanges(isDirty)但仅提示,不阻止关闭WorkProcess.tsx:204-205录入弹窗也使用div+onClick={onClose}模式
涉及文件:
frontend/src/components/ui/Modal.tsx:33-56Modal 组件(遮罩层 onClick={onClose})frontend/src/pages/roster/modals.tsx:641-643AddEmployeeModal useUnsavedChangesfrontend/src/pages/WorkProcess.tsx:204-205录入弹窗
优化方案:
- 弹窗设置为
closeOnOverlayClick={false},禁止点击遮罩层关闭 - 表单数据持久化到
sessionStorage,重新打开时恢复 - 关闭前增加「确认关闭?未保存的数据将丢失」提示
问题23:用工办理未按身份证号查重
模块:用工办理
优先级:P1
状态:待修复
现状描述: 在花名册录入一个人,在用工办理里录入了一个人但没录入身份证号,不显示重复,不知道是否用身份证查重。
代码核查结果:
createEmployee(contract.service.ts:193-272)无查重逻辑——直接创建- 数据库依赖
idCardHash唯一约束,重复时抛出 P2002 错误 errorHandler.ts:27-31P2002 错误返回通用"数据已存在,请勿重复操作"消息- 前端
WorkProcess.tsx的HIRE流程类型使用text类型字段(name、department等),非employee-select INCOME_CERT和LEAVING_CERT也使用text类型手动输入员工信息(:107-114, :129-134)import.routes.ts:330-333导入时有身份证号查重,返回字段级错误信息
涉及文件:
backend/src/services/contract.service.ts:193-272createEmployee(无查重)backend/src/middleware/errorHandler.ts:27-31P2002 错误处理frontend/src/pages/WorkProcess.tsx:56-66HIRE 流程字段定义frontend/src/pages/WorkProcess.tsx:107-114,129-134证明开具字段(手动输入)backend/src/routes/import.routes.ts:330-333导入查重(有字段级错误)
优化方案:
- 用工办理录入时根据姓名+手机号或身份证号查重
- 身份证号为空时用姓名+手机号组合查重
- 发现重复时提示「该员工已存在,是否查看/跳转」
九、证明开具模块
问题24:收入证明等应支持员工下拉选择,直接拉取数据
模块:用工办理 - 证明开具
优先级:P1
状态:待优化
现状描述: 开具收入证明或其他证明时,需要手动粘贴员工信息,应该有员工下拉选项直接拉取数据,避免开具非本公司员工的证明。
代码核查结果:
INCOME_CERT(WorkProcess.tsx:107-114)字段为手动输入:employeeName(text)、idCardNumber(text)、position(text)、monthlyIncome(text)LEAVING_CERT(:129-134)同样为手动输入- 未使用
employee-select类型,不关联花名册 - 但批量开具证明弹窗(
:537-590)已有员工多选列表——单条开具时却无下拉选择 WorkProcess.tsx:746-809有EmployeeSelect组件实现,支持搜索和选择员工WorkProcess.tsx:298-306批量提交时从员工数据自动填充employeeName、idCardNumber、position
涉及文件:
frontend/src/pages/WorkProcess.tsx:107-114INCOME_CERT 字段定义(手动输入)frontend/src/pages/WorkProcess.tsx:129-134LEAVING_CERT 字段定义(手动输入)frontend/src/pages/WorkProcess.tsx:537-590批量开具证明弹窗(有员工选择)frontend/src/pages/WorkProcess.tsx:746-809EmployeeSelect 组件frontend/src/pages/WorkProcess.tsx:298-306批量提交自动填充字段
优化方案:
- 证明开具表单增加员工下拉选择器,支持姓名/手机号搜索
- 选择员工后自动填充身份证号、入职日期、职务、月收入等字段
- 只允许选择本公司在职员工
十、培训记录模块
问题25:培训记录只能选择单个员工,不支持批量/按部门
模块:培训记录
优先级:P1
状态:待优化
现状描述: 添加培训记录只能选择一个员工,但实际培训可能是好几个员工一起,也可能是一个部门甚至整个公司。
代码核查结果:
TrainingRecords.tsx:211-220员工选择为<Select>单选下拉框,employees.map渲染选项AttendanceOvertimeInfo.tsx:79-81中的培训记录新增也为单选- 不支持多选或按部门批量选择
- 表单字段:
employeeId、trainingDate、topic、content、trainer、duration、remark
涉及文件:
frontend/src/pages/roster/TrainingRecords.tsx:200-267培训记录表单(单选员工)frontend/src/pages/roster/AttendanceOvertimeInfo.tsx:10,79-81考勤/培训合并组件backend/src/routes/employee.routes.ts培训记录接口
优化方案:
- 员工选择改为多选模式,支持按部门筛选勾选
- 增加「按部门添加」和「全公司添加」快捷选项
- 批量创建培训记录,每人选一条,共享培训主题/日期/讲师等信息
十一、绩效考核模块
问题26:绩效考核模块过于片面,应支持导入公司自定义考核表
模块:绩效考核
优先级:P2
状态:待优化
现状描述: 绩效考核模块只有简单的得分/等级/评语,每个公司考核类别、评分等差别比较大,现有功能几乎没法用。应支持导入本公司绩效考核表,再进行个人绩效考核统计。
代码核查结果:
PerformanceInfo.tsx:14表单仅包含:period、periodType(月度/季度/年度)、score、grade(A/B/C/D)、result(优秀/合格/需改进/不胜任)、summary、improvementPlan、reviewer、employeeAck- 得分自动计算等级和结果(
:29-39) - 无自定义考核维度、权重、指标
- 不支持导入 Excel 考核表
涉及文件:
frontend/src/pages/roster/PerformanceInfo.tsx:1-118绩效考核完整组件backend/src/routes/employee.routes.ts绩效记录接口backend/prisma/schema.prismaPerformanceRecord 模型
优化方案:
- 增加「绩效模板」管理,支持定义考核维度、权重、评分标准
- 支持导入 Excel 考核表作为模板
- 绩效考核时按模板填写各维度得分,系统按权重计算总分
- 保留现有简单模式作为默认,自定义模板作为高级功能
十二、考勤导入流程
问题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考勤确认页面
优化方案:
- 模板精简为单个考勤 Sheet(与问题7统一处理)
- 导入成功后 toast 提示中增加「点击查看」跳转链接
- 导入成功后自动跳转到考勤确认页面
十三、加班费计算
问题28:加班费计算需重复导入考勤数据
模块:薪税管理 / 考勤管理
优先级:P1
状态:待优化
现状描述: 加班费计算跟考勤不关联,到加班费计算时还需要再导入一遍考勤。
问题分析:
- 与问题9相同,加班费计算模块独立于考勤管理
- 考勤管理中已确认的加班数据未传递到薪税计算
涉及文件:
frontend/src/pages/money/加班费相关backend/src/routes/payroll2.routes.tsbackend/src/routes/import.routes.ts考勤导入(含加班记录 Sheet)backend/src/routes/attendance.routes.ts考勤数据查询
优化方案:
- 与问题9统一处理:加班费从考勤管理读取已确认的加班记录
- 薪税批次创建时自动拉取当月加班数据
优先级汇总
| 优先级 | 编号 | 问题 |
|---|---|---|
| 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 | 绩效考核模块过于片面 |