/** * 统一风险中心 — 汇总展示合同风险、薪酬风险、社保风险、合规风险 * 按风险等级分类,支持快速跳转处理 */ import { useState, useMemo } from 'react' import { useQuery } from '@tanstack/react-query' import { Link } from 'react-router-dom' import { ShieldAlert, AlertTriangle, Clock, Users, FileText, TrendingDown, Calendar, ChevronRight, Filter, } from 'lucide-react' import Card from '../../components/ui/Card' import Button from '../../components/ui/Button' import { InlineAlert } from '../../components/ui/InlineAlert' import api from '../../lib/api' /** 风险等级配置 */ const RISK_LEVELS: Record = { HIGH: { label: '高风险', color: 'text-rose-600', bg: 'bg-rose-50 border-rose-200' }, MEDIUM: { label: '中风险', color: 'text-amber-600', bg: 'bg-amber-50 border-amber-200' }, LOW: { label: '低风险', color: 'text-blue-600', bg: 'bg-blue-50 border-blue-200' }, } /** 风险类型配置 */ const RISK_TYPES: Record = { CONTRACT_EXPIRE: { label: '合同到期', icon: FileText, link: '/roster' }, CONTRACT_UNSIGNED: { label: '未签合同', icon: FileText, link: '/roster' }, PROBATION_EXPIRE: { label: '试用期到期', icon: Clock, link: '/roster' }, SALARY_BELOW_MIN: { label: '工资低于最低标准', icon: TrendingDown, link: '/money' }, SOCIAL_INSURANCE_GAP: { label: '社保断缴', icon: ShieldAlert, link: '/social' }, TERMINATION_RISK: { label: '离职风险', icon: Users, link: '/termination' }, POLICY_UNREAD: { label: '制度未阅读', icon: FileText, link: '/policies' }, } export default function RiskCenter() { const [filterLevel, setFilterLevel] = useState('ALL') const [filterType, setFilterType] = useState('ALL') /** 获取风险列表 */ const { data: risks = [], isLoading } = useQuery({ queryKey: ['risk-center'], queryFn: async () => { const res = await api.get('/compliance/risks') as any return res.data || [] }, }) /** 按级别统计 */ const stats = useMemo(() => { const high = risks.filter((r: any) => r.level === 'HIGH').length const medium = risks.filter((r: any) => r.level === 'MEDIUM').length const low = risks.filter((r: any) => r.level === 'LOW').length return { high, medium, low, total: risks.length } }, [risks]) /** 按类型统计 */ const typeStats = useMemo(() => { const map: Record = {} risks.forEach((r: any) => { map[r.type] = (map[r.type] || 0) + 1 }) return map }, [risks]) /** 筛选后的风险列表 */ const filteredRisks = useMemo(() => { return risks.filter((r: any) => { if (filterLevel !== 'ALL' && r.level !== filterLevel) return false if (filterType !== 'ALL' && r.type !== filterType) return false return true }) }, [risks, filterLevel, filterType]) return (
{/* 页头 */}

统一风险中心

汇总合同、薪酬、社保、合规风险

{/* 风险概览卡片 */}
总风险
{stats.total}
高风险
{stats.high}
中风险
{stats.medium}
低风险
{stats.low}
{/* 高风险告警 */} {stats.high > 0 && ( 当前有 {stats.high} 项高风险事项需要立即处理,请尽快跟进。 )} {/* 风险类型分布 */}

风险类型分布

{Object.entries(RISK_TYPES).map(([key, cfg]) => { const count = typeStats[key] || 0 if (count === 0) return null const Icon = cfg.icon return (
{cfg.label}
{count}
) })} {Object.values(typeStats).every((v) => v === 0) && (
暂无风险项
)}
{/* 筛选器 */}
筛选
{Object.entries(RISK_LEVELS).map(([key, cfg]) => ( ))}
{/* 风险列表 */} {isLoading ? (
加载中...
) : filteredRisks.length === 0 ? (
{risks.length === 0 ? '暂无风险项,一切正常' : '当前筛选条件下无匹配项'}
) : (
{filteredRisks.map((r: any, i: number) => { const levelCfg = RISK_LEVELS[r.level] || RISK_LEVELS.LOW const typeCfg = RISK_TYPES[r.type] || { label: r.type, icon: AlertTriangle, link: '/' } const Icon = typeCfg.icon return (
{levelCfg.label} {typeCfg.label}
{r.message}
{r.employeeName && (
{r.employeeName} · {r.department || ''}
)}
) })}
)}
) }