| | |
| | | import React from 'react'; |
| | | import React, { useState } from 'react'; |
| | | import { Modal, message } from 'antd'; |
| | | import { useCaseData } from '../../contexts/CaseDataContext'; |
| | | import { translateMediationState } from '../../utils/stateTranslator'; |
| | | import { useTaskTimer } from '../../hooks/useTaskTimer'; |
| | | import ProcessAPIService from '../../services/ProcessAPIService'; |
| | | import { getMergedParams } from '../../utils/urlParams'; |
| | | |
| | | // 终态状态(不显示人工接管按钮) |
| | | const TERMINAL_STATES = [2, 3]; // 调解成功、调解失败 |
| | | const TAKEOVER_STATE = 4; // 人工接管 |
| | | |
| | | /** |
| | | * 获取案件ID |
| | | * 优先级:Context > URL参数 > localStorage |
| | | */ |
| | | const resolveCaseId = (caseData) => { |
| | | // 1. 从Context获取 |
| | | if (caseData?.case_id) return String(caseData.case_id); |
| | | |
| | | // 2. 从 URL 参数获取 |
| | | const params = getMergedParams(); |
| | | if (params.caseId) return String(params.caseId); |
| | | |
| | | // 3. 从localStorage获取 |
| | | try { |
| | | const stored = JSON.parse(localStorage.getItem('case_data_timeline') || '{}'); |
| | | if (stored.case_id) return String(stored.case_id); |
| | | } catch { /* ignore */ } |
| | | |
| | | return null; |
| | | }; |
| | | |
| | | /** |
| | | * 获取当前用户名 |
| | | */ |
| | | const resolveUserName = () => { |
| | | try { |
| | | const user = JSON.parse(localStorage.getItem('currentUser') || '{}'); |
| | | return user.user_name || 'No User'; |
| | | } catch { |
| | | return 'No User'; |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * 印章组件 - 终态状态展示(调解成功/失败/人工接管) |
| | | */ |
| | | const TakeoverStamp = ({ state }) => { |
| | | const stateTextMap = { |
| | | 2: '调解成功', |
| | | 3: '调解失败', |
| | | 4: '已人工接管' |
| | | }; |
| | | const text = stateTextMap[state] || '已人工接管'; |
| | | |
| | | return ( |
| | | <div className="takeover-stamp"> |
| | | <span className="takeover-stamp-text"> |
| | | <i className="fas fa-stamp"></i> |
| | | {text} |
| | | </span> |
| | | </div> |
| | | ); |
| | | }; |
| | | |
| | | /** |
| | | * 接管按钮组件 |
| | | */ |
| | | const TakeoverButton = ({ loading, onClick }) => ( |
| | | <button |
| | | className="floating-control-btn" |
| | | onClick={onClick} |
| | | disabled={loading} |
| | | style={loading ? { opacity: 0.6, cursor: 'not-allowed' } : {}} |
| | | > |
| | | {loading ? ( |
| | | <><i className="fas fa-spinner fa-spin"></i>接管中...</> |
| | | ) : ( |
| | | <><i className="fas fa-user-tie"></i>人工接管</> |
| | | )} |
| | | </button> |
| | | ); |
| | | |
| | | /** |
| | | * 底部悬浮控制面板 |
| | | */ |
| | | const FloatingControlPanel = ({ onManualTakeover }) => { |
| | | const { caseData, taskStartTime, isTaskTimeFallback } = useCaseData(); |
| | | const timeline = caseData || {}; |
| | | const FloatingControlPanel = () => { |
| | | const { caseData, taskStartTime, isTaskTimeFallback, refreshData } = useCaseData(); |
| | | const [takeoverLoading, setTakeoverLoading] = useState(false); |
| | | const [confirmVisible, setConfirmVisible] = useState(false); |
| | | |
| | | const timeline = caseData || {}; |
| | | const state = timeline.mediation?.state; |
| | | const nodeName = timeline.current_node?.node_name || ''; |
| | | const orderNo = timeline.current_node?.order_no || 1; |
| | | |
| | | // 使用任务计时Hook获取实时时间 |
| | | const { formattedTime } = useTaskTimer(taskStartTime, isTaskTimeFallback); |
| | | |
| | | // 根据状态生成状态文本 |
| | | let statusText = ''; |
| | | if (state === 1) { |
| | | // 调解中状态 |
| | | statusText = `调解进行中-阶段${orderNo}:${nodeName}`; |
| | | } else { |
| | | // 其他状态 |
| | | statusText = translateMediationState(state) || '调解进行中'; |
| | | // 生成状态文本 |
| | | const statusText = state === 1 |
| | | ? `调解进行中-阶段${orderNo}:${nodeName}` |
| | | : (translateMediationState(state) || '调解进行中'); |
| | | |
| | | /** |
| | | * 处理接管API调用 |
| | | */ |
| | | const executeTakeover = async () => { |
| | | console.log('executeTakeover 开始执行'); |
| | | const caseId = resolveCaseId(caseData); |
| | | console.log('获取到的 caseId:', caseId); |
| | | |
| | | if (!caseId) { |
| | | message.error('无法获取案件ID,请刷新页面后重试'); |
| | | return; |
| | | } |
| | | |
| | | const handleTakeover = () => { |
| | | if (onManualTakeover) { |
| | | onManualTakeover(); |
| | | } else { |
| | | if (window.confirm('确认要人工接管调解吗?接管后将结束AI调解,由工作人员继续处理。')) { |
| | | alert('AI调解已结束,已转为人工接管模式。工作人员将继续处理本次调解。'); |
| | | } |
| | | const userName = resolveUserName(); |
| | | console.log('获取到的 userName:', userName); |
| | | |
| | | setTakeoverLoading(true); |
| | | try { |
| | | console.log('调用 ProcessAPIService.takeover, caseId:', caseId, ', userName:', userName); |
| | | const response = await ProcessAPIService.takeover(caseId, { userName }); |
| | | console.log('接管API返回:', response); |
| | | message.success('人工接管成功'); |
| | | refreshData(); |
| | | } catch (err) { |
| | | console.error('接管API异常:', err); |
| | | handleTakeoverError(err); |
| | | } finally { |
| | | setTakeoverLoading(false); |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * 处理接管错误响应 |
| | | */ |
| | | const handleTakeoverError = (err) => { |
| | | const status = err?.response?.status || err?.status; |
| | | const msg = err?.response?.data?.message || err?.message || ''; |
| | | |
| | | // 400错误:已被接管或已结束 → 重新加载页面 |
| | | if (status === 400) { |
| | | console.warn('接管失败(400):', msg); |
| | | refreshData(); |
| | | return; |
| | | } |
| | | |
| | | // 其他错误 → 提示用户 |
| | | console.error('人工接管失败:', err); |
| | | message.error(msg || '人工接管失败,请稍后重试'); |
| | | }; |
| | | |
| | | /** |
| | | * 点击接管按钮 - 显示确认对话框 |
| | | */ |
| | | const handleTakeover = () => { |
| | | console.log('点击人工接管按钮'); |
| | | setConfirmVisible(true); |
| | | }; |
| | | |
| | | /** |
| | | * 确认接管 |
| | | */ |
| | | const handleConfirmOk = async () => { |
| | | console.log('用户点击确定,开始执行接管'); |
| | | setConfirmVisible(false); |
| | | await executeTakeover(); |
| | | }; |
| | | |
| | | /** |
| | | * 取消接管 |
| | | */ |
| | | const handleConfirmCancel = () => { |
| | | console.log('用户点击取消'); |
| | | setConfirmVisible(false); |
| | | }; |
| | | |
| | | /** |
| | | * 渲染控制区域(按钮或印章) |
| | | */ |
| | | const renderControlAction = () => { |
| | | // 终态状态(调解成功/失败/人工接管):显示印章 |
| | | if (TERMINAL_STATES.includes(state) || state === TAKEOVER_STATE) { |
| | | return <TakeoverStamp state={state} />; |
| | | } |
| | | |
| | | // 调解中:显示接管按钮 |
| | | return <TakeoverButton loading={takeoverLoading} onClick={handleTakeover} />; |
| | | }; |
| | | |
| | | return ( |
| | | <> |
| | | <div className="floating-control-panel"> |
| | | <div className="control-status"> |
| | | <div className="status-indicator"> |
| | |
| | | </div> |
| | | </div> |
| | | <div className="control-action"> |
| | | <button className="floating-control-btn" onClick={handleTakeover}> |
| | | <i className="fas fa-user-tie"></i> |
| | | 人工接管 |
| | | </button> |
| | | {renderControlAction()} |
| | | </div> |
| | | </div> |
| | | |
| | | {/* 人工接管确认对话框 */} |
| | | <Modal |
| | | title="人工接管确认" |
| | | visible={confirmVisible} |
| | | onOk={handleConfirmOk} |
| | | onCancel={handleConfirmCancel} |
| | | okText="确定" |
| | | cancelText="取消" |
| | | confirmLoading={takeoverLoading} |
| | | > |
| | | <p>确定人工接管本调解案件吗?</p> |
| | | </Modal> |
| | | </> |
| | | ); |
| | | }; |
| | | |