feat: 分页组件、Dashboard待办图标、归档与工资条解耦、总览数据优化
- 新增公用 Pagination 组件,Roster/Money/Dashboard 列表加分页 - Dashboard 待办按类型显示不同图标(合同/薪资/解聘/月度) - 待办分为「风险提醒」「月度任务」两个顶层 tab - 归档与工资条生成解耦:归档只锁定批次,工资条单独生成 - 工资条管理新增「从批次汇总生成」按钮 - Dashboard 总览优先从已归档批次 BatchEntry 汇总数据 - 新增工资条生成待办提醒,生成后自动标记完成 - 修复高风险统计只含 CONTRACT/TERMINATION 类型 - 修复月度任务去重逻辑覆盖 SALARY 类型
This commit is contained in:
@@ -7,6 +7,9 @@ import Button from '../components/ui/Button'
|
||||
import { Input, Label, Select } from '../components/ui/Input'
|
||||
import Signal from '../components/ui/Signal'
|
||||
|
||||
// 金额格式化:保留两位小数 + 千分位
|
||||
const fmt = (n: number) => (n || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
||||
|
||||
const REASONS = [
|
||||
{ value: 'NEGOTIATED', label: '协商解除(双方同意分开了)', legalBasis: '《劳动合同法》第36条' },
|
||||
{ value: 'FAULT', label: '员工犯错被辞退(严重违纪/失职等)', legalBasis: '《劳动合同法》第39条' },
|
||||
@@ -304,7 +307,7 @@ export default function Termination() {
|
||||
<div className="text-sm text-gray-600 bg-gray-50 p-3 rounded-md space-y-1">
|
||||
<div className="font-medium">{selectedEmployee.name}({selectedEmployee.department})</div>
|
||||
<div>入职日期:{selectedEmployee.hireDate?.toString().slice(0, 10)}</div>
|
||||
<div>月工资:¥{selectedEmployee.monthlySalary.toLocaleString()}</div>
|
||||
<div>月工资:¥{fmt(selectedEmployee.monthlySalary)}</div>
|
||||
{selectedEmployee.latestContract ? (
|
||||
<div>合同状态:{selectedEmployee.latestContract.contractType === 'UNSIGNED' ? '未签订' : `签订于 ${selectedEmployee.latestContract.signDate?.slice(0, 10) || '未知'}`}</div>
|
||||
) : (
|
||||
@@ -432,7 +435,7 @@ export default function Termination() {
|
||||
<div className="text-sm text-gray-600 bg-gray-50 p-3 rounded-md space-y-1">
|
||||
<div className="font-medium">{selectedEmployee?.name}({selectedEmployee?.department})</div>
|
||||
<div>工作年限:{costResult.years}年{costResult.remainingMonths}个月</div>
|
||||
<div>月工资:¥{costResult.wage.toLocaleString()}/月</div>
|
||||
<div>月工资:¥{fmt(costResult.wage)}/月</div>
|
||||
{costResult.capped && (
|
||||
<div className="text-warning">⚠️ 工资超过社平3倍,已按三倍封顶且最多补偿12个月</div>
|
||||
)}
|
||||
@@ -450,27 +453,27 @@ export default function Termination() {
|
||||
{costResult.isIllegal ? '违法解除赔偿金' : '经济补偿金'}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">补偿月数:{costResult.cappedMonths}个月</div>
|
||||
<div className="text-sm text-gray-500">计算基数:¥{costResult.cappedWage.toLocaleString()}/月</div>
|
||||
<div className="text-sm text-gray-500">计算基数:¥{fmt(costResult.cappedWage)}/月</div>
|
||||
{costResult.isIllegal && (
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500">经济补偿金</span>
|
||||
<span>¥{costResult.basePay.toLocaleString(undefined, { maximumFractionDigits: 2 })}</span>
|
||||
<span>¥{fmt(costResult.basePay)}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-medium">{costResult.isIllegal ? '赔偿金(×2)' : '补偿金'}</span>
|
||||
<span className={`text-lg font-bold ${costResult.isIllegal ? 'text-danger' : 'text-primary'}`}>
|
||||
¥{costResult.severancePay.toLocaleString(undefined, { maximumFractionDigits: 2 })}
|
||||
¥{fmt(costResult.severancePay)}
|
||||
</span>
|
||||
</div>
|
||||
{costResult.noticePay > 0 && (
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<span className="text-gray-500">代通知金</span>
|
||||
<span>¥{costResult.noticePay.toLocaleString()}</span>
|
||||
<span>¥{fmt(costResult.noticePay)}</span>
|
||||
</div>
|
||||
)}
|
||||
{costResult.noticePay > 0 && (
|
||||
<div className="text-xs text-gray-400">含代通知金 ¥{costResult.noticePay.toLocaleString()}</div>
|
||||
<div className="text-xs text-gray-400">含代通知金 ¥{fmt(costResult.noticePay)}</div>
|
||||
)}
|
||||
{costResult.isIllegal && (
|
||||
<div className="flex items-start gap-2 px-3 py-2 rounded-md bg-red-50 text-red-700 text-xs">
|
||||
@@ -493,9 +496,9 @@ export default function Termination() {
|
||||
<div className="text-sm text-gray-500">赔偿月数:{costResult.doubleMonths}个月</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-medium">双倍工资赔偿</span>
|
||||
<span className="text-lg font-bold text-warning">¥{costResult.doublePay.toLocaleString()}</span>
|
||||
<span className="text-lg font-bold text-warning">¥{fmt(costResult.doublePay)}</span>
|
||||
</div>
|
||||
<div className="text-xs text-gray-400">({costResult.doubleMonths}个月 × ¥{costResult.wage.toLocaleString()})</div>
|
||||
<div className="text-xs text-gray-400">({costResult.doubleMonths}个月 × ¥{fmt(costResult.wage)})</div>
|
||||
<div className="flex items-start gap-2 px-3 py-2 rounded-md bg-yellow-50 text-yellow-800 text-xs">
|
||||
<Info className="w-3 h-3 mt-0.5 shrink-0" />
|
||||
<span>入职1个月未签合同,从第2个月起需付双倍工资,最多11个月</span>
|
||||
@@ -507,7 +510,7 @@ export default function Termination() {
|
||||
<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">¥{costResult.grandTotal.toLocaleString(undefined, { maximumFractionDigits: 2 })}</span>
|
||||
<span className="text-xl font-bold text-danger">¥{fmt(costResult.grandTotal)}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -568,9 +571,9 @@ export default function Termination() {
|
||||
</p>
|
||||
{costResult && !costResult.noComp && (
|
||||
<p>
|
||||
经济补偿金:补偿月数 <strong>{costResult.cappedMonths}</strong> 个月,计算基数 <strong>¥{costResult.cappedWage.toLocaleString()}/月</strong>,
|
||||
应付金额 <strong>¥{costResult.severancePay.toLocaleString(undefined, { maximumFractionDigits: 2 })}</strong>
|
||||
{costResult.noticePay > 0 && `(含代通知金 ¥${costResult.noticePay.toLocaleString()})`}
|
||||
经济补偿金:补偿月数 <strong>{costResult.cappedMonths}</strong> 个月,计算基数 <strong>¥{fmt(costResult.cappedWage)}/月</strong>,
|
||||
应付金额 <strong>¥{fmt(costResult.severancePay)}</strong>
|
||||
{costResult.noticePay > 0 && `(含代通知金 ¥${fmt(costResult.noticePay)})`}
|
||||
。
|
||||
</p>
|
||||
)}
|
||||
@@ -579,11 +582,11 @@ export default function Termination() {
|
||||
)}
|
||||
{costResult && !costResult.hasContract && costResult.doubleMonths > 0 && (
|
||||
<p>
|
||||
未签订劳动合同双倍工资:{costResult.doubleMonths}个月,合计 <strong>¥{costResult.doublePay.toLocaleString()}</strong>。
|
||||
未签订劳动合同双倍工资:{costResult.doubleMonths}个月,合计 <strong>¥{fmt(costResult.doublePay)}</strong>。
|
||||
</p>
|
||||
)}
|
||||
{costResult && (
|
||||
<p>合计应付金额:<strong>¥{costResult.grandTotal.toLocaleString(undefined, { maximumFractionDigits: 2 })}</strong></p>
|
||||
<p>合计应付金额:<strong>¥{fmt(costResult.grandTotal)}</strong></p>
|
||||
)}
|
||||
<p>请于解除日期前办理工作交接手续,结清相关费用。</p>
|
||||
<div className="text-right mt-6 space-y-1">
|
||||
@@ -599,20 +602,20 @@ export default function Termination() {
|
||||
<h3 className="font-medium flex items-center gap-2"><Calculator className="w-4 h-4" />费用结算明细</h3>
|
||||
<div className="text-sm space-y-1">
|
||||
<div className="flex justify-between"><span>工作年限</span><span>{costResult.years}年{costResult.remainingMonths}个月</span></div>
|
||||
<div className="flex justify-between"><span>月工资</span><span>¥{costResult.wage.toLocaleString()}/月</span></div>
|
||||
<div className="flex justify-between"><span>月工资</span><span>¥{fmt(costResult.wage)}/月</span></div>
|
||||
{costResult.capped && <div className="text-warning">⚠️ 工资超过社平3倍,已按三倍封顶且最多补偿12个月</div>}
|
||||
{!costResult.noComp && (
|
||||
<>
|
||||
<div className="flex justify-between"><span>补偿月数</span><span>{costResult.cappedMonths}个月</span></div>
|
||||
<div className="flex justify-between"><span>计算基数</span><span>¥{costResult.cappedWage.toLocaleString()}/月</span></div>
|
||||
<div className="flex justify-between font-medium"><span>{costResult.isIllegal ? '违法解除赔偿金(×2)' : '经济补偿金'}</span><span>¥{costResult.severancePay.toLocaleString(undefined, { maximumFractionDigits: 2 })}</span></div>
|
||||
{costResult.noticePay > 0 && <div className="flex justify-between"><span>代通知金</span><span>¥{costResult.noticePay.toLocaleString()}</span></div>}
|
||||
<div className="flex justify-between"><span>计算基数</span><span>¥{fmt(costResult.cappedWage)}/月</span></div>
|
||||
<div className="flex justify-between font-medium"><span>{costResult.isIllegal ? '违法解除赔偿金(×2)' : '经济补偿金'}</span><span>¥{fmt(costResult.severancePay)}</span></div>
|
||||
{costResult.noticePay > 0 && <div className="flex justify-between"><span>代通知金</span><span>¥{fmt(costResult.noticePay)}</span></div>}
|
||||
</>
|
||||
)}
|
||||
{!costResult.hasContract && costResult.doubleMonths > 0 && (
|
||||
<div className="flex justify-between text-warning"><span>未签合同双倍工资({costResult.doubleMonths}个月)</span><span>¥{costResult.doublePay.toLocaleString()}</span></div>
|
||||
<div className="flex justify-between text-warning"><span>未签合同双倍工资({costResult.doubleMonths}个月)</span><span>¥{fmt(costResult.doublePay)}</span></div>
|
||||
)}
|
||||
<div className="flex justify-between border-t pt-2 font-bold text-danger"><span>合计应付</span><span>¥{costResult.grandTotal.toLocaleString(undefined, { maximumFractionDigits: 2 })}</span></div>
|
||||
<div className="flex justify-between border-t pt-2 font-bold text-danger"><span>合计应付</span><span>¥{fmt(costResult.grandTotal)}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user