348 lines
16 KiB
TypeScript
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;
|