import { useState } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { Link } from 'react-router-dom' import { Users, AlertTriangle, CheckSquare, DollarSign, ArrowRight, RefreshCw, FileText, Calendar, TrendingUp, Briefcase, Calculator, Wallet, Building2, Receipt, Check, X, Clock, LayoutDashboard, ListTodo, ShieldAlert } from 'lucide-react' import api from '../lib/api' import Card from '../components/ui/Card' import Button from '../components/ui/Button' import EmptyState from '../components/ui/EmptyState' import Pagination from '../components/ui/Pagination' import type { DashboardData } from '../types' function fmt(n: number) { return `¥${(n || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` } const TODO_ICON_CONFIG: Record = { CONTRACT: { icon: FileText, color: 'text-blue-600', bg: 'bg-blue-50' }, SALARY: { icon: DollarSign, color: 'text-amber-600', bg: 'bg-amber-50' }, TERMINATION: { icon: ShieldAlert, color: 'text-red-600', bg: 'bg-red-50' }, MONTHLY: { icon: Calendar, color: 'text-purple-600', bg: 'bg-purple-50' }, } function TodoIcon({ type, level }: { type: string; level: string }) { const config = TODO_ICON_CONFIG[type] || TODO_ICON_CONFIG.MONTHLY const Icon = config.icon return (
) } export default function Dashboard() { const [todoPage, setTodoPage] = useState(1) const [todoPageSize, setTodoPageSize] = useState(10) const queryClient = useQueryClient() const [activeTab, setActiveTab] = useState<'overview' | 'payroll' | 'risk' | 'task'>('overview') const { data, isLoading, refetch, isFetching } = useQuery({ queryKey: ['dashboard'], queryFn: async () => { const res = await api.get('/dashboard') as any return res.data }, }) const resolveMutation = useMutation({ mutationFn: (id: string) => api.patch(`/dashboard/todos/${id}/resolve`), onSuccess: () => queryClient.invalidateQueries({ queryKey: ['dashboard'] }), }) const ignoreMutation = useMutation({ mutationFn: (id: string) => api.patch(`/dashboard/todos/${id}/ignore`), onSuccess: () => queryClient.invalidateQueries({ queryKey: ['dashboard'] }), }) const riskTodos = data?.todos.filter((t) => t.type === 'CONTRACT' || t.type === 'TERMINATION') || [] const taskTodos = data?.todos.filter((t) => t.type === 'MONTHLY' || t.type === 'SALARY') || [] const filteredTodos = activeTab === 'risk' ? riskTodos : taskTodos if (isLoading) { return
加载中...
} if (!data) return null 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: fmt(data.stats.monthlyOvertimePay), icon: DollarSign, color: 'text-safe' }, ] const payroll = data.payrollSummary const activities = data.monthlyActivities const activityItems = [ { label: '新签合同', value: activities?.newContracts ?? 0, icon: FileText, color: 'text-primary' }, { label: '解聘人数', value: activities?.terminations ?? 0, icon: Users, color: 'text-danger' }, { label: '违纪处理', value: activities?.disciplinaryActions ?? 0, icon: AlertTriangle, color: 'text-warning' }, { label: '考勤记录', value: activities?.attendanceRecords ?? 0, icon: Calendar, color: 'text-gray-600' }, { label: '加班时长', value: `${activities?.overtimeHours ?? 0}h`, icon: TrendingUp, color: 'text-safe' }, { label: '加班费', value: fmt(activities?.overtimePay ?? 0), icon: DollarSign, color: 'text-safe' }, ] const payrollItems = [ { label: '基本工资', value: payroll?.baseSalary ?? 0, icon: Wallet, color: 'text-gray-700' }, { label: '加班费', value: payroll?.overtimePay ?? 0, icon: TrendingUp, color: 'text-gray-700' }, { label: '津贴补贴', value: payroll?.allowance ?? 0, icon: Wallet, color: 'text-gray-700' }, { label: '扣款', value: -(payroll?.deduction ?? 0), icon: Wallet, color: 'text-danger' }, ] const deductionItems = [ { label: '个人社保', value: -(payroll?.socialEmp ?? 0) }, { label: '个人公积金', value: -(payroll?.housingEmp ?? 0) }, { label: '个人所得税', value: -(payroll?.estimatedTax ?? 0) }, ] const tabs = [ { key: 'overview' as const, label: '概览', icon: LayoutDashboard, badge: data.stats.todoCount }, { key: 'payroll' as const, label: '薪税', icon: Calculator, badge: payroll?.payslipCount ?? 0 }, { key: 'risk' as const, label: '风险提醒', icon: AlertTriangle, badge: riskTodos.length }, { key: 'task' as const, label: '月度任务', icon: ListTodo, badge: taskTodos.length }, ] return (

{data.greeting}

{payroll?.month} 月度总览

{/* Tab 导航 */}
{tabs.map((tab) => { const Icon = tab.icon return ( ) })}
{/* 概览 Tab */} {activeTab === 'overview' && (
{/* 统计卡片 */}
{stats.map((stat) => { const Icon = stat.icon return (
{stat.value}
{stat.label}
) })}
{/* 本月工作动态 */}

本月工作动态

{activities?.month}
{activityItems.map((item) => { const Icon = item.icon return (
{item.value}
{item.label}
) })}
{/* 风险分布 */}

风险分布

{data.riskDistribution.contract}
合同风险
{data.riskDistribution.salary}
薪资风险
{data.riskDistribution.termination}
解聘风险
)} {/* 薪税 Tab */} {activeTab === 'payroll' && (

本月薪税费用总览

查看明细
{payroll && payroll.payslipCount > 0 ? (
{/* 工资构成 */}
工资构成
{payrollItems.map((item) => { const Icon = item.icon return (
{item.label}
{fmt(item.value)}
) })}
{/* 应发合计 */}
应发合计 {fmt(payroll.totalPay)}
{/* 扣减项 */}
扣减项
{deductionItems.map((item) => (
{item.label} {fmt(item.value)}
))}
{/* 员工实发 */}
员工实发工资 {fmt(payroll.empNetPay)}
{/* 企业成本 */}
企业用工成本
企业社保 {fmt(payroll.socialOrg)}
企业公积金 {fmt(payroll.housingOrg)}
工资总额 {fmt(payroll.totalPay)}
{payroll.severancePay > 0 && (
经济补偿金 {fmt(payroll.severancePay)}
)}
企业总成本 {fmt(payroll.orgTotalCost)}
{/* 工资条确认状态 */}
工资条确认: 已确认 {payroll.confirmedPayslips} 未确认 {payroll.unconfirmedPayslips} 共 {payroll.payslipCount} 条
) : ( )}
)} {/* 风险提醒 Tab */} {(activeTab === 'risk' || activeTab === 'task') && (
{/* 待办列表 */}

{activeTab === 'risk' ? '风险提醒' : '月度任务'}

{filteredTodos.length} 项
{filteredTodos.length === 0 ? ( ) : ( <> { setTodoPageSize(s); setTodoPage(1) }} />
{filteredTodos.slice((todoPage - 1) * todoPageSize, todoPage * todoPageSize).map((todo) => (
{todo.title} {todo.description}
))}
)}
{/* 已办事项 */} {data.resolvedTodos && data.resolvedTodos.length > 0 && (

已办事项

{data.resolvedTodos.length} 项
{data.resolvedTodos.map((todo) => (
{todo.title} {todo.description}
{todo.resolvedAt ? new Date(todo.resolvedAt).toLocaleDateString('zh-CN', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : ''}
))}
)}
)}
) }