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