tony.cheng
2026-03-17 e8341da1769be538eaec6a9d4bf29491b1301d66
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,26 @@
  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;
  // 调试日志:输出关键数据
  useEffect(() => {
    console.log('===== MediationBoard 数据状态 =====');
    console.log('caseData:', caseData);
    console.log('timeline:', timeline);
    console.log('caseState:', caseState);
    console.log('mediation:', timeline.mediation);
    console.log('================================');
  }, [caseData, caseState, timeline]);
  
  // person_type到avatar类型的映射
  const getAvatarType = (personType) => {
@@ -345,6 +362,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 +558,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 +641,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 +674,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 +723,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 +857,7 @@
      message.success('审核通过!');
      handleCloseAuditModal();
      // 重新加载数据
      loadData();
      handleReload();
    } catch (err) {
      console.error('审核失败:', err);
      message.error('审核失败,请重试');
@@ -736,7 +895,7 @@
      message.success('材料已退回,等待补充提交');
      setReturnModalVisible(false);
      handleCloseAuditModal();
      loadData();
      handleReload();
    } catch (err) {
      console.error('退回失败:', err);
      message.error('退回失败,请重试');
@@ -1307,10 +1466,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 +1478,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 +1501,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 +1566,7 @@
    try {
      const response = await MediationAgreementAPIService.regenerateAgreement(caseId);
      if (response?.data?.agreeContent) {
        setAgreementContent(response.data.agreeContent);
        loadAgreementData(caseId);
        message.success('协议重新生成成功!');
      }
    } catch (err) {
@@ -1478,11 +1612,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 +1638,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>