3v324v23's picture
优化
d6daac3
import React, { useState, useRef, useEffect } from 'react';
import { Send, Loader2, MessageSquare, X } from 'lucide-react';
import { useStore } from '../../store/useStore';
import { generateFlow } from '../../lib/ai';
import { cn } from '../../lib/utils';
const ChatBox: React.FC<{ isMobile?: boolean; onClose?: () => void }> = ({ isMobile, onClose }) => {
const [input, setInput] = useState('');
const { messages, addMessage, isLoading, setLoading, nodes, edges, updateFlow } = useStore();
const messagesEndRef = useRef<HTMLDivElement>(null);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
useEffect(() => {
scrollToBottom();
}, [messages]);
const handleSend = async (customInput?: string) => {
const text = customInput || input;
if (!text.trim() || isLoading) return;
const userMsg = {
id: Date.now().toString(),
role: 'user' as const,
content: text,
createdAt: Date.now(),
};
addMessage(userMsg);
if (!customInput) setInput('');
setLoading(true);
try {
const { nodes: newNodes, edges: newEdges, reply } = await generateFlow(text, nodes, edges);
updateFlow(newNodes, newEdges);
addMessage({
id: (Date.now() + 1).toString(),
role: 'assistant',
content: reply,
createdAt: Date.now(),
});
} catch (error: any) {
console.error('Chat error:', error);
let errorMessage = `抱歉,生成工作流时出现错误:${error.message || '未知错误'}`;
if (typeof error.message === 'string' && (error.message.includes('403') || error.message.includes('insufficient'))) {
errorMessage = "API 余额不足或权限受限。已为您展示演示流程。";
// 演示模式:如果真失败了,手动模拟一个成功响应,让用户先看到效果
setTimeout(() => {
const demoNodes = [
{ id: 'd1', type: 'input', position: { x: 250, y: 0 }, data: { label: '收到订单' } },
{ id: 'd2', type: 'default', position: { x: 250, y: 150 }, data: { label: '检查库存' } },
{ id: 'd3', type: 'default', position: { x: 450, y: 300 }, data: { label: '库存不足' } },
{ id: 'd4', type: 'default', position: { x: 50, y: 300 }, data: { label: '扣减库存' } },
{ id: 'd5', type: 'output', position: { x: 250, y: 450 }, data: { label: '发货' } },
];
const demoEdges = [
{ id: 'e1-2', source: 'd1', target: 'd2', animated: true },
{ id: 'e2-3', source: 'd2', target: 'd3', label: '否' },
{ id: 'e2-4', source: 'd2', target: 'd4', label: '是' },
{ id: 'e4-5', source: 'd4', target: 'd5', animated: true },
];
updateFlow(demoNodes, demoEdges);
}, 100);
}
addMessage({
id: (Date.now() + 1).toString(),
role: 'assistant',
content: errorMessage,
createdAt: Date.now(),
});
} finally {
setLoading(false);
}
};
const examples = [
"创建一个简单的请假审批流程",
"帮我做一个电商下单到发货的流程",
"设计一个 AI 图像生成的处理管道",
"增加一个'经理二审'的节点在现有流程中"
];
return (
<div className={cn("flex flex-col h-full bg-white border-l border-gray-200 shadow-xl", isMobile ? "w-full" : "w-96")}>
<div className="p-4 border-b bg-gray-50 flex justify-between items-center">
<div className="flex items-center gap-2">
<div className="w-2 h-2 bg-green-500 rounded-full animate-pulse" />
<div className="flex flex-col">
<h2 className="font-bold text-gray-800 text-sm">对话工作流助手</h2>
<span className="text-[10px] text-gray-400">Powered by Qwen2.5-7B</span>
</div>
</div>
{isMobile && (
<button onClick={onClose} className="p-1 hover:bg-gray-200 rounded"><X className="w-5 h-5" /></button>
)}
</div>
<div className="flex-1 overflow-y-auto p-4 space-y-6 bg-white">
{messages.length === 0 && (
<div className="text-center py-10 space-y-4">
<div className="bg-blue-100 w-16 h-16 rounded-full flex items-center justify-center mx-auto">
<MessageSquare className="w-8 h-8 text-blue-600" />
</div>
<p className="text-gray-500 text-sm">你好!我是你的工作流助手。<br/>试着告诉我你想创建什么样的流程。</p>
<div className="flex flex-wrap gap-2 justify-center">
{examples.map((ex, i) => (
<button
key={i}
onClick={() => handleSend(ex)}
className="text-xs bg-gray-100 hover:bg-blue-100 text-gray-600 hover:text-blue-700 px-3 py-1.5 rounded-full transition-colors border border-gray-200"
>
{ex}
</button>
))}
</div>
</div>
)}
{messages.map((msg) => (
<div
key={msg.id}
className={cn(
"flex flex-col gap-1 max-w-[90%]",
msg.role === 'user' ? "ml-auto items-end" : "mr-auto items-start"
)}
>
<div
className={cn(
"rounded-2xl px-4 py-2 text-sm shadow-sm",
msg.role === 'user'
? "bg-blue-600 text-white rounded-tr-none"
: "bg-gray-100 text-gray-800 rounded-tl-none border border-gray-200"
)}
>
{msg.content}
</div>
<span className="text-[10px] text-gray-400 px-1">
{new Date(msg.createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</span>
</div>
))}
<div ref={messagesEndRef} />
</div>
<div className="p-4 border-t bg-gray-50 space-y-3">
{isLoading && (
<div className="flex items-center gap-2 text-xs text-blue-600 animate-pulse">
<Loader2 className="w-3 h-3 animate-spin" />
<span>AI 正在思考并生成节点...</span>
</div>
)}
<div className="flex gap-2">
<input
className="flex-1 px-4 py-2.5 bg-white border border-gray-200 rounded-xl text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all"
placeholder="描述你想创建的流程..."
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleSend()}
disabled={isLoading}
/>
<button
className="p-2.5 bg-blue-600 text-white rounded-xl hover:bg-blue-700 disabled:opacity-50 shadow-md hover:shadow-lg transition-all active:scale-95"
onClick={() => handleSend()}
disabled={isLoading}
>
<Send className="w-5 h-5" />
</button>
</div>
</div>
</div>
);
};
export default ChatBox;