/**
|
* 案件数据全局Context
|
* 提供案件数据的全局状态管理和localStorage持久化
|
*/
|
|
import React, { createContext, useContext, useState, useEffect, useRef } from 'react';
|
import { message } from 'antd';
|
import ProcessAPIService from '../services/ProcessAPIService';
|
import { getMergedParams } from '../utils/urlParams';
|
import { mockTimelineData } from '../mocks/timeline';
|
import { getFallbackStartTime, parseTimeString } from '../utils/timeFormatter';
|
|
// 创建Context
|
const CaseDataContext = createContext(null);
|
|
// localStorage键名
|
const STORAGE_KEY = 'case_data_timeline';
|
|
/**
|
* CaseDataProvider组件
|
* 包裹需要访问案件数据的组件树
|
*/
|
export const CaseDataProvider = ({ children }) => {
|
const [caseData, setCaseData] = useState(null);
|
const [processNodes, setProcessNodes] = useState([]); // 流程节点数据
|
const [loading, setLoading] = useState(false);
|
const [error, setError] = useState(null);
|
const [taskStartTime, setTaskStartTime] = useState(null); // 任务开始时间
|
const [isTaskTimeFallback, setIsTaskTimeFallback] = useState(false); // 是否降级模式
|
const [hasLoaded, setHasLoaded] = useState(false); // 防止重复加载
|
|
/**
|
* 从localStorage读取数据
|
*/
|
const loadFromStorage = () => {
|
try {
|
const stored = localStorage.getItem(STORAGE_KEY);
|
if (stored) {
|
return JSON.parse(stored);
|
}
|
} catch (err) {
|
console.error('Failed to load data from localStorage:', err);
|
}
|
return null;
|
};
|
|
/**
|
* 保存数据到localStorage
|
*/
|
const saveToStorage = (data) => {
|
try {
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
} catch (err) {
|
console.error('Failed to save data to localStorage:', err);
|
}
|
};
|
|
/**
|
* 加载任务时间数据
|
*/
|
const loadTaskTime = async (timeline) => {
|
try {
|
const mediationId = timeline.id;
|
const nodeId = timeline.current_node?.id;
|
|
if (!mediationId || !nodeId) {
|
throw new Error('缺少必要的参数: mediation_id 或 node_id');
|
}
|
|
console.log('Loading task time with:', { mediationId, nodeId });
|
|
const response = await ProcessAPIService.getTaskTime(mediationId, nodeId);
|
|
// 解析API返回的开始时间
|
const startTimeStr = response.data?.startTime;
|
if (startTimeStr) {
|
const startTime = parseTimeString(startTimeStr);
|
if (startTime) {
|
setTaskStartTime(startTime);
|
setIsTaskTimeFallback(false);
|
console.log('Task start time loaded:', new Date(startTime).toLocaleString());
|
return;
|
}
|
}
|
|
throw new Error('无法解析开始时间');
|
} catch (err) {
|
console.error('Failed to load task time:', err);
|
|
// 降级到本地计时
|
const fallbackTime = getFallbackStartTime();
|
setTaskStartTime(fallbackTime);
|
setIsTaskTimeFallback(true);
|
message.warning('任务时间API不可用,使用本地计时');
|
console.log('Using fallback start time:', new Date(fallbackTime).toLocaleString());
|
}
|
};
|
/**
|
* 加载案件数据
|
*/
|
const loadCaseData = async (forceRefresh = false) => {
|
// 防止重复加载(除非强制刷新)
|
if (hasLoaded && !forceRefresh) {
|
console.log('Data already loaded, skipping...');
|
return;
|
}
|
|
setLoading(true);
|
setError(null);
|
|
try {
|
// 获取URL参数(合并默认值)
|
const params = getMergedParams();
|
|
console.log('Loading case data with params:', params);
|
|
// 调用API获取数据
|
const response = await ProcessAPIService.getCaseProcessInfo(
|
params.caseId,
|
{
|
caseTypeFirst: params.caseTypeFirst,
|
platform_code: params.platform_code
|
}
|
);
|
|
// 提取timeline数据
|
const timelineData = response.timeline || response.data?.timeline || response;
|
|
// 提取nodes数据(确保为数组)
|
const nodesData = response.nodes || response.data?.nodes || [];
|
|
console.log('API Response:', response);
|
console.log('Extracted nodesData:', nodesData);
|
|
// 更新状态
|
setCaseData(timelineData);
|
setProcessNodes(Array.isArray(nodesData) ? nodesData : []); // 确保为数组
|
setHasLoaded(true); // 标记已加载
|
|
// 保存到localStorage
|
saveToStorage(timelineData);
|
|
// 加载任务时间数据
|
await loadTaskTime(timelineData);
|
|
console.log('Case data loaded successfully:', timelineData);
|
} catch (err) {
|
console.error('Failed to load case data:', err);
|
setError(err.message || '加载案件数据失败');
|
|
// 显示错误提示
|
message.error('加载案件数据失败,请稍后重试');
|
|
// 使用Mock数据(缓存数据不包含nodes,所以统一使用Mock)
|
console.log('===== 使用Mock数据 =====');
|
const mockData = mockTimelineData.data.timeline;
|
const mockNodes = mockTimelineData.data.nodes || [];
|
console.log('mockData:', mockData);
|
console.log('mockNodes:', mockNodes);
|
setCaseData(mockData);
|
setProcessNodes(mockNodes);
|
saveToStorage(mockData);
|
setHasLoaded(true);
|
|
// Mock数据也加载任务时间
|
await loadTaskTime(mockData);
|
} finally {
|
setLoading(false);
|
}
|
};
|
|
/**
|
* 刷新数据(强制从API获取)
|
*/
|
const refreshData = () => {
|
loadCaseData(true);
|
};
|
|
// 防止重复加载的引用标识
|
const loadInitRef = useRef(false);
|
|
/**
|
* 组件挂载时加载数据
|
*/
|
useEffect(() => {
|
console.log('===== CaseDataContext useEffect triggered =====');
|
console.log('loadInitRef.current:', loadInitRef.current);
|
console.log('hasLoaded:', hasLoaded);
|
console.log('processNodes:', processNodes);
|
|
// StrictMode下防止双重调用
|
if (!loadInitRef.current) {
|
loadInitRef.current = true;
|
console.log('Calling loadCaseData()...');
|
loadCaseData();
|
} else {
|
console.log('loadCaseData() skipped due to loadInitRef.current === true');
|
}
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, []); // 只在挂载时执行一次
|
|
// 提供的值
|
const value = {
|
caseData,
|
processNodes, // 流程节点数据
|
loading,
|
error,
|
refreshData,
|
loadCaseData,
|
taskStartTime, // 任务开始时间
|
isTaskTimeFallback // 是否降级模式
|
};
|
|
return (
|
<CaseDataContext.Provider value={value}>
|
{children}
|
</CaseDataContext.Provider>
|
);
|
};
|
|
/**
|
* 自定义Hook:使用案件数据
|
* @returns {Object} { caseData, loading, error, refreshData, loadCaseData }
|
*/
|
export const useCaseData = () => {
|
const context = useContext(CaseDataContext);
|
|
if (!context) {
|
throw new Error('useCaseData must be used within CaseDataProvider');
|
}
|
|
return context;
|
};
|
|
export default CaseDataContext;
|