# 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` 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`:
```tsx
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. 验证 `usePageSize` 的 `page-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. 导出接口增加 `dateFrom`、`dateTo`、`department`、`status` 等查询参数
2. 前端导出时携带当前筛选条件
3. 增加导出确认弹窗,显示筛选范围和预计条数
---
### 问题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-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-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` 有 `manualCorrect` API 调用
- 考勤状态常量定义在 `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-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()`,返回结果仅显示 `total`、`valid`、`invalid` 三个数字(`:60-75`)
- 无详细异常项列表,无跳转操作
- `frontend/src/lib/api-services.ts:700-707` `evidenceApi` 定义了 `list` 和 `verifyAll` 方法
- `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-285` 有 `ReadStats` 组件,展示签收百分比和未签收人数
- 已签收人员列表可展开查看(`:278-285`),显示姓名、部门、签收时间
- **缺少催办通知功能**——无催办按钮
- **未签收人员列表未展示**——仅显示未签收人数(`:273-277`),未列出具体人员
- `frontend/src/lib/api-services.ts:674-696` `policiesApi.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-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-571` 的 `EnterpriseTemplates` 组件中,新建模板仅支持 `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:66` 中 `payslip` 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: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-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. `AddEmployeeModal` 和 `BasicInfo` 编辑表单增加「职务」字段
2. `createEmployeeSchema` 和 `updateEmployeeSchema` 增加 `position: z.string().max(50).optional()`
3. `createEmployee` 和 `updateEmployee` 服务中设置 `position` 字段
---
### 问题19:花名册中社保费用计算与社保模块不一致
**模块**:花名册 / 社保管理
**优先级**:P1
**状态**:待修复
**现状描述**:
花名册里员工个人计算的社保费用与社保模块中不一致。社保模块里已修改了养老医保基数不一致,但花名册里计算还是保持一致。
**代码核查结果**:
- `BasicInfo.tsx:325-330` 显示社保缴费基数和公积金缴费基数,使用统一基数
- `BasicInfo.tsx:364-368` 未设置基数时显示警告提示
- `contract.service.ts:205-206` 创建员工时 `socialInsBase` 和 `housingFundBase` 均默认取 `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` 附件预览弹窗实现:
- **图片**:`
` 在线预览 ✅(`:432`)
- **PDF**:`