557 lines
31 KiB
TypeScript
557 lines
31 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { ArrowLeft, Save, RotateCcw, Play, Pause, Settings, FileText, GitBranch, ListChecks, Shield, Bot, Clock, CheckCircle, X, Plus, ChevronDown, AlertTriangle } from 'lucide-react';
|
|
import { useTheme } from '../contexts/ThemeContext';
|
|
|
|
interface SceneDetailProps {
|
|
sceneId: string | null;
|
|
onBack: () => void;
|
|
}
|
|
|
|
interface FlowNode {
|
|
id: number;
|
|
name: string;
|
|
agent: string;
|
|
status: 'completed' | 'active' | 'pending';
|
|
inputs: number;
|
|
outputs: number;
|
|
inputSource: string[];
|
|
outputFields: string[];
|
|
releaseCondition: string;
|
|
rollbackNode: string;
|
|
allowParallel: boolean;
|
|
description: string;
|
|
}
|
|
|
|
const SceneDetail: React.FC<SceneDetailProps> = ({ sceneId, onBack }) => {
|
|
const { theme } = useTheme();
|
|
const isDark = theme === 'dark';
|
|
|
|
// 主题颜色类
|
|
const cardBg = isDark ? 'bg-slate-800/30' : 'bg-white';
|
|
const cardBorder = isDark ? 'border-blue-800/30' : 'border-red-200';
|
|
const textPrimary = isDark ? 'text-white' : 'text-gray-900';
|
|
const textSecondary = isDark ? 'text-slate-400' : 'text-gray-600';
|
|
const textMuted = isDark ? 'text-slate-500' : 'text-gray-500';
|
|
const btnSecondary = isDark ? 'bg-slate-700/50 hover:bg-slate-600/50 border-blue-800/30 text-slate-300' : 'bg-gray-100 hover:bg-gray-200 border-gray-300 text-gray-700';
|
|
const btnPrimary = isDark ? 'bg-blue-600 hover:bg-blue-500' : 'bg-red-600 hover:bg-red-500';
|
|
const inputBg = isDark ? 'bg-slate-900/50' : 'bg-gray-50';
|
|
const inputBorder = isDark ? 'border-blue-800/30' : 'border-gray-300';
|
|
|
|
const [activeTab, setActiveTab] = useState('flow');
|
|
const [selectedNode, setSelectedNode] = useState<FlowNode | null>(null);
|
|
|
|
const tabs = [
|
|
{ id: 'basic', label: '基础设置', icon: FileText },
|
|
{ id: 'flow', label: '流程编排', icon: GitBranch },
|
|
{ id: 'template', label: '输入输出模板', icon: ListChecks },
|
|
{ id: 'rules', label: '运行规则', icon: Shield },
|
|
];
|
|
|
|
const flowNodes: FlowNode[] = [
|
|
{ id: 1, name: '目标分析', agent: 'GoalAgent', status: 'completed', inputs: 2, outputs: 3, inputSource: ['task_background', 'primary_goal_hint'], outputFields: ['goal_model.primary_goal', 'goal_model.secondary_goal'], releaseCondition: '目标明确且可量化', rollbackNode: '无', allowParallel: false, description: '分析并确定宣传的核心目标' },
|
|
{ id: 2, name: '受众分析', agent: 'AudienceAgent', status: 'completed', inputs: 3, outputs: 4, inputSource: ['goal_model', 'task_background'], outputFields: ['audience_model.core_audience', 'audience_model.secondary_audience'], releaseCondition: '核心受众明确', rollbackNode: '目标分析', allowParallel: false, description: '分析目标受众特征和需求' },
|
|
{ id: 3, name: '行为分析', agent: 'BehaviorAgent', status: 'active', inputs: 4, outputs: 3, inputSource: ['audience_model', 'goal_model'], outputFields: ['behavior_model.target_behavior', 'behavior_model.conversion_path'], releaseCondition: '目标行为明确', rollbackNode: '受众分析', allowParallel: false, description: '设计目标受众的预期行为' },
|
|
{ id: 4, name: '驱动分析', agent: 'DriverAgent', status: 'pending', inputs: 3, outputs: 2, inputSource: ['behavior_model', 'audience_model'], outputFields: ['driver_model.primary_driver', 'driver_model.secondary_driver'], releaseCondition: '核心驱动明确', rollbackNode: '行为分析', allowParallel: true, description: '分析影响受众行为的关键驱动因素' },
|
|
{ id: 5, name: '叙事分析', agent: 'NarrativeAgent', status: 'pending', inputs: 2, outputs: 4, inputSource: ['driver_model', 'audience_model'], outputFields: ['narrative_model.core_narrative', 'narrative_model.message_points'], releaseCondition: '叙事框架明确', rollbackNode: '驱动分析', allowParallel: false, description: '设计传播叙事策略' },
|
|
{ id: 6, name: '逻辑链收敛', agent: 'LogicChainAgent', status: 'pending', inputs: 4, outputs: 2, inputSource: ['goal_model', 'audience_model', 'behavior_model', 'narrative_model'], outputFields: ['strategy_chain.logic_statement', 'strategy_chain.consistency_score'], releaseCondition: '逻辑链校验通过', rollbackNode: '叙事分析', allowParallel: false, description: '验证策略链完整性和一致性' },
|
|
{ id: 7, name: '风险评估', agent: 'RiskAgent', status: 'pending', inputs: 2, outputs: 3, inputSource: ['strategy_chain', 'task_constraints'], outputFields: ['risk_model.risk_items', 'risk_model.mitigation_suggestions'], releaseCondition: '高风险项已处理', rollbackNode: '逻辑链收敛', allowParallel: true, description: '评估策略执行风险' },
|
|
{ id: 8, name: '方案生成', agent: 'DraftAgent', status: 'pending', inputs: 3, outputs: 5, inputSource: ['strategy_chain', 'narrative_model', 'risk_model'], outputFields: ['final_plan', 'execution_timeline', 'evaluation_metrics'], releaseCondition: '方案完整可执行', rollbackNode: '风险评估', allowParallel: false, description: '生成最终宣传方案' },
|
|
];
|
|
|
|
const sceneSummary = {
|
|
flowNodesCount: 8,
|
|
boundAgentsCount: 8,
|
|
serialNodes: 6,
|
|
parallelNodes: 2,
|
|
logicChainEnabled: true,
|
|
lastPublishedTime: '2026-03-18 14:30',
|
|
};
|
|
|
|
const inputTemplate = [
|
|
{ name: '任务标题', type: 'STRING', required: true },
|
|
{ name: '任务背景', type: 'TEXT', required: true },
|
|
{ name: '上级要求', type: 'TEXT', required: false },
|
|
{ name: '时间要求', type: 'DATE', required: true },
|
|
{ name: '资源条件', type: 'JSON', required: false },
|
|
{ name: '风险边界', type: 'TEXT', required: false },
|
|
];
|
|
|
|
const outputTemplate = {
|
|
middle: ['goal_model', 'audience_model', 'behavior_model', 'driver_model', 'narrative_model', 'strategy_chain'],
|
|
final: ['讨论结果摘要', '最终策略链', '正式方案', '风险说明', '评估指标'],
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* 顶部导航 */}
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-4">
|
|
<button
|
|
onClick={onBack}
|
|
className={`p-2 ${isDark ? 'hover:bg-slate-800/50' : 'hover:bg-gray-100'} rounded-lg transition-colors`}
|
|
>
|
|
<ArrowLeft className={`w-5 h-5 ${textSecondary}`} />
|
|
</button>
|
|
<div>
|
|
<div className="flex items-center gap-3">
|
|
<h1 className={`text-2xl font-bold ${textPrimary}`}>业务链条配置详情</h1>
|
|
<span className="px-2.5 py-1 bg-green-500/20 text-green-400 rounded-full text-xs font-medium">
|
|
启用
|
|
</span>
|
|
</div>
|
|
<p className={`${textSecondary} text-sm mt-1`}></p>
|
|
</div>
|
|
</div>
|
|
{/* <div className="flex items-center gap-3">
|
|
<button className={`flex items-center gap-2 px-4 py-2.5 ${btnSecondary} rounded-lg transition-colors`}>
|
|
<RotateCcw className="w-4 h-4" />
|
|
<span className="text-sm">重置</span>
|
|
</button>
|
|
<button className={`flex items-center gap-2 px-4 py-2.5 ${btnPrimary} ${textPrimary} rounded-lg transition-colors`}>
|
|
<Save className="w-4 h-4" />
|
|
<span className="text-sm">保存配置</span>
|
|
</button>
|
|
</div> */}
|
|
</div>
|
|
|
|
{/* Tab切换 */}
|
|
{/* <div className={`flex items-center gap-2 border-b ${cardBorder}`}>
|
|
{tabs.map((tab) => {
|
|
const Icon = tab.icon;
|
|
return (
|
|
<button
|
|
key={tab.id}
|
|
onClick={() => setActiveTab(tab.id)}
|
|
className={`flex items-center gap-2 px-4 py-3 border-b-2 transition-colors ${
|
|
activeTab === tab.id
|
|
? 'border-blue-500 text-blue-400'
|
|
: `border-transparent ${textSecondary} hover:${textPrimary}`
|
|
}`}
|
|
>
|
|
<Icon className="w-4 h-4" />
|
|
<span className="text-sm font-medium">{tab.label}</span>
|
|
</button>
|
|
);
|
|
})}
|
|
</div> */}
|
|
|
|
{/* 内容区 */}
|
|
<div className="grid grid-cols-4 gap-6">
|
|
{/* 主内容区 */}
|
|
<div className="col-span-3">
|
|
{activeTab === 'basic' && (
|
|
<div className="grid grid-cols-2 gap-6">
|
|
{/* 左侧表单区 */}
|
|
<div className={`${cardBg} backdrop-blur-xl ${cardBorder} rounded-xl p-6 space-y-4`}>
|
|
<h3 className={`${textPrimary} font-semibold mb-4`}>基础信息</h3>
|
|
<div>
|
|
<label className={`block ${textSecondary} text-sm mb-2`}>场景名称</label>
|
|
<input
|
|
type="text"
|
|
defaultValue="宣传策略生成"
|
|
className={`w-full px-4 py-2.5 ${inputBg} ${inputBorder} rounded-lg ${textPrimary} text-sm focus:outline-none focus:border-blue-500/50`}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className={`block ${textSecondary} text-sm mb-2`}>场景编码</label>
|
|
<input
|
|
type="text"
|
|
defaultValue="SCENE_001"
|
|
className={`w-full px-4 py-2.5 ${inputBg} ${inputBorder} rounded-lg ${textPrimary} text-sm focus:outline-none focus:border-blue-500/50`}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className={`block ${textSecondary} text-sm mb-2`}>场景说明</label>
|
|
<textarea
|
|
rows={3}
|
|
defaultValue="用于生成完整的宣传策略方案,包含目标、受众、行为、驱动、叙事等维度的分析"
|
|
className={`w-full px-4 py-2.5 ${inputBg} ${inputBorder} rounded-lg ${textPrimary} text-sm focus:outline-none focus:border-blue-500/50 resize-none`}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className={`block ${textSecondary} text-sm mb-2`}>场景分类</label>
|
|
<select className={`w-full px-4 py-2.5 ${inputBg} ${inputBorder} rounded-lg ${textPrimary} text-sm focus:outline-none focus:border-blue-500/50`}>
|
|
<option>策略生成</option>
|
|
<option>方案推演</option>
|
|
<option>活动复盘</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 右侧说明区 */}
|
|
<div className={`${cardBg} backdrop-blur-xl ${cardBorder} rounded-xl p-6 space-y-4`}>
|
|
<h3 className={`${textPrimary} font-semibold mb-4`}>场景说明</h3>
|
|
<div className="p-4 bg-blue-500/10 border border-blue-500/30 rounded-lg">
|
|
<p className="text-blue-400 text-sm font-medium mb-2">场景简介</p>
|
|
<p className={`${textSecondary} text-sm`}>完整的宣传策略生成流程,覆盖从目标分析到方案输出的全链路。</p>
|
|
</div>
|
|
<div className={`p-4 ${inputBg} ${inputBorder} rounded-lg`}>
|
|
<p className={`${textSecondary} text-sm font-medium mb-2`}>最近修改记录</p>
|
|
<p className={`${textSecondary} text-sm`}>2026-03-18 14:30 由 张策略 修改</p>
|
|
</div>
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div className="p-3 bg-cyan-500/10 border border-cyan-500/30 rounded-lg text-center">
|
|
<p className="text-cyan-400 text-2xl font-bold">12</p>
|
|
<p className={`${textSecondary} text-xs`}>关联任务</p>
|
|
</div>
|
|
<div className="p-3 bg-purple-500/10 border border-purple-500/30 rounded-lg text-center">
|
|
<p className="text-purple-400 text-2xl font-bold">8</p>
|
|
<p className={`${textSecondary} text-xs`}>关联智能体</p>
|
|
</div>
|
|
</div>
|
|
<div className={`flex items-center justify-between py-3 border-t ${cardBorder}`}>
|
|
<span className={`${textSecondary} text-sm`}>设为默认场景</span>
|
|
<label className="relative inline-flex items-center cursor-pointer">
|
|
<input type="checkbox" className="sr-only peer" defaultChecked />
|
|
<div className="w-11 h-6 bg-slate-700 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === 'flow' && (
|
|
<div className={`${cardBg} backdrop-blur-xl ${cardBorder} rounded-xl p-6`}>
|
|
<div className="flex items-center justify-between mb-6">
|
|
<h3 className={`${textPrimary} font-semibold`}>流程编排</h3>
|
|
<button className="flex items-center gap-2 px-3 py-1.5 bg-blue-600/20 hover:bg-blue-600/30 border border-blue-500/30 text-blue-400 rounded-lg text-sm transition-colors">
|
|
<Plus className="w-4 h-4" />
|
|
添加节点
|
|
</button>
|
|
</div>
|
|
|
|
{/* 流程图区域 */}
|
|
<div className="relative">
|
|
{/* 流程节点 */}
|
|
<div className="space-y-4">
|
|
{flowNodes.map((node, index) => (
|
|
<div key={node.id} className="relative">
|
|
<div className="flex items-center gap-4">
|
|
{/* 连接线 */}
|
|
{index > 0 && (
|
|
<div className="absolute left-6 -top-4 w-0.5 h-4 bg-blue-500/50" />
|
|
)}
|
|
|
|
{/* 节点卡片 */}
|
|
<button
|
|
onClick={() => setSelectedNode(node)}
|
|
className={`flex-1 flex items-center gap-4 p-4 rounded-xl border-2 transition-all text-left ${
|
|
selectedNode?.id === node.id
|
|
? 'bg-blue-500/20 border-blue-500 shadow-lg shadow-blue-500/20'
|
|
: node.status === 'completed'
|
|
? 'bg-green-500/10 border-green-500/30 hover:border-green-500/50'
|
|
: node.status === 'active'
|
|
? 'bg-blue-500/10 border-blue-500/50 hover:border-blue-500'
|
|
: `${inputBg} ${cardBorder} ${isDark ? 'hover:border-blue-700' : 'hover:border-red-300'}`
|
|
}`}
|
|
>
|
|
{/* 节点序号 */}
|
|
<div className={`w-10 h-10 rounded-full flex items-center justify-center text-sm font-bold ${
|
|
node.status === 'completed'
|
|
? 'bg-green-500 text-white'
|
|
: node.status === 'active'
|
|
? 'bg-blue-500 text-white'
|
|
: isDark ? 'bg-slate-700 text-slate-400' : 'bg-gray-200 text-gray-600'
|
|
}`}>
|
|
{node.status === 'completed' ? <CheckCircle className="w-5 h-5" /> : node.id}
|
|
</div>
|
|
|
|
{/* 节点信息 */}
|
|
<div className="flex-1">
|
|
<div className="flex items-center gap-3 mb-1">
|
|
<h4 className={`${textPrimary} font-medium`}>{node.name}</h4>
|
|
<span className={`px-2 py-0.5 rounded text-xs font-medium ${
|
|
node.status === 'completed' ? 'bg-green-500/20 text-green-400' :
|
|
node.status === 'active' ? 'bg-blue-500/20 text-blue-400' :
|
|
isDark ? 'bg-slate-700 text-slate-400' : 'bg-gray-200 text-gray-600'
|
|
}`}>
|
|
{node.status === 'completed' ? '已完成' : node.status === 'active' ? '进行中' : '待执行'}
|
|
</span>
|
|
{node.allowParallel && (
|
|
<span className="px-2 py-0.5 bg-amber-500/20 text-amber-400 rounded text-xs">
|
|
可并行
|
|
</span>
|
|
)}
|
|
</div>
|
|
<div className={`flex items-center gap-4 text-sm ${textSecondary}`}>
|
|
<span className="flex items-center gap-1">
|
|
<Bot className="w-3 h-3" />
|
|
{node.agent}
|
|
</span>
|
|
<span>输入 {node.inputs}</span>
|
|
<span>输出 {node.outputs}</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 放行条件 */}
|
|
<div className="text-right">
|
|
<p className={`${textMuted} text-xs mb-1`}>放行条件</p>
|
|
<p className={`${textSecondary} text-sm`}>{node.releaseCondition}</p>
|
|
</div>
|
|
|
|
<ChevronDown className={`w-5 h-5 ${textMuted}`} />
|
|
</button>
|
|
|
|
{/* 回退虚线 */}
|
|
{node.rollbackNode !== '无' && (
|
|
<div className="absolute -right-16 top-1/2 transform -translate-y-1/2">
|
|
<div className="flex items-center gap-1 text-amber-400 text-xs">
|
|
<span className="w-8 border-t-2 border-dashed border-amber-400/50" />
|
|
<RotateCcw className="w-3 h-3" />
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* 图例 */}
|
|
<div className={`mt-6 flex items-center gap-6 pt-6 border-t ${cardBorder}`}>
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-3 h-3 rounded-full bg-green-400" />
|
|
<span className={`${textSecondary} text-sm`}>已完成</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-3 h-3 rounded-full bg-blue-400" />
|
|
<span className={`${textSecondary} text-sm`}>进行中</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<div className={`w-3 h-3 rounded-full ${isDark ? 'bg-slate-600' : 'bg-gray-400'}`} />
|
|
<span className={`${textSecondary} text-sm`}>待执行</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-3 h-3 rounded-full bg-amber-400" />
|
|
<span className={`${textSecondary} text-sm`}>可并行</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === 'template' && (
|
|
<div className="space-y-6">
|
|
{/* 任务输入模板 */}
|
|
<div className={`${cardBg} backdrop-blur-xl ${cardBorder} rounded-xl p-6`}>
|
|
<h3 className={`${textPrimary} font-semibold mb-4`}>任务输入模板</h3>
|
|
<div className="grid grid-cols-3 gap-4">
|
|
{inputTemplate.map((field) => (
|
|
<div key={field.name} className={`p-4 ${inputBg} ${inputBorder} rounded-lg`}>
|
|
<div className="flex items-center justify-between mb-2">
|
|
<p className={`${textPrimary} text-sm font-medium`}>{field.name}</p>
|
|
{field.required && (
|
|
<span className="px-2 py-0.5 bg-red-500/20 text-red-400 rounded text-xs">必填</span>
|
|
)}
|
|
</div>
|
|
<p className={`${textMuted} text-xs`}>{field.type}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* 中间输出模板 */}
|
|
<div className={`${cardBg} backdrop-blur-xl ${cardBorder} rounded-xl p-6`}>
|
|
<h3 className={`${textPrimary} font-semibold mb-4`}>中间输出模板</h3>
|
|
<div className="grid grid-cols-3 gap-4">
|
|
{outputTemplate.middle.map((field) => (
|
|
<div key={field} className="p-4 bg-cyan-500/10 border border-cyan-500/30 rounded-lg">
|
|
<p className="text-cyan-400 text-sm font-mono">{field}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* 最终输出模板 */}
|
|
<div className={`${cardBg} backdrop-blur-xl ${cardBorder} rounded-xl p-6`}>
|
|
<h3 className={`${textPrimary} font-semibold mb-4`}>最终输出模板</h3>
|
|
<div className="grid grid-cols-3 gap-4">
|
|
{outputTemplate.final.map((field) => (
|
|
<div key={field} className="p-4 bg-green-500/10 border border-green-500/30 rounded-lg">
|
|
<p className="text-green-400 text-sm">{field}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === 'rules' && (
|
|
<div className={`${cardBg} backdrop-blur-xl ${cardBorder} rounded-xl p-6 space-y-6`}>
|
|
<h3 className={`${textPrimary} font-semibold mb-4`}>运行规则配置</h3>
|
|
|
|
{[
|
|
{ title: '启用状态门控', desc: '每个阶段需要明确放行才能进入下一阶段', enabled: true },
|
|
{ title: '启用逻辑链校验', desc: '自动检查策略链的完整性和一致性', enabled: true },
|
|
{ title: '启用风险强制复核', desc: '高风险项必须人工确认才能继续', enabled: false },
|
|
{ title: '允许局部并行', desc: '部分独立阶段可以并行执行', enabled: true },
|
|
].map((rule, index) => (
|
|
<div key={index} className={`flex items-center justify-between py-4 border-b ${cardBorder} last:border-0`}>
|
|
<div>
|
|
<div className="flex items-center gap-2">
|
|
<p className={`${textPrimary} font-medium`}>{rule.title}</p>
|
|
{rule.enabled && (
|
|
<CheckCircle className="w-4 h-4 text-green-400" />
|
|
)}
|
|
</div>
|
|
<p className={`${textMuted} text-sm mt-1`}>{rule.desc}</p>
|
|
</div>
|
|
<label className="relative inline-flex items-center cursor-pointer">
|
|
<input type="checkbox" className="sr-only peer" defaultChecked={rule.enabled} />
|
|
<div className="w-11 h-6 bg-slate-700 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
|
|
</label>
|
|
</div>
|
|
))}
|
|
|
|
<div className="grid grid-cols-2 gap-6 pt-4">
|
|
<div>
|
|
<label className={`block ${textSecondary} text-sm mb-2`}>最大讨论轮数</label>
|
|
<input
|
|
type="number"
|
|
defaultValue={10}
|
|
className={`w-full px-4 py-2.5 ${inputBg} ${inputBorder} rounded-lg ${textPrimary} text-sm focus:outline-none focus:border-blue-500/50`}
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className={`block ${textSecondary} text-sm mb-2`}>最大回退次数</label>
|
|
<input
|
|
type="number"
|
|
defaultValue={3}
|
|
className={`w-full px-4 py-2.5 ${inputBg} ${inputBorder} rounded-lg ${textPrimary} text-sm focus:outline-none focus:border-blue-500/50`}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="pt-4">
|
|
<label className={`block ${textSecondary} text-sm mb-2`}>异常中断处理方式</label>
|
|
<select className={`w-full px-4 py-2.5 ${inputBg} ${inputBorder} rounded-lg ${textPrimary} text-sm focus:outline-none focus:border-blue-500/50`}>
|
|
<option>自动保存并暂停</option>
|
|
<option>自动回退到上一节点</option>
|
|
<option>通知负责人并等待</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* 右侧场景摘要区 */}
|
|
<div className="space-y-4">
|
|
{/* 场景统计卡片 */}
|
|
<div className={`${cardBg} backdrop-blur-xl ${cardBorder} rounded-xl p-5`}>
|
|
<h3 className={`${textPrimary} font-semibold mb-4`}>场景摘要</h3>
|
|
<div className="space-y-3">
|
|
<div className="flex items-center justify-between p-3 bg-blue-500/10 border border-blue-500/30 rounded-lg">
|
|
<span className={`${textSecondary} text-sm`}>流程节点数</span>
|
|
<span className="text-blue-400 font-bold">{sceneSummary.flowNodesCount}</span>
|
|
</div>
|
|
<div className="flex items-center justify-between p-3 bg-purple-500/10 border border-purple-500/30 rounded-lg">
|
|
<span className={`${textSecondary} text-sm`}>绑定智能体</span>
|
|
<span className="text-purple-400 font-bold">{sceneSummary.boundAgentsCount}</span>
|
|
</div>
|
|
<div className={`flex items-center justify-between p-3 ${inputBg} ${isDark ? 'border-slate-700/30' : 'border-gray-300'} rounded-lg`}>
|
|
<span className={`${textSecondary} text-sm`}>串行节点</span>
|
|
<span className={`${textSecondary}`}>{sceneSummary.serialNodes}</span>
|
|
</div>
|
|
<div className="flex items-center justify-between p-3 bg-amber-500/10 border border-amber-500/30 rounded-lg">
|
|
<span className={`${textSecondary} text-sm`}>并行节点</span>
|
|
<span className="text-amber-400">{sceneSummary.parallelNodes}</span>
|
|
</div>
|
|
<div className="flex items-center justify-between p-3 bg-green-500/10 border border-green-500/30 rounded-lg">
|
|
<span className={`${textSecondary} text-sm`}>逻辑链校验</span>
|
|
<span className="text-green-400">{sceneSummary.logicChainEnabled ? '已启用' : '未启用'}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 最近发布时间 */}
|
|
<div className={`${cardBg} backdrop-blur-xl ${cardBorder} rounded-xl p-5`}>
|
|
<div className={`flex items-center gap-2 ${textSecondary} text-sm mb-2`}>
|
|
<Clock className="w-4 h-4" />
|
|
最近发布时间
|
|
</div>
|
|
<p className={`${textPrimary}`}>{sceneSummary.lastPublishedTime}</p>
|
|
</div>
|
|
|
|
{/* 节点属性面板 */}
|
|
{selectedNode && (
|
|
<div className={`${cardBg} backdrop-blur-xl border border-blue-500/30 rounded-xl p-5`}>
|
|
<div className="flex items-center justify-between mb-4">
|
|
<h3 className={`${textPrimary} font-semibold`}>节点属性</h3>
|
|
<button
|
|
onClick={() => setSelectedNode(null)}
|
|
className={`p-1 ${isDark ? 'hover:bg-slate-700/50' : 'hover:bg-gray-100'} rounded`}
|
|
>
|
|
<X className={`w-4 h-4 ${textSecondary}`} />
|
|
</button>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<div>
|
|
<p className={`${textMuted} text-xs mb-1`}>节点名称</p>
|
|
<p className={`${textPrimary}`}>{selectedNode.name}</p>
|
|
</div>
|
|
<div>
|
|
<p className={`${textMuted} text-xs mb-1`}>绑定智能体</p>
|
|
<p className="text-cyan-400">{selectedNode.agent}</p>
|
|
</div>
|
|
<div>
|
|
<p className={`${textMuted} text-xs mb-1`}>上游依赖</p>
|
|
<div className="space-y-1">
|
|
{selectedNode.inputSource.map((input) => (
|
|
<div key={input} className={`${textSecondary} text-sm ${inputBg} px-2 py-1 rounded`}>
|
|
{input}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<p className={`${textMuted} text-xs mb-1`}>输出字段</p>
|
|
<div className="space-y-1">
|
|
{selectedNode.outputFields.map((output) => (
|
|
<div key={output} className="text-green-400 text-sm bg-green-500/10 px-2 py-1 rounded">
|
|
{output}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<p className={`${textMuted} text-xs mb-1`}>放行条件</p>
|
|
<p className="text-amber-400 text-sm">{selectedNode.releaseCondition}</p>
|
|
</div>
|
|
<div>
|
|
<p className={`${textMuted} text-xs mb-1`}>回退节点</p>
|
|
<p className={`${textSecondary} text-sm`}>{selectedNode.rollbackNode}</p>
|
|
</div>
|
|
<div className="flex items-center justify-between">
|
|
<span className={`${textMuted} text-sm`}>允许并行</span>
|
|
<span className={selectedNode.allowParallel ? 'text-green-400' : `${textSecondary}`}>
|
|
{selectedNode.allowParallel ? '是' : '否'}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className={`mt-4 pt-4 border-t ${cardBorder} space-y-2`}>
|
|
<button className={`w-full flex items-center justify-center gap-2 px-3 py-2 ${btnPrimary} ${textPrimary} rounded-lg text-sm transition-colors`}>
|
|
<Settings className="w-4 h-4" />
|
|
编辑节点
|
|
</button>
|
|
<div className="flex gap-2">
|
|
<button className={`flex-1 flex items-center justify-center gap-1 px-3 py-2 ${btnSecondary} rounded-lg text-sm transition-colors`}>
|
|
<Plus className="w-3 h-3" />
|
|
前置
|
|
</button>
|
|
<button className={`flex-1 flex items-center justify-center gap-1 px-3 py-2 ${btnSecondary} rounded-lg text-sm transition-colors`}>
|
|
<Plus className="w-3 h-3" />
|
|
后置
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default SceneDetail;
|