feat(ssa): Complete Phase 2A frontend integration - multi-step workflow end-to-end
Phase 2A: WorkflowPlannerService, WorkflowExecutorService, Python data quality, 6 bug fixes, DescriptiveResultView, multi-step R code/Word export, MVP UI reuse. V11 UI: Gemini-style, multi-task, single-page scroll, Word export. Architecture: Block-based rendering consensus (4 block types). New R tools: chi_square, correlation, descriptive, logistic_binary, mann_whitney, t_test_paired. Docs: dev summary, block-based plan, status updates, task list v2.0. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
233
frontend-v2/src/modules/ssa/components/ConclusionReport.tsx
Normal file
233
frontend-v2/src/modules/ssa/components/ConclusionReport.tsx
Normal file
@@ -0,0 +1,233 @@
|
||||
/**
|
||||
* 综合结论报告组件
|
||||
*
|
||||
* Phase 2A: 多步骤工作流执行完成后的综合结论展示
|
||||
*/
|
||||
import React, { useState } from 'react';
|
||||
import type { ConclusionReport as ConclusionReportType, WorkflowStepResult } from '../types';
|
||||
|
||||
interface ConclusionReportProps {
|
||||
report: ConclusionReportType;
|
||||
stepResults?: WorkflowStepResult[];
|
||||
}
|
||||
|
||||
interface StepResultDetailProps {
|
||||
stepSummary: ConclusionReportType['step_summaries'][0];
|
||||
stepResult?: WorkflowStepResult;
|
||||
}
|
||||
|
||||
const StepResultDetail: React.FC<StepResultDetailProps> = ({ stepSummary, stepResult }) => {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
return (
|
||||
<div className={`step-result-detail ${expanded ? 'expanded' : ''}`}>
|
||||
<div className="detail-header" onClick={() => setExpanded(!expanded)}>
|
||||
<div className="header-left">
|
||||
<span className="step-badge">步骤 {stepSummary.step_number}</span>
|
||||
<span className="tool-name">{stepSummary.tool_name}</span>
|
||||
{stepSummary.p_value !== undefined && (
|
||||
<span
|
||||
className="p-value-badge"
|
||||
style={{
|
||||
backgroundColor: stepSummary.is_significant ? '#ecfdf5' : '#f8fafc',
|
||||
color: stepSummary.is_significant ? '#059669' : '#64748b',
|
||||
}}
|
||||
>
|
||||
p = {stepSummary.p_value < 0.001 ? '< 0.001' : stepSummary.p_value.toFixed(4)}
|
||||
{stepSummary.is_significant && ' *'}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<span className={`expand-arrow ${expanded ? 'rotated' : ''}`}>▼</span>
|
||||
</div>
|
||||
|
||||
<div className="detail-summary">{stepSummary.summary}</div>
|
||||
|
||||
{expanded && stepResult?.result && (
|
||||
<div className="detail-content">
|
||||
{/* 结果表格 */}
|
||||
{stepResult.result.result_table && (
|
||||
<div className="result-table-wrapper">
|
||||
<table className="result-table">
|
||||
<thead>
|
||||
<tr>
|
||||
{stepResult.result.result_table.headers.map((header, idx) => (
|
||||
<th key={idx}>{header}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{stepResult.result.result_table.rows.map((row, rowIdx) => (
|
||||
<tr key={rowIdx}>
|
||||
{row.map((cell, cellIdx) => (
|
||||
<td key={cellIdx}>{typeof cell === 'number' ? cell.toFixed(4) : cell}</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 图表 */}
|
||||
{stepResult.result.plots && stepResult.result.plots.length > 0 && (
|
||||
<div className="result-plots">
|
||||
{stepResult.result.plots.map((plot, idx) => (
|
||||
<div key={idx} className="plot-item">
|
||||
<div className="plot-title">{plot.title}</div>
|
||||
<img
|
||||
src={plot.imageBase64.startsWith('data:') ? plot.imageBase64 : `data:image/png;base64,${plot.imageBase64}`}
|
||||
alt={plot.title}
|
||||
className="plot-image"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 详细解释 */}
|
||||
{stepResult.result.interpretation && (
|
||||
<div className="interpretation-box">
|
||||
<span className="interpretation-label">💡 解读:</span>
|
||||
<p>{stepResult.result.interpretation}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ConclusionReport: React.FC<ConclusionReportProps> = ({ report, stepResults = [] }) => {
|
||||
const [showFullReport, setShowFullReport] = useState(true);
|
||||
|
||||
const getStepResult = (stepNumber: number): WorkflowStepResult | undefined => {
|
||||
return stepResults.find(r => r.step_number === stepNumber);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="conclusion-report">
|
||||
{/* 报告头部 */}
|
||||
<div className="report-header">
|
||||
<h2 className="report-title">📋 {report.title}</h2>
|
||||
<span className="generated-time">
|
||||
{new Date(report.generated_at).toLocaleString('zh-CN')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* AI 总结摘要 - 始终显示 */}
|
||||
<div className="executive-summary">
|
||||
<div className="summary-header">
|
||||
<span className="summary-icon">🤖</span>
|
||||
<span className="summary-label">AI 综合结论</span>
|
||||
</div>
|
||||
<div className="summary-content">
|
||||
{report.executive_summary}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 主要发现 */}
|
||||
{report.key_findings.length > 0 && (
|
||||
<div className="key-findings">
|
||||
<div className="section-header">
|
||||
<span className="section-icon">🎯</span>
|
||||
<span className="section-title">主要发现</span>
|
||||
</div>
|
||||
<ul className="findings-list">
|
||||
{report.key_findings.map((finding, idx) => (
|
||||
<li key={idx}>{finding}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 统计概览 */}
|
||||
<div className="stats-overview">
|
||||
<div className="stat-card">
|
||||
<span className="stat-icon">📊</span>
|
||||
<span className="stat-value">{report.statistical_summary.total_tests}</span>
|
||||
<span className="stat-label">统计检验</span>
|
||||
</div>
|
||||
<div className="stat-card significant">
|
||||
<span className="stat-icon">✨</span>
|
||||
<span className="stat-value">{report.statistical_summary.significant_results}</span>
|
||||
<span className="stat-label">显著结果</span>
|
||||
</div>
|
||||
<div className="stat-card">
|
||||
<span className="stat-icon">🔬</span>
|
||||
<span className="stat-value">{report.statistical_summary.methods_used.length}</span>
|
||||
<span className="stat-label">分析方法</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 展开/折叠按钮 */}
|
||||
<button
|
||||
className="toggle-details-btn"
|
||||
onClick={() => setShowFullReport(!showFullReport)}
|
||||
>
|
||||
{showFullReport ? '收起详细结果 ▲' : '展开详细结果 ▼'}
|
||||
</button>
|
||||
|
||||
{/* 详细步骤结果 */}
|
||||
{showFullReport && (
|
||||
<div className="step-results-section">
|
||||
<div className="section-header">
|
||||
<span className="section-icon">📝</span>
|
||||
<span className="section-title">详细分析结果</span>
|
||||
</div>
|
||||
<div className="step-results-list">
|
||||
{report.step_summaries.map((stepSummary) => (
|
||||
<StepResultDetail
|
||||
key={stepSummary.step_number}
|
||||
stepSummary={stepSummary}
|
||||
stepResult={getStepResult(stepSummary.step_number)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 建议 */}
|
||||
{report.recommendations.length > 0 && (
|
||||
<div className="recommendations-section">
|
||||
<div className="section-header">
|
||||
<span className="section-icon">💡</span>
|
||||
<span className="section-title">建议</span>
|
||||
</div>
|
||||
<ul className="recommendations-list">
|
||||
{report.recommendations.map((rec, idx) => (
|
||||
<li key={idx}>{rec}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 局限性 */}
|
||||
{report.limitations.length > 0 && (
|
||||
<div className="limitations-section">
|
||||
<div className="section-header">
|
||||
<span className="section-icon">⚠️</span>
|
||||
<span className="section-title">局限性</span>
|
||||
</div>
|
||||
<ul className="limitations-list">
|
||||
{report.limitations.map((lim, idx) => (
|
||||
<li key={idx}>{lim}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 使用的方法列表 */}
|
||||
<div className="methods-used">
|
||||
<span className="methods-label">使用的分析方法:</span>
|
||||
<div className="methods-tags">
|
||||
{report.statistical_summary.methods_used.map((method, idx) => (
|
||||
<span key={idx} className="method-tag">{method}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConclusionReport;
|
||||
Reference in New Issue
Block a user