详细说明: - 基于文旅订单班框架复制创建food-order-demo项目 - 修改端口配置为4174避免冲突 - 更新LandingPage为青莳轻食主题(绿色健康风格) - 重新定义7个食品行业专业Agent: * 市场研究专家:轻食市场分析、客群画像 * 营养配方师:营养成分配比、低卡高蛋白设计 * 供应链管理专家:有机食材供应、溯源体系 * 品牌策划师:品牌定位、店铺空间布局 * 财务分析师:投资预算、ROI分析 * 运营管理专家:运营流程、品控标准 * 食品创业导师:中央协调、方案整合 - 创建专用启动脚本start.sh - 验证系统可正常运行在端口4174 - 实现代码复用率90%,符合预期目标 影响文件: web_frontend/food-order-demo/ 技术栈: React 18 + TypeScript + Tailwind CSS + Zustand
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; |