From 85d9447504b83d4b4b0550d4b5199e284e14a06f Mon Sep 17 00:00:00 2001
From: tony.cheng <chengmingwei_1984122@126.com>
Date: Thu, 12 Mar 2026 17:04:05 +0800
Subject: [PATCH] feat: 数据加载稳定性重构 - 将证据材料和调解协议API集中到CaseDataContext统一管理
---
web-app/src/components/dashboard/TabContainer.jsx | 137 +++++++--------------------
web-app/src/contexts/CaseDataContext.jsx | 117 +++++++++++++++++++++++
2 files changed, 153 insertions(+), 101 deletions(-)
diff --git a/web-app/src/components/dashboard/TabContainer.jsx b/web-app/src/components/dashboard/TabContainer.jsx
index 0767f1c..3e0a166 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';
@@ -447,16 +447,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 +480,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 +529,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 +663,7 @@
message.success('审核通过!');
handleCloseAuditModal();
// 重新加载数据
- loadData();
+ handleReload();
} catch (err) {
console.error('审核失败:', err);
message.error('审核失败,请重试');
@@ -736,7 +701,7 @@
message.success('材料已退回,等待补充提交');
setReturnModalVisible(false);
handleCloseAuditModal();
- loadData();
+ handleReload();
} catch (err) {
console.error('退回失败:', err);
message.error('退回失败,请重试');
@@ -1307,10 +1272,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,7 +1284,6 @@
download: false,
regenerate: false,
});
- const loadedRef = useRef(false);
// 获取 caseId(兼容驼峰和蛇形命名)
const caseId = caseData?.caseId || caseData?.case_id || getMergedParams().caseId;
@@ -1343,36 +1307,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 +1372,7 @@
try {
const response = await MediationAgreementAPIService.regenerateAgreement(caseId);
if (response?.data?.agreeContent) {
- setAgreementContent(response.data.agreeContent);
+ loadAgreementData(caseId);
message.success('协议重新生成成功!');
}
} catch (err) {
@@ -1478,11 +1418,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 +1444,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>
diff --git a/web-app/src/contexts/CaseDataContext.jsx b/web-app/src/contexts/CaseDataContext.jsx
index 7a26b74..958df54 100644
--- a/web-app/src/contexts/CaseDataContext.jsx
+++ b/web-app/src/contexts/CaseDataContext.jsx
@@ -7,6 +7,8 @@
import { message } from 'antd';
import ProcessAPIService from '../services/ProcessAPIService';
import OutboundBotAPIService from '../services/OutboundBotAPIService';
+import EvidenceAPIService from '../services/EvidenceAPIService';
+import MediationAgreementAPIService from '../services/MediationAgreementAPIService';
import { getMergedParams } from '../utils/urlParams';
import { mockTimelineData } from '../mocks/timeline';
import { getFallbackStartTime, parseTimeString } from '../utils/timeFormatter';
@@ -30,6 +32,21 @@
const [taskStartTime, setTaskStartTime] = useState(null); // 任务开始时间
const [isTaskTimeFallback, setIsTaskTimeFallback] = useState(false); // 是否降级模式
const [hasLoaded, setHasLoaded] = useState(false); // 防止重复加载
+
+ // 证据材料数据
+ const [evidenceData, setEvidenceData] = useState({
+ applicantMaterials: [],
+ respondentMaterials: [],
+ loading: false,
+ error: null
+ });
+
+ // 调解协议数据
+ const [agreementData, setAgreementData] = useState({
+ content: '',
+ loading: false,
+ error: null
+ });
/**
* 保存数据到localStorage
@@ -266,6 +283,87 @@
console.log('Using fallback start time:', new Date(fallbackTime).toLocaleString());
}
};
+
+ /**
+ * 加载证据材料数据
+ * @param {Object} params - 参数对象
+ * @param {string} params.caseId - 案件ID
+ * @param {string} params.caseType - 案件类型
+ * @param {string} params.platformCode - 平台编码
+ */
+ const loadEvidenceData = async (params) => {
+ setEvidenceData(prev => ({ ...prev, loading: true, error: null }));
+
+ try {
+ const response = await EvidenceAPIService.getEvidenceList({
+ case_id: params.caseId,
+ case_type: params.caseType || params.caseTypeFirst,
+ platform_code: params.platformCode
+ });
+
+ console.log('Evidence 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) || [];
+
+ setEvidenceData({
+ applicantMaterials: applicantList,
+ respondentMaterials: respondentList,
+ loading: false,
+ error: null
+ });
+
+ console.log('Evidence data loaded:', { applicantList, respondentList });
+ } catch (err) {
+ console.error('加载证据材料失败:', err);
+ setEvidenceData(prev => ({
+ ...prev,
+ loading: false,
+ error: err.message || '加载证据材料失败'
+ }));
+ }
+ };
+
+ /**
+ * 加载调解协议数据
+ * @param {string} caseId - 案件ID
+ */
+ const loadAgreementData = async (caseId) => {
+ setAgreementData(prev => ({ ...prev, loading: true, error: null }));
+
+ try {
+ const response = await MediationAgreementAPIService.generateAgreement(caseId);
+
+ if (response?.data?.agreeContent) {
+ setAgreementData({
+ content: response.data.agreeContent,
+ loading: false,
+ error: null
+ });
+ console.log('Agreement data loaded');
+ } else {
+ setAgreementData({
+ content: '',
+ loading: false,
+ error: '协议内容为空'
+ });
+ }
+ } catch (err) {
+ console.error('加载调解协议失败:', err);
+ setAgreementData({
+ content: '',
+ loading: false,
+ error: err.message || '加载调解协议失败'
+ });
+ }
+ };
+
/**
* 加载案件数据
*/
@@ -325,6 +423,17 @@
setCaseData(timelineData);
setProcessNodes(Array.isArray(nodesData) ? nodesData : []); // 确保为数组
setHasLoaded(true); // 标记已加载
+
+ // 并行加载证据材料和调解协议数据(在终态检查之前,确保数据完整性)
+ await Promise.all([
+ loadEvidenceData({
+ caseId: params.caseId,
+ caseType: params.caseType || params.caseTypeFirst,
+ caseTypeFirst: params.caseTypeFirst,
+ platformCode: params.platform_code
+ }),
+ loadAgreementData(params.caseId)
+ ]);
// 检查终态状态(调解成功/失败/人工接管),终态不执行外呼和存储
const mediationState = timelineData.mediation?.state;
@@ -400,7 +509,13 @@
refreshData,
loadCaseData,
taskStartTime, // 任务开始时间
- isTaskTimeFallback // 是否降级模式
+ isTaskTimeFallback, // 是否降级模式
+ // 证据材料数据
+ evidenceData,
+ loadEvidenceData,
+ // 调解协议数据
+ agreementData,
+ loadAgreementData
};
return (
--
Gitblit v1.8.0