详细说明: - 修复了@n8n/config包的TypeScript配置错误 - 移除了不存在的jest-expect-message类型引用 - 清理了所有TypeScript构建缓存 - 更新了可行性分析文档,添加了技术实施方案 - 更新了Agent prompt文档 - 添加了会展策划工作流文档 - 包含了n8n-chinese-translation子项目 - 添加了exhibition-demo展示系统框架
104 lines
3.5 KiB
TypeScript
104 lines
3.5 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { motion } from 'framer-motion';
|
|
import { FileText, Image, Loader2 } from 'lucide-react';
|
|
|
|
interface ContentGeneratorProps {
|
|
content?: string;
|
|
images?: string[];
|
|
speed?: number;
|
|
}
|
|
|
|
const ContentGenerator: React.FC<ContentGeneratorProps> = ({
|
|
content = "正在生成会展策划方案内容...",
|
|
images = [],
|
|
speed = 35
|
|
}) => {
|
|
const [displayedContent, setDisplayedContent] = useState('');
|
|
const [currentIndex, setCurrentIndex] = useState(0);
|
|
const [loadingImages, setLoadingImages] = useState<Set<number>>(new Set());
|
|
|
|
useEffect(() => {
|
|
if (currentIndex < content.length) {
|
|
const timeout = setTimeout(() => {
|
|
setDisplayedContent(prev => prev + content[currentIndex]);
|
|
setCurrentIndex(prev => prev + 1);
|
|
}, speed);
|
|
return () => clearTimeout(timeout);
|
|
}
|
|
}, [currentIndex, content, speed]);
|
|
|
|
const handleImageLoad = (index: number) => {
|
|
setLoadingImages(prev => {
|
|
const newSet = new Set(prev);
|
|
newSet.delete(index);
|
|
return newSet;
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
{/* Text Content */}
|
|
<div className="p-4 bg-neutral-50 dark:bg-neutral-900 rounded-lg">
|
|
<div className="flex items-center gap-2 mb-3">
|
|
<FileText className="w-4 h-4 text-blue-500" />
|
|
<span className="text-sm font-medium text-neutral-600 dark:text-neutral-400">
|
|
生成中...
|
|
</span>
|
|
</div>
|
|
|
|
<div className="font-mono text-sm text-neutral-700 dark:text-neutral-300 whitespace-pre-wrap">
|
|
{displayedContent}
|
|
{currentIndex < content.length && (
|
|
<span className="inline-block w-2 h-4 bg-blue-500 animate-pulse ml-1" />
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Images */}
|
|
{images.length > 0 && (
|
|
<div className="space-y-3">
|
|
<div className="flex items-center gap-2">
|
|
<Image className="w-4 h-4 text-purple-500" />
|
|
<span className="text-sm font-medium text-neutral-600 dark:text-neutral-400">
|
|
相关图片
|
|
</span>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-3 gap-3">
|
|
{images.map((src, index) => (
|
|
<motion.div
|
|
key={index}
|
|
initial={{ opacity: 0, scale: 0.8 }}
|
|
animate={{ opacity: 1, scale: 1 }}
|
|
transition={{ delay: index * 0.2 }}
|
|
className="relative aspect-video rounded-lg overflow-hidden bg-neutral-100 dark:bg-neutral-800"
|
|
>
|
|
{loadingImages.has(index) && (
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<Loader2 className="w-6 h-6 animate-spin text-neutral-400" />
|
|
</div>
|
|
)}
|
|
<img
|
|
src={src}
|
|
alt={`Generated content ${index + 1}`}
|
|
className="w-full h-full object-cover"
|
|
onLoad={() => handleImageLoad(index)}
|
|
onLoadStart={() => setLoadingImages(prev => new Set(prev).add(index))}
|
|
/>
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Stats */}
|
|
<div className="flex items-center gap-6 text-xs text-neutral-500 dark:text-neutral-400">
|
|
<span>字符: {displayedContent.length}</span>
|
|
<span>速度: {speed}字/秒</span>
|
|
<span>进度: {Math.round((currentIndex / content.length) * 100)}%</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ContentGenerator; |