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:
selfrelease
2026-08-17 17:33:48 +08:00
parent 5196d7c80a
commit aea88fd385
10 changed files with 146 additions and 108 deletions
+47 -29
View File
@@ -214,10 +214,10 @@ export default function Dashboard() {
const yearCost = data.yearCostSummary
const stats = [
{ label: '在管员工', value: data.stats.employeeCount, icon: Users, color: 'text-primary' },
{ label: '高风险', value: data.stats.highRiskCount, icon: AlertTriangle, color: 'text-danger' },
{ label: '待办事项', value: data.stats.todoCount, icon: CheckSquare, color: 'text-warning' },
{ label: '工资条', value: payroll?.payslipCount ?? 0, icon: Receipt, color: 'text-safe' },
{ label: '在管员工', value: data.stats.employeeCount, icon: Users, color: 'text-primary', to: '/roster' },
{ label: '高风险', value: data.stats.highRiskCount, icon: AlertTriangle, color: 'text-danger', to: '/risk-center' },
{ label: '待办事项', value: data.stats.todoCount, icon: CheckSquare, color: 'text-warning', to: '', tab: 'risk' as const },
{ label: '工资条', value: payroll?.payslipCount ?? 0, icon: Receipt, color: 'text-safe', to: '/money' },
]
// 当月人力成本
@@ -385,16 +385,13 @@ export default function Dashboard() {
{/* 概览 Tab */}
{activeTab === 'overview' && (
<div className="space-y-3">
<PageGuide>
/
</PageGuide>
{/* 统计卡片 */}
{isSectionVisible('overview_stats') && (
<div className="grid grid-cols-2 md:grid-cols-4 gap-2">
{stats.map((stat) => {
const Icon = stat.icon
return (
<Card key={stat.label} className="flex items-center gap-2.5">
const inner = (
<Card key={stat.label} className="flex items-center gap-2.5 hover:shadow-md transition-shadow cursor-pointer">
<Icon className={`w-6 h-6 ${stat.color}`} />
<div>
<div className="text-base font-bold">{stat.value}</div>
@@ -402,6 +399,10 @@ export default function Dashboard() {
</div>
</Card>
)
if (stat.tab) {
return <div key={stat.label} onClick={() => setActiveTab(stat.tab)}>{inner}</div>
}
return <Link key={stat.label} to={stat.to}>{inner}</Link>
})}
</div>
)}
@@ -428,18 +429,46 @@ export default function Dashboard() {
</ResponsiveContainer>
<div className="absolute inset-0 flex flex-col items-center justify-center px-2 overflow-hidden">
<span className={`text-2xl font-bold leading-none ${complianceScore?.level === 'safe' ? 'text-safe' : complianceScore?.level === 'warning' ? 'text-warning' : 'text-danger'}`}>{complianceScore?.overallScore ?? complianceScore?.totalScore ?? 0}</span>
<span className="text-xs text-gray-500 mt-1 truncate max-w-full">{complianceScore?.levelLabel ?? ''}</span>
<span className="text-xs text-gray-500 mt-1 truncate max-w-full">{complianceScore?.levelLabel ?? complianceScore?.summary ?? ''}</span>
</div>
</div>
{/* 维度评分 */}
<div className="flex-1 grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-2">
{complianceScore?.dimensions?.map((dim: any) => (
<Link key={dim.key} to={dim.key === 'contract' ? '/roster' : dim.key === 'policy' ? '/policies' : dim.key === 'attendance' ? '/attendance' : dim.key === 'salary' ? '/money' : '/social'} className="flex flex-col items-center p-1.5 rounded-lg hover:bg-gray-50 transition-colors">
<span className={`text-lg font-bold ${dim.score >= 85 ? 'text-safe' : dim.score >= 60 ? 'text-warning' : 'text-danger'}`}>{dim.score}</span>
<span className="text-xs text-gray-600">{dim.name}</span>
{dim.todoCount > 0 && <span className="text-[10px] text-gray-400">{dim.todoCount}</span>}
</Link>
))}
<div className="flex-1 grid grid-cols-2 sm:grid-cols-4 lg:grid-cols-7 gap-2">
{complianceScore?.dimensions?.map((dim: any) => {
const dimRouteMap: Record<string, string> = {
contract: '/roster',
salary: '/money',
social: '/social',
attendance: '/attendance',
policy: '/policies',
termination: '/termination',
evidence: '/evidence',
}
return (
<Link key={dim.key} to={dimRouteMap[dim.key] || '/roster'} className="flex flex-col items-center p-1.5 rounded-lg hover:bg-gray-50 transition-colors group relative">
<span className={`text-lg font-bold ${dim.score >= 85 ? 'text-safe' : dim.score >= 60 ? 'text-warning' : 'text-danger'}`}>{dim.score}</span>
<span className="text-xs text-gray-600">{dim.name}</span>
{dim.todoCount > 0 && <span className="text-[10px] text-gray-400">{dim.todoCount}</span>}
{/* hover 显示得分依据 */}
{dim.findings && dim.findings.length > 0 && (
<div className="hidden group-hover:block absolute top-full left-1/2 -translate-x-1/2 z-20 mt-1 w-56 bg-gray-900 text-white text-xs rounded-lg p-2.5 shadow-lg whitespace-normal">
<div className="font-medium mb-1"></div>
{dim.findings.map((f: string, i: number) => (
<div key={i} className="text-gray-300 leading-relaxed mb-0.5"> {f}</div>
))}
{dim.recommendations && dim.recommendations.length > 0 && (
<>
<div className="font-medium mt-1.5 mb-0.5 text-amber-300"></div>
{dim.recommendations.map((r: string, i: number) => (
<div key={i} className="text-gray-300 leading-relaxed mb-0.5"> {r}</div>
))}
</>
)}
</div>
)}
</Link>
)
})}
</div>
</div>
{/* 合同到期预警内联 */}
@@ -575,9 +604,6 @@ export default function Dashboard() {
{/* 人力成本 Tab */}
{activeTab === 'cost' && (
<div className="space-y-3">
<PageGuide>
</PageGuide>
{/* 成本专属指标卡片 */}
{isSectionVisible('cost_metrics') && (
<div className="grid grid-cols-2 md:grid-cols-4 gap-2">
@@ -842,9 +868,6 @@ export default function Dashboard() {
{/* 人员分析 Tab */}
{activeTab === 'workforce' && (
<div className="space-y-3">
<PageGuide>
</PageGuide>
{/* 本月工作动态 */}
{isSectionVisible('workforce_activities') && (
<Card>
@@ -977,11 +1000,6 @@ export default function Dashboard() {
{/* 风险提醒 Tab */}
{(activeTab === 'risk' || activeTab === 'task') && (
<div className="space-y-3">
<PageGuide>
{activeTab === 'risk'
? '风险提醒页汇总合同到期、未签合同、离职风险、退休预警等高风险待办事项。可批量处理或逐项解决,处理完成后自动归档至已办事项。'
: '月度任务页汇总社保办理、发薪批次等周期性待办事项。可批量处理或逐项解决,处理完成后自动归档至已办事项。'}
</PageGuide>
{/* 风险分布饼图(仅风险提醒Tab) */}
{activeTab === 'risk' && isSectionVisible('risk_distribution') && (
<Card>