'use client'; import { useEffect, useMemo, useState } from 'react'; import { Activity, AlertTriangle, Blocks, Gauge, Users } from 'lucide-react'; import { RouteGuard } from '@/components/auth/Guard'; import { api } from '@/lib/api'; import { useAuthStore } from '@/stores/useAuthStore'; import { useMemberStore } from '@/stores/useMemberStore'; interface ManagementOverview { activeVersionCount: number; overdueItemCount: number; blockedItemCount: number; riskCounts: Record; memberLoads: Array<{ memberId: string; openItemCount: number }>; activeVersions: Array<{ id: string; name: string; projectId?: string | null; releaseDate?: string | null }>; overdueItems: Array<{ type: string; id: string; title: string; versionId: string; ownerId?: string | null; dueAt?: string | null }>; blockedItems: Array<{ type: string; id: string; title: string; versionId: string; ownerId?: string | null }>; highRiskVersions: Array<{ versionId: string; riskLevel: string; riskScore: number }>; } const TYPE_LABEL: Record = { version_plan: '计划', dev_task: '开发', test_case: '测试', bug: 'Bug', }; function ManagementPageInner() { const user = useAuthStore((s) => s.user); const role = useMemberStore((s) => s.roles.find((item) => item.id === user?.roleId)); const [overview, setOverview] = useState(null); const [loading, setLoading] = useState(false); const permissions = useMemo(() => role?.permissions ?? [], [role?.permissions]); useEffect(() => { if (!user?.id) return; setLoading(true); const params = new URLSearchParams({ actorId: user.id }); if (permissions.length > 0) params.set('permissions', permissions.join(',')); api.get(`/management/overview?${params.toString()}`) .then(setOverview) .catch(() => setOverview(null)) .finally(() => setLoading(false)); }, [permissions, user?.id]); const riskTotal = overview ? Object.values(overview.riskCounts).reduce((sum, count) => sum + count, 0) : 0; return (

管理驾驶舱

关系表实时聚合,不读取 AppData。

{loading ? '刷新中' : '已同步'}
逾期与阻塞
{[...(overview?.overdueItems ?? []), ...(overview?.blockedItems ?? [])].slice(0, 12).map((item) => (
{TYPE_LABEL[item.type] ?? item.type} {item.title} {item.ownerId ?? '-'}
))} {(!overview || (overview.overdueItems.length + overview.blockedItems.length) === 0) && (
暂无逾期或阻塞事项
)}
成员负载
{(overview?.memberLoads ?? []).slice(0, 10).map((item) => (
{item.memberId} {item.openItemCount}
))} {(!overview || overview.memberLoads.length === 0) && (
暂无负载数据
)}
); } function Metric({ icon: Icon, label, value, tone = 'neutral' }: { icon: any; label: string; value: number; tone?: 'neutral' | 'warn' | 'danger' | 'risk' }) { const toneClass = { neutral: 'text-[var(--accent)] bg-blue-50', warn: 'text-orange-700 bg-orange-50', danger: 'text-red-700 bg-red-50', risk: 'text-purple-700 bg-purple-50', }[tone]; return (
{label}
{value}
); } export default function ManagementPage() { return ( ); }