From ecc5214d74317a9f661c0088339a196b8e9a63ca Mon Sep 17 00:00:00 2001 From: Bortlesboat Date: Sat, 21 Mar 2026 09:50:33 -0400 Subject: [PATCH] feat: add report download functionality Add ability to download generated reports in Markdown or JSON format. - Backend: extend existing download endpoint with ?format=json query param - Frontend: add downloadReport API helper and download button with format dropdown menu in Step4Report component Closes #251 Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/app/api/report.py | 40 ++++++-- frontend/src/api/report.js | 11 +++ frontend/src/components/Step4Report.vue | 122 +++++++++++++++++++++--- 3 files changed, 152 insertions(+), 21 deletions(-) diff --git a/backend/app/api/report.py b/backend/app/api/report.py index e05c73c3..5a2f6fdc 100644 --- a/backend/app/api/report.py +++ b/backend/app/api/report.py @@ -393,40 +393,60 @@ def list_reports(): @report_bp.route('//download', methods=['GET']) def download_report(report_id: str): """ - 下载报告(Markdown格式) - - 返回Markdown文件 + 下载报告(Markdown或JSON格式) + + Query参数: + format: 下载格式,支持 "markdown"(默认)或 "json" + + 返回对应格式的文件 """ try: report = ReportManager.get_report(report_id) - + if not report: return jsonify({ "success": False, "error": f"报告不存在: {report_id}" }), 404 - + + download_format = request.args.get('format', 'markdown').lower() + + if download_format == 'json': + import json + import tempfile + report_dict = report.to_dict() + with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False, encoding='utf-8') as f: + json.dump(report_dict, f, ensure_ascii=False, indent=2) + temp_path = f.name + + return send_file( + temp_path, + as_attachment=True, + download_name=f"{report_id}.json", + mimetype='application/json' + ) + + # Default: markdown format md_path = ReportManager._get_report_markdown_path(report_id) - + if not os.path.exists(md_path): - # 如果MD文件不存在,生成一个临时文件 import tempfile with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f: f.write(report.markdown_content) temp_path = f.name - + return send_file( temp_path, as_attachment=True, download_name=f"{report_id}.md" ) - + return send_file( md_path, as_attachment=True, download_name=f"{report_id}.md" ) - + except Exception as e: logger.error(f"下载报告失败: {str(e)}") return jsonify({ diff --git a/frontend/src/api/report.js b/frontend/src/api/report.js index c89a67d8..b2e81c2c 100644 --- a/frontend/src/api/report.js +++ b/frontend/src/api/report.js @@ -49,3 +49,14 @@ export const getReport = (reportId) => { export const chatWithReport = (data) => { return requestWithRetry(() => service.post('/api/report/chat', data), 3, 1000) } + +/** + * Get the download URL for a report + * @param {string} reportId + * @param {string} format - 'markdown' or 'json' + * @returns {string} download URL + */ +export const getReportDownloadUrl = (reportId, format = 'markdown') => { + const baseURL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:5001' + return `${baseURL}/api/report/${reportId}/download?format=${format}` +} diff --git a/frontend/src/components/Step4Report.vue b/frontend/src/components/Step4Report.vue index 22f2bdcf..15d1a629 100644 --- a/frontend/src/components/Step4Report.vue +++ b/frontend/src/components/Step4Report.vue @@ -127,14 +127,36 @@ - - + +
+
+ + +
+ +
@@ -392,7 +414,7 @@