feat: AI历史记录、企业信息seed、员工状态自动刷新、风险预测数据准确性优化
- 智能问答/风险预测/合同审查/案例匹配 均支持历史记录保存、加载、删除 - 后端 predict/predict-stream 补充离职记录详情和预计算工龄/天数,避免AI计算错误 - Organization 增加 contactName/contactPhone 字段,seed 企业信息 - Settings 页面修复 orgData/usersData 取值路径错误 - Roster/Termination/Contracts mutation 增加 roster cache invalidation - 员工档案页面 tab 固定、内容区域滚动到窗口底部 - 解聘证据链显示逻辑修复(协商一致离职 vs 员工主动离职)
This commit is contained in:
@@ -82,7 +82,7 @@ export default function Roster() {
|
||||
mutationFn: (data: any) => api.post('/termination/draft', {
|
||||
employeeId: data.employeeId,
|
||||
type: 'RESIGNATION',
|
||||
reason: 'NEGOTIATED',
|
||||
reason: 'RESIGNATION',
|
||||
terminationDate: data.terminationDate,
|
||||
resignationReason: data.resignationReason,
|
||||
remark: data.remark,
|
||||
@@ -792,8 +792,8 @@ function EmployeeProfile({ employeeId, onBack }: { employeeId: string; onBack: (
|
||||
if (!profile) return <div className="text-center py-8 text-gray-400">员工不存在</div>
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex flex-col h-[calc(100vh-120px)]">
|
||||
<div className="flex items-center gap-3 shrink-0 pb-3">
|
||||
<button onClick={onBack} className="text-gray-400 hover:text-gray-600">
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
@@ -803,7 +803,7 @@ function EmployeeProfile({ employeeId, onBack }: { employeeId: string; onBack: (
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-1 border-b overflow-x-auto">
|
||||
<div className="flex gap-1 border-b overflow-x-auto shrink-0">
|
||||
{tabs.map((t) => {
|
||||
const Icon = t.icon
|
||||
return (
|
||||
@@ -821,6 +821,7 @@ function EmployeeProfile({ employeeId, onBack }: { employeeId: string; onBack: (
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-y-auto pt-3">
|
||||
{tab === 'basic' && <BasicInfo profile={profile} />}
|
||||
{tab === 'contract' && <ContractInfo employeeId={employeeId} contracts={profile.contracts} hireDate={profile.hireDate} />}
|
||||
{tab === 'payslip' && <PayslipInfo payslips={profile.payslips} />}
|
||||
@@ -832,6 +833,7 @@ function EmployeeProfile({ employeeId, onBack }: { employeeId: string; onBack: (
|
||||
{tab === 'termination' && <TerminationInfo employeeId={employeeId} profile={profile} records={profile.terminations} />}
|
||||
{tab === 'attachment' && <AttachmentInfo employeeId={employeeId} attachments={profile.attachments} />}
|
||||
{tab === 'evidence' && <EvidenceChain employeeId={employeeId} />}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -903,6 +905,13 @@ function BasicInfo({ profile }: { profile: any }) {
|
||||
{ label: '开户行', value: profile.bankName || '未填写' },
|
||||
{ label: '银行账号', value: profile.bankAccount || '未填写' },
|
||||
{ label: '状态', value: profile.status === 'ACTIVE' ? '在职' : '离职' },
|
||||
...(profile.status !== 'ACTIVE' && profile.terminations && profile.terminations.length > 0
|
||||
? [{ label: '离职日期', value: profile.terminations
|
||||
.map((t: any) => t.terminationDate?.toString().slice(0, 10))
|
||||
.filter(Boolean)
|
||||
.sort()
|
||||
.reverse()[0] || '未记录' }]
|
||||
: []),
|
||||
]
|
||||
const special = [
|
||||
{ label: '孕期', value: profile.isPregnant },
|
||||
@@ -2808,6 +2817,17 @@ function EvidenceChain({ employeeId }: { employeeId: string }) {
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
|
||||
const riskStyle: Record<string, string> = {
|
||||
DANGER: 'bg-red-50 border-red-300 text-red-700',
|
||||
HIGH: 'bg-orange-50 border-orange-300 text-orange-700',
|
||||
MEDIUM: 'bg-amber-50 border-amber-300 text-amber-700',
|
||||
}
|
||||
const riskIcon: Record<string, string> = {
|
||||
DANGER: '🔴',
|
||||
HIGH: '🟠',
|
||||
MEDIUM: '🟡',
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<Card>
|
||||
@@ -2831,14 +2851,41 @@ function EvidenceChain({ employeeId }: { employeeId: string }) {
|
||||
<div className="text-gray-500">未签字</div>
|
||||
<div className="text-xl font-bold text-warning">{data.summary.unsigned}</div>
|
||||
</div>
|
||||
{data.summary.riskCount > 0 && (
|
||||
<div className="text-xs text-center">
|
||||
<div className="text-gray-500">风险项</div>
|
||||
<div className="text-xl font-bold text-danger">{data.summary.riskCount}</div>
|
||||
</div>
|
||||
)}
|
||||
<Button onClick={handleExport}>导出证据链</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{data.risks && data.risks.length > 0 && (
|
||||
<Card>
|
||||
<h3 className="text-xs font-medium mb-3 flex items-center gap-2">
|
||||
<AlertTriangle className="w-4 h-4 text-danger" />
|
||||
风险提醒({data.risks.length}项)
|
||||
</h3>
|
||||
<div className="space-y-2">
|
||||
{data.risks.map((r: any, i: number) => (
|
||||
<div key={i} className={`border rounded-lg p-3 ${riskStyle[r.level] || 'bg-gray-50 border-gray-200 text-gray-600'}`}>
|
||||
<div className="flex items-center gap-2">
|
||||
<span>{riskIcon[r.level] || '⚠'}</span>
|
||||
<span className="font-medium text-xs">{r.title}</span>
|
||||
<span className="text-xs opacity-70">{r.category}</span>
|
||||
</div>
|
||||
<div className="text-xs mt-1 opacity-90">{r.description}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
<div className="space-y-2">
|
||||
{data.evidence.map((e: any, i: number) => (
|
||||
<Card key={i}>
|
||||
<Card key={i} className={e.riskLevel === 'HIGH' ? 'border-orange-300' : ''}>
|
||||
<div className="flex items-start gap-3">
|
||||
<span className={`px-2 py-0.5 rounded border text-xs shrink-0 ${categoryColor[e.category] || 'bg-gray-50 text-gray-600 border-gray-200'}`}>
|
||||
{e.category}
|
||||
@@ -2849,6 +2896,7 @@ function EvidenceChain({ employeeId }: { employeeId: string }) {
|
||||
<span className="text-xs text-gray-400">{e.date}</span>
|
||||
{e.acknowledged === true && <span className="text-xs text-safe">✓ 已签字</span>}
|
||||
{e.acknowledged === false && <span className="text-xs text-warning">⚠ 未签字</span>}
|
||||
{e.riskLevel === 'HIGH' && <span className="text-xs text-danger">⚠ 高风险</span>}
|
||||
</div>
|
||||
<div className="text-xs text-gray-600 mt-1">{e.description}</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user