feat: 电子签署完整流程 + 证据链体系

## 后端
- esign.routes.ts 重写:
  - 发起签署时校验组织电子签开关(POLICY/PAYSLIP/ONBOARDING)
  - 自动从模板渲染文件内容(CONTRACT→劳动合同模板,RESIGNATION→离职协议模板)
  - 创建签署记录时自动创建证据链(发起签署事件)
  - 新增签署详情接口、证据链查看接口
  - 取消签署追加证据链事件
  - 签署状态查询自动处理过期记录
- portal.routes.ts 员工端 esign 重写:
  - 新增验证码发送接口(/esign/:id/send-code)
  - 签署操作增加验证码校验(5次错误限制)
  - 签署完成追加证据链(IP/UA/时间戳/验证码/签署人)
  - 签署完成回写合同 signMethod + attachmentName(签署证据)
  - 列表和详情接口自动处理过期记录

## 前端
- ESign.tsx 管理端重写:
  - 发起签署增加场景选择(5种场景)
  - 场景选择后自动填充默认文件标题
  - 新增签署详情页(文件内容预览 + 证据链时间线)
  - 签署流程说明
- MyEsign.tsx 员工端重写:
  - 签署操作增加验证码确认流程
  - 60秒倒计时限制
  - 文件内容预览
  - 签署完成状态展示
- api-services.ts:
  - esignApi 增加 detail/evidence 接口
  - portalApi 增加 esignSendCode 接口
  - signEsign 增加 verifyCode 参数

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
selfrelease
2026-08-15 16:19:37 +08:00
parent d216e4a57a
commit b6fc93cc61
5 changed files with 748 additions and 133 deletions
+11 -4
View File
@@ -641,10 +641,14 @@ export const benefitApi = {
export const esignApi = {
list: (params?: { status?: string; scene?: string }) =>
get('/esign', { params: params || {} }).then(unwrap<any[]>()),
create: (data: { contractId?: string; employeeId: string; documentTitle: string; documentContent?: string; remark?: string; scene?: string }) =>
create: (data: { contractId?: string; employeeId: string; documentTitle: string; documentContent?: string; remark?: string; scene?: string; templateId?: string; templateVars?: Record<string, string> }) =>
post('/esign/create', data),
detail: (id: string) =>
get(`/esign/${id}`).then(unwrap<any>()),
status: (id: string) =>
get(`/esign/${id}/status`).then(unwrap<any>()),
evidence: (id: string) =>
get(`/esign/${id}/evidence`).then(unwrap<any[]>()),
cancel: (id: string) =>
post(`/esign/${id}/cancel`),
}
@@ -1089,9 +1093,12 @@ export const portalApi = {
/** 电子签署详情 */
esignDetail: (id: string) =>
portalGet(`/esign/${id}`).then(unwrap<any>()),
/** 签署操作 */
signEsign: (id: string) =>
portalPost(`/esign/${id}/sign`).then(unwrap<any>()),
/** 发送签署验证码 */
esignSendCode: (id: string) =>
portalPost(`/esign/${id}/send-code`).then(unwrap<any>()),
/** 签署操作(需验证码) */
signEsign: (id: string, verifyCode: string) =>
portalPost(`/esign/${id}/sign`, { verifyCode }).then(unwrap<any>()),
/** 我的培训记录 */
myTraining: () =>
portalGet('/training').then(unwrap<any[]>()),