From 2461efdde98a2509c7ac5a9fd56629aaecb8b281 Mon Sep 17 00:00:00 2001
From: tony.cheng <chengmingwei_1984122@126.com>
Date: Tue, 17 Mar 2026 12:05:37 +0800
Subject: [PATCH] enhance: 增强AI调解状态控制功能的健壮性和调试能力

---
 web-app/src/components/dashboard/TabContainer.jsx |  327 +++++++++++++++++++++++++++++++++++++-----------------
 1 files changed, 224 insertions(+), 103 deletions(-)

diff --git a/web-app/src/components/dashboard/TabContainer.jsx b/web-app/src/components/dashboard/TabContainer.jsx
index 3f5bd66..40b8521 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, forwardRef, useImperativeHandle } from 'react';
+import React, { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
 import { useCaseData } from '../../contexts/CaseDataContext';
 import { formatDuration, formatSuccessRate, formatRoundCount } from '../../utils/stateTranslator';
 import ProcessAPIService from '../../services/ProcessAPIService';
@@ -154,9 +154,16 @@
   const [loading, setLoading] = useState(false);
   const [error, setError] = useState(null);
   
+  // 状态控制相关状态
+  const [controlLoading, setControlLoading] = useState(false);
+  const [confirmModalVisible, setConfirmModalVisible] = useState(false);
+  const [controlAction, setControlAction] = useState(null); // 'terminate' or 'resume'
+  const [remark, setRemark] = useState('');
+  
   // 获取案件数据
-  const { caseData } = useCaseData();
+  const { caseData, refreshData } = useCaseData();
   const timeline = caseData || {};
+  const caseState = timeline.mediation?.state;
   
   // person_type到avatar类型的映射
   const getAvatarType = (personType) => {
@@ -345,6 +352,108 @@
     return avatarText || (avatar === 'applicant' ? '申' : avatar === 'respondent' ? '被' : '调');
   };
 
+  // 状态控制按钮显示逻辑
+  const shouldShowControlButton = () => {
+    const show = caseState === 0 || caseState === 1 || caseState === 5;
+    console.log('状态控制按钮显示检查:', {
+      caseState,
+      show,
+      conditions: {
+        'caseState === 0': caseState === 0,
+        'caseState === 1': caseState === 1,
+        'caseState === 5': caseState === 5
+      }
+    });
+    return show;
+  };
+
+  const getControlButtonProps = () => {
+    console.log('获取按钮属性:', { caseState });
+    
+    if (caseState === 0 || caseState === 1) {
+      return {
+        text: '终止',
+        style: 'terminate',
+        action: 'terminate'
+      };
+    } else if (caseState === 5) {
+      return {
+        text: '恢复',
+        style: 'resume',
+        action: 'resume'
+      };
+    }
+    
+    console.log('未匹配到按钮属性,返回null');
+    return null;
+  };
+
+  // 处理状态控制按钮点击
+  const handleControlButtonClick = (action) => {
+    console.log('状态控制按钮点击:', { action });
+    setControlAction(action);
+    setConfirmModalVisible(true);
+  };
+
+  // 处理确认对话框确认
+  const handleConfirmOk = async () => {
+    console.log('确认对话框确认:', { controlAction, remark });
+    
+    if (!controlAction) {
+      console.warn('控制动作为空');
+      return;
+    }
+    
+    setControlLoading(true);
+    try {
+      const params = getMergedParams();
+      const actionCode = controlAction === 'terminate' ? 0 : 1;
+      
+      console.log('准备调用API:', {
+        caseId: params.caseId,
+        actionCode,
+        userName: localStorage.getItem('userName') || '调解员',
+        remark: remark || ''
+      });
+      
+      // 验证必要参数
+      if (!params.caseId) {
+        throw new Error('案件ID不能为空');
+      }
+      
+      await ProcessAPIService.updateMediationState(params.caseId, {
+        action: actionCode,
+        userName: localStorage.getItem('userName') || '调解员',
+        remark: remark || ''
+      });
+      
+      message.success('案件状态更新成功');
+      setConfirmModalVisible(false);
+      setRemark('');
+      setControlAction(null);
+      
+      // 刷新数据
+      refreshData();
+    } catch (error) {
+      console.error('状态更新失败:', error);
+      const errorMessage = error.message || '状态更新失败,请稍后重试';
+      message.error(errorMessage);
+      
+      // 如果是网络错误,提供更多帮助信息
+      if (errorMessage.includes('网络') || errorMessage.includes('Network')) {
+        message.info('请检查网络连接或联系管理员');
+      }
+    } finally {
+      setControlLoading(false);
+    }
+  };
+
+  // 处理确认对话框取消
+  const handleConfirmCancel = () => {
+    setConfirmModalVisible(false);
+    setControlAction(null);
+    setRemark('');
+  };
   return (
     <>
       <div className="mediation-summary" style={{
@@ -439,6 +548,81 @@
           </div>
         ))}
       </div>
+
+      {/* 状态控制按钮区域 */}
+      {shouldShowControlButton() && (() => {
+        const buttonProps = getControlButtonProps();
+        if (!buttonProps) return null;
+        
+        return (
+          <div style={{
+            marginTop: 20,
+            paddingTop: 15,
+            borderTop: '1px solid #e9ecef',
+            display: 'flex',
+            justifyContent: 'center',
+            gap: 12
+          }}>
+            <button
+              onClick={() => handleControlButtonClick(buttonProps.action)}
+              disabled={controlLoading}
+              style={{
+                padding: '10px 20px',
+                borderRadius: 'var(--border-radius)',
+                fontWeight: 600,
+                fontSize: '0.9rem',
+                cursor: controlLoading ? 'not-allowed' : 'pointer',
+                display: 'flex',
+                alignItems: 'center',
+                gap: 6,
+                border: 'none',
+                ...(buttonProps.style === 'terminate' ? {
+                  background: '#1A6FB8',
+                  color: 'white',
+                } : {
+                  background: '#52c41a',
+                  color: 'white',
+                }),
+                opacity: controlLoading ? 0.6 : 1,
+              }}
+            >
+              {controlLoading ? (
+                <><i className="fas fa-spinner fa-spin"></i>处理中...</>
+              ) : (
+                <><i className="fas fa-pause-circle"></i>{buttonProps.text}</>
+              )}
+            </button>
+          </div>
+        );
+      })()}
+
+      {/* 状态控制确认对话框 */}
+      <Modal
+        title={controlAction === 'terminate' ? '确认终止调解' : '确认恢复调解'}
+        visible={confirmModalVisible}
+        onOk={handleConfirmOk}
+        onCancel={handleConfirmCancel}
+        okText="确定"
+        cancelText="取消"
+        confirmLoading={controlLoading}
+      >
+        <p>
+          {controlAction === 'terminate' 
+            ? '确定要终止当前AI调解流程吗?终止后调解将暂停,可在适当时机恢复。' 
+            : '确定要恢复AI调解流程吗?恢复后将从当前位置继续调解。'}
+        </p>
+        <div style={{ marginTop: 15 }}>
+          <label style={{ display: 'block', marginBottom: 5, fontWeight: 500 }}>
+            备注(可选):
+          </label>
+          <Input.TextArea
+            value={remark}
+            onChange={(e) => setRemark(e.target.value)}
+            placeholder="请输入操作备注..."
+            rows={3}
+          />
+        </div>
+      </Modal>
     </>
   );
 };
