tony.cheng
2026-02-12 4d5e72b8da64108455b541eeb13aef5132b6bbc7
web-app/src/components/dashboard/FloatingControlPanel.jsx
@@ -1,43 +1,197 @@
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">
@@ -53,12 +207,23 @@
        </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>
    </>
  );
};