Files
TurboHR/frontend/src/components/ui/SignMethodChoice.tsx
T
freedakgmail 5c016914dd fix: 非列表页面姓名显示处也加上身份证号
后端:
- employee.routes.ts: checkPhone 返回 idCardNumber
- risk.service.ts: 风险项查询加上 idCardNumber 并解密
- dashboard.routes.ts: 风险中心返回 employeeIdCardNumber

前端:
- Termination.tsx: 离职管理所有姓名显示处加上身份证号(员工概况、通知书、PDF导出、下拉选项)
- Roster.tsx: 弹窗标题加上身份证号,signChoice 传递 idCardNumber
- SignMethodChoice.tsx: 签署方式弹窗显示身份证号
- Contracts.tsx: 手机查重提示加上身份证号
- RiskCenter.tsx: 风险卡片员工姓名后显示身份证号
2026-08-18 07:46:43 +08:00

170 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 签署方式选择弹窗
* 花名册操作(重新入职/合同续签/薪酬变更/调岗调动)成功后,
* 弹窗让HR选择是走电子签署还是线下手签。
*
* - 电子签:自动创建电子签署记录(status=PENDING),员工在员工端签署
* - 线下手签:跳转到电子签署页面的线下手签登记
* - 跳过:不创建签署记录
*/
import { useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { toast } from 'sonner'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { PenTool, FileCheck, SkipForward, Shield } from 'lucide-react'
import { esignApi } from '../../lib/api-services'
import Modal from './Modal'
import Button from './Button'
interface SignMethodChoiceProps {
open: boolean
onClose: () => void
/** 员工ID */
employeeId: string
/** 员工姓名(用于显示) */
employeeName: string
/** 员工身份证号(用于显示) */
employeeIdCardNumber?: string
/** 签署场景 */
scene: 'CONTRACT' | 'RESIGNATION' | 'POLICY' | 'PAYSLIP' | 'ONBOARDING'
/** 文件标题 */
documentTitle: string
/** 关联合同ID(可选) */
contractId?: string
/** 备注 */
remark?: string
/** 操作名称(如"重新入职"、"合同续签" */
actionName: string
}
const SCENE_LABELS: Record<string, string> = {
CONTRACT: '劳动合同',
RESIGNATION: '离职协议',
POLICY: '规章制度',
PAYSLIP: '工资条',
ONBOARDING: '入职文件',
}
export default function SignMethodChoice({
open,
onClose,
employeeId,
employeeName,
employeeIdCardNumber,
scene,
documentTitle,
contractId,
remark,
actionName,
}: SignMethodChoiceProps) {
const navigate = useNavigate()
const queryClient = useQueryClient()
const [creating, setCreating] = useState(false)
const createEsignMutation = useMutation({
mutationFn: (data: any) => esignApi.create(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['esign-records'] })
toast.success('电子签署已发起,员工可在员工端查看并签署', {
action: { label: '查看签署', onClick: () => navigate('/esign') },
})
onClose()
},
onError: (err: any) => toast.error(err?.response?.data?.error?.message || '创建失败'),
})
/** 选择电子签署 */
const handleEsign = () => {
setCreating(true)
createEsignMutation.mutate({
employeeId,
contractId,
scene,
documentTitle,
remark: remark || `${actionName}时自动发起`,
})
setCreating(false)
}
/** 选择线下手签 → 跳转到签署页面 */
const handlePaperSign = () => {
onClose()
// 通过 URL 参数传递信息,签署页面读取后自动打开线下手签登记
const params = new URLSearchParams({
action: 'paper-sign',
employeeId,
scene,
documentTitle,
})
if (contractId) params.set('contractId', contractId)
navigate(`/esign?${params.toString()}`)
}
/** 跳过 */
const handleSkip = () => {
toast.info('已跳过签署,可稍后在「电子签署」页面手动发起')
onClose()
}
return (
<Modal open={open} onClose={onClose} title={`${actionName}成功 — 选择签署方式`} size="sm">
<div className="space-y-4">
<div className="text-sm text-gray-600 bg-gray-50 rounded-md p-3">
<div className="font-medium text-gray-700">{employeeName} · {SCENE_LABELS[scene] || scene}{employeeIdCardNumber && <span className="ml-1 text-gray-400 font-mono text-xs">{employeeIdCardNumber}</span>}</div>
<div className="text-xs text-gray-400 mt-1">{documentTitle}</div>
</div>
<div className="text-xs text-gray-500 flex items-start gap-1.5">
<Shield className="w-3.5 h-3.5 mt-0.5 shrink-0 text-primary" />
<div>{SCENE_LABELS[scene] || '文件'}</div>
</div>
<div className="space-y-2">
{/* 电子签署 */}
<button
onClick={handleEsign}
disabled={creating}
className="w-full flex items-center gap-3 p-3 border-2 border-blue-200 bg-blue-50/50 rounded-lg hover:border-blue-400 hover:bg-blue-50 transition text-left disabled:opacity-50"
>
<div className="w-10 h-10 rounded-lg bg-blue-100 flex items-center justify-center shrink-0">
<PenTool className="w-5 h-5 text-blue-600" />
</div>
<div className="flex-1 min-w-0">
<div className="text-sm font-medium text-gray-800"></div>
<div className="text-xs text-gray-500 mt-0.5"></div>
</div>
</button>
{/* 线下手签 */}
<button
onClick={handlePaperSign}
className="w-full flex items-center gap-3 p-3 border-2 border-orange-200 bg-orange-50/50 rounded-lg hover:border-orange-400 hover:bg-orange-50 transition text-left"
>
<div className="w-10 h-10 rounded-lg bg-orange-100 flex items-center justify-center shrink-0">
<FileCheck className="w-5 h-5 text-orange-600" />
</div>
<div className="flex-1 min-w-0">
<div className="text-sm font-medium text-gray-800">线</div>
<div className="text-xs text-gray-500 mt-0.5"></div>
</div>
</button>
{/* 跳过 */}
<button
onClick={handleSkip}
className="w-full flex items-center gap-3 p-3 border-2 border-gray-200 bg-gray-50/50 rounded-lg hover:border-gray-300 hover:bg-gray-50 transition text-left"
>
<div className="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center shrink-0">
<SkipForward className="w-5 h-5 text-gray-500" />
</div>
<div className="flex-1 min-w-0">
<div className="text-sm font-medium text-gray-600"></div>
<div className="text-xs text-gray-400 mt-0.5"></div>
</div>
</button>
</div>
</div>
</Modal>
)
}