feat: 工作台合规评分优化、面包屑补全、花名册斑马纹
- 合规评分维度拆分:薪酬社保拆为薪酬工资+社保公积金两个独立维度 - 合同管理评分纳入待签署合同(signDate为null)扣分 - 维度评分hover显示得分依据findings和建议recommendations - 概览统计卡片支持点击跳转(在管员工/高风险/待办/工资条) - 面包屑补全10个缺失路由,统一分组名与侧边栏一致 - 移除子Tab中重复的PageGuide组件 - 花名册列表添加斑马纹隔行变色 - 薪酬结构模版表格列宽优化,计算公式列截断+title显示 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
@@ -1441,6 +1441,7 @@ export async function getComplianceScore(orgId: string) {
|
||||
const [
|
||||
totalEmployees,
|
||||
unsignedContracts,
|
||||
pendingSignContracts,
|
||||
expiringContracts,
|
||||
pendingRisks,
|
||||
highRisks,
|
||||
@@ -1456,6 +1457,14 @@ export async function getComplianceScore(orgId: string) {
|
||||
prisma.employee.count({ where: { orgId, status: 'ACTIVE' } }),
|
||||
// 未签合同:完全无合同记录的在职员工
|
||||
prisma.employee.count({ where: { orgId, status: 'ACTIVE', contracts: { none: {} } } }),
|
||||
// 待签署:有合同记录但 signDate 为 null(合同已创建但未完成签署)
|
||||
prisma.employee.count({
|
||||
where: {
|
||||
orgId,
|
||||
status: 'ACTIVE',
|
||||
contracts: { some: { signDate: null } },
|
||||
},
|
||||
}),
|
||||
prisma.laborContract.count({
|
||||
where: {
|
||||
orgId,
|
||||
@@ -1483,8 +1492,8 @@ export async function getComplianceScore(orgId: string) {
|
||||
}),
|
||||
])
|
||||
|
||||
// 5 维度评分计算
|
||||
const contractScore = Math.max(0, 100 - unsignedContracts * 15 - expiringContracts * 5)
|
||||
// 5 维度评分计算(未签合同 + 待签署都扣分)
|
||||
const contractScore = Math.max(0, 100 - unsignedContracts * 15 - pendingSignContracts * 10 - expiringContracts * 5)
|
||||
const policyScore = Math.max(0, 100 - policiesWithoutPublish * 12)
|
||||
const attendanceScore = Math.max(0, 100 - attendanceUnconfirmed * 8)
|
||||
const salaryScore = totalPayslips > 0
|
||||
@@ -1493,7 +1502,7 @@ export async function getComplianceScore(orgId: string) {
|
||||
const socialScore = (socialConfig ? 50 : 0) + (housingConfig ? 50 : 0)
|
||||
|
||||
const dimensions = [
|
||||
{ key: 'contract', name: '合同管理', score: contractScore, todoCount: unsignedContracts + expiringContracts },
|
||||
{ key: 'contract', name: '合同管理', score: contractScore, todoCount: unsignedContracts + pendingSignContracts + expiringContracts },
|
||||
{ key: 'policy', name: '规章制度', score: policyScore, todoCount: policiesWithoutPublish },
|
||||
{ key: 'attendance', name: '考勤工时', score: attendanceScore, todoCount: attendanceUnconfirmed },
|
||||
{ key: 'salary', name: '薪酬工资', score: salaryScore, todoCount: unconfirmedPayslips },
|
||||
@@ -1613,6 +1622,7 @@ export async function getHealthCheck(orgId: string) {
|
||||
const [
|
||||
totalEmployees,
|
||||
unsignedContracts,
|
||||
pendingSignContracts,
|
||||
expiringContracts,
|
||||
policiesWithoutPublish,
|
||||
totalPolicies,
|
||||
@@ -1632,6 +1642,14 @@ export async function getHealthCheck(orgId: string) {
|
||||
prisma.employee.count({ where: { orgId, status: 'ACTIVE' } }),
|
||||
// 未签合同:完全无合同记录的在职员工
|
||||
prisma.employee.count({ where: { orgId, status: 'ACTIVE', contracts: { none: {} } } }),
|
||||
// 待签署:有合同记录但 signDate 为 null(合同已创建但未完成签署)
|
||||
prisma.employee.count({
|
||||
where: {
|
||||
orgId,
|
||||
status: 'ACTIVE',
|
||||
contracts: { some: { signDate: null } },
|
||||
},
|
||||
}),
|
||||
prisma.laborContract.count({
|
||||
where: {
|
||||
orgId,
|
||||
@@ -1665,37 +1683,49 @@ export async function getHealthCheck(orgId: string) {
|
||||
{
|
||||
key: 'contract',
|
||||
name: '合同管理',
|
||||
score: Math.max(0, 100 - unsignedContracts * 15 - expiringContracts * 5),
|
||||
score: Math.max(0, 100 - unsignedContracts * 15 - pendingSignContracts * 10 - expiringContracts * 5),
|
||||
findings: [
|
||||
unsignedContracts > 0 ? `${unsignedContracts} 名员工未签书面合同,存在双倍工资风险` : '全员已签书面合同',
|
||||
unsignedContracts > 0 ? `${unsignedContracts} 名员工未签书面合同,存在双倍工资风险` : '全员已有合同记录',
|
||||
pendingSignContracts > 0 ? `${pendingSignContracts} 份合同待签署(已创建未完成签署)` : '',
|
||||
expiringContracts > 0 ? `${expiringContracts} 份合同 30 天内到期,需及时续签` : '无即将到期合同',
|
||||
].filter(Boolean),
|
||||
recommendations: [
|
||||
unsignedContracts > 0 ? '立即补签书面合同,超过1个月未签需支付双倍工资' : '',
|
||||
pendingSignContracts > 0 ? '尽快完成待签署合同的签署流程,未签署合同在仲裁中可能无效' : '',
|
||||
expiringContracts > 0 ? '提前30天发送续签意向书,保留协商证据' : '',
|
||||
].filter(Boolean),
|
||||
},
|
||||
{
|
||||
key: 'salary_social',
|
||||
name: '薪酬社保',
|
||||
key: 'salary',
|
||||
name: '薪酬工资',
|
||||
score: totalPayslips > 0
|
||||
? Math.max(0, 100 - Math.round((unconfirmedPayslips / totalPayslips) * 100))
|
||||
: 100,
|
||||
findings: [
|
||||
totalPayslips > 0
|
||||
? `${unconfirmedPayslips}/${totalPayslips} 张工资条未确认`
|
||||
: '暂无工资条记录',
|
||||
].filter(Boolean),
|
||||
recommendations: [
|
||||
unconfirmedPayslips > 0 ? '提醒员工及时确认工资条,保留薪酬合规证据' : '',
|
||||
].filter(Boolean),
|
||||
},
|
||||
{
|
||||
key: 'social',
|
||||
name: '社保公积金',
|
||||
score: Math.round(
|
||||
(socialConfig ? 40 : 0) +
|
||||
(housingConfig ? 20 : 0) +
|
||||
(totalPayslips > 0 ? Math.max(0, 20 - Math.round((unconfirmedPayslips / totalPayslips) * 20)) : 20) +
|
||||
(socialConfig ? 50 : 0) +
|
||||
(housingConfig ? 30 : 0) +
|
||||
(employeesNoSocial === 0 ? 20 : Math.max(0, 20 - employeesNoSocial * 2))
|
||||
),
|
||||
findings: [
|
||||
socialConfig ? '社保配置已设置' : '未配置社保比例,建议尽快设置',
|
||||
housingConfig ? '公积金配置已设置' : '未配置公积金比例',
|
||||
totalPayslips > 0
|
||||
? `${unconfirmedPayslips}/${totalPayslips} 张工资条未确认`
|
||||
: '暂无工资条记录',
|
||||
employeesNoSocial > 0 ? `${employeesNoSocial} 名员工社保记录异常` : '社保缴纳记录正常',
|
||||
].filter(Boolean),
|
||||
recommendations: [
|
||||
!socialConfig ? '前往社保管理页面设置企业/个人缴纳比例' : '',
|
||||
!housingConfig ? '根据当地政策配置公积金比例' : '',
|
||||
unconfirmedPayslips > 0 ? '提醒员工及时确认工资条,保留薪酬合规证据' : '',
|
||||
].filter(Boolean),
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user