From cd65fdeabc462afd4fe99e4c789a243bf9b64ee6 Mon Sep 17 00:00:00 2001
From: shimai <shimai@example.com>
Date: Fri, 27 Feb 2026 16:28:27 +0800
Subject: [PATCH] fix:修复调解协议无法下载

---
 web-app/src/components/dashboard/TabContainer.jsx |   81 +++++++++++++++++++++++++++++++++++++---
 1 files changed, 75 insertions(+), 6 deletions(-)

diff --git a/web-app/src/components/dashboard/TabContainer.jsx b/web-app/src/components/dashboard/TabContainer.jsx
index b16768d..639d7b2 100644
--- a/web-app/src/components/dashboard/TabContainer.jsx
+++ b/web-app/src/components/dashboard/TabContainer.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect, useRef } from 'react';
+import React, { useState, useEffect, useRef, forwardRef, useImperativeHandle } from 'react';
 import { useCaseData } from '../../contexts/CaseDataContext';
 import { formatDuration, formatSuccessRate, formatRoundCount } from '../../utils/stateTranslator';
 import ProcessAPIService from '../../services/ProcessAPIService';
@@ -6,16 +6,25 @@
 import MediationAgreementAPIService from '../../services/MediationAgreementAPIService';
 import { getMergedParams } from '../../utils/urlParams';
 import { message, Spin, Tag, Modal, Button, Input, Image } from 'antd';
+import config from '../../config/env';
 
 const { TextArea } = Input;
 
 /**
  * 选项卡容器组件 - 4个选项卡
+ * 通过 forwardRef 暴露 switchTab 方法供父组件调用
  */
-const TabContainer = () => {
+const TabContainer = forwardRef((props, ref) => {
   const [activeTab, setActiveTab] = useState('mediation-data-board');
   // 证据材料汇总Tab的审核状态badge
   const [evidenceBadge, setEvidenceBadge] = useState(null);
+
+  // 暴露 switchTab 方法给父组件
+  useImperativeHandle(ref, () => ({
+    switchTab: (tabKey) => {
+      setActiveTab(tabKey);
+    }
+  }));
 
   const tabs = [
     { key: 'mediation-data-board', label: '调解分析', icon: 'fa-chart-line' },
@@ -76,7 +85,7 @@
       </div>
     </div>
   );
-};
+});
 
 /**
  * 调解数据看板
@@ -1282,6 +1291,32 @@
     });
   };
 
+  const resolveDownloadFileName = (contentDisposition) => {
+    if (!contentDisposition) return '';
+    const utf8Match = contentDisposition.match(/filename\*\s*=\s*UTF-8''([^;]+)/i);
+    if (utf8Match?.[1]) {
+      try {
+        return decodeURIComponent(utf8Match[1]);
+      } catch (err) {
+        return utf8Match[1];
+      }
+    }
+    const fileNameMatch = contentDisposition.match(/filename\s*=\s*"?([^"]+)"?/i);
+    if (fileNameMatch?.[1]) return fileNameMatch[1];
+    return '';
+  };
+
+  const triggerFileDownload = (blob, fileName) => {
+    const url = window.URL.createObjectURL(blob);
+    const link = document.createElement('a');
+    link.href = url;
+    link.download = fileName;
+    document.body.appendChild(link);
+    link.click();
+    link.remove();
+    window.URL.revokeObjectURL(url);
+  };
+
   // 首次加载协议内容
   const loadAgreement = async () => {
     if (!caseId) {
@@ -1333,11 +1368,45 @@
     if (!caseId) return;
     setActionLoading(prev => ({ ...prev, download: true }));
     try {
-      await MediationAgreementAPIService.downloadAgreement(caseId);
-      message.success('协议下载成功!');
+      const token = localStorage.getItem('access_token');
+      const response = await fetch(`${config.baseURL}/api/v1/medi-agreement/download`, {
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json',
+          ...(token ? { Authorization: `Bearer ${token}` } : {})
+        },
+        body: JSON.stringify({ caseId }),
+        credentials: config.withCredentials ? 'include' : 'omit'
+      });
+
+      const contentType = response.headers.get('Content-Type') || '';
+      if (contentType.includes('application/pdf')) {
+        if (!response.ok) {
+          throw new Error('下载协议失败,请稍后重试');
+        }
+        const blob = await response.blob();
+        const contentDisposition = response.headers.get('Content-Disposition') || '';
+        const fileName = resolveDownloadFileName(contentDisposition) || '调解协议书.pdf';
+        triggerFileDownload(blob, fileName);
+        message.success('协议下载成功!');
+        return;
+      }
+
+      const data = await response.json();
+      if (data?.code !== 200 && data?.code !== 201) {
+        throw new Error(data?.message || '下载协议失败,请稍后重试');
+      }
+      if (data?.data?.needConfirm || data?.message === '待确认') {
+        message.warning('协议待确认,请先确认协议');
+        return;
+      }
+      if (data?.data?.agreeContent) {
+        setAgreementContent(data.data.agreeContent);
+      }
+      message.success(data?.message || '已返回协议内容');
     } catch (err) {
       console.error('下载协议失败:', err);
-      message.error('下载协议失败,请稍后重试');
+      message.error(err?.message || '下载协议失败,请稍后重试');
     } finally {
       setActionLoading(prev => ({ ...prev, download: false }));
     }

--
Gitblit v1.8.0