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
+32 -1
View File
@@ -2,8 +2,9 @@ import { Router } from 'express'
import { authMiddleware, AuthRequest } from '../middleware/auth'
import { auditLog } from '../middleware/auditLog'
import { terminationChecklistSchema } from '../schemas/termination.schema'
import { createTermination, createResignation, revokeTermination, getTerminations, getChecklistForReason, assessRisk, batchTerminatePreview, batchTerminate, createDraft, updateDraft, submitForApproval, approveTermination, rejectTermination, executeTermination, cancelTermination, getDrafts, getTerminationDetail, getDefaultHandoverItems } from '../services/termination.service'
import { createTermination, createResignation, revokeTermination, getTerminations, getChecklistForReason, assessRisk, batchTerminatePreview, batchTerminate, createDraft, updateDraft, submitForApproval, approveTermination, rejectTermination, executeTermination, cancelTermination, getDrafts, getTerminationDetail, getDefaultHandoverItems, validateTerminationStep } from '../services/termination.service'
import prisma from '../lib/prisma'
import { createEvidence } from '../services/evidence.service'
const router = Router()
@@ -63,6 +64,14 @@ router.post('/', authMiddleware, async (req: AuthRequest, res, next) => {
const data = terminationChecklistSchema.parse(req.body)
const result = await createTermination(req.user!.orgId, req.user!.id, data)
await auditLog(req, 'TERMINATE', 'EMPLOYEE', data.employeeId, { reason: data.reason })
await createEvidence({
orgId: req.user!.orgId,
category: 'TERMINATION',
refId: result.id,
employeeId: data.employeeId,
events: [{ action: '解聘流程启动', timestamp: new Date().toISOString(), ip: req.ip, userAgent: req.headers['user-agent'] }],
createdBy: req.user!.id,
}).catch(() => {})
res.json({ success: true, data: result })
} catch (err) {
next(err)
@@ -246,6 +255,14 @@ router.post('/draft/:id/execute', authMiddleware, async (req: AuthRequest, res,
try {
const result = await executeTermination(req.user!.orgId, req.params.id, req.user!.id)
await auditLog(req, 'EXECUTE_TERMINATION', 'TERMINATION_RECORD', req.params.id, {})
await createEvidence({
orgId: req.user!.orgId,
category: 'TERMINATION',
refId: req.params.id,
employeeId: (result as any)?.employeeId,
events: [{ action: '解聘执行', timestamp: new Date().toISOString(), ip: req.ip, userAgent: req.headers['user-agent'] }],
createdBy: req.user!.id,
}).catch(() => {})
res.json({ success: true, data: result })
} catch (err: any) {
if (err?.code === 'CONFLICT' || err?.code === 'NOT_FOUND') {
@@ -269,4 +286,18 @@ router.post('/draft/:id/cancel', authMiddleware, async (req: AuthRequest, res, n
}
})
// 步骤前置校验
router.get('/draft/:id/validate-step', authMiddleware, async (req: AuthRequest, res, next) => {
try {
const step = parseInt(req.query.step as string) || 1
const result = await validateTerminationStep(req.user!.orgId, req.params.id, step)
res.json({ success: true, data: result })
} catch (err: any) {
if (err?.code === 'NOT_FOUND') {
return res.status(404).json({ success: false, error: { code: err.code, message: err.message } })
}
next(err)
}
})
export default router