import { useState } from 'react' import { toast } from 'sonner' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { useConfirm } from '../hooks/useConfirm' import { Gift, Plus, Settings as SettingsIcon, X, Users } from 'lucide-react' import { benefitApi, employeeApi } from '../lib/api-services' import PageGuide from '../components/ui/PageGuide' import Card from '../components/ui/Card' import Button from '../components/ui/Button' import { Input, Label } from '../components/ui/Input' import { InlineAlert } from '../components/ui/InlineAlert' import Modal from '../components/ui/Modal' const fmt = (n: number) => (n || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) const BENEFIT_CATEGORIES: Record = { TRANSPORT: { label: '交通补贴', color: 'bg-blue-50 text-blue-700 border border-blue-200' }, MEAL: { label: '餐补', color: 'bg-orange-50 text-orange-700 border border-orange-200' }, HOUSING: { label: '住房补贴', color: 'bg-teal-50 text-teal-700 border border-teal-200' }, COMMUNICATION: { label: '通讯补贴', color: 'bg-purple-50 text-purple-700 border border-purple-200' }, HEALTH_CHECK: { label: '体检', color: 'bg-green-50 text-green-700 border border-green-200' }, HOLIDAY: { label: '节日福利', color: 'bg-red-50 text-red-700 border border-red-200' }, BIRTHDAY: { label: '生日福利', color: 'bg-pink-50 text-pink-700 border border-pink-200' }, OTHER: { label: '其他', color: 'bg-gray-50 text-gray-700 border border-gray-200' }, } const FREQUENCY_LABELS: Record = { MONTHLY: '每月', QUARTERLY: '每季', YEARLY: '每年', ONE_TIME: '一次性', } const DEFAULT_PLAN = { name: '', category: 'TRANSPORT', amount: 0, frequency: 'MONTHLY', taxDeductible: false, description: '', } export default function EmployeeBenefits() { const queryClient = useQueryClient() const confirm = useConfirm() const [tab, setTab] = useState<'plans' | 'summary'>('plans') const [showAddPlan, setShowAddPlan] = useState(false) const [editingPlan, setEditingPlan] = useState(null) const [selectedPlanId, setSelectedPlanId] = useState(null) const [newPlan, setNewPlan] = useState({ ...DEFAULT_PLAN }) const [showEnrollModal, setShowEnrollModal] = useState(false) const [enrollEmployeeIds, setEnrollEmployeeIds] = useState([]) const [enrollEffectiveFrom, setEnrollEffectiveFrom] = useState(new Date().toISOString().slice(0, 7)) const { data: plans = [], isLoading } = useQuery({ queryKey: ['benefit-plans'], queryFn: async () => { return await benefitApi.plans() }, }) const { data: enrollments = [], isLoading: enrollLoading } = useQuery({ queryKey: ['benefit-enrollments', selectedPlanId], queryFn: async () => { if (!selectedPlanId) return [] return await benefitApi.enrollments(selectedPlanId) }, enabled: !!selectedPlanId, }) const { data: employeeSummary = [] } = useQuery({ queryKey: ['benefit-employee-summary'], queryFn: async () => { return await benefitApi.employeeSummary() }, enabled: tab === 'summary', }) const { data: rosterData } = useQuery({ queryKey: ['employees-for-benefit'], queryFn: async () => { return await employeeApi.allLite({ status: 'ACTIVE' }) }, enabled: showEnrollModal, }) const savePlanMutation = useMutation({ mutationFn: async (data: any) => { if (editingPlan) { return benefitApi.savePlan(data, editingPlan.id) as any } return benefitApi.savePlan(data) as any }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['benefit-plans'] }) setShowAddPlan(false) setEditingPlan(null) setNewPlan({ ...DEFAULT_PLAN }) toast.success(editingPlan ? '福利方案已更新' : '福利方案已创建') }, onError: () => toast.error('保存失败'), }) const deletePlanMutation = useMutation({ mutationFn: (id: string) => benefitApi.removePlan(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['benefit-plans'] }) setSelectedPlanId(null) toast.success('福利方案已删除') }, }) const enrollMutation = useMutation({ mutationFn: async (data: { employeeIds: string[]; effectiveFrom: string }) => benefitApi.enroll(selectedPlanId!, data) as any, onSuccess: (res: any) => { queryClient.invalidateQueries({ queryKey: ['benefit-enrollments'] }) queryClient.invalidateQueries({ queryKey: ['benefit-employee-summary'] }) setShowEnrollModal(false) setEnrollEmployeeIds([]) toast.success(`已添加 ${res.data?.enrolled || 0} 名员工`) }, onError: () => toast.error('参保失败'), }) const terminateMutation = useMutation({ mutationFn: (enrollmentId: string) => benefitApi.terminateEnrollment(enrollmentId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['benefit-enrollments'] }) queryClient.invalidateQueries({ queryKey: ['benefit-employee-summary'] }) toast.success('已终止福利') }, }) const handleEdit = (plan: any) => { setEditingPlan(plan) setNewPlan({ ...plan }) setShowAddPlan(true) } const handleSave = () => { if (!newPlan.name?.trim()) { toast.error('请填写方案名称'); return } savePlanMutation.mutate(newPlan) } return (

员工福利

管理员工福利方案、参保人员及福利汇总

员工福利包括交通补贴、餐补、住房补贴、通讯补贴、体检、节日福利等。 管理福利方案、批量参保、查看员工福利汇总。 员工福利是系统增值服务模块,支持按方案/按员工维度管理福利,可关联薪资计算。 {/* Tab 切换 */}
{(['plans', 'summary'] as const).map((t) => ( ))} {tab === 'plans' && ( )}
{/* ========== 福利方案 Tab ========== */} {tab === 'plans' && ( <> {isLoading ? (
加载中...
) : plans.length === 0 ? (
暂无福利方案,点击「新增方案」创建
) : (
{plans.map((plan: any) => { const catCfg = BENEFIT_CATEGORIES[plan.category] || BENEFIT_CATEGORIES.OTHER const isSelected = selectedPlanId === plan.id return (
setSelectedPlanId(isSelected ? null : plan.id)}>
{catCfg.label}

{plan.name}

e.stopPropagation()}>
金额¥{fmt(plan.amount)} / {FREQUENCY_LABELS[plan.frequency] || plan.frequency}
税前扣除{plan.taxDeductible ? '是' : '否'}
参保人数{plan._count?.enrollments || 0} 人
{plan.description &&

{plan.description}

}
) })}
)} {/* 参保人员 */} {selectedPlanId && (

参保人员({enrollments.length}人)

{enrollLoading ? (
加载中...
) : enrollments.length === 0 ? (
暂无参保人员
) : (
{enrollments.map((e: any) => ( ))}
姓名 部门 生效月份 截止月份 状态 操作
{e.name} {e.department} {e.effectiveFrom || '—'} {e.effectiveTo || '至今'} {e.status === 'ACTIVE' ? '有效' : '已终止'} {e.status === 'ACTIVE' && ( )}
)}
)} )} {/* ========== 员工汇总 Tab ========== */} {tab === 'summary' && ( {employeeSummary.length === 0 ? (
暂无员工福利数据
) : (
{employeeSummary.map((e: any) => ( ))}
姓名 部门 福利项 月度合计
{e.name} {e.department}
{e.benefits.map((b: any, i: number) => ( {b.planName} ¥{fmt(b.amount)} ))}
¥{fmt(e.totalMonthly)}
)}
)} {/* 新增/编辑方案 Modal */} {showAddPlan && (
setShowAddPlan(false)}>
e.stopPropagation()}>

{editingPlan ? '编辑福利方案' : '新增福利方案'}

setNewPlan({ ...newPlan, name: e.target.value })} placeholder="如:2024年度交通补贴" />
setNewPlan({ ...newPlan, amount: parseFloat(e.target.value) || 0 })} />
setNewPlan({ ...newPlan, description: e.target.value })} placeholder="适用条件、发放规则等" />
)} {/* 批量参保 Modal */} {showEnrollModal && ( setShowEnrollModal(false)} title="批量参保" size="lg">
选择需要参保的员工,设置生效月份后点击「确认参保」。
setEnrollEffectiveFrom(e.target.value)} className="!w-32" />
{rosterData?.map((emp: any) => ( ))}
0} onChange={(e) => { if (e.target.checked) { setEnrollEmployeeIds(rosterData?.map((emp: any) => emp.id) || []) } else { setEnrollEmployeeIds([]) } }} /> 姓名 部门 状态
{ if (e.target.checked) setEnrollEmployeeIds([...enrollEmployeeIds, emp.id]) else setEnrollEmployeeIds(enrollEmployeeIds.filter((id) => id !== emp.id)) }} /> {emp.name} {emp.department} 在职
已选 {enrollEmployeeIds.length} 人
)}
) }