18 KiB
20260805 优化需求清单
基于用户反馈整理,对照系统代码逐一分析问题根因及优化方案。
问题1:花名册身份证号复制后粘贴为乱码
模块:花名册
优先级:P0
状态:待修复
现状描述: 花名册列表和员工详情页均支持点击身份证号复制,但用户反馈复制后粘贴出来是乱码。
问题分析:
- 列表页
Roster.tsx:522-526:点击脱敏身份证号时调用navigator.clipboard.writeText(e.idCardNumber)复制完整身份证号 - 详情页
BasicInfo.tsx:200-213:同样使用navigator.clipboard.writeText(profile.idCardNumber)复制 navigator.clipboard.writeText在非 HTTPS 环境或部分浏览器下可能静默失败,clipboard API 返回的 Promise 可能被 reject- 当前
.catch(() => toast.error('复制失败'))仅提示失败,但用户可能看到"已复制"提示后实际粘贴为空或乱码 - 可能原因:
idCardNumber字段经过加密存储,解密后的值可能包含不可见字符或编码问题
涉及文件:
frontend/src/pages/Roster.tsx:522-526frontend/src/pages/roster/BasicInfo.tsx:200-213
优化方案:
- 检查
idCardNumber字段是否经过decrypt()解密,确认复制的是明文而非加密后的乱码 - 增加 fallback 方案:当
navigator.clipboard不可用时,使用document.execCommand('copy')+ 隐藏 textarea 兜底 - 复制后增加验证:读取 clipboard 内容验证是否与原始值一致
- 确认后端返回的
idCardNumber已正确解密为明文
问题2:薪税管理筛选条件需精确到年月日,且每笔工资需有创建时间
模块:薪税管理
优先级:P1
状态:待优化
现状描述: 薪税管理中筛选条件仅支持按月(YYYY-MM)筛选,无法精确到具体日期。同时发薪批次列表未显示创建时间,难以区分同月多笔工资。
问题分析:
BatchTab.tsx:101-103:筛选条件为month(YYYY-MM)、monthFrom、monthTo,均为月份级别- 后端
payroll2.routes.ts:151-168:查询参数month、monthFrom、monthTo也只支持月份级别 PayrollBatchschema 有createdAt字段(schema.prisma:710),但前端列表未展示- 同月可创建多个批次(
batchNo区分),但用户无法直观看出创建先后顺序
涉及文件:
frontend/src/pages/money/BatchTab.tsx:101-103, 220-240backend/src/routes/payroll2.routes.ts:151-168backend/prisma/schema.prisma:691-719(PayrollBatch model)
优化方案:
- 批次列表增加「创建时间」列,显示
createdAt(格式:YYYY-MM-DD HH:mm) - 筛选条件增加日期范围选择器(
dateFrom/dateTo),后端按createdAt过滤 - 列表默认按
createdAt desc排序(当前按month desc, batchNo asc) - 批次详情中每条工资条目也可展示创建/修改时间
问题3:社保公积金无法创建和保存新的政策比例
模块:社保公积金
优先级:P0
状态:待修复
现状描述: 用户在社保公积金页面创建新版本政策比例时无法保存成功。
问题分析:
- 前端
SocialInsurance.tsx:183-201:createVersionMutation和createHousingVersionMutation调用后端 API - 后端
social.routes.ts:126-168:创建社保配置版本时,检查同一城市同一生效月份是否已有版本,如有则返回 400 错误 - 后端
social.routes.ts:509-549:创建公积金配置版本同样检查重复 - 可能原因:
- 前端
newVersion.city默认为'北京',但后端socialConfigFields中city为optional,若前端未传或传空可能导致where条件匹配到city: null的已有记录 - 后端
prevCurrent查询where: { orgId, isCurrent: true }未按城市过滤(社保),可能将其他城市的当前版本也标记为失效 - 前端
createVersionMutation的onSuccess未显示错误详情,onError未定义,用户可能看不到错误信息 z.object校验可能因前端传入的字段类型不匹配(如number传为string)而静默失败
- 前端
涉及文件:
frontend/src/pages/SocialInsurance.tsx:183-201, 786-789backend/src/routes/social.routes.ts:11-26, 120-168, 503-549backend/prisma/schema.prisma:430-445(SocialInsuranceConfig model)
优化方案:
- 后端
prevCurrent查询增加city过滤条件,避免误将其他城市的版本标记失效 - 前端
createVersionMutation和createHousingVersionMutation增加onError回调,显示后端返回的错误信息 - 前端提交前校验必填字段(城市、生效月份、各比例),确保类型正确
- 后端
createVersionSchema的city字段改为z.string().min(1)必填,避免 null 匹配问题 - 增加 try-catch 日志输出,方便排查具体失败原因
问题4:证据链无法导出,导出证据链显示导出失败
模块:证据链
优先级:P0
状态:待修复
现状描述: 员工档案 → 证据链页面,点击「导出证据链」按钮提示"导出失败"。
问题分析:
- 前端
EvidenceChain.tsx:47-63:handleExport使用fetch请求/api/v1/roster/${employeeId}/evidence-chain/export,获取 blob 后下载 - 后端
roster.routes.ts:582-747:使用ExcelJS生成 xlsx 文件并返回 - 可能原因:
- 后端
ExcelJS依赖未在服务器安装(package.json中有exceljs: ^4.4.0,但服务器可能未执行npm install) workbook.xlsx.write(res)写入流可能因 res 已设置 header 但写入失败而报错- 前端
fetch请求未携带Content-Type: application/json,但后端返回的是二进制流,res.blob()可能解析失败 - 服务器内存不足导致 ExcelJS 生成大文件失败
- Nginx 代理可能对大响应体有超时或大小限制
- 后端
涉及文件:
frontend/src/pages/roster/EvidenceChain.tsx:47-63backend/src/routes/roster.routes.ts:582-747backend/package.json:22(exceljs 依赖)
优化方案:
- 确认服务器已安装 exceljs 依赖(
npm ls exceljs) - 后端增加错误日志:
catch (err) { console.error('证据链导出失败:', err); next(err) } - 前端
handleExport增加详细错误处理:读取res.text()获取后端错误信息 - 后端
workbook.xlsx.write(res)改为workbook.xlsx.writeBuffer()然后res.send(buffer),避免流写入问题 - 检查 Nginx
proxy_buffer_size和proxy_read_timeout配置
问题5:用工办理中离职证明无法自主选择模板,导出为txt格式且格式混乱
模块:用工办理
优先级:P0
状态:待优化
现状描述: 用工办理中开具离职证明时只能使用系统默认模板,导出的证明是 txt 文档格式混乱,希望能自主选择模板且能直接电子签章后提供给员工。
问题分析:
- 前端
WorkProcess.tsx:129-135:LEAVING_CERT表单已有enterpriseTemplateId字段(enterprise-template类型),支持选择企业自定义模板 - 后端
work-process.service.ts:246-261:generateDocument函数已支持企业模板渲染(formData.enterpriseTemplateId) - 但生成文件扩展名为
.doc(work-process.service.ts:259, 289),实际内容为纯文本,非真正的 Word 文档 EnterpriseTemplateSelect组件(WorkProcess.tsx:722-744)已实现模板选择下拉框,但用户可能未创建企业模板- 导出的文书存储在
workProcess.documents字段(JSON 数组),未关联电子签章流程
涉及文件:
frontend/src/pages/WorkProcess.tsx:129-135, 722-744backend/src/services/work-process.service.ts:245-290backend/src/routes/work-process.routes.ts:136-192backend/src/routes/enterprise-template.routes.ts
优化方案:
- 导出格式优化:将纯文本
.doc改为生成真正的 Word 文档(使用docx库)或 PDF 格式 - 模板选择增强:在离职证明表单中增加模板预览功能,选择模板后可实时预览渲染效果
- 电子签章集成:审批通过后自动创建电子签署记录(类似入职流程
work-process.routes.ts:168-186),场景为RESIGNATION - 文书下载优化:前端增加文书下载按钮,支持直接下载 PDF/Word 格式
- 模板提示:当无企业模板时,增加快捷跳转链接到「模板库 → 企业文本库」创建
问题6:用工办理中多个模块功能重复
模块:用工办理
优先级:P2
状态:待优化
现状描述: 用工办理中多个流程类型功能重复,都是录入员工信息和合同时间,希望合并精简。
问题分析:
work-process.service.ts:8-22:共定义 13 类流程- 功能重复的流程:
HIRE(员工录用)和ONBOARD(员工入职):都涉及录入员工信息和创建合同CUSTOM_CONTRACT(自定义合同签署)和CHANGE(合同变更)和RENEW(合同续签):都是合同相关操作TERMINATE(合同终止)和RESCIND(合同解除):都是结束劳动关系INCOME_CERT(收入证明)和LEAVING_CERT(离职证明):都是开具证明文书
- 前端
WorkProcess.tsx的FORM_FIELDS配置中多个流程字段高度重叠(employeeName、idCardNumber、startDate、endDate 等)
涉及文件:
backend/src/services/work-process.service.ts:8-22frontend/src/pages/WorkProcess.tsx(FORM_FIELDS 配置)
优化方案:
- 合并入离职类:将
HIRE和ONBOARD合并为「入职办理」,区分"新员工入职"和"录用+入职一步完成"两种模式 - 合并合同类:将
CUSTOM_CONTRACT、CHANGE、RENEW合并为「合同签署/变更」,通过子类型区分 - 合并解聘类:将
TERMINATE和RESCIND合并为「解除/终止合同」,通过原因字段区分 - 合并证明类:将
INCOME_CERT和LEAVING_CERT合并为「开具证明」,通过证明类型切换模板 - 保留独立流程:
CONFIRM(转正)、SUSPEND(中止)、FLEXIBLE(灵活用工)、INFO_SUBMIT(信息变更)保持独立 - 合并后流程类型从 13 个精简为约 8 个,减少用户选择困难
问题7:违纪记录员工签字确认后企业端需可下载违纪确认证明
模块:违纪记录
优先级:P0
状态:待新增
现状描述: 员工在员工端签字确认违纪记录后,企业端没有可下载的违纪确认证明文件。
问题分析:
- 前端
DisciplinaryInfo.tsx:仅展示违纪记录列表和新增表单,无下载/导出功能 - 后端
roster.routes.ts:478-490:证据链中包含违纪记录信息,但无单独的违纪确认证明导出接口 DisciplinaryRecordschema(schema.prisma:565-577)有employeeAck、ackDate、ackMethod、witness、attachmentUrl字段,但无独立的证明生成功能- 培训记录已有签收单导出的先例可参考
涉及文件:
frontend/src/pages/roster/DisciplinaryInfo.tsxfrontend/src/pages/roster/PerformanceRecords.tsx(同样需要下载功能)backend/src/routes/roster.routes.ts(需新增导出接口)backend/prisma/schema.prisma:565-577(DisciplinaryRecord model)
优化方案:
- 后端新增
GET /roster/:employeeId/disciplinary/:recordId/certificate接口,生成违纪确认证明 PDF - 证明内容包含:企业名称、员工姓名、身份证号、违纪事实、处理结果、签字确认状态、确认日期、见证人
- 前端
DisciplinaryInfo.tsx在已签字的记录上增加「下载确认证明」按钮 - 同步为绩效考核记录增加类似的确认证明下载功能
- 证明格式使用 PDF(使用
pdfkit或puppeteer生成)
问题8:医疗期计算只有全国和上海两个地区政策
模块:医疗期计算器
优先级:P2
状态:待优化
现状描述: 医疗期计算器仅支持"全国(通用规定)"和"上海(特殊规定)"两个地区选项,其他有特殊政策的地区无法选择。
问题分析:
- 前端
MedicalPeriodCalculator.tsx:42-86:calculateMedicalPeriod函数硬编码了region: 'shanghai' | 'national'两种逻辑 - 地区选择为固定下拉框(
MedicalPeriodCalculator.tsx:146-153),只有两个选项 - 后端
special-status.service.ts:68-76:calculateMedicalMonths函数也仅按全国通用标准计算,未区分地区 - 各地特殊政策举例:
- 广东:按实际工作年限和本单位工作年限分档
- 北京:与全国规定一致但有补充细则
- 江苏、浙江等省份有各自的地方规定
涉及文件:
frontend/src/pages/tools/MedicalPeriodCalculator.tsx:29-107, 146-153backend/src/services/special-status.service.ts:68-76
优化方案:
- 将地区政策配置改为数据驱动,支持动态添加地区规则
- 新增
medicalPeriodPolicy配置表或 JSON 配置,存储各地政策分档规则 - 前端地区选择改为可搜索下拉框,支持从配置中动态加载
- 管理员可在系统设置中添加自定义地区政策(工龄分档 → 医疗期月数 → 累计周期月数)
- 预置全国通用、上海、广东、北京等常见地区政策
- 后端
calculateMedicalMonths函数同步支持按地区查询配置
问题9:绩效考核需区分月度/年度考核,得分与等级应关联
模块:绩效考核
优先级:P0
状态:待优化
现状描述:
- 绩效考核无法区分月度考核与年度考核
- 录入的得分和等级二者无关联,应按得分自动分等级
问题分析:
PerformanceRecordschema(schema.prisma:624-643):period字段为自由文本(YYYY-MM或YYYY-Q1),无考核类型字段score(Float)和grade(String,A/B/C/D)是独立字段,前端表单分别输入,无联动逻辑result(EXCELLENT/QUALIFIED/NEED_IMPROVE/UNQUALIFIED)也与score和grade无关联- 前端
PerformanceInfo.tsx:38-48:考核周期为自由输入框,得分和等级分别独立选择 - 前端
PerformanceRecords.tsx:212-228:考核周期使用type="month"选择器,仅支持月度
涉及文件:
frontend/src/pages/roster/PerformanceInfo.tsx:14, 38-48frontend/src/pages/roster/PerformanceRecords.tsx:181-270backend/prisma/schema.prisma:624-643(PerformanceRecord model)backend/src/routes/roster.routes.ts:1064-1097
优化方案:
- 新增考核类型字段:
PerformanceRecord增加periodType字段(MONTHLY/QUARTERLY/YEARLY),前端表单增加类型选择 - 考核周期选择优化:根据
periodType动态切换输入方式(月度→ month 选择器,季度→ Q1/Q2/Q3/Q4 选择,年度→ year 选择器) - 得分等级自动关联:
- 前端输入得分后自动计算等级和结果:
- 90-100 → A(优秀 EXCELLENT)
- 80-89 → B(合格 QUALIFIED)
- 60-79 → C(需改进 NEED_IMPROVE)
- 0-59 → D(不胜任 UNQUALIFIED)
- 等级和结果字段变为只读,由得分自动填充(可手动覆盖,覆盖后标记为"手动调整")
- 前端输入得分后自动计算等级和结果:
- 后端校验:保存时校验得分与等级的匹配性,若不一致记录日志
- 列表展示:绩效考核列表页增加考核类型筛选(月度/季度/年度)
问题10:花名册劳动合同无法下载,且不应能删除
模块:花名册 → 劳动合同
优先级:P0
状态:待修复
现状描述:
- 员工花名册中的劳动合同附件无法下载,点击附件和下载按钮都无反应
- 劳动合同作为重要资料可以修改或覆盖,但不应该能删除
问题分析:
- 前端
ContractInfo.tsx:258-289:合同附件展示区域尝试解析c.attachmentUrl(JSON 或 data URL),使用<a href={att.url} download={att.name}>下载 - 附件以 base64 data URL 形式存储在数据库中,
<a>标签的download属性对 data URL 在某些浏览器下不生效 - 下载无反应的可能原因:
- data URL 过长,浏览器阻止下载
attachmentUrl字段存储的是 JSON 字符串,解析失败时回退逻辑可能未正确处理<a>标签点击事件被外层<button>或其他事件拦截
- 删除问题:
- 前端
ContractInfo.tsx:300-306:有删除按钮,调用deleteContractMutation - 后端
employee.routes.ts:279-294:DELETE /contracts/:contractId直接物理删除合同记录 - 合同作为重要法律文件,应禁止删除,仅允许新增或修改(覆盖)
- 前端
涉及文件:
frontend/src/pages/roster/ContractInfo.tsx:258-289, 300-306backend/src/routes/employee.routes.ts:279-294backend/src/services/contract.service.ts:713-764
优化方案:
- 下载修复:
- 将 data URL 转为 Blob URL 后再触发下载(已有
dataToBlobUrl函数用于预览,下载也应用相同逻辑) - 下载按钮改为
onClick事件主动创建<a>元素并 click,而非依赖<a>标签的download属性 - 或改为调用后端接口下载(后端返回文件流),避免前端处理大 data URL
- 将 data URL 转为 Blob URL 后再触发下载(已有
- 禁止删除:
- 移除前端删除按钮,改为「作废」按钮(将合同标记为
VOID状态而非物理删除) - 后端
DELETE /contracts/:contractId改为PATCH /contracts/:contractId/void,仅更新状态 - schema 中
LaborContract增加status字段(ACTIVE/VOID),作废后不在正常列表展示但保留记录 - 证据链中保留作废合同记录,标注"已作废"
- 移除前端删除按钮,改为「作废」按钮(将合同标记为
- 允许覆盖:新增合同时若日期完全相同则提示"已存在相同日期合同,确认覆盖?"(当前是直接报错拒绝)
优先级汇总
| 编号 | 问题 | 优先级 | 模块 |
|---|---|---|---|
| 1 | 花名册身份证号复制乱码 | P0 | 花名册 |
| 2 | 薪税管理筛选精确到日+创建时间 | P1 | 薪税管理 |
| 3 | 社保公积金无法创建保存新政策 | P0 | 社保公积金 |
| 4 | 证据链导出失败 | P0 | 证据链 |
| 5 | 离职证明模板选择+格式+电子签章 | P0 | 用工办理 |
| 6 | 用工办理模块功能重复 | P2 | 用工办理 |
| 7 | 违纪记录签字后下载确认证明 | P0 | 违纪记录 |
| 8 | 医疗期计算增加其他地区政策 | P2 | 医疗期计算器 |
| 9 | 绩效考核月度/年度区分+得分等级关联 | P0 | 绩效考核 |
| 10 | 劳动合同无法下载+不应能删除 | P0 | 花名册 |
已确认无需修改
(暂无)