Files
Agent-n8n/web_frontend/food-order-demo/src/pages/WorkflowPageV2.tsx

546 lines
19 KiB
TypeScript
Raw Normal View History

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;