feat: 系统优化Phase2 - 面包屑导航/侧边栏间距/制度公示阅读签收/模板变量中文化/通知类型补全

- 面包屑导航组件,集成至TopNav header
- 侧边栏菜单分组间距增大,分组间分隔线
- 制度公示员工阅读签收:PolicyReadRecord模型、portal路由、管理端阅读统计
- 修复Policies.tsx民主程序推进bug(字段名/API路径/参数)
- 用工文本模板变量名英文转中文显示
- 通知类型TYPE_LABELS补全(RISK_ALERT/SOCIAL_INS/OVERTIME_ALERT/PAYSLIP_READY)
- 通知示例数据补充
- h2标题统一为text-sm font-medium
- 新增run.md
This commit is contained in:
selfrelease
2026-07-26 20:32:38 +08:00
parent 9cb0d1f63b
commit d79e3baa34
71 changed files with 18561 additions and 3230 deletions
+107
View File
@@ -808,3 +808,110 @@ export async function getTerminationDetail(orgId: string, recordId: string) {
updatedAt: record.updatedAt.toISOString().slice(0, 10),
}
}
/**
* 解聘流程步骤前置校验
* 返回是否可以继续、阻断原因、警告信息
*/
export async function validateTerminationStep(orgId: string, recordId: string, step: number) {
const record = await prisma.terminationRecord.findFirst({
where: { id: recordId, orgId },
include: { employee: true },
})
if (!record) {
throw { code: 'NOT_FOUND', message: '记录不存在' }
}
const employee = record.employee
const reason = record.reason as string
const result: {
step: number
canProceed: boolean
blockReason?: string
warning?: string
legalBasis?: string
} = { step, canProceed: true }
// Step 1: 选择员工 — 检查特殊群体
if (step === 1) {
// 三期女职工 + 非过错解除 → 阻止
if (employee.isPregnant && reason !== 'FAULT' && reason !== 'RESIGNATION') {
result.canProceed = false
result.blockReason = '该员工处于孕期/哺乳期,法律禁止以非过错理由解除劳动合同(《劳动合同法》第四十二条)'
result.legalBasis = '《劳动合同法》第四十二条:女职工在孕期、产期、哺乳期内,用人单位不得依照第四十条、第四十一条的规定解除劳动合同'
}
// 工伤期间 + 非过错解除 → 阻止
if (employee.isWorkInjured && reason !== 'FAULT' && reason !== 'RESIGNATION') {
result.canProceed = false
result.blockReason = '该员工工伤期间,法律禁止以非过错理由解除劳动合同(《劳动合同法》第四十二条)'
result.legalBasis = '《劳动合同法》第四十二条:在本单位患职业病或者因工负伤并被确认丧失或者部分丧失劳动能力的,用人单位不得依照第四十条、第四十一条的规定解除劳动合同'
}
// 医疗期内 + 非过错解除 → 阻止
if (employee.isInMedicalPeriod && reason !== 'FAULT' && reason !== 'RESIGNATION') {
result.canProceed = false
result.blockReason = '该员工处于医疗期内,法律禁止以非过错理由解除劳动合同(《劳动合同法》第四十二条)'
result.legalBasis = '《劳动合同法》第四十二条:患病或者非因工负伤,在规定的医疗期内的,用人单位不得依照第四十条、第四十一条的规定解除劳动合同'
}
}
// Step 3: 合规检查 — 检查关键合规项
if (step === 3) {
const checklist = record.checklist as any
const overrides = record.checklistOverrides as any
// 过错解除:必须通知工会
if (reason === 'FAULT') {
const notifyUnion = checklist?.notify_union
const override = overrides?.notify_union
if (!notifyUnion && !override?.checked) {
result.warning = '未确认"已通知工会"。如未通知工会即解除,可能被认定为违法解除程序(2N 赔偿风险)'
result.legalBasis = '《劳动合同法》第四十三条:用人单位单方解除劳动合同,应当事先将理由通知工会'
}
}
// 非过错解除:必须支付补偿金
if (reason === 'NONFAULT' || reason === 'NEGOTIATED' || reason === 'LAYOFF') {
const compPaid = checklist?.compensation_paid
const override = overrides?.compensation_paid
if (!compPaid && !override?.checked) {
result.warning = '未确认"已支付经济补偿金"。非过错解除必须支付经济补偿金(N),未支付将面临劳动监察处罚和仲裁风险'
result.legalBasis = '《劳动合同法》第四十六条:用人单位依照本法第三十六条、第四十条、第四十一条规定解除劳动合同的,应当向劳动者支付经济补偿'
}
}
}
// Step 4: 费用结算 — 必须先计算补偿金
if (step === 4) {
if (reason !== 'RESIGNATION' && record.compensation === 0) {
const compPaid = (record.checklist as any)?.compensation_paid
if (!compPaid) {
result.canProceed = false
result.blockReason = '补偿金尚未计算。请先在 Step 4 费用结算中计算经济补偿金,再继续后续流程'
}
}
}
// Step 5: 工作交接 — 检查交接清单
if (step === 5) {
const handoverItems = record.handoverItems as any[]
if (handoverItems && handoverItems.length > 0) {
const incomplete = handoverItems.filter(h => !h.done)
if (incomplete.length > 0) {
result.warning = `还有 ${incomplete.length} 项工作交接未完成:${incomplete.map(h => h.label).join('、')}。建议完成后再执行解聘`
}
}
}
// 合同已到期 + 选择"解除"而非"终止" → 警告
if (step === 2 && reason !== 'EXPIRED' && reason !== 'RESIGNATION') {
const latestContract = await prisma.laborContract.findFirst({
where: { employeeId: employee.id, orgId },
orderBy: { createdAt: 'desc' },
})
if (latestContract?.endDate && new Date(latestContract.endDate) < new Date()) {
result.warning = '该员工合同已到期。建议使用"到期终止"(EXPIRED)而非解除,流程更简单且法律风险更低'
}
}
return result
}