@@ -447,16 +631,16 @@
  * 证据材料汇总
  */
 const EvidenceBoard = ({ onStatusChange }) => {
-  const [loading, setLoading] = useState(false);
-  const [error, setError] = useState(null);
+  // 从 Context 获取证据材料数据
+  const { evidenceData, loadEvidenceData } = useCaseData();
+  const { applicantMaterials, respondentMaterials, loading, error } = evidenceData;
+  
   // 审核弹窗状态
   const [auditModalVisible, setAuditModalVisible] = useState(false);
   const [currentAuditItem, setCurrentAuditItem] = useState(null);
   const [auditRemark, setAuditRemark] = useState('');
   const [auditLoading, setAuditLoading] = useState(false);
   const [returnModalVisible, setReturnModalVisible] = useState(false);
-  const [applicantMaterials, setApplicantMaterials] = useState([]);
-  const [respondentMaterials, setRespondentMaterials] = useState([]);
   // 弹窗数据状态
   const [modalDataLoading, setModalDataLoading] = useState(false);
   const [personInfo, setPersonInfo] = useState(null);
@@ -480,59 +664,24 @@
     return '已审核';
   };
 
-  // 加载数据
-  const loadData = async () => {
-    // 使用getMergedParams获取参数(URL参数优先,默认值兜底)
-    const params = getMergedParams();
-    const caseId = params.caseId;
-    const caseType = params.caseType ||params.caseTypeFirst;
-    const platformCode = params.platform_code;
-    
-    console.log('EvidenceBoard loadData params:', { caseId, caseType, platformCode });
-    
-    setLoading(true);
-    setError(null);
-    
-    try {
-      // 调用API获取数据
-      const response = await EvidenceAPIService.getEvidenceList({
-        case_id: caseId,
-        case_type: caseType,
-        platform_code: platformCode
-      });
-      
-      console.log('EvidenceBoard API response:', response);
-      
-      const responseData = response.data || [];
-      
-      // 分离申请人和被申请人材料
-      const applicantData = responseData.find(item => item.per_type === '15_020008-1');
-      const respondentData = responseData.find(item => item.per_type === '15_020008-2');
-      
-      const applicantList = applicantData?.file_list?.slice(0, applicantData.file_count) || [];
-      const respondentList = respondentData?.file_list?.slice(0, respondentData.file_count) || [];
-      
-      setApplicantMaterials(applicantList);
-      setRespondentMaterials(respondentList);
-      
-      // 计算并通知Tab标题的整体审核状态
-      const overallStatus = calculateOverallTabStatus(applicantList, respondentList);
-      if (onStatusChange) {
-        onStatusChange(overallStatus);
-      }
-      
-    } catch (err) {
-      console.error('加载证据材料失败:', err);
-      setError('数据加载失败,请稍后重试');
-    } finally {
-      setLoading(false);
-    }
-  };
-
-  // 组件挂载时加载数据
+  // 监听数据变化,通知Tab标题状态
   useEffect(() => {
-    loadData();
-  }, []); // eslint-disable-line react-hooks/exhaustive-deps
+    const overallStatus = calculateOverallTabStatus(applicantMaterials, respondentMaterials);
+    if (onStatusChange) {
+      onStatusChange(overallStatus);
+    }
+  }, [applicantMaterials, respondentMaterials]); // eslint-disable-line react-hooks/exhaustive-deps
+
+  // 重新加载函数
+  const handleReload = () => {
+    const params = getMergedParams();
+    loadEvidenceData({
+      caseId: params.caseId,
+      caseType: params.caseType || params.caseTypeFirst,
+      caseTypeFirst: params.caseTypeFirst,
+      platformCode: params.platform_code
+    });
+  };
 
   // 计算区域内整体审核状态(用于卡片标题旁的Tag显示)
   const calculateOverallStatus = (materials) => {
@@ -564,7 +713,7 @@
           <i className="fas fa-exclamation-circle"></i> 数据加载失败
         </div>
         <div>{error}</div>
-        <button onClick={loadData} style={{
+        <button onClick={handleReload} style={{
           marginTop: 15, padding: '8px 16px', backgroundColor: '#1890ff',
           color: 'white', border: 'none', borderRadius: 4, cursor: 'pointer'
         }}>重新加载</button>
@@ -698,7 +847,7 @@
       message.success('审核通过!');
       handleCloseAuditModal();
       // 重新加载数据
-      loadData();
+      handleReload();
     } catch (err) {
       console.error('审核失败:', err);
       message.error('审核失败,请重试');
@@ -736,7 +885,7 @@
       message.success('材料已退回,等待补充提交');
       setReturnModalVisible(false);
       handleCloseAuditModal();
-      loadData();
+      handleReload();
     } catch (err) {
       console.error('退回失败:', err);
       message.error('退回失败,请重试');
@@ -1307,10 +1456,10 @@
  * 调解协议
  */
 const AgreementSection = () => {
-  const { caseData } = useCaseData();
-  const [agreementContent, setAgreementContent] = useState('');
-  const [loading, setLoading] = useState(false);
-  const [error, setError] = useState(null);
+  // 从 Context 获取调解协议数据
+  const { caseData, agreementData, loadAgreementData } = useCaseData();
+  const { content: agreementContent, loading, error } = agreementData;
+  
   const [editModalVisible, setEditModalVisible] = useState(false);
   const [editContent, setEditContent] = useState('');
   const [editLoading, setEditLoading] = useState(false);
@@ -1319,10 +1468,9 @@
     download: false,
     regenerate: false,
   });
-  const loadedRef = useRef(false);
 
-  // 获取 caseId
-  const caseId = caseData?.caseId || getMergedParams().caseId;
+  // 获取 caseId(兼容驼峰和蛇形命名)
+  const caseId = caseData?.caseId || caseData?.case_id || getMergedParams().caseId;
 
   // 处理协议内容展示(纯文本,处理换行)
   const renderAgreementContent = (content) => {
@@ -1343,36 +1491,12 @@
     });
   };
 
