forked from xiaohei/taiji-pda-v0
fix: 同时显示渠道资源配置和租户资源分配
- 添加 channelAllocatedResources 状态变量存储新接口数据 - 同时调用两个接口: getChannelAllocatedResources 和 getChannelTenantsResources - 渠道资源分配详情: 显示自定义Agent配额、平台Agent配额、模型供应商、模型配额 - 租户资源分配: 保留原有的每个租户资源分配列表显示
This commit is contained in:
+147
-31
@@ -80,7 +80,8 @@ export default function AdminDashboard() {
|
||||
// 新增:渠道管理对话框状态
|
||||
const [isChannelDetailsDialogOpen, setIsChannelDetailsDialogOpen] = useState(false)
|
||||
const [isChannelEditDialogOpen, setIsChannelEditDialogOpen] = useState(false)
|
||||
const [channelTenantsResources, setChannelTenantsResources] = useState<any>(null) // 渠道租户资源分配数据
|
||||
const [channelAllocatedResources, setChannelAllocatedResources] = useState<any>(null) // 渠道资源分配详情(新接口)
|
||||
const [channelTenantsResources, setChannelTenantsResources] = useState<any>(null) // 租户资源分配数据(旧接口)
|
||||
const [channelTenantsResourcesLoading, setChannelTenantsResourcesLoading] = useState(false)
|
||||
const [isViewTenantsDialogOpen, setIsViewTenantsDialogOpen] = useState(false)
|
||||
const [isAddTenantDialogOpen, setIsAddTenantDialogOpen] = useState(false)
|
||||
@@ -1044,6 +1045,7 @@ export default function AdminDashboard() {
|
||||
const handleViewChannelDetails = async (channel: (typeof channels)[0]) => {
|
||||
setSelectedChannel(channel)
|
||||
setIsChannelDetailsDialogOpen(true)
|
||||
setChannelAllocatedResources(null)
|
||||
setChannelTenantsResources(null)
|
||||
setChannelTenantsResourcesLoading(true)
|
||||
|
||||
@@ -1062,14 +1064,21 @@ export default function AdminDashboard() {
|
||||
console.error('Failed to fetch channel details:', error)
|
||||
}
|
||||
|
||||
// 加载渠道资源分配详情(使用新接口)
|
||||
// 同时加载渠道资源分配详情(新接口)和租户资源分配(旧接口)
|
||||
try {
|
||||
const resourcesResponse = await TaijiAPIClient.getChannelAllocatedResources(channel.id)
|
||||
if (resourcesResponse.success && resourcesResponse.data) {
|
||||
setChannelTenantsResources(resourcesResponse.data)
|
||||
const [allocatedResponse, tenantsResponse] = await Promise.all([
|
||||
TaijiAPIClient.getChannelAllocatedResources(channel.id),
|
||||
TaijiAPIClient.getChannelTenantsResources(channel.id)
|
||||
])
|
||||
|
||||
if (allocatedResponse.success && allocatedResponse.data) {
|
||||
setChannelAllocatedResources(allocatedResponse.data)
|
||||
}
|
||||
if (tenantsResponse.success && tenantsResponse.data) {
|
||||
setChannelTenantsResources(tenantsResponse.data)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch channel allocated resources:', error)
|
||||
console.error('Failed to fetch channel resources:', error)
|
||||
} finally {
|
||||
setChannelTenantsResourcesLoading(false)
|
||||
}
|
||||
@@ -2357,32 +2366,32 @@ export default function AdminDashboard() {
|
||||
<div className="text-center py-4 text-muted-foreground">
|
||||
{language === "zh" ? "加载中..." : "Loading..."}
|
||||
</div>
|
||||
) : channelTenantsResources ? (
|
||||
) : channelAllocatedResources ? (
|
||||
<div className="space-y-6">
|
||||
{/* Summary Statistics */}
|
||||
{channelTenantsResources.summary && (
|
||||
{channelAllocatedResources.summary && (
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 p-4 bg-muted/30 rounded-lg">
|
||||
<div className="text-center">
|
||||
<p className="text-lg font-bold text-foreground">
|
||||
{channelTenantsResources.summary.totalCustomAgentCpuQuota || 0}
|
||||
{channelAllocatedResources.summary.totalCustomAgentCpuQuota || 0}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">{language === "zh" ? "自定义Agent CPU配额" : "Custom Agent CPU Quota"}</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-lg font-bold text-foreground">
|
||||
{channelTenantsResources.summary.totalCustomAgentMemoryQuota || 0}GB
|
||||
{channelAllocatedResources.summary.totalCustomAgentMemoryQuota || 0}GB
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">{language === "zh" ? "自定义Agent内存配额" : "Custom Agent Memory Quota"}</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-lg font-bold text-foreground">
|
||||
{channelTenantsResources.summary.totalPlatformAgentPodUsed || 0}/{channelTenantsResources.summary.totalPlatformAgentPodQuota || 0}
|
||||
{channelAllocatedResources.summary.totalPlatformAgentPodUsed || 0}/{channelAllocatedResources.summary.totalPlatformAgentPodQuota || 0}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">{language === "zh" ? "平台Agent Pod使用" : "Platform Agent Pods Used"}</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-lg font-bold text-foreground">
|
||||
{channelTenantsResources.summary.totalModels || 0}
|
||||
{channelAllocatedResources.summary.totalModels || 0}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">{language === "zh" ? "模型总数" : "Total Models"}</p>
|
||||
</div>
|
||||
@@ -2390,7 +2399,7 @@ export default function AdminDashboard() {
|
||||
)}
|
||||
|
||||
{/* Custom Agent Quota */}
|
||||
{channelTenantsResources.customAgentQuota && (
|
||||
{channelAllocatedResources.customAgentQuota && (
|
||||
<div className="p-4 border border-border rounded-lg">
|
||||
<h5 className="font-medium text-foreground mb-3 flex items-center gap-2">
|
||||
<span className="w-2 h-2 rounded-full bg-purple-500"></span>
|
||||
@@ -2400,27 +2409,27 @@ export default function AdminDashboard() {
|
||||
<div>
|
||||
<p className="text-muted-foreground mb-1">CPU {language === "zh" ? "配额" : "Quota"}</p>
|
||||
<p className="font-semibold text-foreground">
|
||||
{channelTenantsResources.customAgentQuota.cpuAllocatedToTenants || 0} / {channelTenantsResources.customAgentQuota.cpuQuota || 0} {language === "zh" ? "核" : "cores"}
|
||||
{channelAllocatedResources.customAgentQuota.cpuAllocatedToTenants || 0} / {channelAllocatedResources.customAgentQuota.cpuQuota || 0} {language === "zh" ? "核" : "cores"}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{language === "zh" ? "可用" : "Available"}: {channelTenantsResources.customAgentQuota.cpuAvailable || 0} {language === "zh" ? "核" : "cores"}
|
||||
{language === "zh" ? "可用" : "Available"}: {channelAllocatedResources.customAgentQuota.cpuAvailable || 0} {language === "zh" ? "核" : "cores"}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-muted-foreground mb-1">{language === "zh" ? "内存配额" : "Memory Quota"}</p>
|
||||
<p className="font-semibold text-foreground">
|
||||
{channelTenantsResources.customAgentQuota.memoryAllocatedToTenants || 0} / {channelTenantsResources.customAgentQuota.memoryQuota || 0} GB
|
||||
{channelAllocatedResources.customAgentQuota.memoryAllocatedToTenants || 0} / {channelAllocatedResources.customAgentQuota.memoryQuota || 0} GB
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{language === "zh" ? "可用" : "Available"}: {channelTenantsResources.customAgentQuota.memoryAvailable || 0} GB
|
||||
{language === "zh" ? "可用" : "Available"}: {channelAllocatedResources.customAgentQuota.memoryAvailable || 0} GB
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="w-16 h-16 rounded-full border-4 border-purple-500/30 flex items-center justify-center">
|
||||
<span className="text-lg font-bold text-purple-500">
|
||||
{channelTenantsResources.customAgentQuota.cpuQuota > 0
|
||||
? Math.round((channelTenantsResources.customAgentQuota.cpuAllocatedToTenants / channelTenantsResources.customAgentQuota.cpuQuota) * 100)
|
||||
{channelAllocatedResources.customAgentQuota.cpuQuota > 0
|
||||
? Math.round((channelAllocatedResources.customAgentQuota.cpuAllocatedToTenants / channelAllocatedResources.customAgentQuota.cpuQuota) * 100)
|
||||
: 0}%
|
||||
</span>
|
||||
</div>
|
||||
@@ -2432,15 +2441,15 @@ export default function AdminDashboard() {
|
||||
)}
|
||||
|
||||
{/* Platform Agents */}
|
||||
{channelTenantsResources.platformAgents && channelTenantsResources.platformAgents.length > 0 && (
|
||||
{channelAllocatedResources.platformAgents && channelAllocatedResources.platformAgents.length > 0 && (
|
||||
<div className="p-4 border border-border rounded-lg">
|
||||
<h5 className="font-medium text-foreground mb-3 flex items-center gap-2">
|
||||
<span className="w-2 h-2 rounded-full bg-blue-500"></span>
|
||||
{language === "zh" ? "平台 Agent 配额" : "Platform Agent Quota"}
|
||||
<Badge variant="secondary" className="text-xs ml-2">{channelTenantsResources.platformAgents.length}</Badge>
|
||||
<Badge variant="secondary" className="text-xs ml-2">{channelAllocatedResources.platformAgents.length}</Badge>
|
||||
</h5>
|
||||
<div className="space-y-3 max-h-[200px] overflow-y-auto">
|
||||
{channelTenantsResources.platformAgents.map((agent: any, idx: number) => (
|
||||
{channelAllocatedResources.platformAgents.map((agent: any, idx: number) => (
|
||||
<div key={agent.templateName || idx} className="flex items-center justify-between p-3 bg-background rounded-lg">
|
||||
<div>
|
||||
<p className="font-medium text-foreground">{agent.templateDisplayName || agent.templateName}</p>
|
||||
@@ -2463,15 +2472,15 @@ export default function AdminDashboard() {
|
||||
)}
|
||||
|
||||
{/* Model Providers */}
|
||||
{channelTenantsResources.modelProviders && channelTenantsResources.modelProviders.length > 0 && (
|
||||
{channelAllocatedResources.modelProviders && channelAllocatedResources.modelProviders.length > 0 && (
|
||||
<div className="p-4 border border-border rounded-lg">
|
||||
<h5 className="font-medium text-foreground mb-3 flex items-center gap-2">
|
||||
<span className="w-2 h-2 rounded-full bg-orange-500"></span>
|
||||
{language === "zh" ? "模型供应商" : "Model Providers"}
|
||||
<Badge variant="secondary" className="text-xs ml-2">{channelTenantsResources.modelProviders.length}</Badge>
|
||||
<Badge variant="secondary" className="text-xs ml-2">{channelAllocatedResources.modelProviders.length}</Badge>
|
||||
</h5>
|
||||
<div className="space-y-3 max-h-[200px] overflow-y-auto">
|
||||
{channelTenantsResources.modelProviders.map((provider: any, idx: number) => (
|
||||
{channelAllocatedResources.modelProviders.map((provider: any, idx: number) => (
|
||||
<div key={provider.providerId || idx} className="p-3 bg-background rounded-lg">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -2501,12 +2510,12 @@ export default function AdminDashboard() {
|
||||
)}
|
||||
|
||||
{/* Models */}
|
||||
{channelTenantsResources.models && channelTenantsResources.models.length > 0 && (
|
||||
{channelAllocatedResources.models && channelAllocatedResources.models.length > 0 && (
|
||||
<div className="p-4 border border-border rounded-lg">
|
||||
<h5 className="font-medium text-foreground mb-3 flex items-center gap-2">
|
||||
<span className="w-2 h-2 rounded-full bg-green-500"></span>
|
||||
{language === "zh" ? "模型配额" : "Model Quota"}
|
||||
<Badge variant="secondary" className="text-xs ml-2">{channelTenantsResources.models.length}</Badge>
|
||||
<Badge variant="secondary" className="text-xs ml-2">{channelAllocatedResources.models.length}</Badge>
|
||||
</h5>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
@@ -2520,7 +2529,7 @@ export default function AdminDashboard() {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{channelTenantsResources.models.map((model: any, idx: number) => (
|
||||
{channelAllocatedResources.models.map((model: any, idx: number) => (
|
||||
<tr key={model.modelName || idx} className="border-b border-border/50 last:border-0">
|
||||
<td className="py-2 font-medium text-foreground">{model.modelName}</td>
|
||||
<td className="py-2 text-muted-foreground">{model.providerName || '-'}</td>
|
||||
@@ -2536,9 +2545,9 @@ export default function AdminDashboard() {
|
||||
)}
|
||||
|
||||
{/* No resources allocated */}
|
||||
{!channelTenantsResources.customAgentQuota &&
|
||||
(!channelTenantsResources.platformAgents || channelTenantsResources.platformAgents.length === 0) &&
|
||||
(!channelTenantsResources.models || channelTenantsResources.models.length === 0) && (
|
||||
{!channelAllocatedResources.customAgentQuota &&
|
||||
(!channelAllocatedResources.platformAgents || channelAllocatedResources.platformAgents.length === 0) &&
|
||||
(!channelAllocatedResources.models || channelAllocatedResources.models.length === 0) && (
|
||||
<div className="text-center py-8 text-muted-foreground">
|
||||
{language === "zh" ? "暂无资源分配" : "No resources allocated"}
|
||||
</div>
|
||||
@@ -2550,6 +2559,113 @@ export default function AdminDashboard() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Tenant Resource Allocation */}
|
||||
<div className="border-t border-border pt-6">
|
||||
<h4 className="text-base font-semibold text-foreground mb-4">
|
||||
{language === "zh" ? "租户资源分配" : "Tenant Resource Allocation"}
|
||||
</h4>
|
||||
|
||||
{channelTenantsResourcesLoading ? (
|
||||
<div className="text-center py-4 text-muted-foreground">
|
||||
{language === "zh" ? "加载中..." : "Loading..."}
|
||||
</div>
|
||||
) : channelTenantsResources ? (
|
||||
<div className="space-y-4">
|
||||
{/* Tenant Summary */}
|
||||
{channelTenantsResources.summary && (
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 p-3 bg-muted/30 rounded-lg">
|
||||
<div className="text-center">
|
||||
<p className="text-lg font-bold text-foreground">{channelTenantsResources.summary.totalTenants || 0}</p>
|
||||
<p className="text-xs text-muted-foreground">{language === "zh" ? "租户总数" : "Total Tenants"}</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-lg font-bold text-foreground">{channelTenantsResources.summary.tenantsWithCustomAgents || 0}</p>
|
||||
<p className="text-xs text-muted-foreground">{language === "zh" ? "有自定义Agent" : "With Custom Agents"}</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-lg font-bold text-foreground">{channelTenantsResources.summary.tenantsWithPlatformAgents || 0}</p>
|
||||
<p className="text-xs text-muted-foreground">{language === "zh" ? "有平台Agent" : "With Platform Agents"}</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="text-lg font-bold text-foreground">{channelTenantsResources.summary.tenantsWithModels || 0}</p>
|
||||
<p className="text-xs text-muted-foreground">{language === "zh" ? "有模型配额" : "With Models"}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Tenants List */}
|
||||
{channelTenantsResources.tenants && channelTenantsResources.tenants.length > 0 ? (
|
||||
<div className="space-y-3 max-h-[300px] overflow-y-auto">
|
||||
{channelTenantsResources.tenants.map((tenant: any) => (
|
||||
<div key={tenant.tenantId} className="p-3 rounded-lg border border-border bg-background">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium text-foreground">{tenant.tenantName}</span>
|
||||
<Badge variant={tenant.status === "active" ? "default" : "secondary"} className="text-xs">
|
||||
{tenant.status === "active" ? (language === "zh" ? "活跃" : "Active") : (language === "zh" ? "已暂停" : "Suspended")}
|
||||
</Badge>
|
||||
</div>
|
||||
<span className="text-xs text-muted-foreground">{tenant.tenantEmail}</span>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 text-sm">
|
||||
{/* Custom Agent Quota */}
|
||||
{tenant.customAgentQuota && (
|
||||
<div className="flex items-center gap-4 text-muted-foreground">
|
||||
<span className="text-purple-500">●</span>
|
||||
<span>{language === "zh" ? "自定义Agent" : "Custom Agent"}:</span>
|
||||
<span>CPU {tenant.customAgentQuota.cpuUsed}/{tenant.customAgentQuota.cpuQuota}</span>
|
||||
<span>{language === "zh" ? "内存" : "Mem"} {tenant.customAgentQuota.memoryUsed}/{tenant.customAgentQuota.memoryQuota}GB</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Platform Agents */}
|
||||
{tenant.platformAgents && tenant.platformAgents.length > 0 && (
|
||||
<div className="flex items-center gap-4 text-muted-foreground flex-wrap">
|
||||
<span className="text-blue-500">●</span>
|
||||
<span>{language === "zh" ? "平台Agent" : "Platform Agent"}:</span>
|
||||
{tenant.platformAgents.map((agent: any, idx: number) => (
|
||||
<span key={idx}>{agent.templateName} ({agent.podUsed}/{agent.podQuota})</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Models */}
|
||||
{tenant.models && tenant.models.length > 0 && (
|
||||
<div className="flex items-center gap-4 text-muted-foreground flex-wrap">
|
||||
<span className="text-green-500">●</span>
|
||||
<span>{language === "zh" ? "模型" : "Models"}:</span>
|
||||
{tenant.models.map((model: any, idx: number) => (
|
||||
<span key={idx}>{model.modelName}</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* No resources */}
|
||||
{!tenant.customAgentQuota &&
|
||||
(!tenant.platformAgents || tenant.platformAgents.length === 0) &&
|
||||
(!tenant.models || tenant.models.length === 0) && (
|
||||
<div className="text-muted-foreground text-xs">
|
||||
{language === "zh" ? "暂无资源分配" : "No resources allocated"}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center py-4 text-muted-foreground">
|
||||
{language === "zh" ? "暂无租户" : "No tenants"}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center py-4 text-muted-foreground">
|
||||
{language === "zh" ? "暂无租户数据" : "No tenant data available"}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
|
||||
Reference in New Issue
Block a user