| 'use client'; | |
| import { useState } from 'react'; | |
| export default function NodeLibrary() { | |
| const [activeCategory, setActiveCategory] = useState('trigger'); | |
| const [searchTerm, setSearchTerm] = useState(''); | |
| const nodeCategories = [ | |
| { id: 'trigger', name: '触发器', icon: 'ri-play-line' }, | |
| { id: 'action', name: '动作', icon: 'ri-tools-line' }, | |
| { id: 'condition', name: '条件', icon: 'ri-question-line' }, | |
| { id: 'data', name: '数据', icon: 'ri-database-line' }, | |
| { id: 'ai', name: 'AI', icon: 'ri-brain-line' }, | |
| { id: 'integration', name: '集成', icon: 'ri-links-line' } | |
| ]; | |
| const nodes = { | |
| trigger: [ | |
| { id: 'schedule', name: '定时触发', icon: '⏰', desc: '按时间表执行' }, | |
| { id: 'webhook', name: 'Webhook', icon: '🔗', desc: 'HTTP请求触发' }, | |
| { id: 'file-upload', name: '文件上传', icon: '📁', desc: '文件上传时触发' }, | |
| { id: 'email', name: '邮件触发', icon: '📧', desc: '收到邮件时触发' }, | |
| { id: 'database', name: '数据库变更', icon: '🗄️', desc: '数据变更时触发' } | |
| ], | |
| action: [ | |
| { id: 'http-request', name: 'HTTP请求', icon: '🌐', desc: '发送HTTP请求' }, | |
| { id: 'send-email', name: '发送邮件', icon: '📮', desc: '发送电子邮件' }, | |
| { id: 'file-operation', name: '文件操作', icon: '📄', desc: '文件增删改查' }, | |
| { id: 'notification', name: '消息通知', icon: '🔔', desc: '发送通知消息' }, | |
| { id: 'data-transform', name: '数据转换', icon: '🔄', desc: '转换数据格式' } | |
| ], | |
| condition: [ | |
| { id: 'if-else', name: '条件判断', icon: '🤔', desc: '根据条件分支' }, | |
| { id: 'filter', name: '数据过滤', icon: '🔍', desc: '过滤数据项' }, | |
| { id: 'loop', name: '循环处理', icon: '🔁', desc: '循环执行操作' }, | |
| { id: 'delay', name: '延时等待', icon: '⏱️', desc: '延时一段时间' }, | |
| { id: 'merge', name: '数据合并', icon: '🔀', desc: '合并多个数据源' } | |
| ], | |
| data: [ | |
| { id: 'mysql', name: 'MySQL', icon: '🐬', desc: 'MySQL数据库' }, | |
| { id: 'mongodb', name: 'MongoDB', icon: '🍃', desc: 'MongoDB数据库' }, | |
| { id: 'redis', name: 'Redis', icon: '📦', desc: 'Redis缓存' }, | |
| { id: 'csv', name: 'CSV文件', icon: '📊', desc: 'CSV格式数据' }, | |
| { id: 'json', name: 'JSON数据', icon: '📋', desc: 'JSON格式数据' } | |
| ], | |
| ai: [ | |
| { id: 'text-generation', name: '文本生成', icon: '✍️', desc: 'AI文本生成' }, | |
| { id: 'text-analysis', name: '文本分析', icon: '🔎', desc: '文本内容分析' }, | |
| { id: 'translation', name: '文本翻译', icon: '🌍', desc: '多语言翻译' }, | |
| { id: 'sentiment', name: '情感分析', icon: '😊', desc: '情感倾向分析' }, | |
| { id: 'ocr', name: '图片识别', icon: '👁️', desc: 'OCR文字识别' } | |
| ], | |
| integration: [ | |
| { id: 'slack', name: 'Slack', icon: '💬', desc: 'Slack消息推送' }, | |
| { id: 'wechat', name: '微信', icon: '💚', desc: '微信消息推送' }, | |
| { id: 'dingtalk', name: '钉钉', icon: '📱', desc: '钉钉消息推送' }, | |
| { id: 'github', name: 'GitHub', icon: '🐙', desc: 'GitHub集成' }, | |
| { id: 'google-sheets', name: 'Google Sheets', icon: '📈', desc: 'Google表格' } | |
| ] | |
| }; | |
| const handleDragStart = (e: React.DragEvent, node: any) => { | |
| e.dataTransfer.setData('application/json', JSON.stringify({ | |
| type: 'node', | |
| nodeType: node.id, | |
| ...node | |
| })); | |
| }; | |
| const filteredNodes = searchTerm | |
| ? nodes[activeCategory as keyof typeof nodes]?.filter(node => | |
| node.name.toLowerCase().includes(searchTerm.toLowerCase()) || | |
| node.desc.toLowerCase().includes(searchTerm.toLowerCase()) | |
| ) | |
| : nodes[activeCategory as keyof typeof nodes]; | |
| return ( | |
| <div className="h-full flex flex-col"> | |
| {/* 搜索框 */} | |
| <div className="p-4 border-b border-gray-200"> | |
| <div className="relative"> | |
| <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> | |
| <div className="w-4 h-4 flex items-center justify-center"> | |
| <i className="ri-search-line text-gray-400"></i> | |
| </div> | |
| </div> | |
| <input | |
| type="text" | |
| placeholder="搜索节点..." | |
| value={searchTerm} | |
| onChange={(e) => setSearchTerm(e.target.value)} | |
| className="pl-10 pr-4 py-2 w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm" | |
| /> | |
| </div> | |
| </div> | |
| {/* 分类标签 */} | |
| <div className="p-4 border-b border-gray-200"> | |
| <div className="grid grid-cols-2 gap-2"> | |
| {nodeCategories.map((category) => ( | |
| <button | |
| key={category.id} | |
| onClick={() => setActiveCategory(category.id)} | |
| className={`p-2 rounded-lg text-sm font-medium transition-colors cursor-pointer ${ | |
| activeCategory === category.id | |
| ? 'bg-blue-100 text-blue-700' | |
| : 'text-gray-600 hover:bg-gray-100' | |
| }`} | |
| > | |
| <div className="flex items-center space-x-2"> | |
| <div className="w-4 h-4 flex items-center justify-center"> | |
| <i className={category.icon}></i> | |
| </div> | |
| <span>{category.name}</span> | |
| </div> | |
| </button> | |
| ))} | |
| </div> | |
| </div> | |
| {/* 节点列表 */} | |
| <div className="flex-1 overflow-y-auto p-4"> | |
| <div className="space-y-2"> | |
| {filteredNodes?.map((node) => ( | |
| <div | |
| key={node.id} | |
| draggable | |
| onDragStart={(e) => handleDragStart(e, node)} | |
| className="p-3 bg-white border border-gray-200 rounded-lg hover:shadow-md transition-all cursor-move" | |
| > | |
| <div className="flex items-start space-x-3"> | |
| <div className="w-8 h-8 bg-gray-100 rounded-lg flex items-center justify-center flex-shrink-0"> | |
| <span className="text-sm">{node.icon}</span> | |
| </div> | |
| <div className="flex-1 min-w-0"> | |
| <h4 className="font-medium text-gray-900 text-sm">{node.name}</h4> | |
| <p className="text-xs text-gray-500 mt-1 line-clamp-2">{node.desc}</p> | |
| </div> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| {/* 模板快捷入口 */} | |
| <div className="p-4 border-t border-gray-200"> | |
| <button className="w-full p-3 bg-blue-50 text-blue-700 rounded-lg hover:bg-blue-100 transition-colors cursor-pointer"> | |
| <div className="flex items-center justify-center space-x-2"> | |
| <div className="w-4 h-4 flex items-center justify-center"> | |
| <i className="ri-template-line"></i> | |
| </div> | |
| <span className="text-sm font-medium">使用模板</span> | |
| </div> | |
| </button> | |
| </div> | |
| </div> | |
| ); | |
| } |