import React, { useState, useEffect } from 'react';
import {
FileText, Plus, Search, ChevronLeft, Download, Edit3, Eye,
ThumbsUp, ThumbsDown, BrainCircuit, History, LayoutTemplate,
Play, CheckCircle2, Loader2, BarChart3, PieChart, TrendingUp, Settings2, MoreVertical,
X, Send, Bold, Italic, Underline, AlignLeft, AlignCenter, AlignRight,
List, ListOrdered, Heading1, Heading2, Heading3, Quote, Clock
} from 'lucide-react';
// --- Mock Data ---
const MOCK_REPORTS = [
{ id: 1, title: '2023年Q4华东区销售数据分析', summary: '深度分析华东大区Q4的销售趋势,重点指出了双十一期间客单价下滑及局部地区物流受阻导致的业绩波动。', status: 'completed', date: '2023-12-01', category: '销售', version: 'v1.2', templateId: 't1' },
{ id: 2, title: '双十一大促营销效果复盘报告', summary: '针对双十一全周期各投放渠道的ROI进行评估,分析流量与转化漏斗,为后续营销资源分配提供建议。', status: 'completed', date: '2023-11-15', category: '营销', version: 'v1.0', templateId: 't2' },
{ id: 3, title: 'Q3核心产品活跃度环比分析', summary: '对比分析Q3各核心产品线的DAU与用户留存率,找出活跃度下降的关键节点与用户群体。', status: 'generating', date: '2023-10-05', category: '用户', version: 'v1.0', templateId: 't3' },
{ id: 4, title: '年度渠道转化率对比模型', summary: '建立各获客渠道的转化率评估模型,追踪从点击到最终成单的全链路数据,优化渠道投放策略。', status: 'completed', date: '2023-09-20', category: '销售', version: 'v2.1', templateId: 't4' },
];
const MOCK_TEMPLATES = {
't1': { id: 't1', name: '销售复盘通用模板', metrics: ['总销售额', '客单价', '退货率'], dimensions: ['时间(按周)', '地区', '商品类目'], prompt: '请根据所选指标和维度,分析Q4的销售趋势,重点指出业绩异常波动的区域,并给出可执行的促销建议。', content: '
报告标题:【销售复盘自动分析报告】
一、 整体数据概览
根据您选择的维度(时间、地区、商品类目),我们对核心指标(总销售额、客单价、退货率)进行了整体分析。
[此处将由AI自动填入整体数据表现和同环比情况]
二、 核心指标拆解分析
1. 总销售额的趋势与分布
[此处将由AI结合时间维度展开详细的图表描述与数据拆解]
三、 关键异常与业务洞察
[此处将由AI提取数据中的异常点,并给出针对性的促销建议]
四、 总结与建议
[此处将由AI总结全文核心结论,并输出可落地的Action Item]
' },
't2': { id: 't2', name: '大促营销分析模板', metrics: ['曝光量', '点击率', '转化率'], dimensions: ['投放渠道', '活动时间段'], prompt: '分析各渠道的ROI,评估本次大促的营销资源分配是否合理。', content: '报告标题:【大促营销分析报告】
一、 流量与转化漏斗
[漏斗数据概览]
二、 渠道ROI评估
[AI评估结果与资源分配建议]
' },
};
// --- 富文本通用样式 ---
const proseStyles = "prose max-w-none [&>h1]:text-3xl [&>h1]:font-bold [&>h2]:text-2xl [&>h2]:font-bold [&>h3]:text-xl [&>h3]:font-bold [&>h4]:text-lg [&>h4]:font-semibold [&>p]:mb-3 [&>ul]:list-disc [&>ul]:pl-5 [&>ol]:list-decimal [&>ol]:pl-5 [&>blockquote]:border-l-4 [&>blockquote]:border-gray-300 [&>blockquote]:pl-4 [&>blockquote]:italic [&>blockquote]:text-gray-500";
// --- 在线文档工具栏组件 (用于直接在已有div上使用) ---
function EditorToolbar() {
const execCmd = (cmd, arg = null) => document.execCommand(cmd, false, arg);
const Btn = ({ icon: Icon, cmd, arg, title }) => (
);
return (
);
}
// --- 完整的在线文档编辑器组件 ---
function RichTextEditor({ value, onChange, placeholder, minHeight = '300px' }) {
const editorRef = React.useRef(null);
React.useEffect(() => {
if (editorRef.current && editorRef.current.innerHTML !== value) {
editorRef.current.innerHTML = value || '';
}
}, [value]);
const handleInput = () => {
if (onChange && editorRef.current) {
onChange(editorRef.current.innerHTML);
}
};
return (
);
}
export default function App() {
const [currentView, setCurrentView] = useState('list'); // 'list', 'create', 'reportDetail', 'templateDetail'
const [selectedReportId, setSelectedReportId] = useState(null);
const [selectedTemplateId, setSelectedTemplateId] = useState(null);
const [reports, setReports] = useState(MOCK_REPORTS);
// 导航辅助函数
const goToList = () => setCurrentView('list');
const goToCreate = () => setCurrentView('create');
const goToReportDetail = (id) => { setSelectedReportId(id); setCurrentView('reportDetail'); };
const goToTemplateDetail = (id) => { setSelectedTemplateId(id); setCurrentView('templateDetail'); };
const handleCreateComplete = (newReport) => {
setReports([newReport, ...reports]); // 将新报告插入列表最前方
setCurrentView('list'); // 提交后返回列表页查看生成状态,不再直接跳入详情
};
return (
{/* 顶部导航栏 */}
{/* 主体内容区域 */}
{currentView === 'list' && }
{currentView === 'create' && }
{currentView === 'reportDetail' && }
{currentView === 'templateDetail' && setCurrentView('reportDetail')} />}
);
}
// ==========================================
// 1. 报告列表页 Component
// ==========================================
function ReportList({ onGoToCreate, onGoToDetail, reports }) {
const [activeTab, setActiveTab] = useState('全部');
const [searchQuery, setSearchQuery] = useState('');
const tabs = ['全部', '销售', '营销', '用户'];
const filteredReports = reports.filter(r =>
(activeTab === '全部' || r.category === activeTab) &&
r.title.toLowerCase().includes(searchQuery.toLowerCase())
);
return (
{filteredReports.map(report => (
onGoToDetail(report.id)}
className="group border border-gray-100 rounded-xl p-5 hover:shadow-md hover:border-blue-100 transition-all cursor-pointer bg-white relative overflow-hidden flex flex-col h-full"
>
{/* 第一行:报告标题 */}
{report.title}
{/* 第二行:报告内容简述 */}
{report.summary || '暂无内容简述...'}
{/* 第三行:最近更新时间 + 生成状态 */}
{report.status === 'completed' ? (
更新于 {report.date}
已生成
) : (
报告生成中...
)}
))}
{filteredReports.length === 0 && (
没有找到相关报告
)}
);
}
// --- 多选下拉组件 (支持搜索) ---
function MultiSelect({ options, selected, onChange, placeholder }) {
const [isOpen, setIsOpen] = React.useState(false);
const [searchTerm, setSearchTerm] = React.useState('');
const wrapperRef = React.useRef(null);
React.useEffect(() => {
function handleClickOutside(event) {
if (wrapperRef.current && !wrapperRef.current.contains(event.target)) {
setIsOpen(false);
}
}
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, [wrapperRef]);
const filteredOptions = options.filter(o => o.toLowerCase().includes(searchTerm.toLowerCase()));
const toggleOption = (opt) => {
if (selected.includes(opt)) {
onChange(selected.filter(i => i !== opt));
} else {
onChange([...selected, opt]);
}
};
const removeOption = (e, opt) => {
e.stopPropagation();
onChange(selected.filter(i => i !== opt));
};
return (
setIsOpen(!isOpen)}
>
{selected.length === 0 && {placeholder}}
{selected.map(s => (
{s}
))}
{isOpen && (
{filteredOptions.map(o => (
{
e.stopPropagation();
toggleOption(o);
}}
className="flex items-center px-3 py-2.5 hover:bg-gray-50 rounded-lg cursor-pointer group transition-colors"
>
{selected.includes(o) ? : null}
{o}
))}
{filteredOptions.length === 0 &&
无匹配选项
}
)}
);
}
// ==========================================
// 2. 创建报告页 (包含模板创建流程)
// ==========================================
function CreateReportFlow({ onComplete, onCancel }) {
// steps: 1: 配置参数, 1.5: 生成模版中, 2: 确认并编辑模版, 2.5: 提交生成中, 3: 提交成功
const [step, setStep] = useState(1);
const [formData, setFormData] = useState({
name: '新数据分析报告',
metrics: ['销售额', '客单价'],
dimensions: ['时间', '地区'],
prompt: '请分析近期数据趋势,总结关键发现。'
});
const [templateContent, setTemplateContent] = useState('');
// 扩展了指标和维度的数量,以体现下拉搜索组件的优势
const availableMetrics = ['销售额', '订单量', '客单价', '转化率', 'DAU', '留存率', '退款率', '毛利率', '净利润', '新客获取成本(CAC)', '客户终身价值(LTV)', '复购率', 'ARPU', '页面浏览量(PV)', '独立访客(UV)'];
const availableDimensions = ['时间', '地区', '渠道', '新老客', '商品类目', '终端类型', '用户等级', '支付方式', '营销活动', '门店', '省份', '城市', '年龄段', '性别'];
const handleGenerateTemplate = () => {
setStep(1.5);
// 模拟 LLM 生成模版框架的过程
setTimeout(() => {
const mockGeneratedTemplate = `报告标题:【${formData.name}】自动分析报告
一、 整体数据概览
根据您选择的维度(${formData.dimensions.join('、')}),我们对核心指标(${formData.metrics.join('、')})进行了整体分析。
[此处将由AI自动填入整体数据表现和同环比情况]
二、 核心指标拆解分析
1. ${formData.metrics[0] || '核心指标'}的趋势与分布
[此处将由AI结合${formData.dimensions[0] || '所选维度'}展开详细的图表描述与数据拆解]
三、 关键异常与业务洞察
[此处将由AI根据提示词: "${formData.prompt}" 提取数据中的异常点,并给出针对性的业务建议]
四、 总结与建议
[此处将由AI总结全文核心结论,并输出可落地的Action Item]
`;
setTemplateContent(mockGeneratedTemplate);
setStep(2);
}, 2000);
};
const handleGenerateReport = () => {
setStep(2.5);
// 模拟提交生成报告的任务
setTimeout(() => {
setStep(3);
}, 1500);
};
// 渲染步骤条的辅助函数
const getStepStatus = (stepNum) => {
if (Math.floor(step) === stepNum || (step === 2.5 && stepNum === 3)) return 'active';
if (Math.floor(step) > stepNum) return 'completed';
return 'pending';
};
return (
{/* Stepper */}
{[
{ num: 1, label: '配置参数并生成模版' },
{ num: 2, label: '查看与编辑模版正文' },
{ num: 3, label: '生成数据报告' }
].map((s) => {
const status = getStepStatus(s.num);
return (
{status === 'completed' ? : s.num}
{s.label}
);
})}
{step === 1 && (
1. 配置报告参数
报告基础信息
setFormData({...formData, name: e.target.value})}
className="w-full px-4 py-3 border border-gray-200 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none font-medium text-lg"
placeholder="输入报告名称"
/>
1. 选择分析指标 (Metrics)
setFormData({...formData, metrics: newMetrics})}
placeholder="请选择或搜索分析指标..."
/>
2. 选择分析维度 (Dimensions)
setFormData({...formData, dimensions: newDimensions})}
placeholder="请选择或搜索分析维度..."
/>
3. 编写AI提示词 (Prompt)
使用预设提示词
)}
{step === 1.5 && (
LLM 正在构建报告模版...
正在根据您的指标和维度,规划最合理的分析框架与段落结构
)}
{step === 2 && (
2. 查看并编辑模版正文
这是 LLM 为您生成的报告框架,您可以直接编辑文本、调整排版和布局。
)}
{step === 2.5 && (
正在提交报告生成任务...
正在封装模版和数据参数,即将提交给AI引擎进行深度分析
)}
{step === 3 && (
报告生成任务已提交
系统正在后台为您深度分析数据并生成报告。预计需要 1-3 分钟,您可以随时在列表页查看生成状态。
)}
);
}
// ==========================================
// 3. 报告详情页 Component
// ==========================================
function ReportDetail({ reportId, onBack, onGoToTemplate }) {
const [isEditing, setIsEditing] = useState(false);
const [showCoT, setShowCoT] = useState(false);
// 评价系统状态
const [feedbackType, setFeedbackType] = useState(null); // 'up' | 'down' | null
const [feedbackText, setFeedbackText] = useState('');
const [isFeedbackSubmitted, setIsFeedbackSubmitted] = useState(false);
// 定时生成配置状态
const [showScheduleModal, setShowScheduleModal] = useState(false);
const [scheduleConfig, setScheduleConfig] = useState({ enabled: false, time: '09:00' });
const [tempSchedule, setTempSchedule] = useState({ ...scheduleConfig }); // 用于弹窗内的临时修改状态
const report = MOCK_REPORTS.find(r => r.id === reportId) || MOCK_REPORTS[0];
const isGenerating = report.status === 'generating';
const handleFeedbackSubmit = () => {
if (feedbackText.trim()) {
setIsFeedbackSubmitted(true);
// 实际应用中这里会调用接口提交反馈
}
};
const handleSaveSchedule = () => {
setScheduleConfig({ ...tempSchedule });
setShowScheduleModal(false);
// 实际应用中这里会调用接口保存定时任务配置
};
return (
{/* 头部信息与操作区 */}
{report.title}
生成时间: {report.date}
{/* 右上角操作按钮 */}
{!isEditing ? (
<>
{/* 模拟下拉菜单 */}
{report.version} (当前)
v1.1
v1.0
{!isGenerating && (
<>
>
)}
>
) : (
)}
{/* 报告正文 */}
{isGenerating ? (
报告生成中...
AI正在深度分析您的数据,这可能需要1-3分钟时间,请耐心等待或返回列表处理其他工作。
) : (
<>
{isEditing ? (
<>
您当前处于编辑模式,可以使用上方工具栏修改下方文本排版。
>
) : null}
执行摘要
根据2023年Q4数据分析,华东区整体销售额达到 ¥12,450,000,环比Q3增长 15.2%。然而,客单价出现小幅下滑(-3%),主要由于双十一期间的大额满减活动导致。退货率稳定在 4.5% 的健康区间。
关键图表视图
{/* 模拟图表区域 1 */}
{/* 模拟图表区域 2 */}
异常发现与建议
- 异常预警: 浙江大区在12月第二周出现了罕见的销量暴跌(环比下降22%),结合外部数据,初步判断受局部物流受阻影响。
- 策略建议: 针对客单价下滑问题,建议在接下来的Q1开展"高客单商品连带销售"策略,提升关联组合搭配的推荐权重。
>
)}
{/* 底部功能区:评价与思维链 */}
{!isGenerating && (
{!isFeedbackSubmitted ? (
<>
报告质量如何?
>
) : (
感谢您的反馈!
)}
{/* 展开的评价输入框 */}
{feedbackType && !isFeedbackSubmitted && (
setFeedbackText(e.target.value)}
placeholder="请描述您的具体建议或报告中不准确的地方..."
className="flex-1 px-3 py-1.5 text-sm border-none focus:ring-0 outline-none text-gray-700 bg-transparent"
onKeyDown={(e) => e.key === 'Enter' && handleFeedbackSubmit()}
/>
)}
)}
{/* 定时生成配置弹窗 */}
{showScheduleModal && (
定时生成配置
开启每日自动生成
系统将基于当前模板,自动为您更新数据并生成新报告
setTempSchedule({ ...tempSchedule, enabled: !tempSchedule.enabled })}
className={`w-11 h-6 rounded-full flex items-center p-1 cursor-pointer transition-colors ${tempSchedule.enabled ? 'bg-indigo-500' : 'bg-gray-300'}`}
>
{tempSchedule.enabled && (
)}
)}
{/* AI 思维链侧边抽屉 */}
{showCoT && !isGenerating && (
setShowCoT(false)}>
)}
AI 推理思维链
Step 1: 提取与解析
提取输入:华东区 Q4 销售数据集 (包含订单、退款、SKU维度).
识别模板指令:"重点指出业绩异常波动的区域".
Step 2: 计算与推理
执行计算:按省份计算每周环比增长率...
发现异常值:浙江省 12周2 销售额(X) 较 12周1 销售额(Y) 下降 22.4%, Z-score {">"} 3.
Step 3: 归因与生成
交叉对比:物流时效表显示该时段该区域平均延迟送达率飙升.
生成结论:将异常归因为物流受阻,整合入报告正文.
);
}
// ==========================================
// 4. 模板详情页 Component
// ==========================================
function TemplateDetail({ templateId, onBack }) {
const [isEditing, setIsEditing] = useState(false);
const [hasChanges, setHasChanges] = useState(false);
const originalTemplate = MOCK_TEMPLATES[templateId] || MOCK_TEMPLATES['t1'];
const [templateData, setTemplateData] = useState({...originalTemplate});
const handlePromptChange = (e) => {
setTemplateData({...templateData, prompt: e.target.value});
setHasChanges(true);
};
return (
{/* 头部 */}
模板配置详情
ID: {templateData.id}
{!isEditing ? (
) : (
)}
{/* 提示信息 */}
{isEditing && hasChanges && (
模板已修改。保存后,基于此模板的历史报告 需要重新生成 才能应用新配置。
)}
{/* 内容区 */}
{ setTemplateData({...templateData, name: e.target.value}); setHasChanges(true); }}
disabled={!isEditing}
className={`w-full px-4 py-2 rounded-lg outline-none transition-all ${
isEditing ? 'border border-gray-300 focus:ring-2 focus:ring-indigo-500 focus:border-transparent bg-white' : 'border border-transparent bg-gray-50 text-gray-700'
}`}
/>
分析指标 (Metrics)
{templateData.metrics.map(m => (
{m}
{isEditing && ×}
))}
{isEditing &&
}
分析维度 (Dimensions)
{templateData.dimensions.map(d => (
{d}
{isEditing && ×}
))}
{isEditing &&
}
{isEditing ? (
) : (
)}
{/* 模板正文内容区 */}
{isEditing ? (
{ setTemplateData({...templateData, content: val}); setHasChanges(true); }}
minHeight="350px"
placeholder="在此编辑模版正文排版与内容..."
/>
) : (
)}
);
}