import { useState, useEffect, useCallback } from 'react'; import { Bell, Webhook, Check, Settings, TestTube, ToggleLeft, ToggleRight, Loader2, AlertCircle, Plus, Trash2 } from 'lucide-react'; import { Card, CardContent } from '@/components/ui/card'; import { notificationApi } from '@/api/notification'; import { ConfirmDialog } from '@/components/ConfirmDialog'; interface NotificationChannel { id: number; name: string; channel_type: string; config: Record; is_enabled: boolean; created_at: string; updated_at: string; } const channelStyle: Record = { webhook_feishu: { bg: 'bg-[rgba(0,150,255,0.15)]', color: 'text-[#00a8ff]', label: '飞书' }, dingtalk: { bg: 'bg-[rgba(0,160,255,0.15)]', color: 'text-[#0089ff]', label: '钉钉' }, email: { bg: 'bg-[rgba(220,50,32,0.15)]', color: 'text-[#ef5350]', label: '邮件' }, }; export default function NotificationsPage() { const [channels, setChannels] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [testingId, setTestingId] = useState(null); const [testResult, setTestResult] = useState<{ id: number; success: boolean; message: string } | null>(null); const [deleteConfirm, setDeleteConfirm] = useState<{msg: string; onConfirm: () => void} | null>(null); const fetchChannels = useCallback(async () => { try { setLoading(true); setError(null); const data = await notificationApi.list(); setChannels(data); } catch (err: any) { setError(err?.response?.data?.detail || '获取通知渠道失败'); } finally { setLoading(false); } }, []); useEffect(() => { fetchChannels(); }, [fetchChannels]); const toggleChannel = useCallback(async (id: number) => { const ch = channels.find(c => c.id === id); if (!ch) return; try { await notificationApi.update(id, { is_enabled: !ch.is_enabled }); setChannels(prev => prev.map(c => c.id === id ? { ...c, is_enabled: !c.is_enabled } : c)); } catch {} }, [channels]); const handleTest = useCallback(async (id: number) => { setTestingId(id); setTestResult(null); try { const res = await notificationApi.test(id); setTestResult({ id, success: res.success, message: res.message }); } catch (err: any) { setTestResult({ id, success: false, message: err?.response?.data?.detail || '测试失败' }); } finally { setTestingId(null); setTimeout(() => setTestResult(null), 5000); } }, []); const handleDelete = useCallback(async (id: number) => { setDeleteConfirm({msg: '确认删除该通知渠道?', onConfirm: async () => { try { await notificationApi.delete(id); setChannels(prev => prev.filter(c => c.id !== id)); } catch {} }}); return; }, []); if (loading) { return (
); } if (error) { return (

{error}

); } return (

通知渠道配置

配置多渠道通知,支持飞书、钉钉、邮件

{channels.length === 0 ? (

暂无通知渠道,请联系管理员配置

) : ( <>
{[ { label: '渠道总数', value: channels.length, icon: Webhook, color: 'text-blue-400', bg: 'bg-blue-400/10' }, { label: '已启用', value: channels.filter(c => c.is_enabled).length, icon: Check, color: 'text-green-400', bg: 'bg-green-400/10' }, { label: '未启用', value: channels.filter(c => !c.is_enabled).length, icon: Bell, color: 'text-gray-400', bg: 'bg-gray-400/10' }, ].map((s, i) => (
{s.value}
{s.label}
))}
{channels.map(channel => { const style = channelStyle[channel.channel_type] || { bg: 'bg-gray-400/10', color: 'text-gray-400', label: channel.channel_type }; return (
{channel.name}
{style.label} {channel.config?.webhook_url && {channel.config.webhook_url.substring(0, 40)}...}
{testResult && testResult.id === channel.id && (
{testResult.message}
)}
); })}
)} { deleteConfirm?.onConfirm(); setDeleteConfirm(null); }} onCancel={() => setDeleteConfirm(null)} />
); }