aigc/src/pages/Report/index.tsx
2025-04-17 22:27:59 +08:00

303 lines
9.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState } from 'react';
import { PageContainer } from '@ant-design/pro-components';
import { InboxOutlined, FileExcelOutlined, BarChartOutlined, FileWordOutlined } from '@ant-design/icons';
import { Upload, Card, Button, Steps, message, Input, Spin, Result, Space, Progress, Alert, Typography } from 'antd';
import type { UploadFile } from 'antd/es/upload/interface';
import ReactECharts from 'echarts-for-react';
import styles from './index.less';
const { Dragger } = Upload;
const { TextArea } = Input;
const { Title, Paragraph } = Typography;
const ReportPage: React.FC = () => {
const [fileList, setFileList] = useState<UploadFile[]>([]);
const [currentStep, setCurrentStep] = useState(0);
const [loading, setLoading] = useState(false);
const [goal, setGoal] = useState('');
const [reportData, setReportData] = useState<any>(null);
const [wordUrl, setWordUrl] = useState<string>('');
const [progress, setProgress] = useState(0);
const [previewData, setPreviewData] = useState<any>(null);
const handleUpload = async (file: File) => {
setLoading(true);
try {
const formData = new FormData();
formData.append('file', file);
// 模拟上传进度
const timer = setInterval(() => {
setProgress(prev => {
if (prev >= 99) {
clearInterval(timer);
return 99;
}
return prev + 10;
});
}, 200);
// 模拟预览数据
setTimeout(() => {
setPreviewData({
columns: ['日期', '销售额', '利润'],
data: [
['2023-01', 1000, 200],
['2023-02', 1500, 300],
['2023-03', 1200, 250],
]
});
setProgress(100);
message.success('文件上传成功');
setCurrentStep(1);
setLoading(false);
clearInterval(timer);
}, 2000);
} catch (error) {
message.error('文件上传失败');
setLoading(false);
}
};
const generateReport = async () => {
setLoading(true);
try {
let progress = 0;
const timer = setInterval(() => {
progress += 20;
if (progress <= 100) {
setProgress(progress);
}
}, 1000);
setTimeout(() => {
clearInterval(timer);
setProgress(100);
setWordUrl('https://example.com/report.docx');
setReportData({
title: '数据分析报告',
summary: '根据您提供的数据,我们生成了详细的分析报告...',
charts: [
{
title: '销售趋势',
type: 'line',
data: {
title: {
text: '销售趋势分析'
},
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
data: ['1月', '2月', '3月']
},
yAxis: {
type: 'value'
},
series: [{
name: '销售额',
type: 'line',
data: [1000, 1500, 1200],
smooth: true
}]
}
}
]
});
message.success('报告生成成功');
setCurrentStep(2);
setLoading(false);
}, 5000);
} catch (error) {
message.error('报告生成失败');
setLoading(false);
}
};
const renderPreview = () => {
if (!previewData) return null;
return (
<Card className={styles.previewCard} title="数据预览">
<table className={styles.previewTable}>
<thead>
<tr>
{previewData.columns.map((col: string) => (
<th key={col}>{col}</th>
))}
</tr>
</thead>
<tbody>
{previewData.data.map((row: any[], index: number) => (
<tr key={index}>
{row.map((cell, cellIndex) => (
<td key={cellIndex}>{cell}</td>
))}
</tr>
))}
</tbody>
</table>
</Card>
);
};
const steps = [
{
title: '上传文件',
icon: <FileExcelOutlined />,
content: (
<div className={styles.uploadStep}>
<Alert
message="支持的文件格式"
description="Excel文件 (.xlsx, .xls) 或 CSV文件 (.csv)"
type="info"
showIcon
style={{ marginBottom: 24 }}
/>
<Dragger
fileList={fileList}
beforeUpload={(file) => {
const isExcelOrCsv =
file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
file.type === 'application/vnd.ms-excel' ||
file.type === 'text/csv';
if (!isExcelOrCsv) {
message.error('只支持上传 Excel 或 CSV 文件!');
return false;
}
setFileList([file]);
handleUpload(file);
return false;
}}
onRemove={() => {
setFileList([]);
setCurrentStep(0);
setPreviewData(null);
return true;
}}
>
<p className="ant-upload-drag-icon">
<InboxOutlined />
</p>
<p className="ant-upload-text"></p>
<p className="ant-upload-hint"> Excel CSV </p>
</Dragger>
{loading && (
<Progress percent={progress} status="active" style={{ marginTop: 24 }} />
)}
{renderPreview()}
</div>
),
},
{
title: '设置目标',
icon: <BarChartOutlined />,
content: (
<div className={styles.goalStep}>
<Title level={4}></Title>
<Paragraph type="secondary">
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</Paragraph>
<TextArea
placeholder="请输入你想要分析的目标,例如:分析销售趋势、客户分布等"
rows={4}
value={goal}
onChange={(e) => setGoal(e.target.value)}
className={styles.goalInput}
/>
<Button
type="primary"
onClick={generateReport}
disabled={!goal}
size="large"
className={styles.generateButton}
>
</Button>
{loading && (
<div className={styles.progressContainer}>
<Progress percent={progress} status="active" />
<Paragraph type="secondary">...</Paragraph>
</div>
)}
</div>
),
},
{
title: '查看报告',
icon: <FileWordOutlined />,
content: wordUrl && (
<div className={styles.reportStep}>
<Card>
<Result
status="success"
title="报告生成成功"
subTitle="您可以查看报告预览或下载完整报告"
extra={[
<Button
type="primary"
icon={<FileWordOutlined />}
onClick={() => window.open(wordUrl)}
key="download"
size="large"
>
</Button>,
<Button
onClick={() => {
setCurrentStep(0);
setFileList([]);
setGoal('');
setWordUrl('');
setPreviewData(null);
setReportData(null);
}}
key="again"
size="large"
>
</Button>,
]}
/>
{reportData && (
<div className={styles.reportPreview}>
<Title level={4}>{reportData.title}</Title>
<Paragraph>{reportData.summary}</Paragraph>
{reportData.charts.map((chart: any, index: number) => (
<Card key={index} title={chart.title} className={styles.chartCard}>
<ReactECharts option={chart.data} style={{ height: 300 }} />
</Card>
))}
</div>
)}
</Card>
</div>
),
},
];
return (
<PageContainer
title="智能报告生成"
subTitle="上传数据文件,快速生成专业分析报告"
>
<Card className={styles.container}>
<Steps
current={currentStep}
items={steps}
className={styles.steps}
/>
<div className={styles.content}>
<Spin spinning={loading}>{steps[currentStep].content}</Spin>
</div>
</Card>
</PageContainer>
);
};
export default ReportPage;