import { useEffect, useState, useCallback } from 'react' import { apiFetch } from './utils/api' import { RefreshCw, Server, Wifi, WifiOff, Power, PowerOff, ChevronRight, ChevronDown, Hash, Loader2 } from 'lucide-react' interface EndpointRow { name: string connected: boolean guildCount: number status: string } interface Guild { id: string name: string icon?: string description?: string } interface Channel { id: string name: string type?: number } type Tab = 'overview' | 'guilds' export default function QQDashboard() { const [endpoints, setEndpoints] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState('') const [tab, setTab] = useState('overview') const [actionLoading, setActionLoading] = useState>({}) // Guild browser state const [selectedEndpoint, setSelectedEndpoint] = useState('') const [guilds, setGuilds] = useState([]) const [guildsLoading, setGuildsLoading] = useState(false) const [expandedGuild, setExpandedGuild] = useState(null) const [channels, setChannels] = useState>({}) const [channelsLoading, setChannelsLoading] = useState(null) const fetchData = useCallback(async () => { setLoading(true) setError('') try { const res = await apiFetch('/api/qq/endpoints') const json = await res.json() if (json.success) setEndpoints(json.data) else setError(json.error || '获取数据失败') } catch { setError('无法连接服务器') } finally { setLoading(false) } }, []) useEffect(() => { fetchData() }, [fetchData]) const toggleConnect = async (name: string, connected: boolean) => { setActionLoading(prev => ({ ...prev, [name]: true })) try { const endpoint = connected ? 'disconnect' : 'connect' const res = await apiFetch(`/api/qq/endpoints/${encodeURIComponent(name)}/${endpoint}`, { method: 'POST' }) const json = await res.json() if (!json.success) setError(json.error || '操作失败') await fetchData() } catch { setError('操作失败') } finally { setActionLoading(prev => ({ ...prev, [name]: false })) } } const loadGuilds = async (endpointName: string) => { setSelectedEndpoint(endpointName) setGuildsLoading(true) setGuilds([]) setExpandedGuild(null) setChannels({}) try { const res = await apiFetch(`/api/qq/endpoints/${encodeURIComponent(endpointName)}/guilds`) const json = await res.json() if (json.success) setGuilds(json.data) else setError(json.error || '获取频道失败') } catch { setError('获取频道失败') } finally { setGuildsLoading(false) } } const toggleGuild = async (guildId: string) => { if (expandedGuild === guildId) { setExpandedGuild(null); return } setExpandedGuild(guildId) if (channels[guildId]) return setChannelsLoading(guildId) try { const res = await apiFetch(`/api/qq/endpoints/${encodeURIComponent(selectedEndpoint)}/guilds/${encodeURIComponent(guildId)}/channels`) const json = await res.json() if (json.success) setChannels(prev => ({ ...prev, [guildId]: json.data })) } catch { /* ignore */ } finally { setChannelsLoading(null) } } const onlineEndpoints = endpoints.filter((e) => e.connected) return (
{/* Header */}

QQ 官方机器人

{error &&
{error}
} {/* Tabs */}
{/* Overview Tab */} {tab === 'overview' && ( <> {!loading && !endpoints.length && !error && (
暂无 QQ Endpoint 实例
)}
{endpoints.map((endpoint) => (
{endpoint.name} {endpoint.connected ? 在线 : 离线}
频道数{endpoint.guildCount}
{endpoint.connected && ( )}
))}
)} {/* Guilds Tab */} {tab === 'guilds' && (
{/* Endpoint selector */} {onlineEndpoints.length > 0 && (
)} {!onlineEndpoints.length &&
暂无在线 Endpoint
} {guildsLoading &&
} {/* Guild → Channel tree */} {!guildsLoading && guilds.length > 0 && (
{guilds.map((g) => (
{expandedGuild === g.id && (
{channelsLoading === g.id ?
加载中…
: channels[g.id]?.length ?
{channels[g.id].map(ch => (
{ch.name} {ch.id}
))}
:
无子频道
}
)}
))}
)} {!guildsLoading && selectedEndpoint && !guilds.length &&
该机器人未加入任何频道
}
)}
) }