feat: 社保公积金版本管理 + 员工基数调整 + 加班费计算优化 + UI组件改进

- SocialInsuranceConfig 版本化(effectiveFrom/effectiveTo/isCurrent/adjustmentDone)
- 社保配置版本管理接口(列表/新建/按月获取/当前版本)
- 员工基数调整:预览全部在职员工、上年月均工资计算建议基数、可编辑表格、确认后批量保存
- payroll.service 按批次月份匹配对应版本社保配置
- risk.service / seed.ts 同步更新
- 前端社保tab重构为版本管理+调整+试算
- 加班费计算三步流程、CSV导入、批次导入
- UI组件、Dashboard、合同、薪酬等页面优化
This commit is contained in:
freedakgmail
2026-07-23 16:32:46 +08:00
parent 2a09d31ccc
commit c618710a52
31 changed files with 2245 additions and 745 deletions
+23 -23
View File
@@ -61,15 +61,15 @@ export default function Compensation() {
]
return (
<div className="space-y-4">
<h1 className="text-lg font-semibold"></h1>
<div className="space-y-3">
<h1 className="text-xs font-medium"></h1>
<div className="flex gap-1 border-b">
{tabs.map((t) => (
<button
key={t.key}
onClick={() => setTab(t.key)}
className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
className={`px-3 py-1.5 text-xs font-medium border-b-2 transition-colors ${
tab === t.key ? 'border-primary text-primary' : 'border-transparent text-gray-500 hover:text-gray-700'
}`}
>
@@ -150,7 +150,7 @@ function SeveranceCalculator() {
<div className="grid md:grid-cols-2 gap-4">
<Card>
<h2 className="font-medium mb-4"></h2>
<div className="space-y-4">
<div className="space-y-3">
<EmployeeSelector employees={employees} selectedId={selectedEmpId} onSelect={handleSelectEmp} />
<div>
<Label></Label>
@@ -189,19 +189,19 @@ function SeveranceCalculator() {
<h2 className="font-medium mb-4 flex items-center gap-2"><Calculator className="w-5 h-5" /></h2>
{result ? (
<div className="space-y-3">
<div className="text-sm text-gray-500"><span className="text-gray-900">{result.reason}</span></div>
<div className="text-sm text-gray-500"><span className="text-gray-900">{result.years}{result.remainingMonths}</span></div>
<div className="text-xs text-gray-500"><span className="text-gray-900">{result.reason}</span></div>
<div className="text-xs text-gray-500"><span className="text-gray-900">{result.years}{result.remainingMonths}</span></div>
{result.noComp ? (
<div className="px-3 py-2 rounded-md bg-gray-50 text-gray-700 text-sm">
<div className="px-3 py-2 rounded-md bg-gray-50 text-gray-700 text-xs">
{result.reasonNote}
</div>
) : (
<>
<div className="text-sm text-gray-500"><span className="text-gray-900">{result.compMonths}</span></div>
<div className="text-xs text-gray-500"><span className="text-gray-900">{result.compMonths}</span></div>
{result.capped && (
<div className="text-sm text-warning"> 312</div>
<div className="text-xs text-warning"> 312</div>
)}
<div className="text-sm text-gray-500"><span className="text-gray-900">¥{fmt(result.wage)}/</span></div>
<div className="text-xs text-gray-500"><span className="text-gray-900">¥{fmt(result.wage)}/</span></div>
<div className="border-t pt-3 space-y-2">
<div className="flex items-center justify-between">
<span className="font-medium">{result.isIllegal ? '经济补偿金' : '应付金额'}</span>
@@ -211,7 +211,7 @@ function SeveranceCalculator() {
<>
<div className="flex items-center justify-between">
<span className="font-medium text-danger">×2</span>
<span className="text-xl font-bold text-danger">¥{fmt(result.totalPay)}</span>
<span className="text-lg font-bold text-danger">¥{fmt(result.totalPay)}</span>
</div>
<div className="text-xs text-gray-400">{result.compMonths} × ¥{fmt(result.wage)} × 2</div>
</>
@@ -219,7 +219,7 @@ function SeveranceCalculator() {
<>
<div className="flex items-center justify-between">
<span className="font-medium">{result.reason}</span>
<span className="text-xl font-bold text-primary">¥{fmt(result.totalPay)}</span>
<span className="text-lg font-bold text-primary">¥{fmt(result.totalPay)}</span>
</div>
<div className="text-xs text-gray-400">{result.compMonths} × ¥{fmt(result.wage)})
{result.noticePay > 0 && <span className="block"> ¥{fmt(result.noticePay)}</span>}
@@ -228,12 +228,12 @@ function SeveranceCalculator() {
)}
</div>
{result.reasonNote && (
<div className={`flex items-start gap-2 px-3 py-2 rounded-md text-sm ${result.isIllegal ? 'bg-red-50 text-red-700' : 'bg-blue-50 text-blue-700'}`}>
<div className={`flex items-start gap-2 px-3 py-2 rounded-md text-xs ${result.isIllegal ? 'bg-red-50 text-red-700' : 'bg-blue-50 text-blue-700'}`}>
<Info className="w-4 h-4 mt-0.5 shrink-0" />
<span>{result.reasonNote}</span>
</div>
)}
<div className="flex items-start gap-2 px-3 py-2 rounded-md bg-blue-50 text-blue-700 text-sm">
<div className="flex items-start gap-2 px-3 py-2 rounded-md bg-blue-50 text-blue-700 text-xs">
<Info className="w-4 h-4 mt-0.5 shrink-0" />
<span>116116</span>
</div>
@@ -241,7 +241,7 @@ function SeveranceCalculator() {
)}
</div>
) : (
<div className="text-gray-400 text-sm"></div>
<div className="text-gray-400 text-xs"></div>
)}
</Card>
</div>
@@ -303,7 +303,7 @@ function DoubleSalaryCalculator() {
<div className="grid md:grid-cols-2 gap-4">
<Card>
<h2 className="font-medium mb-4"></h2>
<div className="space-y-4">
<div className="space-y-3">
<EmployeeSelector employees={employees} selectedId={selectedEmpId} onSelect={handleSelectEmp} />
<div>
<Label></Label>
@@ -333,24 +333,24 @@ function DoubleSalaryCalculator() {
<h2 className="font-medium mb-4 flex items-center gap-2"><AlertCircle className="w-5 h-5 text-warning" /></h2>
{result ? (
<div className="space-y-3">
<div className="text-sm text-gray-500"><span className="text-gray-900">{hireDate}</span></div>
<div className="text-sm text-gray-500"><span className="text-gray-900">{hasContract ? contractDate || '未填写' : '未签订'}</span></div>
<div className="text-sm text-gray-500"><span className="text-gray-900">{result.startDate.toISOString().slice(0, 10)}</span></div>
<div className="text-sm text-gray-500"><span className="text-gray-900">{result.endDate.toISOString().slice(0, 10)}</span></div>
<div className="text-xs text-gray-500"><span className="text-gray-900">{hireDate}</span></div>
<div className="text-xs text-gray-500"><span className="text-gray-900">{hasContract ? contractDate || '未填写' : '未签订'}</span></div>
<div className="text-xs text-gray-500"><span className="text-gray-900">{result.startDate.toISOString().slice(0, 10)}</span></div>
<div className="text-xs text-gray-500"><span className="text-gray-900">{result.endDate.toISOString().slice(0, 10)}</span></div>
<div className="border-t pt-3">
<div className="flex items-center justify-between">
<span className="font-medium"></span>
<span className="text-xl font-bold text-danger">¥{fmt(result.totalPay)}</span>
<span className="text-lg font-bold text-danger">¥{fmt(result.totalPay)}</span>
</div>
<div className="text-xs text-gray-400 mt-1">{result.months} × ¥{fmt(monthlyWage)}</div>
</div>
<div className="flex items-start gap-2 px-3 py-2 rounded-md bg-blue-50 text-blue-700 text-sm">
<div className="flex items-start gap-2 px-3 py-2 rounded-md bg-blue-50 text-blue-700 text-xs">
<Info className="w-4 h-4 mt-0.5 shrink-0" />
<span>1211</span>
</div>
</div>
) : (
<div className="text-gray-400 text-sm"></div>
<div className="text-gray-400 text-xs"></div>
)}
</Card>
</div>