ux: 待签合同列表新增「待签内容」列,展示场景标签+签署方式
每行员工直接显示待签文件的类型(劳动合同/离职协议/规章制度等) 和签署方式(电子签/线下手签),无需展开即可看出是什么待签。 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
+274
@@ -0,0 +1,274 @@
|
||||
# 20260816 优化清单
|
||||
|
||||
> 来源:用户测试反馈(编号 4-13,共 10 项)
|
||||
> 结合 TurboHR 当期代码梳理,按模块归类,标注根因 / 修复方向 / 优先级 / 涉及文件。
|
||||
> 优先级:P0 紧急(影响业务正确性/合规)|P1 高(流程阻塞或合规风险)|P2 中(体验/增强)|P3 规划(新模块)
|
||||
|
||||
---
|
||||
|
||||
## 一、花名册 · 添加雇员(问题 4-8)
|
||||
|
||||
### 问题 4:合同状态未签署时仍显示"正常"
|
||||
- **现状**:`backend/src/services/contract.service.ts:74` 注释明确"有合同记录(FIXED/UNFIXED/LABOR/INTERNSHIP),即使 signDate 为 null 也按正常合同处理",`getContractStatus` 在有合同记录时直接返回 `active`/`正常`,未区分"已签署"与"待签署"。
|
||||
- **修复方向**:
|
||||
1. `getContractStatus` 增加"待签署"判定:有合同记录但 `signDate` 为空,或关联的电子签署记录状态为 `PENDING`/未 `COMPLETED` 时,返回 `status: 'pending_sign'`,`statusText: '待签署'`,`riskLevel: 'medium'`。
|
||||
2. 仅当 `signDate` 已填且(无电子签署或电子签署已 COMPLETED)时才返回 `active`/`正常`。
|
||||
3. 前端花名册列表/详情同步展示"待签署"标签。
|
||||
- **优先级**:P1 高
|
||||
- **涉及文件**:`backend/src/services/contract.service.ts`、`frontend/src/pages/Roster.tsx`、`frontend/src/pages/roster/ContractInfo.tsx`
|
||||
|
||||
### 问题 5:社保状态未办理时仍显示"正常"
|
||||
- **现状**:花名册添加雇员表单(`roster/modals.tsx:719`)有 `socialInsBase`/`socialInsStartMonth`/`housingFundBase`/`housingFundStartMonth`,但无"社保状态"字段;花名册列表/详情也未展示社保办理状态,默认视为"正常"。
|
||||
- **修复方向**:
|
||||
1. 新增社保状态字段(派生而非存储):根据 `socialInsStartMonth` 是否填写、是否已做"办理完成"操作判定。
|
||||
2. 状态枚举:`待办`(未填写基数/起始月或未办理完成)、`正常`(已办理且在缴)、`停缴`(已设截止月)。
|
||||
3. 后端 `employee.service.ts` 在花名册列表返回 `socialInsStatus` 派生字段。
|
||||
4. 前端花名册列表/详情展示社保状态标签。
|
||||
5. 提供"办理完成"操作入口(可在社保公积金模块或花名册操作栏)。
|
||||
- **优先级**:P1 高
|
||||
- **涉及文件**:`backend/src/services/employee.service.ts`、`backend/src/services/contract.service.ts`(或新增 `social-insurance.service.ts` 派生方法)、`frontend/src/pages/Roster.tsx`、`frontend/src/pages/roster/PayslipSocialInfo.tsx`
|
||||
|
||||
### 问题 6:证件号码未验证有效性
|
||||
- **现状**:`roster/modals.tsx:886` `canSubmit` 仅校验 `idCardNumber.length >= 18`,未做身份证校验位(第 18 位)、出生日期合法性、月份/日期范围等有效性验证;`handleIdCardChange`(755 行)只做性别推导和查重,未做格式校验。
|
||||
- **修复方向**:
|
||||
1. 新增身份证有效性校验:校验位算法(前 17 位加权求和取模映射第 18 位)、出生日期合法、月份 01-12、日期合法。
|
||||
2. 校验失败时 `ageWarning` 返回 `BLOCK`,阻断保存。
|
||||
3. 后端 `employee.service.ts` 创建/更新员工时同步做校验(双保险)。
|
||||
4. 支持非身份证类型(护照/港澳台通行证)时跳过 18 位校验(已部分实现,需确认)。
|
||||
- **优先级**:P1 高
|
||||
- **涉及文件**:`frontend/src/pages/roster/modals.tsx`(`handleIdCardChange`、`canSubmit`)、`backend/src/services/employee.service.ts`、`backend/src/routes/employee.routes.ts`
|
||||
|
||||
### 问题 7:超龄人员仍可选择劳动合同
|
||||
- **现状**:`roster/modals.tsx:784` 超龄仅 `WARN` 提示"已达法定退休年龄,建议确认是否按退休处理",未阻断选择 `FIXED`/`UNFIXED` 劳动合同;超龄人员依法应签劳务协议/实习协议,不能签劳动合同。
|
||||
- **修复方向**:
|
||||
1. 年龄 ≥ 法定退休年龄(男 60、女干部 55、女工人 50)时,合同类型下拉移除 `FIXED`/`UNFIXED`,仅保留 `LABOR`/`INTERNSHIP`/`UNSIGNED`,并提示"超龄人员不可签订劳动合同,请选择劳务协议"。
|
||||
2. 强制选 `LABOR` 时联动问题 8 的社保基数置 0 逻辑。
|
||||
3. 后端 `employee.service.ts`/`work-process.service.ts` 增加校验:超龄 + 劳动合同类型组合拒绝并返回 400。
|
||||
4. `femaleWorkerType` 字段用于判定女职工退休年龄(干部 55/工人 50),需在年龄计算时引用。
|
||||
- **优先级**:P1 高
|
||||
- **涉及文件**:`frontend/src/pages/roster/modals.tsx`(合同类型下拉、`ageWarning` 逻辑)、`backend/src/services/employee.service.ts`、`backend/src/services/work-process.service.ts`
|
||||
|
||||
### 问题 8:签署劳务协议员工社保基数未置 0 且仍可缴纳社保
|
||||
- **现状**:`roster/modals.tsx:983` 社保缴费基数为独立输入框,选择 `LABOR` 合同类型时未联动置 0 或禁用;劳务协议人员依法不缴纳社保。
|
||||
- **修复方向**:
|
||||
1. 合同类型选择 `LABOR`/`INTERNSHIP` 时,社保缴费基数、公积金缴费基数自动置 0 且字段禁用,社保开始年月清空。
|
||||
2. UI 提示"劳务协议/实习协议人员不缴纳社保公积金"。
|
||||
3. 后端 `employee.service.ts` 保存时校验:`contractType === LABOR` 且 `socialInsBase > 0` 时拒绝或强制置 0。
|
||||
4. 薪税批次生成时排除劳务协议人员,或其社保公积金项强制为 0。
|
||||
- **优先级**:P1 高
|
||||
- **涉及文件**:`frontend/src/pages/roster/modals.tsx`(社保公积金区联动)、`backend/src/services/employee.service.ts`、`backend/src/services/payroll.service.ts`
|
||||
|
||||
---
|
||||
|
||||
## 二、工作台(问题 9-10)
|
||||
|
||||
### 问题 9:待办事项无法点击跳转
|
||||
- **现状**:`Dashboard.tsx:1148` 已有 `<Link to={todo.actionUrl}>`,但部分待办的 `actionUrl` 为空、指向不存在的路由,或后端 `dashboard.service.ts` 生成待办时未填充 `actionUrl`,导致点击无反应或跳转 404。
|
||||
- **修复方向**:
|
||||
1. 排查后端 `dashboard.service.ts` 各类待办(CONTRACT/TERMINATION/ONBOARDING/RETIREMENT/MONTHLY/SALARY)的 `actionUrl` 生成逻辑,确保每条待办都有有效跳转目标。
|
||||
2. `actionUrl` 为空时前端降级为"查看详情"按钮,弹窗展示待办内容,而非死链。
|
||||
3. 校验 `actionUrl` 指向的路由真实存在(如 `/roster?employeeId=xxx`、`/termination?draftId=xxx`)。
|
||||
4. 跳转后自动定位到对应员工/草稿/合同。
|
||||
- **优先级**:P1 高
|
||||
- **涉及文件**:`backend/src/services/dashboard.service.ts`、`frontend/src/pages/Dashboard.tsx`(`TodoIcon`/待办列表渲染)
|
||||
|
||||
### 问题 10:风险提醒内容不合理(含反向引导 bug)
|
||||
- **现状**:`Dashboard.tsx:202` 风险提醒按 `todo.type` 过滤(CONTRACT/TERMINATION/ONBOARDING/RETIREMENT),具体提醒文案、阈值、优先级、跳转目标生成逻辑在后端 `risk.service.ts`。存在两类问题:
|
||||
|
||||
**问题 A · 反向引导(典型 bug)**:`risk.service.ts:309-337` 三期/医疗期/工伤员工的风险提醒,文案是"解聘受限/不得解除劳动合同",但 `actionUrl` 却指向 `/termination?employee=xxx`(解聘向导页面)。等于提示"该员工不能解聘,点这里去解聘她"——完全反向引导,可能诱导 HR 误操作违法解除。
|
||||
|
||||
```ts
|
||||
// risk.service.ts:309-317(错误示例)
|
||||
if (emp.isPregnant) {
|
||||
risks.push({
|
||||
type: 'TERMINATION',
|
||||
title: `${emp.name}处于孕期/哺乳期,解聘受限`,
|
||||
description: '三期女职工不得依非过错理由解除劳动合同...',
|
||||
actionUrl: `/termination?employee=${encodeURIComponent(emp.name)}`, // ← 反向引导
|
||||
})
|
||||
}
|
||||
// isInMedicalPeriod(319-328)、isWorkInjured(329-338)同样指向 /termination
|
||||
```
|
||||
|
||||
**问题 B · 提醒规则/阈值/去重不合理**:部分提醒内容重复、阈值不合理或与实际业务无关(如合同到期 30 天提前量偏短,同一合同到期可能生成多条提醒)。
|
||||
|
||||
- **修复方向**:
|
||||
1. **修正反向引导(问题 A,P0 紧急)**:
|
||||
- 三期/医疗期/工伤员工的"解聘受限"提醒,`actionUrl` 改为指向员工详情或特殊状态页(`/roster?employee=xxx` 或 `/special-status?type=PREGNANCY`),让 HR 确认员工状态,而非跳到解聘页面。
|
||||
- 这类提醒的本质是"信息提示 + 合规警示",不是"可操作待办"。应在解聘向导 step 2 合规检查时硬阻断(Termination 已实现法定禁止情形阻断),工作台只做信息展示。
|
||||
- 可考虑将这类提醒的 `type` 从 `TERMINATION` 改为 `COMPLIANCE`(合规提示),与可操作的解聘待办区分。
|
||||
2. **梳理风险提醒规则清单(问题 B)**:合同到期(30/60/90 天分级)、未签合同(>30 天/>1 年)、试用期将满、退休预警、社保断缴、离职未办结等。
|
||||
3. **每条提醒明确**:触发条件、提醒文案模板、优先级、跳转目标、是否可忽略、是"可操作待办"还是"信息提示"。
|
||||
4. **去除重复提醒**(如同一合同到期生成多条),合并同类项。
|
||||
5. **调整阈值至合理区间**(如合同到期提醒从 30 天提前到 60 天,给 HR 反应时间)。
|
||||
6. **前端按优先级排序展示**,高风险置顶;信息提示类提醒不提供"去处理"按钮,仅提供"查看详情"。
|
||||
- **优先级**:P0 紧急(问题 A 反向引导)/ P2 中(问题 B 规则优化)
|
||||
- **涉及文件**:`backend/src/services/risk.service.ts`(`detectTerminationRisks` 309-342 行、`detectSpecialStatusRisks`)、`frontend/src/pages/Dashboard.tsx`(风险提醒 Tab 渲染、按 type 区分操作按钮)
|
||||
|
||||
---
|
||||
|
||||
## 三、薪税管理 · 发放批次(问题 11、13)
|
||||
|
||||
### 问题 11:获取工资时自动识别试用期并填充试用期工资
|
||||
- **现状**:不需要新增"获取试用期工资"按钮。已有的"获取工资"逻辑(创建批次、添加人员时自动填充 `baseSalary`)应自动识别员工是否处于试用期,是则填 `probationSalary`,否则填 `monthlySalary`。
|
||||
|
||||
**根因**:当前 3 处获取工资逻辑用了**错误的试用期判定**,判定的是"入职不到 1 年"而非"当前在试用期内":
|
||||
|
||||
```ts
|
||||
// payroll2.routes.ts:355-359(创建批次 copy_last 模式)
|
||||
// payroll2.routes.ts:610-614(添加人员到批次)
|
||||
// payroll.routes.ts:380-388(旧版批量生成工资条)
|
||||
if (emp.contracts?.[0]?.probationSalary
|
||||
&& new Date(emp.contracts[0].startDate) > new Date(Date.now() - 365 * 24 * 60 * 60 * 1000)) {
|
||||
baseSalary = emp.contracts[0].probationSalary // ← 只要入职不到1年就用试用期工资,转正后仍用
|
||||
} else if (emp.monthlySalary) { ... }
|
||||
```
|
||||
|
||||
问题:员工转正后(如试用期 3 个月,入职 6 个月),仍会被判定为试用期,`baseSalary` 错误填为 `probationSalary` 而非转正工资。
|
||||
|
||||
**正确逻辑**(`payroll.service.ts:326-331`、`payroll.service.ts:600-603` 已实现):
|
||||
```ts
|
||||
const probationEnd = new Date(contract.startDate)
|
||||
probationEnd.setMonth(probationEnd.getMonth() + contract.probationMonths)
|
||||
if (probationEnd > new Date()) { ... } // 试用期未结束
|
||||
```
|
||||
|
||||
- **修复方向**:
|
||||
1. 抽取统一的试用期判定工具函数 `isInProbation(contract, referenceDate?)`:`contract.startDate + contract.probationMonths > referenceDate`(默认当前日期,批次场景按批次月份的月末判定)。
|
||||
2. 修正 3 处错误逻辑,统一调用 `isInProbation`:
|
||||
- `payroll2.routes.ts:355-359`(创建批次 copy_last 模式)
|
||||
- `payroll2.routes.ts:610-614`(添加人员到批次)
|
||||
- `payroll.routes.ts:380-388`(旧版批量生成工资条,如仍在用)
|
||||
3. 判定为试用期 → `baseSalary = contract.probationSalary`;已转正 → `baseSalary = employee.monthlySalary`。
|
||||
4. 批次场景按批次月份判定(如批次月份是 2026-08,则判定 2026-08-31 时员工是否仍在试用期),避免跨月转正时填错。
|
||||
5. `probationSalary` 为 0 或未填时,即使试用期也回退用 `monthlySalary`(兼容旧数据)。
|
||||
- **优先级**:P1 高(影响工资发放正确性)
|
||||
- **涉及文件**:`backend/src/routes/payroll2.routes.ts`(355-359、610-614)、`backend/src/routes/payroll.routes.ts`(380-388)、`backend/src/services/payroll.service.ts`(抽取 `isInProbation` 工具函数,或放 `contract.service.ts`)
|
||||
|
||||
### 问题 13:编辑薪资新增"获取提成奖金"功能
|
||||
- **现状**:`money/BatchTab.tsx` 已有 `BONUS` 批次类型(`isBonus` 分支,617 行),但普通薪资批次编辑薪资时无"获取提成奖金"按钮,无法从提成奖金数据按月拉取填充 `bonus` 字段。
|
||||
- **修复方向**:
|
||||
1. 编辑薪资步骤新增"获取提成奖金"按钮,调用后端接口按批次月份从提成奖金表拉取每位员工的提成奖金。
|
||||
2. 拉取的奖金填入 `entry.bonus` 字段,支持正负值(奖金/扣款)。
|
||||
3. 后端新增接口 `POST /payroll2/batches/:id/fetch-bonus`,按批次月份查询提成奖金表并返回。
|
||||
4. 前端展示获取结果摘要(X 人提成奖金已填充,合计 ¥Y),支持覆盖确认。
|
||||
5. 依赖问题 12 的提成奖金数据表先建立。
|
||||
- **优先级**:P2 中(依赖问题 12)
|
||||
- **涉及文件**:`frontend/src/pages/money/BatchTab.tsx`(编辑薪资步骤按钮区)、`backend/src/routes/payroll.routes.ts`、`backend/src/services/payroll.service.ts`、`backend/src/services/commission-bonus.service.ts`(新增)
|
||||
|
||||
---
|
||||
|
||||
## 四、提成奖金 · 新模块(问题 12)
|
||||
|
||||
### 问题 12:团队页面下新增提成奖金页面
|
||||
- **现状**:`SidebarNav.tsx:44` "团队"分组下仅有花名册,无提成奖金入口;`money/BatchTab.tsx` 的 `BONUS` 批次是年终奖发放,非按月提成奖金管理。需新建独立提成奖金模块。
|
||||
- **修复方向**:
|
||||
1. **数据模型**:新增 `CommissionBonus` 表(`orgId`、`employeeId`、`month` YYYY-MM、`amount` 支持正负值、`remark`、`createdAt`、`createdBy`),唯一索引 `(orgId, employeeId, month)`。
|
||||
2. **后端**:新增 `commission-bonus.service.ts` + `commission-bonus.routes.ts`:
|
||||
- `GET /commission-bonus?month=YYYY-MM` 按月列表
|
||||
- `POST /commission-bonus/batch` 按月批量导入(JSON 数组或 Excel)
|
||||
- `PUT /commission-bonus/:id` 编辑单条
|
||||
- `DELETE /commission-bonus/:id` 删除单条
|
||||
- `GET /commission-bonus/template` 下载导入模板
|
||||
3. **前端**:新增 `frontend/src/pages/CommissionBonus.tsx` 页面:
|
||||
- 月份选择器(默认当月)
|
||||
- 列表展示该月所有员工提成奖金(员工/部门/金额/备注)
|
||||
- 支持单条编辑、删除
|
||||
- "批量导入"按钮(上传 Excel,字段:员工姓名/证件号码/月份/金额/备注)
|
||||
- 金额支持正负值(正为奖金,负为扣款)
|
||||
- 合计展示(总奖金/总扣款/净额)
|
||||
4. **菜单**:`SidebarNav.tsx` "团队"分组新增"提成奖金"入口,路由 `/commission-bonus`。
|
||||
5. **权限**:新增权限码 `PERM_COMMISSION_BONUS_VIEW`/`PERM_COMMISSION_BONUS_EDIT`/`PERM_COMMISSION_BONUS_IMPORT`。
|
||||
- **优先级**:P3 规划(新模块)
|
||||
- **涉及文件**:`backend/prisma/schema.prisma`(新增 `CommissionBonus` 模型 + migration)、`backend/src/services/commission-bonus.service.ts`(新增)、`backend/src/routes/commission-bonus.routes.ts`(新增)、`frontend/src/pages/CommissionBonus.tsx`(新增)、`frontend/src/components/layout/SidebarNav.tsx`、`frontend/src/App.tsx`(路由注册)、`frontend/src/lib/api-services.ts`(`commissionBonusApi`)
|
||||
|
||||
---
|
||||
|
||||
## 优先级汇总
|
||||
|
||||
| 优先级 | 问题 | 说明 |
|
||||
|--------|------|------|
|
||||
| **P0 紧急** | 10A | 风险提醒反向引导(三期/医疗期/工伤提醒跳转到解聘页面) |
|
||||
| **P1 高** | 4、5、6、7、8、9、11 | 花名册添加雇员合规校验 + 工作台待办跳转 + 试用期工资判定修正 |
|
||||
| **P2 中** | 10B、13 | 风险提醒规则优化 + 薪资批次获取提成奖金(13 依赖 12) |
|
||||
| **P3 规划** | 12 | 提成奖金新模块(需单独排期 + DB migration) |
|
||||
|
||||
---
|
||||
|
||||
## 建议执行顺序
|
||||
|
||||
1. **第一批(P0 + P1,立即)**:
|
||||
- 问题 10A(风险提醒反向引导,改 actionUrl)← 最先,改动小风险高
|
||||
- 问题 11(试用期工资判定修正)← 纯后端逻辑修正,抽取 `isInProbation` 工具函数
|
||||
- 问题 6(证件号码有效性校验)→ 问题 7(超龄人员合同类型限制)→ 问题 8(劳务协议社保联动)→ 问题 4(合同状态待签署)→ 问题 5(社保状态待办)→ 问题 9(待办跳转)
|
||||
- 问题 6/7/8 均在 `roster/modals.tsx` 添加雇员表单,可一并修改;问题 4/5 在后端 `contract.service.ts`/`employee.service.ts` 派生字段,前后端协同。
|
||||
2. **第二批(P2,本迭代)**:
|
||||
- 问题 10B(风险提醒规则梳理)→ 问题 13(获取提成奖金,依赖问题 12)
|
||||
3. **第三批(P3,规划)**:
|
||||
- 问题 12(提成奖金新模块,含 DB migration)→ 问题 13(获取提成奖金,依赖 12 完成)
|
||||
|
||||
---
|
||||
|
||||
## 备注
|
||||
|
||||
- 本清单基于当期代码静态分析,部分"根因"为推断,实际修复前需运行复现确认。
|
||||
- 问题 12 涉及数据库 schema 变更(新增 `CommissionBonus` 表),需走 migration + 回滚方案。
|
||||
- 问题 4/5 的"待签署""待办"状态为派生字段,不新增存储字段,避免数据冗余;如需记录"办理完成"操作时间,可新增 `socialInsConfirmedAt` 等时间戳字段。
|
||||
- 问题 7 的超龄判定需引用 `femaleWorkerType`(干部 55/工人 50),需确认该字段在添加雇员表单已正确填写。
|
||||
- 问题 13 依赖问题 12 的提成奖金数据表,不可先行实现。
|
||||
|
||||
---
|
||||
|
||||
## 五、已完成优化(20260816 补充)
|
||||
|
||||
> 以下为本次会话中已完成并部署的优化项。
|
||||
|
||||
### ✅ 已完成 1:员工端密码登录完整支持
|
||||
|
||||
- **默认密码**:创建员工时自动设置,密码 = 手机号后6位(无手机号则 `123456`)
|
||||
- **管理员重置密码**:花名册详情 → "重置密码"按钮,重置为手机号后6位
|
||||
- **员工修改密码**:员工端导航栏 → "修改密码",需验证旧密码,新密码至少6位
|
||||
- **Excel 批量导入**:导入时自动设置默认密码(手机号后6位),与新增员工一致
|
||||
- **现有员工补初始化**:已通过脚本批量补设 512 名无密码员工(478 人手机号后6位,34 人无手机号用 123456)
|
||||
- **涉及文件**:
|
||||
- `backend/src/services/contract.service.ts`(createEmployee 添加 passwordHash)
|
||||
- `backend/src/routes/import.routes.ts`(Excel 导入添加 passwordHash)
|
||||
- `backend/src/routes/employee.routes.ts`(新增 `POST /employees/:id/reset-password`)
|
||||
- `backend/src/routes/portal.routes.ts`(新增 `POST /portal/change-password`)
|
||||
- `frontend/src/pages/roster/BasicInfo.tsx`(重置密码按钮 + 默认密码提示)
|
||||
- `frontend/src/pages/portal/PortalNav.tsx`(修改密码弹窗)
|
||||
- `frontend/src/lib/api-services.ts`(resetPassword / changePassword 接口)
|
||||
|
||||
### ✅ 已完成 2:员工端 portalAxios response interceptor 修复
|
||||
|
||||
- **问题**:`portalAxios` 缺少 response interceptor,导致 `unwrap` 取到的是 `{ success, data: {...} }` 而非 `{ token, employee }`,密码登录/验证码登录/扫码自动登录全部失败
|
||||
- **修复**:给 `portalAxios` 添加与管理端 `api` 实例一致的 response interceptor `(response) => response.data`
|
||||
- **涉及文件**:`frontend/src/lib/api-services.ts`
|
||||
|
||||
### ✅ 已完成 3:同企业内员工身份证和手机号唯一性完整校验
|
||||
|
||||
- **createEmployee**:新增手机号查重硬校验(原有身份证查重)
|
||||
- **updateEmployee**:新增手机号 + 身份证查重(排除自身)
|
||||
- **Excel 批量导入**:新增身份证 + 手机号查重(重复则跳过并记录错误日志)
|
||||
- **portal 登录安全**:密码登录改为 `findMany` 遍历校验,避免跨组织手机号重复时登录到错误员工
|
||||
- **涉及文件**:
|
||||
- `backend/src/services/contract.service.ts`(createEmployee / updateEmployee 查重)
|
||||
- `backend/src/routes/import.routes.ts`(导入查重)
|
||||
- `backend/src/routes/portal.routes.ts`(login / send-code / verify-code 跨组织安全)
|
||||
|
||||
### ✅ 已完成 4:电子签署改为「待签合同」,按员工聚合 + 催办功能
|
||||
|
||||
- **菜单改名**:「电子签署」→「待签合同」
|
||||
- **页面重构为两个 Tab**:
|
||||
- **待签合同**:按员工聚合,每行一个员工,显示待签数,点击展开查看该员工名下所有待签文件(EsignRecord PENDING/SIGNING + LaborContract signDate 为空)
|
||||
- **签署记录**:原有全部签署记录列表(保留状态/场景筛选)
|
||||
- **催办功能**:点击催办 → 生成 24 小时有效的一次性自动登录链接(指向员工端签署页)→ 弹窗展示二维码 + 可复制链接 → HR 发给员工扫码直接进入签署
|
||||
- **后端新增接口**:
|
||||
- `GET /esign/pending` — 待签合同按员工聚合列表
|
||||
- `POST /esign/remind` — 催办生成自动登录链接
|
||||
- **涉及文件**:
|
||||
- `backend/src/routes/esign.routes.ts`(pending + remind 接口)
|
||||
- `frontend/src/components/layout/SidebarNav.tsx`(菜单改名)
|
||||
- `frontend/src/pages/ESign.tsx`(Tab 切换 + PendingTab 组件 + 催办弹窗)
|
||||
- `frontend/src/lib/api-services.ts`(pending / remind 接口调用)
|
||||
|
||||
@@ -824,7 +824,8 @@ function PendingTab({ pendingList, loading, onRemind, remindLoading, remindData,
|
||||
<th className="py-2 px-3 text-left">员工</th>
|
||||
<th className="py-2 px-3 text-left">部门</th>
|
||||
<th className="py-2 px-3 text-left">手机号</th>
|
||||
<th className="py-2 px-3 text-center">待签数</th>
|
||||
<th className="py-2 px-3 text-left">待签内容</th>
|
||||
<th className="py-2 px-3 text-center">数量</th>
|
||||
<th className="py-2 px-3 text-right">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -849,6 +850,21 @@ function PendingTab({ pendingList, loading, onRemind, remindLoading, remindData,
|
||||
</td>
|
||||
<td className="py-2 px-3 text-gray-500">{emp.department || '—'}</td>
|
||||
<td className="py-2 px-3 text-gray-500">{emp.phone || '—'}</td>
|
||||
<td className="py-2 px-3">
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{emp.pendingItems.map((item: any, idx: number) => {
|
||||
const sceneCfg = SCENE_CONFIG[item.scene]
|
||||
const methodCfg = SIGN_METHOD_CONFIG[item.signMethod] || SIGN_METHOD_CONFIG.ELECTRONIC
|
||||
return (
|
||||
<span key={idx} className={`inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-xs ${sceneCfg ? sceneCfg.color : 'bg-gray-100 text-gray-600'}`} title={item.title}>
|
||||
{sceneCfg ? sceneCfg.label : item.title}
|
||||
<span className={`px-1 rounded text-[10px] ${methodCfg.color}`}>{methodCfg.label}</span>
|
||||
{item.type === 'contract' && <span className="text-[10px] text-gray-400">未登记</span>}
|
||||
</span>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</td>
|
||||
<td className="py-2 px-3 text-center">
|
||||
<span className="inline-flex items-center justify-center px-2 py-0.5 rounded-full text-xs bg-yellow-100 text-yellow-700 font-medium">
|
||||
{emp.pendingItems.length}
|
||||
|
||||
Reference in New Issue
Block a user