Files
deerflow-code/web-code/pages/StrategyResult.tsx
T
2026-05-28 18:29:37 +08:00

348 lines
16 KiB
TypeScript

import React, { useState } from 'react';
import {
FileText,
Clock,
CheckCircle,
AlertTriangle,
ChevronDown,
ChevronRight,
Target,
Users,
TrendingUp,
MessageSquare,
Megaphone,
Shield,
ArrowRight,
Download,
RefreshCw,
Eye,
} from 'lucide-react';
import { useTheme } from '../contexts/ThemeContext';
import { PageType } from '../components/Sidebar';
interface StrategyResultProps {
onNavigate: (page: PageType) => void;
}
const StrategyResult: React.FC<StrategyResultProps> = ({ onNavigate }) => {
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 [expandedSection, setExpandedSection] = useState<string | null>('goal');
const strategyChain = [
{ id: 1, name: '目标', icon: Target, color: 'blue', status: 'completed' },
{ id: 2, name: '受众', icon: Users, color: 'cyan', status: 'completed' },
{ id: 3, name: '行为', icon: TrendingUp, color: 'green', status: 'completed' },
{ id: 4, name: '驱动', icon: MessageSquare, color: 'yellow', status: 'completed' },
{ id: 5, name: '叙事', icon: Megaphone, color: 'purple', status: 'completed' },
{ id: 6, name: '风险', icon: Shield, color: 'red', status: 'completed' },
];
const versionHistory = [
{ version: 'V3', time: '2026-03-19 15:30', changes: ['叙事由"事件主导"改为"人物+事件结合"', '风险项已修正'] },
{ version: 'V2', time: '2026-03-19 14:20', changes: ['受众由"全体青年"收窄为"基层青年"', '目标行为已明确'] },
{ version: 'V1', time: '2026-03-19 10:30', changes: ['初始版本生成'] },
];
const toggleSection = (section: string) => {
setExpandedSection(expandedSection === section ? null : section);
};
return (
<div className="space-y-6">
{/* 顶部信息栏 */}
<div className={`${cardBg} backdrop-blur-xl ${cardBorder} rounded-xl p-5`}>
<div className="flex items-center justify-between">
<div className="flex items-center gap-6">
<div className="flex items-center gap-3">
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-blue-500/20 to-cyan-500/20 flex items-center justify-center">
<FileText className="w-6 h-6 text-blue-400" />
</div>
<div>
<h2 className={`${textPrimary} font-bold text-lg`}>五四青年节宣传活动策略</h2>
<p className={`${textSecondary} text-sm`}>策略讨论结果 · 已收敛</p>
</div>
</div>
<div className="h-12 w-px bg-blue-800/30" />
<div className="grid grid-cols-4 gap-6 text-sm">
<div>
<p className={`${textMuted}`}>使用场景</p>
<p className={`${textPrimary} font-medium`}>宣传策略生成</p>
</div>
<div>
<p className={`${textMuted}`}>运行时长</p>
<p className={`${textPrimary} font-medium`}>5小时32分</p>
</div>
<div>
<p className={`${textMuted}`}>当前版本</p>
<p className="text-cyan-400 font-medium">V3</p>
</div>
<div>
<p className={`${textMuted}`}>完成度</p>
<p className="text-green-400 font-medium flex items-center gap-1">
<CheckCircle className="w-4 h-4" /> 100%
</p>
</div>
</div>
</div>
<div className="flex items-center gap-3">
<button
onClick={() => onNavigate('task-scene')}
className={`flex items-center gap-2 px-4 py-2.5 ${btnSecondary} rounded-lg text-sm transition-colors`}
>
<Eye className="w-4 h-4" />
返回工作台
</button>
<button className={`flex items-center gap-2 px-4 py-2.5 ${btnPrimary} text-white rounded-lg text-sm transition-colors`}>
<FileText className="w-4 h-4" />
生成最终方案
<ArrowRight className="w-4 h-4" />
</button>
</div>
</div>
</div>
<div className="grid grid-cols-3 gap-6">
{/* 左侧 - 策略链和详情 (70%) */}
<div className="col-span-2 space-y-6">
{/* 策略链可视化 */}
<div className={`${cardBg} backdrop-blur-xl ${cardBorder} rounded-xl p-5`}>
<h3 className={`${textPrimary} font-semibold mb-4`}>策略链结果</h3>
<div className="flex items-center justify-between">
{strategyChain.map((item, index) => {
const Icon = item.icon;
return (
<React.Fragment key={item.id}>
<div className="flex flex-col items-center">
<div className={`w-14 h-14 rounded-xl bg-${item.color}-500/20 border-2 border-${item.color}-500/50 flex items-center justify-center mb-2`}>
<Icon className={`w-7 h-7 text-${item.color}-400`} />
</div>
<span className={`text-${item.color}-400 text-sm font-medium`}>{item.name}</span>
<span className={`${textMuted} text-xs mt-1 flex items-center gap-1`}>
<CheckCircle className="w-3 h-3 text-green-400" /> 已确认
</span>
</div>
{index < strategyChain.length - 1 && (
<ArrowRight className="w-6 h-6 text-slate-600 mb-6" />
)}
</React.Fragment>
);
})}
</div>
</div>
{/* 详细结果 */}
<div className={`${cardBg} backdrop-blur-xl ${cardBorder} rounded-xl overflow-hidden`}>
{[
{
id: 'goal',
title: '目标',
icon: Target,
color: 'blue',
content: {
primary: '提升品牌在Z世代用户中的认知度',
secondary: '建立品牌年轻化形象,增强用户情感连接',
},
},
{
id: 'audience',
title: '受众',
icon: Users,
color: 'cyan',
content: {
primary: '核心受众:18-25岁基层青年',
secondary: '次级受众:25-30岁职场新人',
insights: ['社交媒体重度用户', '注重性价比', '偏好真实、有态度的品牌'],
},
},
{
id: 'behavior',
title: '行为',
icon: TrendingUp,
color: 'green',
content: {
primary: '主动参与话题讨论、生成UGC内容',
conversion: '浏览→点赞→评论→转发→创作',
},
},
{
id: 'driver',
title: '驱动',
icon: MessageSquare,
color: 'yellow',
content: {
primary: '社交认同与自我表达',
secondary: ['归属感需求', '表达欲驱动', '群体认同激励'],
},
},
{
id: 'narrative',
title: '叙事',
icon: Megaphone,
color: 'purple',
content: {
primary: '真实、有态度的青年故事',
approach: '人物+事件结合,以真实故事传递品牌价值观',
},
},
{
id: 'risk',
title: '风险',
icon: Shield,
color: 'red',
content: {
risks: ['预算有限,需控制成本', '青年群体注意力分散', 'UGC质量难以保证'],
suggestions: ['提前布局预热', '设置激励机制', '建立内容审核机制'],
},
},
].map((section) => {
const Icon = section.icon;
const isExpanded = expandedSection === section.id;
return (
<div key={section.id} className={`border-b ${cardBorder} last:border-0`}>
<button
onClick={() => toggleSection(section.id)}
className="w-full flex items-center justify-between p-4 hover:bg-slate-700/20 transition-colors"
>
<div className="flex items-center gap-3">
<div className={`w-10 h-10 rounded-lg bg-${section.color}-500/20 flex items-center justify-center`}>
<Icon className={`w-5 h-5 text-${section.color}-400`} />
</div>
<div className="text-left">
<p className={`${textPrimary} font-medium`}>{section.title}</p>
<p className={`text-${section.color}-400 text-sm`}>{section.content.primary}</p>
</div>
</div>
{isExpanded ? (
<ChevronDown className={`w-5 h-5 ${textSecondary}`} />
) : (
<ChevronRight className={`w-5 h-5 ${textSecondary}`} />
)}
</button>
{isExpanded && (
<div className="px-4 pb-4 pt-0 ml-13">
<div className={`p-4 ${inputBg} rounded-lg space-y-3`}>
{Object.entries(section.content).map(([key, value]) => (
<div key={key}>
<p className={`${textMuted} text-xs uppercase mb-1`}>{key}</p>
{Array.isArray(value) ? (
<ul className={`list-disc list-inside ${textSecondary} text-sm space-y-1`}>
{value.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
) : (
<p className={`${textPrimary} text-sm`}>{value}</p>
)}
</div>
))}
</div>
</div>
)}
</div>
);
})}
</div>
</div>
{/* 右侧 - 摘要和版本记录 (30%) */}
<div className="space-y-6">
{/* 关键结论摘要 */}
<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="p-3 bg-blue-500/10 border border-blue-500/30 rounded-lg">
<p className="text-blue-400 text-xs font-medium mb-1">为什么选择这类受众</p>
<p className={`${textPrimary} text-sm`}>基层青年是品牌年轻化战略的核心突破口,具有强社交传播属性</p>
</div>
<div className="p-3 bg-green-500/10 border border-green-500/30 rounded-lg">
<p className="text-green-400 text-xs font-medium mb-1">为什么设计这种行为目标</p>
<p className={`${textPrimary} text-sm`}>UGC是最高效的信任传递方式,用户生成内容具有真实性和可信度</p>
</div>
<div className="p-3 bg-purple-500/10 border border-purple-500/30 rounded-lg">
<p className="text-purple-400 text-xs font-medium mb-1">为什么采用这种叙事</p>
<p className={`${textPrimary} text-sm`}>真实故事比硬广更能打动年轻人,人物视角增强代入感</p>
</div>
</div>
</div>
{/* 版本对比记录 */}
<div className={`${cardBg} backdrop-blur-xl ${cardBorder} rounded-xl p-5`}>
<div className="flex items-center justify-between mb-4">
<h3 className={`${textPrimary} font-semibold`}>版本迭代记录</h3>
<button className="flex items-center gap-1 text-blue-400 text-sm hover:text-blue-300 transition-colors">
<RefreshCw className="w-4 h-4" />
对比
</button>
</div>
<div className="space-y-3">
{versionHistory.map((version, index) => (
<div
key={version.version}
className={`p-3 rounded-lg border ${
index === 0
? 'bg-cyan-500/10 border-cyan-500/30'
: `${inputBg} border-slate-700/30`
}`}
>
<div className="flex items-center justify-between mb-2">
<span className={`font-bold ${
index === 0 ? 'text-cyan-400' : textSecondary
}`}>
{version.version}
</span>
<span className={`${textMuted} text-xs`}>{version.time}</span>
</div>
<ul className="space-y-1">
{version.changes.map((change, i) => (
<li key={i} className={`${textPrimary} text-xs flex items-start gap-1`}>
<span className={textSecondary}>•</span>
{change}
</li>
))}
</ul>
</div>
))}
</div>
</div>
{/* 导出选项 */}
<div className={`${cardBg} backdrop-blur-xl ${cardBorder} rounded-xl p-5`}>
<h3 className={`${textPrimary} font-semibold mb-4`}>导出选项</h3>
<div className="space-y-2">
<button className={`w-full flex items-center justify-between p-3 ${inputBg} hover:bg-slate-800/50 ${inputBorder} rounded-lg transition-colors`}>
<div className="flex items-center gap-3">
<FileText className="w-5 h-5 text-blue-400" />
<span className={`${textPrimary} text-sm`}>导出讨论记录</span>
</div>
<Download className={`w-4 h-4 ${textSecondary}`} />
</button>
<button className={`w-full flex items-center justify-between p-3 ${inputBg} hover:bg-slate-800/50 ${inputBorder} rounded-lg transition-colors`}>
<div className="flex items-center gap-3">
<FileText className="w-5 h-5 text-green-400" />
<span className={`${textPrimary} text-sm`}>导出策略结果</span>
</div>
<Download className={`w-4 h-4 ${textSecondary}`} />
</button>
</div>
</div>
</div>
</div>
</div>
);
};
export default StrategyResult;