feat: 完善AI会展策划多Agent协同演示系统
功能实现: - ✨ 添加需求输入弹窗,支持新能源汽车展模板快速填充 - ✨ 实现7个AI Agent完整执行序列,包含详细的终端输出 - ✨ 添加图片生成过程展示,支持实际图片预览 - ✨ 实现结果查看弹窗,展示生成统计和内容章节 - ✨ 添加图片骨架屏loading动画,优化加载体验 技术优化: - 🎨 实现真实的进度条卡顿效果,模拟实际加载过程 - 🎨 优化终端滚动和内容显示,支持多种输出类型 - 🎨 添加Agent头像显示和执行状态指示 - 🎨 实现图片延迟加载,确保执行流程连贯 - 🎨 简化骨架屏动画,提升真实感 文件修改: - web_frontend/exhibition-demo/src/pages/WorkflowPageV4.tsx - web_frontend/exhibition-demo/src/components/RequirementModal.tsx - web_frontend/exhibition-demo/src/components/ResultModal.tsx - web_frontend/exhibition-demo/public/data/ (添加展会图片资源) 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -45,6 +45,7 @@ const WorkflowPageV4 = () => {
|
||||
const [showRequirementModal, setShowRequirementModal] = useState(false);
|
||||
const [showResultModal, setShowResultModal] = useState(false);
|
||||
const [userRequirement, setUserRequirement] = useState('');
|
||||
const [imageLoadingStates, setImageLoadingStates] = useState<{ [key: string]: boolean }>({});
|
||||
const terminalRef = useRef<HTMLDivElement>(null);
|
||||
const intervalRef = useRef<number | null>(null);
|
||||
const progressLineIdRef = useRef<string | null>(null);
|
||||
@@ -170,25 +171,27 @@ const WorkflowPageV4 = () => {
|
||||
{ type: 'info', content: '🎨 Generating test drive area visualization...' },
|
||||
{ type: 'output', content: 'Prompt: "EV test drive track, outdoor exhibition area"' },
|
||||
{ type: 'progress', content: 'Generating: 试驾小景.jpg', target: 100, stutters: [45, 78] },
|
||||
{ type: 'output', content: '' },
|
||||
{ type: 'output', content: '╔═══════════════════════════════════════════╗' },
|
||||
{ type: 'output', content: '║ IMAGE PREVIEW: 试驾体验区 ║' },
|
||||
{ type: 'output', content: '╠═══════════════════════════════════════════╣' },
|
||||
{ type: 'output', content: '║ ╭──────────────────────────╮ ║' },
|
||||
{ type: 'output', content: '║ │ ═══════════════════════ │ ║' },
|
||||
{ type: 'output', content: '║ │ ║ TEST DRIVE TRACK ║ │ ║' },
|
||||
{ type: 'output', content: '║ │ ║ ╱╲ 🚗 ➜➜➜ ║ │ ║' },
|
||||
{ type: 'output', content: '║ │ ║ ╱ ╲ ╱──────╲ ║ │ ║' },
|
||||
{ type: 'output', content: '║ │ ║ ╱ ╲ ║ │ ║' },
|
||||
{ type: 'output', content: '║ │ ═══════════════════════ │ ║' },
|
||||
{ type: 'output', content: '║ ╰──────────────────────────╯ ║' },
|
||||
{ type: 'output', content: '╚═══════════════════════════════════════════╝' },
|
||||
{ type: 'image',
|
||||
content: '📷 IMAGE PREVIEW: 试驾体验区',
|
||||
imageSrc: '/data/会展策划/image/2.试驾小景.jpg',
|
||||
imageAlt: '试驾体验区实景'
|
||||
},
|
||||
{ type: 'file', content: '✓ Generated: 试驾小景.jpg (2.8MB)' },
|
||||
{ type: 'info', content: '' },
|
||||
{ type: 'info', content: '🎨 Generating brand showcase images...' },
|
||||
{ type: 'progress', content: 'Generating: 小米汽车.jpg', target: 100, stutters: [34, 67] },
|
||||
{ type: 'image',
|
||||
content: '📷 IMAGE PREVIEW: 小米汽车展台',
|
||||
imageSrc: '/data/会展策划/image/3.小米汽车.jpg',
|
||||
imageAlt: '小米汽车展示'
|
||||
},
|
||||
{ type: 'file', content: '✓ Generated: 小米汽车.jpg (1.9MB)' },
|
||||
{ type: 'progress', content: 'Generating: 博览会.jpg', target: 100, stutters: [56, 89] },
|
||||
{ type: 'image',
|
||||
content: '📷 IMAGE PREVIEW: 博览会全景',
|
||||
imageSrc: '/data/会展策划/image/博览会.jpg',
|
||||
imageAlt: '博览会全景图'
|
||||
},
|
||||
{ type: 'file', content: '✓ Generated: 博览会.jpg (3.5MB)' },
|
||||
{ type: 'info', content: '' },
|
||||
{ type: 'info', content: 'Creating exhibition hall 3D layout...' },
|
||||
@@ -492,14 +495,24 @@ const WorkflowPageV4 = () => {
|
||||
const now = new Date();
|
||||
const timestamp = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}.${now.getMilliseconds().toString().padStart(3, '0')}`;
|
||||
|
||||
const lineId = Math.random().toString(36).substr(2, 9);
|
||||
const newLine = {
|
||||
...line,
|
||||
id: Math.random().toString(36).substr(2, 9),
|
||||
id: lineId,
|
||||
timestamp
|
||||
};
|
||||
|
||||
setTerminalLines(prev => [...prev, newLine]);
|
||||
|
||||
// 如果是图片类型,设置加载状态
|
||||
if (line.type === 'image' && line.imageSrc) {
|
||||
setImageLoadingStates(prev => ({ ...prev, [lineId]: true }));
|
||||
// 确保loading动画显示足够时间
|
||||
setTimeout(() => {
|
||||
setImageLoadingStates(prev => ({ ...prev, [lineId]: false }));
|
||||
}, getRandomDelay(2000, 3500));
|
||||
}
|
||||
|
||||
// 自动滚动到底部
|
||||
setTimeout(() => {
|
||||
if (terminalRef.current) {
|
||||
@@ -617,10 +630,16 @@ const WorkflowPageV4 = () => {
|
||||
content: '=' .repeat(60)
|
||||
});
|
||||
|
||||
// 更新store状态为完成
|
||||
const store = useDemoStore.getState();
|
||||
store.setProgress(100);
|
||||
|
||||
// 显示结果弹窗
|
||||
console.log('All agents completed, showing result modal...');
|
||||
setTimeout(() => {
|
||||
console.log('Setting showResultModal to true');
|
||||
setShowResultModal(true);
|
||||
}, 1000);
|
||||
}, 2000);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -641,13 +660,18 @@ const WorkflowPageV4 = () => {
|
||||
});
|
||||
|
||||
// 根据类型设置延迟
|
||||
const delay =
|
||||
let delay =
|
||||
output.type === 'system' ? getRandomDelay(100, 300) :
|
||||
output.type === 'install' ? getRandomDelay(200, 400) :
|
||||
output.type === 'file' ? getRandomDelay(100, 200) :
|
||||
output.type === 'info' && output.content === '' ? 50 :
|
||||
getRandomDelay(30, 100);
|
||||
|
||||
// 如果是图片类型,等待图片加载完成
|
||||
if (output.type === 'image') {
|
||||
delay = getRandomDelay(2500, 3500); // 等待图片加载动画完成
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, delay));
|
||||
}
|
||||
}
|
||||
@@ -667,7 +691,7 @@ const WorkflowPageV4 = () => {
|
||||
|
||||
// 监听Agent变化
|
||||
useEffect(() => {
|
||||
if (status === 'running' && currentAgentIndex >= 0 && currentAgentIndex < agentSequence.length) {
|
||||
if (status === 'running' && currentAgentIndex >= 0) {
|
||||
executeAgent(currentAgentIndex);
|
||||
}
|
||||
}, [currentAgentIndex]);
|
||||
@@ -842,6 +866,12 @@ const WorkflowPageV4 = () => {
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||
background: #555;
|
||||
}
|
||||
.animation-delay-200 {
|
||||
animation-delay: 200ms;
|
||||
}
|
||||
.animation-delay-400 {
|
||||
animation-delay: 400ms;
|
||||
}
|
||||
`}</style>
|
||||
|
||||
<AnimatePresence>
|
||||
@@ -863,16 +893,39 @@ const WorkflowPageV4 = () => {
|
||||
{line.content}
|
||||
</span>
|
||||
{line.imageSrc && (
|
||||
<div className="mt-2 mb-2 inline-block">
|
||||
<img
|
||||
src={line.imageSrc}
|
||||
alt={line.imageAlt || 'Generated image'}
|
||||
className="max-w-md rounded-lg border-2 border-gray-700 shadow-xl"
|
||||
style={{ maxHeight: '300px' }}
|
||||
onError={(e) => {
|
||||
e.currentTarget.style.display = 'none';
|
||||
}}
|
||||
/>
|
||||
<div className="mt-3 mb-3">
|
||||
{imageLoadingStates[line.id] ? (
|
||||
// 简单的骨架屏Loading效果
|
||||
<div className="relative w-96 h-64 bg-gray-800 rounded-lg border border-gray-700 overflow-hidden">
|
||||
{/* 骨架屏脉冲动画 */}
|
||||
<div className="absolute inset-0">
|
||||
<div className="h-full w-full bg-gradient-to-r from-gray-800 via-gray-700 to-gray-800 animate-pulse" />
|
||||
</div>
|
||||
|
||||
{/* 简单的loading指示器 */}
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 bg-gray-500 rounded-full animate-pulse" />
|
||||
<div className="w-2 h-2 bg-gray-500 rounded-full animate-pulse animation-delay-200" />
|
||||
<div className="w-2 h-2 bg-gray-500 rounded-full animate-pulse animation-delay-400" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
// 实际图片(淡入效果)
|
||||
<motion.img
|
||||
src={line.imageSrc}
|
||||
alt={line.imageAlt || 'Generated image'}
|
||||
className="max-w-md rounded-lg border-2 border-gray-700 shadow-xl"
|
||||
style={{ maxHeight: '300px' }}
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
onError={(e) => {
|
||||
e.currentTarget.style.display = 'none';
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user