-  // 首次加载协议内容
-  const loadAgreement = async () => {
-    if (!caseId) {
-      setError('缺少案件ID,无法加载协议');
-      return;
-    }
-    setLoading(true);
-    setError(null);
-    try {
-      const response = await MediationAgreementAPIService.generateAgreement(caseId);
-      if (response?.data?.agreeContent) {
-        setAgreementContent(response.data.agreeContent);
-      } else {
-        setError('协议内容为空');
-      }
-    } catch (err) {
-      console.error('加载协议失败:', err);
-      setError('加载协议失败,请稍后重试');
-    } finally {
-      setLoading(false);
+  // 重新加载协议
+  const handleReload = () => {
+    if (caseId) {
+      loadAgreementData(caseId);
     }
   };
-
-  // 组件挂载时加载协议
-  useEffect(() => {
-    if (caseId && !loadedRef.current) {
-      loadedRef.current = true;
-      loadAgreement();
-    }
-  }, [caseId]); // eslint-disable-line react-hooks/exhaustive-deps
 
   // 确认协议
   const handleConfirmAgreement = async () => {
@@ -1432,7 +1556,7 @@
     try {
       const response = await MediationAgreementAPIService.regenerateAgreement(caseId);
       if (response?.data?.agreeContent) {
-        setAgreementContent(response.data.agreeContent);
+        loadAgreementData(caseId);
         message.success('协议重新生成成功!');
       }
     } catch (err) {
@@ -1478,11 +1602,8 @@
       await MediationAgreementAPIService.updateAgreement(caseId, editContent);
       message.success('协议修改保存成功!');
       handleCloseEditModal();
-      // 刷新父页面协议内容
-      const response = await MediationAgreementAPIService.generateAgreement(caseId);
-      if (response?.data?.agreeContent) {
-        setAgreementContent(response.data.agreeContent);
-      }
+      // 刷新协议内容
+      loadAgreementData(caseId);
     } catch (err) {
       console.error('保存协议失败:', err);
       message.error('保存协议失败,请稍后重试');
@@ -1507,7 +1628,7 @@
         <div style={{ fontSize: '1.2rem', marginBottom: 10 }}>
           <i className="fas fa-exclamation-circle"></i> {error}
         </div>
-        <button onClick={loadAgreement} style={{
+        <button onClick={handleReload} style={{
           marginTop: 15, padding: '8px 16px', backgroundColor: '#1890ff',
           color: 'white', border: 'none', borderRadius: 4, cursor: 'pointer'
         }}>重新加载</button>

--
Gitblit v1.8.0