| | |
| | | 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'; |
| | |
| | | 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) => { |
| | |
| | | return avatarText || (avatar === 'applicant' ? '申' : avatar === 'respondent' ? '被' : '调'); |
| | | }; |
| | | |
| | | // 状态控制按钮显示逻辑 |
| | | const shouldShowControlButton = () => { |
| | | // 转换为数字类型进行比较,兼容字符串和数字 |
| | | const stateNum = Number(caseState); |
| | | const show = stateNum === 0 || stateNum === 1 || stateNum === 5; |
| | | console.log('状态控制按钮显示检查:', { |
| | | caseState, |
| | | caseStateType: typeof caseState, |
| | | stateNum, |
| | | show, |
| | | conditions: { |
| | | 'stateNum === 0': stateNum === 0, |
| | | 'stateNum === 1': stateNum === 1, |
| | | 'stateNum === 5': stateNum === 5 |
| | | } |
| | | }); |
| | | return show; |
| | | }; |
| | | |
| | | const getControlButtonProps = () => { |
| | | // 转换为数字类型进行比较 |
| | | const stateNum = Number(caseState); |
| | | console.log('获取按钮属性:', { caseState, caseStateType: typeof caseState, stateNum }); |
| | | |
| | | if (stateNum === 0 || stateNum === 1) { |
| | | return { |
| | | text: '终止', |
| | | style: 'terminate', |
| | | action: 'terminate' |
| | | }; |
| | | } else if (stateNum === 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={{ |
| | |
| | | </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> |
| | | </> |
| | | ); |
| | | }; |
| | |
| | | * 证据材料汇总 |
| | | */ |
| | | 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); |
| | |
| | | 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) => { |
| | |
| | | <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> |
| | |
| | | message.success('审核通过!'); |
| | | handleCloseAuditModal(); |
| | | // 重新加载数据 |
| | | loadData(); |
| | | handleReload(); |
| | | } catch (err) { |
| | | console.error('审核失败:', err); |
| | | message.error('审核失败,请重试'); |
| | |
| | | message.success('材料已退回,等待补充提交'); |
| | | setReturnModalVisible(false); |
| | | handleCloseAuditModal(); |
| | | loadData(); |
| | | handleReload(); |
| | | } catch (err) { |
| | | console.error('退回失败:', err); |
| | | message.error('退回失败,请重试'); |
| | |
| | | * 调解协议 |
| | | */ |
| | | 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); |
| | |
| | | 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) => { |
| | |
| | | }); |
| | | }; |
| | | |
| | | // 首次加载协议内容 |
| | | 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 () => { |
| | |
| | | if (!caseId) return; |
| | | setActionLoading(prev => ({ ...prev, download: true })); |
| | | try { |
| | | await MediationAgreementAPIService.downloadAgreement(caseId); |
| | | // 调用API获取PDF文件流 |
| | | const response = await MediationAgreementAPIService.downloadAgreement(caseId); |
| | | |
| | | // 创建Blob对象(PDF格式) |
| | | const blob = new Blob([response.data], { |
| | | type: 'application/pdf' |
| | | }); |
| | | |
| | | // 创建下载链接 |
| | | const url = window.URL.createObjectURL(blob); |
| | | const link = document.createElement('a'); |
| | | link.href = url; |
| | | link.download = `调解协议_${caseId}.pdf`; |
| | | |
| | | // 触发下载 |
| | | document.body.appendChild(link); |
| | | link.click(); |
| | | document.body.removeChild(link); |
| | | |
| | | // 清理URL对象 |
| | | window.URL.revokeObjectURL(url); |
| | | |
| | | message.success('协议下载成功!'); |
| | | } catch (err) { |
| | | console.error('下载协议失败:', err); |
| | |
| | | if (!caseId) return; |
| | | setActionLoading(prev => ({ ...prev, regenerate: true })); |
| | | try { |
| | | const response = await MediationAgreementAPIService.generateAgreement(caseId); |
| | | const response = await MediationAgreementAPIService.regenerateAgreement(caseId); |
| | | if (response?.data?.agreeContent) { |
| | | setAgreementContent(response.data.agreeContent); |
| | | loadAgreementData(caseId); |
| | | message.success('协议重新生成成功!'); |
| | | } |
| | | } catch (err) { |
| | |
| | | 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('保存协议失败,请稍后重试'); |
| | |
| | | <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> |