import { useState, useMemo } from 'react' import { useQuery } from '@tanstack/react-query' import { Calculator, Info, AlertCircle } from 'lucide-react' import api from '../lib/api' import Card from '../components/ui/Card' import Button from '../components/ui/Button' import { Input, Label, Select } from '../components/ui/Input' // 金额格式化:保留两位小数 + 千分位 const fmt = (n: number) => (n || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) interface EmployeeOption { id: string name: string department: string hireDate: string monthlySalary: number status: string contracts?: any[] } function useEmployees() { return useQuery({ queryKey: ['roster-for-compensation'], queryFn: async () => { const res = await api.get('/roster') as any return res.data }, }) } function EmployeeSelector({ employees, selectedId, onSelect }: { employees?: EmployeeOption[] selectedId: string onSelect: (emp: EmployeeOption | null) => void }) { return (
) } export default function Compensation() { const [tab, setTab] = useState<'severance' | 'double'>('severance') const tabs: { key: typeof tab; label: string }[] = [ { key: 'severance', label: '经济补偿金' }, { key: 'double', label: '双倍工资' }, ] return (

补偿计算

根据解除原因快速测算经济补偿与双倍工资

{tabs.map((t) => ( ))}
{tab === 'severance' && } {tab === 'double' && }
) } function SeveranceCalculator() { const { data: employees } = useEmployees() const [selectedEmpId, setSelectedEmpId] = useState('') const [hireDate, setHireDate] = useState('') const [leaveDate, setLeaveDate] = useState('') const [avgWage, setAvgWage] = useState(8000) const [reason, setReason] = useState('negotiated') const [socialAvgWage, setSocialAvgWage] = useState(0) const [result, setResult] = useState(null) const handleSelectEmp = (emp: EmployeeOption | null) => { setSelectedEmpId(emp?.id || '') if (emp) { setHireDate(emp.hireDate?.toString().slice(0, 10) || '') setAvgWage(emp.monthlySalary || 8000) } } const reasonMap: Record = { negotiated: { label: '协商一致解除', multiplier: 1, extra: '', illegal: false }, fault: { label: '员工过错解除', multiplier: 0, extra: '员工过错解除,无需支付经济补偿金', illegal: false }, nonfault: { label: '非过错解除', multiplier: 1, extra: '额外支付1个月代通知金', illegal: false }, layoff: { label: '经济性裁员', multiplier: 1, extra: '', illegal: false }, expired: { label: '合同到期不续签', multiplier: 1, extra: '用人单位不续签或降低条件续签', illegal: false }, illegal: { label: '违法解除', multiplier: 2, extra: '违法解除劳动合同,按经济补偿金的2倍支付赔偿金(《劳动合同法》第87条)', illegal: true }, } const handleCalculate = () => { if (!hireDate || !leaveDate) return const hire = new Date(hireDate) const leave = new Date(leaveDate) const totalMonths = (leave.getFullYear() - hire.getFullYear()) * 12 + (leave.getMonth() - hire.getMonth()) const years = Math.floor(totalMonths / 12) const remainingMonths = totalMonths % 12 let compMonths: number if (remainingMonths >= 6) compMonths = years + 1 else if (remainingMonths > 0) compMonths = years + 0.5 else compMonths = years if (compMonths <= 0) compMonths = 0.5 let wage = avgWage let capped = false if (socialAvgWage > 0 && avgWage > socialAvgWage * 3) { wage = socialAvgWage * 3 compMonths = Math.min(compMonths, 12) capped = true } const r = reasonMap[reason] const basePay = wage * compMonths let totalPay = basePay * r.multiplier let noticePay = 0 if (reason === 'nonfault') { noticePay = wage totalPay += noticePay } setResult({ years, remainingMonths, compMonths, wage, totalPay, basePay, totalMonths, capped, reason: r.label, reasonNote: r.extra, noticePay, noComp: r.multiplier === 0, isIllegal: r.illegal }) } return (

填写信息

setHireDate(e.target.value)} />
setLeaveDate(e.target.value)} />
setAvgWage(Number(e.target.value) || 0)} />
setSocialAvgWage(Number(e.target.value) || 0)} placeholder="用于三倍封顶计算" />

计算结果

{result ? (
离职原因:{result.reason}
工作年限:{result.years}年{result.remainingMonths}个月
{result.noComp ? (
{result.reasonNote}
) : ( <>
补偿月数:{result.compMonths}个月
{result.capped && (
⚠️ 工资超过社平3倍,已按三倍封顶且最多补偿12个月
)}
计算基数:¥{fmt(result.wage)}/月
{result.isIllegal ? '经济补偿金' : '应付金额'} ¥{fmt(result.basePay)}
{result.isIllegal ? ( <>
违法解除赔偿金(×2) ¥{fmt(result.totalPay)}
({result.compMonths}个月 × ¥{fmt(result.wage)} × 2)
) : ( <>
{result.reason} ¥{fmt(result.totalPay)}
({result.compMonths}个月 × ¥{fmt(result.wage)}) {result.noticePay > 0 && 含代通知金 ¥{fmt(result.noticePay)}}
)}
{result.reasonNote && (
{result.reasonNote}
)}
满1年补1个月,满6个月不满1年按1年算,不满6个月补半个月
)}
) : (
填写信息后点击「计算」按钮
)}
) } function DoubleSalaryCalculator() { const { data: employees } = useEmployees() const [selectedEmpId, setSelectedEmpId] = useState('') const [monthlyWage, setMonthlyWage] = useState(8000) const [hireDate, setHireDate] = useState('') const [hasContract, setHasContract] = useState(false) const [contractDate, setContractDate] = useState('') const handleSelectEmp = (emp: EmployeeOption | null) => { setSelectedEmpId(emp?.id || '') if (emp) { setHireDate(emp.hireDate?.toString().slice(0, 10) || '') setMonthlyWage(emp.monthlySalary || 8000) const latestContract = emp.contracts?.find((c: any) => c.signDate) if (latestContract) { setHasContract(true) setContractDate(latestContract.signDate?.toString().slice(0, 10) || '') } else { setHasContract(false) setContractDate('') } } } const result = useMemo(() => { if (!hireDate) return null const hire = new Date(hireDate) const startDate = new Date(hire) startDate.setMonth(startDate.getMonth() + 1) startDate.setDate(startDate.getDate() + 1) let endDate = new Date(hire) endDate.setFullYear(endDate.getFullYear() + 1) if (hasContract && contractDate) { const contract = new Date(contractDate) const daysDiff = Math.floor((contract.getTime() - hire.getTime()) / (1000 * 60 * 60 * 24)) if (daysDiff > 30) { endDate = contract } } const months = Math.min( Math.floor((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24 * 30.44)), 11, ) const totalPay = monthlyWage * Math.max(months, 0) return { startDate, endDate, months: Math.max(months, 0), totalPay } }, [monthlyWage, hireDate, hasContract, contractDate]) return (

填写信息

setMonthlyWage(Number(e.target.value) || 0)} />
setHireDate(e.target.value)} />
{hasContract && (
setContractDate(e.target.value)} />
)}

计算结果

{result ? (
入职日期:{hireDate}
合同签订:{hasContract ? contractDate || '未填写' : '未签订'}
双倍工资起算:{result.startDate.toISOString().slice(0, 10)}
双倍工资截止:{result.endDate.toISOString().slice(0, 10)}
需赔 ¥{fmt(result.totalPay)}
({result.months}个月 × ¥{fmt(monthlyWage)})
法律规定:入职1个月没签合同,从第2个月起要付双倍工资,最多11个月
) : (
请填写入职日期
)}
) }