From 8c7fb9921960858d12456e86248ef30b3dbc1d7d Mon Sep 17 00:00:00 2001 From: selfrelease Date: Sun, 16 Aug 2026 12:06:15 +0800 Subject: [PATCH] =?UTF-8?q?ux:=20=E5=BE=85=E7=AD=BE=E5=90=88=E5=90=8C?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E6=96=B0=E5=A2=9E=E3=80=8C=E5=BE=85=E7=AD=BE?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E3=80=8D=E5=88=97=EF=BC=8C=E5=B1=95=E7=A4=BA?= =?UTF-8?q?=E5=9C=BA=E6=99=AF=E6=A0=87=E7=AD=BE+=E7=AD=BE=E7=BD=B2?= =?UTF-8?q?=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 每行员工直接显示待签文件的类型(劳动合同/离职协议/规章制度等) 和签署方式(电子签/线下手签),无需展开即可看出是什么待签。 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- 20260816-优化.md | 274 +++++++++++++++++++++++++++++++++++ frontend/src/pages/ESign.tsx | 18 ++- 2 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 20260816-优化.md diff --git a/20260816-优化.md b/20260816-优化.md new file mode 100644 index 0000000..66c60a0 --- /dev/null +++ b/20260816-优化.md @@ -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` 已有 ``,但部分待办的 `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 接口调用) + diff --git a/frontend/src/pages/ESign.tsx b/frontend/src/pages/ESign.tsx index a5a6ba9..085d333 100644 --- a/frontend/src/pages/ESign.tsx +++ b/frontend/src/pages/ESign.tsx @@ -824,7 +824,8 @@ function PendingTab({ pendingList, loading, onRemind, remindLoading, remindData, 员工 部门 手机号 - 待签数 + 待签内容 + 数量 操作 @@ -849,6 +850,21 @@ function PendingTab({ pendingList, loading, onRemind, remindLoading, remindData, {emp.department || '—'} {emp.phone || '—'} + +
+ {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 ( + + {sceneCfg ? sceneCfg.label : item.title} + {methodCfg.label} + {item.type === 'contract' && 未登记} + + ) + })} +
+ {emp.pendingItems.length}