Files
SBrainCO/client/src/pages/AgentsPage.tsx
T
selfrelease d373fb5f0c feat: 新增智脑决策中心模块(决策中心/AI员工组织/知识资产)
- 新增 DecisionsPage、AgentsPage、KnowledgePage 三个页面
- 新增 DecisionCard、DecisionDrawer、DecisionSummary、KnowledgeCard、LoopHealthBar、AgentCard 组件
- 新增 decision-aggregator 决策聚合逻辑和 agents 数据
- Layout 导航重构:新增智脑决策分组,调整系统配置和经营本体分组
- BossPage 集成决策摘要和闭环健康度,利润机会池支持警告样式
- TasksPage 增强任务展示
- 抽屉式详情统一改为居中弹窗风格
- data.ts 后端调整
- run.md 更新数据库连接说明
- 新增玄谋智脑实际版建设方案文档

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-14 19:34:04 +08:00

112 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* AI 员工组织页面
*
* 展示玄谋智脑的 10 个 AI 员工角色,包括经营参谋长(首席)和 9 个专业员工。
* 今日交付数从对应 API 动态获取。
*/
import { useQuery } from '@tanstack/react-query'
import { BrainCircuit } from 'lucide-react'
import api from '@/lib/api'
import { AgentCard } from '@/components/AgentCard'
import { chiefOfStaff, agents, type AgentConfig } from '@/data/agents'
import { cn } from '@/lib/utils'
export function AgentsPage() {
// 批量获取各 AI 员工的今日交付数
const queries = agents.map((agent, idx) =>
useQuery({
queryKey: ['agent-delivery', agent.code],
queryFn: () => api.get(agent.deliveryApi || ''),
enabled: !!agent.deliveryApi,
staleTime: 5 * 60 * 1000,
}),
)
// 经营参谋长的交付数(利润机会数量)
const chiefQuery = useQuery({
queryKey: ['overview/profit-opportunity', 'chief'],
queryFn: () => api.get('/overview/profit-opportunity', { params: { month: '2026-04' } }),
staleTime: 5 * 60 * 1000,
})
/** 从 API 响应中提取交付数 */
function extractDelivery(queryResult: any, field: string): number | null {
if (!queryResult) return null
const data = (queryResult as any)?.data
if (!data) return null
// 支持 a.b.c 路径
const parts = field.split('.')
let val: any = data
for (const p of parts) {
val = val?.[p]
if (val === undefined) return null
}
const num = typeof val === 'number' ? val : parseInt(String(val), 10)
return isNaN(num) ? null : num
}
const chiefDeliveries = chiefQuery.data
? extractDelivery(chiefQuery.data, 'items.length') ?? (chiefQuery.data as any)?.data?.items?.length ?? 0
: null
return (
<div className="space-y-4">
{/* 页面标题 */}
<div>
<h1 className="flex items-center gap-2 text-xl font-bold">
<BrainCircuit className="h-5 w-5 text-blue-600" />
AI
</h1>
<p className="mt-1 text-sm text-muted-foreground">
AI SOP
</p>
</div>
{/* 经营参谋长(首席卡片) */}
<div className="rounded-lg border-2 border-blue-200 bg-gradient-to-r from-blue-50 to-white p-4">
<div className="flex items-center gap-4">
<div className="flex h-14 w-14 items-center justify-center rounded-xl bg-blue-600">
<BrainCircuit className="h-7 w-7 text-white" />
</div>
<div className="flex-1">
<small className="text-[10px] font-medium text-blue-600">AI CHIEF OF STAFF</small>
<h2 className="text-lg font-bold">{chiefOfStaff.name}</h2>
<p className="text-sm text-muted-foreground">{chiefOfStaff.roleTarget}</p>
</div>
<div className="text-right">
<p className="text-xs text-muted-foreground"></p>
<b className="text-2xl font-bold text-blue-600">{chiefDeliveries ?? '…'}</b>
<p className="text-[10px] text-muted-foreground"></p>
</div>
</div>
</div>
{/* AI 员工网格 */}
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{agents.map((agent, idx) => (
<AgentCard
key={agent.code}
agent={agent}
deliveries={
queries[idx]?.data
? extractDelivery(queries[idx].data, agent.deliveryField || '') ?? 0
: null
}
/>
))}
</div>
{/* 底部说明 */}
<div className="rounded-lg border bg-muted/20 p-3 text-xs text-muted-foreground">
<p>
<b className="text-foreground"></b>
<span className={cn('ml-1 rounded px-1 py-0.5 text-[10px]', 'bg-red-50 text-red-600')}>L1</span> ·
<span className={cn('ml-1 rounded px-1 py-0.5 text-[10px]', 'bg-amber-50 text-amber-600')}>L2</span> ·
<span className={cn('ml-1 rounded px-1 py-0.5 text-[10px]', 'bg-green-50 text-green-600')}>L3</span>
</p>
<p className="mt-1"> AI API </p>
</div>
</div>
)
}