546 lines
19 KiB
TypeScript
546 lines
19 KiB
TypeScript
|
|
import { useState, useEffect, useRef } from 'react';
|
|||
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|||
|
|
import { useDemoStore } from '@/store/demoStore';
|
|||
|
|
import { Play, Pause, RotateCcw, Maximize2, Terminal } from 'lucide-react';
|
|||
|
|
|
|||
|
|
// Terminal line type
|
|||
|
|
interface TerminalLine {
|
|||
|
|
id: string;
|
|||
|
|
timestamp: string;
|
|||
|
|
type: 'info' | 'success' | 'warning' | 'error' | 'system' | 'output';
|
|||
|
|
agent?: string;
|
|||
|
|
content: string;
|
|||
|
|
typing?: boolean;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const WorkflowPageV2 = () => {
|
|||
|
|
const { agents, startDemo, pauseDemo, reset, status } = useDemoStore();
|
|||
|
|
const [terminalLines, setTerminalLines] = useState<TerminalLine[]>([]);
|
|||
|
|
const [currentAgentIndex, setCurrentAgentIndex] = useState(0);
|
|||
|
|
const [elapsedTime, setElapsedTime] = useState(0);
|
|||
|
|
const terminalRef = useRef<HTMLDivElement>(null);
|
|||
|
|
const intervalRef = useRef<number | null>(null);
|
|||
|
|
|
|||
|
|
// Agent执行序列 - 更丰富的输出
|
|||
|
|
const agentSequence = [
|
|||
|
|
{
|
|||
|
|
agent: agents[0], // 信息检索
|
|||
|
|
duration: 12000,
|
|||
|
|
output: [
|
|||
|
|
'>>> 初始化信息检索系统...',
|
|||
|
|
'[INFO] 连接数据库: mongodb://data-server:27017',
|
|||
|
|
'[INFO] 加载索引: automotive_industry_2024.idx',
|
|||
|
|
'正在执行查询...',
|
|||
|
|
'SELECT * FROM exhibitions WHERE region="长三角" AND industry="新能源汽车"',
|
|||
|
|
'> 查询耗时: 342ms',
|
|||
|
|
'> 返回记录: 2,847 条',
|
|||
|
|
'',
|
|||
|
|
'=== 数据分析开始 ===',
|
|||
|
|
'[1/5] 产业规模分析...',
|
|||
|
|
' └─ 2023年产值: 3.2万亿元 (↑32% YoY)',
|
|||
|
|
' └─ 企业数量: 5,832家',
|
|||
|
|
' └─ 从业人员: 186万人',
|
|||
|
|
'[2/5] 主要企业扫描...',
|
|||
|
|
' └─ 特斯拉(上海): 年产能75万辆',
|
|||
|
|
' └─ 蔚来汽车: 累计交付38万辆',
|
|||
|
|
' └─ 理想汽车: 月销量突破5万',
|
|||
|
|
' └─ 小鹏汽车: 智能化领先',
|
|||
|
|
'[3/5] 展会历史数据...',
|
|||
|
|
' └─ 2023年展会: 126场',
|
|||
|
|
' └─ 平均规模: 3.2万平方米',
|
|||
|
|
' └─ 平均观众: 4.5万人次',
|
|||
|
|
'[4/5] 竞品分析...',
|
|||
|
|
' └─ 北京车展: 影响力指数92',
|
|||
|
|
' └─ 广州车展: 影响力指数88',
|
|||
|
|
' └─ 成都车展: 影响力指数76',
|
|||
|
|
'[5/5] 趋势预测...',
|
|||
|
|
' └─ 2024年增长预期: 28%',
|
|||
|
|
' └─ 热点技术: 固态电池、自动驾驶L4',
|
|||
|
|
'',
|
|||
|
|
'✓ 市场调研报告已生成 (15.3MB)',
|
|||
|
|
'✓ 数据已同步至共享空间'
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
agent: agents[1], // 设计专家
|
|||
|
|
duration: 12000,
|
|||
|
|
output: [
|
|||
|
|
'>>> 启动设计引擎 v3.2.1...',
|
|||
|
|
'[LOAD] 加载品牌识别系统...',
|
|||
|
|
'[LOAD] 加载空间规划算法...',
|
|||
|
|
'',
|
|||
|
|
'=== 视觉识别设计 ===',
|
|||
|
|
'Analyzing brand requirements...',
|
|||
|
|
'> 主题关键词: [科技, 创新, 绿色, 智能]',
|
|||
|
|
'> 色彩心理学分析中...',
|
|||
|
|
' Primary: #0EA5E9 (Trust & Technology)',
|
|||
|
|
' Secondary: #10B981 (Growth & Sustainability)',
|
|||
|
|
' Accent: #F59E0B (Energy & Innovation)',
|
|||
|
|
'',
|
|||
|
|
'=== 空间布局优化 ===',
|
|||
|
|
'Running layout optimization algorithm...',
|
|||
|
|
'Iteration 1/100: Score=72.3',
|
|||
|
|
'Iteration 50/100: Score=86.7',
|
|||
|
|
'Iteration 100/100: Score=94.2',
|
|||
|
|
'',
|
|||
|
|
'最优布局方案:',
|
|||
|
|
'┌─────────────────────────┐',
|
|||
|
|
'│ A区: 整车展示 (15,000㎡) │',
|
|||
|
|
'│ ├─ 豪华品牌: 5,000㎡ │',
|
|||
|
|
'│ ├─ 新势力: 6,000㎡ │',
|
|||
|
|
'│ └─ 传统转型: 4,000㎡ │',
|
|||
|
|
'├─────────────────────────┤',
|
|||
|
|
'│ B区: 核心零部件 (10,000㎡)│',
|
|||
|
|
'│ ├─ 动力电池: 4,000㎡ │',
|
|||
|
|
'│ ├─ 电机电控: 3,000㎡ │',
|
|||
|
|
'│ └─ 智能座舱: 3,000㎡ │',
|
|||
|
|
'└─────────────────────────┘',
|
|||
|
|
'',
|
|||
|
|
'Generating 3D preview...',
|
|||
|
|
'> Rendering: ████████████ 100%',
|
|||
|
|
'',
|
|||
|
|
'✓ 设计方案已完成',
|
|||
|
|
'✓ 导出文件: design_plan_v1.pdf (48.2MB)'
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
agent: agents[2], // 财务预算
|
|||
|
|
duration: 10000,
|
|||
|
|
output: [
|
|||
|
|
'>>> 财务分析系统 v2.0',
|
|||
|
|
'Loading financial models...',
|
|||
|
|
'',
|
|||
|
|
'=== 成本核算 ===',
|
|||
|
|
'Calculating venue costs...',
|
|||
|
|
'> 场地租赁: ¥3,000,000',
|
|||
|
|
' └─ 展馆: ¥2,500,000',
|
|||
|
|
' └─ 会议室: ¥500,000',
|
|||
|
|
'Calculating construction costs...',
|
|||
|
|
'> 展台搭建: ¥4,500,000',
|
|||
|
|
' └─ 特装展位: ¥3,000,000',
|
|||
|
|
' └─ 标准展位: ¥1,500,000',
|
|||
|
|
'Calculating operational costs...',
|
|||
|
|
'> 运营费用: ¥2,000,000',
|
|||
|
|
' └─ 人员: ¥800,000',
|
|||
|
|
' └─ 营销: ¥1,200,000',
|
|||
|
|
'',
|
|||
|
|
'=== 收入预测 ===',
|
|||
|
|
'Revenue projection model running...',
|
|||
|
|
'> 展位销售: ¥8,500,000',
|
|||
|
|
' └─ 特装: 100个×¥100,000',
|
|||
|
|
' └─ 标准: 500个×¥15,000',
|
|||
|
|
'> 赞助收入: ¥3,000,000',
|
|||
|
|
'> 门票收入: ¥1,500,000',
|
|||
|
|
'',
|
|||
|
|
'Financial metrics:',
|
|||
|
|
'> Total Cost: ¥10,000,000',
|
|||
|
|
'> Total Revenue: ¥13,000,000',
|
|||
|
|
'> Net Profit: ¥3,000,000',
|
|||
|
|
'> ROI: 30%',
|
|||
|
|
'> Break-even: Day 2',
|
|||
|
|
'',
|
|||
|
|
'✓ 财务模型构建完成',
|
|||
|
|
'✓ 风险评估: 低风险'
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
agent: agents[3], // 格式编辑
|
|||
|
|
duration: 8000,
|
|||
|
|
output: [
|
|||
|
|
'>>> 文档处理引擎启动...',
|
|||
|
|
'[FORMAT] 检查文档结构...',
|
|||
|
|
'> 章节数量: 6',
|
|||
|
|
'> 总字数: 12,847',
|
|||
|
|
'> 图表数量: 24',
|
|||
|
|
'',
|
|||
|
|
'=== 格式优化 ===',
|
|||
|
|
'Applying style guide...',
|
|||
|
|
'> 标题层级: H1-H4',
|
|||
|
|
'> 字体规范: 思源黑体',
|
|||
|
|
'> 行间距: 1.5倍',
|
|||
|
|
'',
|
|||
|
|
'Checking consistency...',
|
|||
|
|
'> 术语统一: 187处修正',
|
|||
|
|
'> 数字格式: 92处标准化',
|
|||
|
|
'> 标点符号: 45处优化',
|
|||
|
|
'',
|
|||
|
|
'Generating TOC...',
|
|||
|
|
'> 一级标题: 6个',
|
|||
|
|
'> 二级标题: 18个',
|
|||
|
|
'> 三级标题: 42个',
|
|||
|
|
'',
|
|||
|
|
'✓ 文档格式优化完成'
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
agent: agents[4], // 活动执行
|
|||
|
|
duration: 10000,
|
|||
|
|
output: [
|
|||
|
|
'>>> 执行计划生成器 v1.5',
|
|||
|
|
'',
|
|||
|
|
'=== 时间轴规划 ===',
|
|||
|
|
'D-180: 项目启动',
|
|||
|
|
'D-150: 招展启动',
|
|||
|
|
'D-120: 媒体发布',
|
|||
|
|
'D-90: 展位确认',
|
|||
|
|
'D-60: 搭建方案确定',
|
|||
|
|
'D-30: 现场勘察',
|
|||
|
|
'D-7: 搭建开始',
|
|||
|
|
'D-1: 最终检查',
|
|||
|
|
'D+0: 展会开幕',
|
|||
|
|
'',
|
|||
|
|
'=== 人员部署 ===',
|
|||
|
|
'Total staff: 126人',
|
|||
|
|
'> 管理团队: 6人',
|
|||
|
|
'> 展务团队: 30人',
|
|||
|
|
'> 接待团队: 40人',
|
|||
|
|
'> 技术团队: 20人',
|
|||
|
|
'> 安保团队: 30人',
|
|||
|
|
'',
|
|||
|
|
'=== 物料清单 ===',
|
|||
|
|
'Generating BOM...',
|
|||
|
|
'> 展台物料: 2,847项',
|
|||
|
|
'> 宣传物料: 523项',
|
|||
|
|
'> 办公用品: 189项',
|
|||
|
|
'',
|
|||
|
|
'✓ 执行方案已生成'
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
agent: agents[5], // 营销宣传
|
|||
|
|
duration: 10000,
|
|||
|
|
output: [
|
|||
|
|
'>>> 营销策略引擎 v4.0',
|
|||
|
|
'',
|
|||
|
|
'=== 渠道分析 ===',
|
|||
|
|
'Analyzing marketing channels...',
|
|||
|
|
'> 社交媒体覆盖: 500万+',
|
|||
|
|
'> 行业媒体: 126家',
|
|||
|
|
'> KOL资源: 89位',
|
|||
|
|
'',
|
|||
|
|
'=== 推广计划 ===',
|
|||
|
|
'Phase 1: 预热期 (D-90 to D-60)',
|
|||
|
|
'> 软文发布: 30篇',
|
|||
|
|
'> 视频内容: 15条',
|
|||
|
|
'> 直播预告: 5场',
|
|||
|
|
'',
|
|||
|
|
'Phase 2: 高峰期 (D-60 to D-30)',
|
|||
|
|
'> 广告投放: ¥800,000',
|
|||
|
|
'> 媒体专访: 10次',
|
|||
|
|
'> 路演活动: 8场',
|
|||
|
|
'',
|
|||
|
|
'Phase 3: 冲刺期 (D-30 to D-0)',
|
|||
|
|
'> 倒计时海报: 每日更新',
|
|||
|
|
'> 展商专访: 20家',
|
|||
|
|
'> 观众互动: 10个活动',
|
|||
|
|
'',
|
|||
|
|
'Expected reach:',
|
|||
|
|
'> 曝光量: 2000万+',
|
|||
|
|
'> 互动量: 50万+',
|
|||
|
|
'> 转化率: 2.5%',
|
|||
|
|
'',
|
|||
|
|
'✓ 营销方案制定完成'
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
agent: agents[6], // 中央协调
|
|||
|
|
duration: 8000,
|
|||
|
|
output: [
|
|||
|
|
'>>> 协调控制中心 v3.0',
|
|||
|
|
'',
|
|||
|
|
'=== 数据汇总 ===',
|
|||
|
|
'Collecting agent outputs...',
|
|||
|
|
'> 市场分析: ✓ Complete',
|
|||
|
|
'> 设计方案: ✓ Complete',
|
|||
|
|
'> 财务预算: ✓ Complete',
|
|||
|
|
'> 文档格式: ✓ Complete',
|
|||
|
|
'> 执行计划: ✓ Complete',
|
|||
|
|
'> 营销策略: ✓ Complete',
|
|||
|
|
'',
|
|||
|
|
'=== 协同优化 ===',
|
|||
|
|
'Cross-validating data...',
|
|||
|
|
'> 预算-设计匹配度: 96%',
|
|||
|
|
'> 营销-市场匹配度: 92%',
|
|||
|
|
'> 执行可行性评分: 94%',
|
|||
|
|
'',
|
|||
|
|
'Resolving conflicts...',
|
|||
|
|
'> 时间冲突: 0',
|
|||
|
|
'> 资源冲突: 0',
|
|||
|
|
'> 逻辑冲突: 0',
|
|||
|
|
'',
|
|||
|
|
'=== 最终输出 ===',
|
|||
|
|
'Generating final document...',
|
|||
|
|
'> 合并章节: 6个',
|
|||
|
|
'> 总页数: 68页',
|
|||
|
|
'> 附件数: 12个',
|
|||
|
|
'',
|
|||
|
|
'✓ 策划方案生成完成',
|
|||
|
|
'✓ 所有系统已同步'
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
// 添加终端行
|
|||
|
|
const addTerminalLine = (line: Omit<TerminalLine, 'id' | 'timestamp'>) => {
|
|||
|
|
const now = new Date();
|
|||
|
|
const timestamp = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;
|
|||
|
|
|
|||
|
|
setTerminalLines(prev => [...prev, {
|
|||
|
|
...line,
|
|||
|
|
id: Math.random().toString(36).substr(2, 9),
|
|||
|
|
timestamp
|
|||
|
|
}]);
|
|||
|
|
|
|||
|
|
// 自动滚动到底部
|
|||
|
|
setTimeout(() => {
|
|||
|
|
if (terminalRef.current) {
|
|||
|
|
terminalRef.current.scrollTop = terminalRef.current.scrollHeight;
|
|||
|
|
}
|
|||
|
|
}, 50);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 模拟Agent执行
|
|||
|
|
const simulateAgentExecution = async () => {
|
|||
|
|
if (currentAgentIndex >= agentSequence.length) {
|
|||
|
|
addTerminalLine({
|
|||
|
|
type: 'system',
|
|||
|
|
content: '========== 所有Agent执行完成 =========='
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const current = agentSequence[currentAgentIndex];
|
|||
|
|
const agent = current.agent;
|
|||
|
|
|
|||
|
|
// 开始执行
|
|||
|
|
addTerminalLine({
|
|||
|
|
type: 'system',
|
|||
|
|
content: `[Agent启动] ${agent.name} (${agent.model})`
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 逐行输出
|
|||
|
|
for (let i = 0; i < current.output.length; i++) {
|
|||
|
|
await new Promise(resolve => setTimeout(resolve, current.duration / current.output.length));
|
|||
|
|
|
|||
|
|
if (status !== 'running') return;
|
|||
|
|
|
|||
|
|
addTerminalLine({
|
|||
|
|
type: current.output[i].startsWith('✓') ? 'success' :
|
|||
|
|
current.output[i].startsWith('>') ? 'output' : 'info',
|
|||
|
|
agent: agent.name,
|
|||
|
|
content: current.output[i]
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 执行下一个Agent
|
|||
|
|
setCurrentAgentIndex(prev => prev + 1);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 开始演示
|
|||
|
|
useEffect(() => {
|
|||
|
|
if (status === 'running' && currentAgentIndex < agentSequence.length) {
|
|||
|
|
simulateAgentExecution();
|
|||
|
|
}
|
|||
|
|
}, [status, currentAgentIndex]);
|
|||
|
|
|
|||
|
|
// 计时器
|
|||
|
|
useEffect(() => {
|
|||
|
|
if (status === 'running') {
|
|||
|
|
intervalRef.current = setInterval(() => {
|
|||
|
|
setElapsedTime(prev => prev + 100);
|
|||
|
|
}, 100);
|
|||
|
|
} else {
|
|||
|
|
if (intervalRef.current) {
|
|||
|
|
clearInterval(intervalRef.current);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return () => {
|
|||
|
|
if (intervalRef.current) {
|
|||
|
|
clearInterval(intervalRef.current);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}, [status]);
|
|||
|
|
|
|||
|
|
// 重置
|
|||
|
|
const handleReset = () => {
|
|||
|
|
reset();
|
|||
|
|
setTerminalLines([]);
|
|||
|
|
setCurrentAgentIndex(0);
|
|||
|
|
setElapsedTime(0);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 格式化时间
|
|||
|
|
const formatTime = (ms: number) => {
|
|||
|
|
const seconds = Math.floor(ms / 1000);
|
|||
|
|
const minutes = Math.floor(seconds / 60);
|
|||
|
|
const remainingSeconds = seconds % 60;
|
|||
|
|
return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="min-h-screen bg-gray-50 flex flex-col">
|
|||
|
|
{/* 顶部控制栏 */}
|
|||
|
|
<div className="bg-white border-b border-gray-200 px-6 py-3 flex items-center justify-between">
|
|||
|
|
<div className="flex items-center gap-4">
|
|||
|
|
<h1 className="text-lg font-semibold text-gray-900">AI会展策划系统 - 多Agent协同演示</h1>
|
|||
|
|
<div className="flex items-center gap-2">
|
|||
|
|
<button
|
|||
|
|
onClick={status === 'idle' ? startDemo : pauseDemo}
|
|||
|
|
className="px-3 py-1.5 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors flex items-center gap-1.5"
|
|||
|
|
>
|
|||
|
|
{status === 'idle' || status === 'paused' ? (
|
|||
|
|
<>
|
|||
|
|
<Play className="w-4 h-4" />
|
|||
|
|
<span>开始</span>
|
|||
|
|
</>
|
|||
|
|
) : (
|
|||
|
|
<>
|
|||
|
|
<Pause className="w-4 h-4" />
|
|||
|
|
<span>暂停</span>
|
|||
|
|
</>
|
|||
|
|
)}
|
|||
|
|
</button>
|
|||
|
|
<button
|
|||
|
|
onClick={handleReset}
|
|||
|
|
className="px-3 py-1.5 bg-gray-600 text-white rounded-md hover:bg-gray-700 transition-colors flex items-center gap-1.5"
|
|||
|
|
>
|
|||
|
|
<RotateCcw className="w-4 h-4" />
|
|||
|
|
<span>重置</span>
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div className="text-sm text-gray-600">
|
|||
|
|
运行时间: {formatTime(elapsedTime)} / 03:00
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* 主内容区 */}
|
|||
|
|
<div className="flex-1 flex">
|
|||
|
|
{/* 左侧:n8n工作流 */}
|
|||
|
|
<div className="w-1/2 border-r border-gray-200 bg-white">
|
|||
|
|
<div className="h-full flex flex-col">
|
|||
|
|
<div className="px-4 py-2 border-b border-gray-200 flex items-center justify-between bg-gray-50">
|
|||
|
|
<div className="flex items-center gap-2">
|
|||
|
|
<div className="w-3 h-3 rounded-full bg-green-500"></div>
|
|||
|
|
<span className="text-sm font-medium text-gray-700">工作流可视化</span>
|
|||
|
|
</div>
|
|||
|
|
<button className="p-1 hover:bg-gray-200 rounded transition-colors">
|
|||
|
|
<Maximize2 className="w-4 h-4 text-gray-600" />
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
<div className="flex-1 relative">
|
|||
|
|
<iframe
|
|||
|
|
src="http://localhost:5678/workflow/XbfF8iRI4a69hmYS"
|
|||
|
|
className="w-full h-full border-0"
|
|||
|
|
title="n8n Workflow"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* 右侧:终端执行区 */}
|
|||
|
|
<div className="w-1/2 bg-gray-900">
|
|||
|
|
<div className="h-full flex flex-col">
|
|||
|
|
<div className="px-4 py-2 bg-gray-800 flex items-center justify-between">
|
|||
|
|
<div className="flex items-center gap-2">
|
|||
|
|
<Terminal className="w-4 h-4 text-green-400" />
|
|||
|
|
<span className="text-sm font-mono text-green-400">Agent Execution Terminal</span>
|
|||
|
|
</div>
|
|||
|
|
<div className="flex gap-1">
|
|||
|
|
<div className="w-3 h-3 rounded-full bg-red-500"></div>
|
|||
|
|
<div className="w-3 h-3 rounded-full bg-yellow-500"></div>
|
|||
|
|
<div className="w-3 h-3 rounded-full bg-green-500"></div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div
|
|||
|
|
ref={terminalRef}
|
|||
|
|
className="flex-1 overflow-y-auto p-4 font-mono text-sm"
|
|||
|
|
style={{ backgroundColor: '#0a0a0a' }}
|
|||
|
|
>
|
|||
|
|
<AnimatePresence>
|
|||
|
|
{terminalLines.map((line) => (
|
|||
|
|
<motion.div
|
|||
|
|
key={line.id}
|
|||
|
|
initial={{ opacity: 0, y: 10 }}
|
|||
|
|
animate={{ opacity: 1, y: 0 }}
|
|||
|
|
transition={{ duration: 0.2 }}
|
|||
|
|
className="mb-1"
|
|||
|
|
>
|
|||
|
|
<span className="text-gray-500">[{line.timestamp}]</span>
|
|||
|
|
{line.agent && (
|
|||
|
|
<span className="text-cyan-400 ml-2">{line.agent}:</span>
|
|||
|
|
)}
|
|||
|
|
<span className={`ml-2 ${
|
|||
|
|
line.type === 'success' ? 'text-green-400' :
|
|||
|
|
line.type === 'error' ? 'text-red-400' :
|
|||
|
|
line.type === 'warning' ? 'text-yellow-400' :
|
|||
|
|
line.type === 'system' ? 'text-purple-400' :
|
|||
|
|
line.type === 'output' ? 'text-blue-400' :
|
|||
|
|
'text-gray-300'
|
|||
|
|
}`}>
|
|||
|
|
{line.content}
|
|||
|
|
</span>
|
|||
|
|
</motion.div>
|
|||
|
|
))}
|
|||
|
|
</AnimatePresence>
|
|||
|
|
|
|||
|
|
{/* 光标 */}
|
|||
|
|
{status === 'running' && (
|
|||
|
|
<motion.span
|
|||
|
|
animate={{ opacity: [1, 0] }}
|
|||
|
|
transition={{ duration: 0.5, repeat: Infinity }}
|
|||
|
|
className="inline-block w-2 h-4 bg-green-400"
|
|||
|
|
/>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Agent状态栏 */}
|
|||
|
|
<div className="px-4 py-3 bg-gray-800 border-t border-gray-700">
|
|||
|
|
<div className="grid grid-cols-7 gap-2">
|
|||
|
|
{agents.map((agent, index) => (
|
|||
|
|
<div
|
|||
|
|
key={agent.id}
|
|||
|
|
className={`flex flex-col items-center gap-1 px-2 py-2 rounded-lg transition-all ${
|
|||
|
|
index < currentAgentIndex ? 'bg-green-900/50 border border-green-700' :
|
|||
|
|
index === currentAgentIndex ? 'bg-blue-900 border border-blue-500 animate-pulse' :
|
|||
|
|
'bg-gray-800 border border-gray-700'
|
|||
|
|
}`}
|
|||
|
|
>
|
|||
|
|
<span className={`text-2xl ${
|
|||
|
|
index < currentAgentIndex ? 'animate-none' :
|
|||
|
|
index === currentAgentIndex ? 'animate-bounce' :
|
|||
|
|
''
|
|||
|
|
}`}>{agent.icon}</span>
|
|||
|
|
<span className={`text-xs text-center ${
|
|||
|
|
index < currentAgentIndex ? 'text-green-400' :
|
|||
|
|
index === currentAgentIndex ? 'text-blue-400' :
|
|||
|
|
'text-gray-500'
|
|||
|
|
}`}>{agent.name}</span>
|
|||
|
|
<div className={`w-full h-1 rounded-full mt-1 ${
|
|||
|
|
index < currentAgentIndex ? 'bg-green-500' :
|
|||
|
|
index === currentAgentIndex ? 'bg-blue-500' :
|
|||
|
|
'bg-gray-700'
|
|||
|
|
}`}>
|
|||
|
|
{index === currentAgentIndex && (
|
|||
|
|
<div className="h-full bg-blue-400 rounded-full animate-pulse"
|
|||
|
|
style={{width: '50%'}}></div>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
))}
|
|||
|
|
</div>
|
|||
|
|
<div className="mt-2 text-center text-xs text-gray-400">
|
|||
|
|
总进度: {Math.round((currentAgentIndex / agentSequence.length) * 100)}% |
|
|||
|
|
当前阶段: {currentAgentIndex < agentSequence.length ? agentSequence[currentAgentIndex]?.agent.name : '已完成'}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export default WorkflowPageV2;
|