import { useState } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { Link } from 'react-router-dom' import { DollarSign, Check, TrendingUp, Download } 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' // 金额格式化:保留两位小数 + 千分位 const fmt = (n: number) => (n || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) const portalApi = api.create({ baseURL: '/api/v1/portal' }) portalApi.interceptors.request.use((config: any) => { const token = localStorage.getItem('portalToken') if (token) config.headers.Authorization = `Bearer ${token}` return config }) export default function Payslip() { const queryClient = useQueryClient() const [month, setMonth] = useState(new Date().toISOString().slice(0, 7)) const [showHistory, setShowHistory] = useState(false) const { data, isLoading } = useQuery({ queryKey: ['payslip', month], queryFn: async () => { const res = await portalApi.get('/payslip', { params: { month } }) as any return res.data?.data ?? null }, }) const { data: history } = useQuery({ queryKey: ['payslip-history'], queryFn: async () => { const res = await portalApi.get('/payslip/history') as any return res.data?.data ?? [] }, }) const confirmMutation = useMutation({ mutationFn: (id: string) => portalApi.post(`/payslip/${id}/confirm`), onSuccess: () => queryClient.invalidateQueries({ queryKey: ['payslip'] }), }) const employee = JSON.parse(localStorage.getItem('portalEmployee') || '{}') const handleExport = () => { if (!history || history.length === 0) return const headers = ['月份', '基本工资', '加班费', '津贴', '扣款', '应发合计', '确认状态'] const rows = history.map((p: any) => [ p.month, p.baseSalary, p.overtimePay, p.allowance, p.deduction, p.totalPay, p.confirmedAt ? '已确认' : '未确认', ]) const csv = [headers, ...rows].map(r => r.join(',')).join('\n') const blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8' }) const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = `工资条_${employee.name || '员工'}_${new Date().toISOString().slice(0, 10)}.csv` a.click() URL.revokeObjectURL(url) } const sortedHistory = [...(history || [])].sort((a: any, b: any) => a.month.localeCompare(b.month)) const maxPay = Math.max(...sortedHistory.map((p: any) => Number(p.totalPay) || 0), 1) return (

我的工资条

{employee.name} 我的合同
setMonth(e.target.value)} className="px-3 py-2 rounded-md border border-gray-300 text-sm" />
{showHistory && sortedHistory.length > 0 && (

近 {sortedHistory.length} 个月工资趋势

{sortedHistory.map((p: any) => (
{p.month}
¥{fmt(Number(p.totalPay))}
))}
)} {isLoading ? (
加载中...
) : !data ? ( ) : (
基本工资 ¥{fmt(Number(data.baseSalary))}
{data.overtimePay > 0 && (
加班费 ¥{fmt(Number(data.overtimePay))}
)} {data.allowance > 0 && (
津贴 ¥{fmt(Number(data.allowance))}
)} {data.deduction > 0 && (
扣款 -¥{fmt(Number(data.deduction))}
)}
应发合计 ¥{fmt(Number(data.totalPay))}
{data.confirmedAt ? (
已确认({new Date(data.confirmedAt).toLocaleString()})
) : ( )}
)}
) }