| 'use client'; | |
| import { useState } from 'react'; | |
| interface WorkflowSettingsProps { | |
| workflowData: any; | |
| selectedNode: any; | |
| onUpdateWorkflow: (data: any) => void; | |
| } | |
| export default function WorkflowSettings({ workflowData, selectedNode, onUpdateWorkflow }: WorkflowSettingsProps) { | |
| const [activeTab, setActiveTab] = useState('general'); | |
| const tabs = [ | |
| { id: 'general', name: '基本设置', icon: 'ri-settings-line' }, | |
| { id: 'triggers', name: '触发器', icon: 'ri-play-line' }, | |
| { id: 'variables', name: '变量', icon: 'ri-code-line' }, | |
| { id: 'security', name: '权限', icon: 'ri-shield-line' } | |
| ]; | |
| const categories = [ | |
| { value: 'data-processing', label: '数据处理' }, | |
| { value: 'content-creation', label: '内容创作' }, | |
| { value: 'automation', label: '任务自动化' }, | |
| { value: 'integration', label: '系统集成' } | |
| ]; | |
| const handleInputChange = (field: string, value: any) => { | |
| onUpdateWorkflow({ ...workflowData, [field]: value }); | |
| }; | |
| const renderGeneralSettings = () => ( | |
| <div className="space-y-6"> | |
| <div> | |
| <label className="block text-sm font-medium text-gray-700 mb-2">工作流名称</label> | |
| <input | |
| type="text" | |
| placeholder="输入工作流名称" | |
| value={workflowData.name} | |
| onChange={(e) => handleInputChange('name', e.target.value)} | |
| className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm" | |
| /> | |
| </div> | |
| <div> | |
| <label className="block text-sm font-medium text-gray-700 mb-2">描述</label> | |
| <textarea | |
| placeholder="描述工作流的功能和用途" | |
| rows={3} | |
| maxLength={500} | |
| value={workflowData.description} | |
| onChange={(e) => handleInputChange('description', e.target.value)} | |
| className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm resize-none" | |
| /> | |
| <div className="text-xs text-gray-500 mt-1">{workflowData.description.length}/500</div> | |
| </div> | |
| <div> | |
| <label className="block text-sm font-medium text-gray-700 mb-2">分类</label> | |
| <select | |
| value={workflowData.category} | |
| onChange={(e) => handleInputChange('category', e.target.value)} | |
| className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm pr-8" | |
| > | |
| {categories.map((category) => ( | |
| <option key={category.value} value={category.value}> | |
| {category.label} | |
| </option> | |
| ))} | |
| </select> | |
| </div> | |
| <div> | |
| <label className="block text-sm font-medium text-gray-700 mb-3">执行设置</label> | |
| <div className="space-y-3"> | |
| <div className="flex items-center justify-between"> | |
| <span className="text-sm text-gray-700">并行执行</span> | |
| <div className="w-10 h-6 bg-gray-300 rounded-full relative cursor-pointer"> | |
| <div className="w-4 h-4 bg-white rounded-full absolute left-1 top-1"></div> | |
| </div> | |
| </div> | |
| <div className="flex items-center justify-between"> | |
| <span className="text-sm text-gray-700">错误时停止</span> | |
| <div className="w-10 h-6 bg-blue-600 rounded-full relative cursor-pointer"> | |
| <div className="w-4 h-4 bg-white rounded-full absolute right-1 top-1"></div> | |
| </div> | |
| </div> | |
| <div className="flex items-center justify-between"> | |
| <span className="text-sm text-gray-700">保存执行日志</span> | |
| <div className="w-10 h-6 bg-blue-600 rounded-full relative cursor-pointer"> | |
| <div className="w-4 h-4 bg-white rounded-full absolute right-1 top-1"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| const renderTriggerSettings = () => ( | |
| <div className="space-y-6"> | |
| <div> | |
| <h4 className="font-medium text-gray-900 mb-3">触发条件</h4> | |
| <div className="space-y-3"> | |
| <div className="p-3 border border-gray-200 rounded-lg"> | |
| <div className="flex items-center justify-between mb-2"> | |
| <span className="font-medium text-sm">定时触发</span> | |
| <div className="w-8 h-5 bg-blue-600 rounded-full relative cursor-pointer"> | |
| <div className="w-3 h-3 bg-white rounded-full absolute right-1 top-1"></div> | |
| </div> | |
| </div> | |
| <input | |
| type="text" | |
| placeholder="0 0 * * *" | |
| className="w-full px-3 py-2 border border-gray-300 rounded text-sm" | |
| /> | |
| <p className="text-xs text-gray-500 mt-1">Cron表达式格式</p> | |
| </div> | |
| <div className="p-3 border border-gray-200 rounded-lg"> | |
| <div className="flex items-center justify-between mb-2"> | |
| <span className="font-medium text-sm">Webhook触发</span> | |
| <div className="w-8 h-5 bg-gray-300 rounded-full relative cursor-pointer"> | |
| <div className="w-3 h-3 bg-white rounded-full absolute left-1 top-1"></div> | |
| </div> | |
| </div> | |
| <div className="text-xs text-gray-500"> | |
| URL: https://api.example.com/webhook/abc123 | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| const renderVariableSettings = () => ( | |
| <div className="space-y-6"> | |
| <div> | |
| <div className="flex items-center justify-between mb-3"> | |
| <h4 className="font-medium text-gray-900">环境变量</h4> | |
| <button className="px-3 py-1 bg-blue-600 text-white rounded text-sm hover:bg-blue-700 transition-colors cursor-pointer whitespace-nowrap"> | |
| 添加变量 | |
| </button> | |
| </div> | |
| <div className="space-y-2"> | |
| <div className="flex items-center space-x-2"> | |
| <input | |
| type="text" | |
| placeholder="变量名" | |
| className="flex-1 px-3 py-2 border border-gray-300 rounded text-sm" | |
| /> | |
| <input | |
| type="text" | |
| placeholder="变量值" | |
| className="flex-1 px-3 py-2 border border-gray-300 rounded text-sm" | |
| /> | |
| <button className="p-2 text-red-600 hover:bg-red-50 rounded cursor-pointer"> | |
| <div className="w-4 h-4 flex items-center justify-center"> | |
| <i className="ri-delete-bin-line"></i> | |
| </div> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div> | |
| <h4 className="font-medium text-gray-900 mb-3">全局配置</h4> | |
| <div className="space-y-3"> | |
| <div> | |
| <label className="block text-sm text-gray-700 mb-1">超时时间(秒)</label> | |
| <input | |
| type="number" | |
| defaultValue="300" | |
| className="w-full px-3 py-2 border border-gray-300 rounded text-sm" | |
| /> | |
| </div> | |
| <div> | |
| <label className="block text-sm text-gray-700 mb-1">重试次数</label> | |
| <input | |
| type="number" | |
| defaultValue="3" | |
| className="w-full px-3 py-2 border border-gray-300 rounded text-sm" | |
| /> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| const renderSecuritySettings = () => ( | |
| <div className="space-y-6"> | |
| <div> | |
| <h4 className="font-medium text-gray-900 mb-3">访问权限</h4> | |
| <div className="space-y-3"> | |
| <div className="flex items-center justify-between"> | |
| <span className="text-sm text-gray-700">公开工作流</span> | |
| <div className="w-10 h-6 bg-gray-300 rounded-full relative cursor-pointer"> | |
| <div className="w-4 h-4 bg-white rounded-full absolute left-1 top-1"></div> | |
| </div> | |
| </div> | |
| <div className="flex items-center justify-between"> | |
| <span className="text-sm text-gray-700">需要API密钥</span> | |
| <div className="w-10 h-6 bg-blue-600 rounded-full relative cursor-pointer"> | |
| <div className="w-4 h-4 bg-white rounded-full absolute right-1 top-1"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div> | |
| <h4 className="font-medium text-gray-900 mb-3">数据安全</h4> | |
| <div className="space-y-3"> | |
| <div className="flex items-center justify-between"> | |
| <span className="text-sm text-gray-700">加密存储</span> | |
| <div className="w-10 h-6 bg-blue-600 rounded-full relative cursor-pointer"> | |
| <div className="w-4 h-4 bg-white rounded-full absolute right-1 top-1"></div> | |
| </div> | |
| </div> | |
| <div className="flex items-center justify-between"> | |
| <span className="text-sm text-gray-700">审计日志</span> | |
| <div className="w-10 h-6 bg-blue-600 rounded-full relative cursor-pointer"> | |
| <div className="w-4 h-4 bg-white rounded-full absolute right-1 top-1"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| const renderNodeSettings = () => { | |
| if (!selectedNode) { | |
| return ( | |
| <div className="flex items-center justify-center h-64 text-gray-500"> | |
| <div className="text-center"> | |
| <div className="w-16 h-16 bg-gray-200 rounded-full flex items-center justify-center mx-auto mb-4"> | |
| <div className="w-8 h-8 flex items-center justify-center"> | |
| <i className="ri-cursor-line text-2xl"></i> | |
| </div> | |
| </div> | |
| <p className="font-medium">选择一个节点</p> | |
| <p className="text-sm mt-1">点击画布上的节点来配置参数</p> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <div className="space-y-6"> | |
| <div className="flex items-center space-x-3"> | |
| <div className="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center"> | |
| <span className="text-lg">{selectedNode.icon}</span> | |
| </div> | |
| <div> | |
| <h3 className="font-semibold text-gray-900">{selectedNode.name}</h3> | |
| <p className="text-sm text-gray-500">节点配置</p> | |
| </div> | |
| </div> | |
| <div> | |
| <label className="block text-sm font-medium text-gray-700 mb-2">节点名称</label> | |
| <input | |
| type="text" | |
| defaultValue={selectedNode.name} | |
| className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm" | |
| /> | |
| </div> | |
| <div> | |
| <label className="block text-sm font-medium text-gray-700 mb-2">描述</label> | |
| <textarea | |
| placeholder="节点功能描述" | |
| rows={3} | |
| className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm resize-none" | |
| /> | |
| </div> | |
| {/* 根据节点类型显示不同的配置选项 */} | |
| <div> | |
| <h4 className="font-medium text-gray-900 mb-3">参数配置</h4> | |
| <div className="space-y-3"> | |
| <div> | |
| <label className="block text-sm text-gray-700 mb-1">输入参数</label> | |
| <input | |
| type="text" | |
| placeholder="参数值" | |
| className="w-full px-3 py-2 border border-gray-300 rounded text-sm" | |
| /> | |
| </div> | |
| <div> | |
| <label className="block text-sm text-gray-700 mb-1">输出格式</label> | |
| <select className="w-full px-3 py-2 border border-gray-300 rounded text-sm pr-8"> | |
| <option>JSON</option> | |
| <option>XML</option> | |
| <option>TEXT</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| return ( | |
| <div className="h-full flex flex-col"> | |
| {/* 标签页 */} | |
| <div className="p-4 border-b border-gray-200"> | |
| <div className="grid grid-cols-2 gap-1"> | |
| {tabs.map((tab) => ( | |
| <button | |
| key={tab.id} | |
| onClick={() => setActiveTab(tab.id)} | |
| className={`p-2 rounded-lg text-xs font-medium transition-colors cursor-pointer ${ | |
| activeTab === tab.id | |
| ? 'bg-blue-100 text-blue-700' | |
| : 'text-gray-600 hover:bg-gray-100' | |
| }`} | |
| > | |
| <div className="flex flex-col items-center space-y-1"> | |
| <div className="w-4 h-4 flex items-center justify-center"> | |
| <i className={tab.icon}></i> | |
| </div> | |
| <span>{tab.name}</span> | |
| </div> | |
| </button> | |
| ))} | |
| </div> | |
| </div> | |
| {/* 内容区域 */} | |
| <div className="flex-1 overflow-y-auto p-4"> | |
| {selectedNode && activeTab === 'general' ? renderNodeSettings() : | |
| activeTab === 'general' ? renderGeneralSettings() : | |
| activeTab === 'triggers' ? renderTriggerSettings() : | |
| activeTab === 'variables' ? renderVariableSettings() : | |
| activeTab === 'security' ? renderSecuritySettings() : null} | |
| </div> | |
| </div> | |
| ); | |
| } |