/** * EmployeeProfileShell — 员工档案壳组件 * 提供统一的员工详情页布局:摘要头部 + 标签导航 + 内容区域 * 可被 Roster、Termination、SocialInsurance 等页面复用 */ import { useState, ReactNode } from 'react' import { X, Phone, Mail, MapPin, Calendar, Briefcase, DollarSign, AlertTriangle } from 'lucide-react' import { fmt, DetailTab, TAB_GROUPS } from './shared' interface EmployeeSummary { id: string name: string department: string status: string position?: string hireDate?: string monthlySalary?: number phone?: string email?: string city?: string idCardNumber?: string probationInfo?: { isProbation: boolean isExpiring: boolean daysToConfirm: number } contractStatus?: string latestContract?: { contractType?: string endDate?: string } hasTermination?: boolean latestTerminationStatus?: string latestTerminationType?: string } interface EmployeeProfileShellProps { /** 员工摘要数据 */ profile: EmployeeSummary /** 返回按钮回调 */ onBack: () => void /** 标签计数获取函数 */ getTabCount?: (key: DetailTab) => number /** 当前激活标签 */ activeTab?: DetailTab /** 标签切换回调 */ onTabChange?: (tab: DetailTab) => void /** 是否为在职状态 */ isActive?: boolean /** 隐藏的标签列表(如离职员工隐藏考勤绩效) */ hiddenTabs?: DetailTab[] /** 自定义标签渲染(覆盖默认 TAB_GROUPS) */ tabGroups?: typeof TAB_GROUPS /** 内容区域渲染函数 */ children?: (tab: DetailTab) => ReactNode } /** 合同类型标签映射 */ const CONTRACT_TYPE_LABELS: Record = { FIXED: { label: '固定期限', style: 'bg-blue-50 text-blue-700 border border-blue-200' }, UNFIXED: { label: '无固定期', style: 'bg-purple-50 text-purple-700 border border-purple-200' }, LABOR: { label: '劳务协议', style: 'bg-amber-50 text-amber-700 border border-amber-200' }, INTERNSHIP: { label: '实习协议', style: 'bg-teal-50 text-teal-700 border border-teal-200' }, DISPATCH: { label: '劳务派遣', style: 'bg-cyan-50 text-cyan-700 border border-cyan-200' }, OUTSOURCING: { label: '业务外包', style: 'bg-slate-50 text-slate-700 border border-slate-200' }, PARTTIME: { label: '兼职协议', style: 'bg-indigo-50 text-indigo-700 border border-indigo-200' }, UNSIGNED: { label: '未签合同', style: 'bg-gray-100 text-gray-500 border border-gray-200' }, } /** 合同状态标签映射 */ const CONTRACT_STATUS_LABELS: Record = { expired: { label: '已过期', style: 'bg-red-50 text-rose-700' }, unsigned_over_year: { label: '未签署(超1年)', style: 'bg-red-50 text-rose-700' }, unsigned_over_30: { label: '未签署(超30天)', style: 'bg-red-50 text-rose-700' }, unsigned: { label: '未签署', style: 'bg-yellow-50 text-amber-700' }, expiring: { label: '即将到期', style: 'bg-yellow-50 text-amber-700' }, active: { label: '正常', style: 'bg-green-50 text-emerald-700' }, unfixed: { label: '正常', style: 'bg-green-50 text-emerald-700' }, } export default function EmployeeProfileShell({ profile, onBack, getTabCount, activeTab: controlledTab, onTabChange, isActive = true, hiddenTabs = [], tabGroups = TAB_GROUPS, children, }: EmployeeProfileShellProps) { const [internalTab, setInternalTab] = useState('basic') const tab = controlledTab ?? internalTab const setTab = onTabChange ?? setInternalTab const contractType = profile.latestContract?.contractType const contractTypeCfg = CONTRACT_TYPE_LABELS[contractType || ''] || CONTRACT_TYPE_LABELS.UNSIGNED const contractStatusCfg = CONTRACT_STATUS_LABELS[profile.contractStatus || ''] || { label: '无合同', style: 'bg-gray-100 text-gray-500' } return (
{/* 头部:返回按钮 + 员工摘要卡片 */}

{profile.name} — 完整档案

{/* 员工摘要卡片 */}
{/* 左侧:基本信息 */}
{/* 头像占位 */}
{profile.name?.charAt(0) || '?'}
{profile.name} {profile.status === 'ACTIVE' ? '在职' : profile.status === 'PRE_HIRE' ? '预入职' : '离职'} {profile.probationInfo?.isProbation && ( 试用期{profile.probationInfo.isExpiring ? `即将到期(${profile.probationInfo.daysToConfirm}天)` : `剩${profile.probationInfo.daysToConfirm}天`} )} {profile.hasTermination && profile.status === 'ACTIVE' && profile.latestTerminationStatus !== 'CANCELLED' && profile.latestTerminationStatus !== 'COMPLETED' && ( {profile.latestTerminationType === 'RESIGNATION' ? '待离职' : '待解聘'} )}
{profile.department && ( {profile.department} )} {profile.position && ( {profile.position} )} {profile.hireDate && ( 入职 {profile.hireDate.toString().slice(0, 10)} )} {profile.monthlySalary != null && ( ¥{fmt(profile.monthlySalary)}/月 )}
{/* 联系方式 */}
{profile.phone && ( {profile.phone} )} {profile.email && ( {profile.email} )} {profile.city && ( {profile.city} )} {profile.idCardNumber && ( {profile.idCardNumber} )}
{/* 右侧:合同状态 */}
{contractTypeCfg.label} {contractStatusCfg.label} {(profile.contractStatus === 'expired' || profile.contractStatus === 'unsigned_over_year' || profile.contractStatus === 'unsigned_over_30') && ( 需处理 )}
{/* 标签导航 */}
{tabGroups.map((group) => (
{group.tabs .filter((t) => isActive || !hiddenTabs.includes(t.key)) .map((t) => { const count = getTabCount?.(t.key) ?? 0 return ( ) })}
))}
{/* 内容区域 */}
{children?.(tab)}
) }