From ea12f670293895bc21bf52db1aa3cee9ab454fda Mon Sep 17 00:00:00 2001
From: tony.cheng <chengmingwei_1984122@126.com>
Date: Tue, 17 Mar 2026 14:12:03 +0800
Subject: [PATCH] refactor: 将终止/恢复按钮从TabContainer移动到FloatingControlPanel组件
---
web-app/src/components/dashboard/FloatingControlPanel.jsx | 179 +++++++++++++++++++++++++
web-app/src/components/dashboard/TabContainer.jsx | 201 ----------------------------
document/原型/index.html | 4
3 files changed, 181 insertions(+), 203 deletions(-)
diff --git "a/document/\345\216\237\345\236\213/index.html" "b/document/\345\216\237\345\236\213/index.html"
index 39f6197..8260512 100644
--- "a/document/\345\216\237\345\236\213/index.html"
+++ "b/document/\345\216\237\345\236\213/index.html"
@@ -1997,6 +1997,10 @@
<i class="fas fa-user-tie"></i>
人工接管
</button>
+ <button class="floating-control-btn" id="floatingStopBtn">
+ <i class="fas fa-user-tie"></i>
+ 人工接管
+ </button>
</div>
</div>
diff --git a/web-app/src/components/dashboard/FloatingControlPanel.jsx b/web-app/src/components/dashboard/FloatingControlPanel.jsx
index a0f75c3..093ba6d 100644
--- a/web-app/src/components/dashboard/FloatingControlPanel.jsx
+++ b/web-app/src/components/dashboard/FloatingControlPanel.jsx
@@ -1,14 +1,17 @@
import React, { useState } from 'react';
-import { Modal, message } from 'antd';
+import { Modal, message, Input } 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 { TextArea } = Input;
+
// 终态状态(不显示人工接管按钮)
const TERMINAL_STATES = [2, 3]; // 调解成功、调解失败
const TAKEOVER_STATE = 4; // 人工接管
+const PAUSED_STATE = 5; // 已终止/暂停状态
/**
* 获取案件ID
@@ -89,6 +92,12 @@
const { caseData, taskStartTime, isTaskTimeFallback, refreshData } = useCaseData();
const [takeoverLoading, setTakeoverLoading] = useState(false);
const [confirmVisible, setConfirmVisible] = useState(false);
+
+ // 状态控制相关状态
+ const [controlLoading, setControlLoading] = useState(false);
+ const [controlConfirmVisible, setControlConfirmVisible] = useState(false);
+ const [controlAction, setControlAction] = useState(null); // 'terminate' or 'resume'
+ const [remark, setRemark] = useState('');
const timeline = caseData || {};
const state = timeline.mediation?.state;
@@ -101,6 +110,98 @@
const statusText = state === 1
? `调解进行中-阶段${orderNo}:${nodeName}`
: (translateMediationState(state) || '调解进行中');
+
+ // ==================== 状态控制按钮逻辑 ====================
+
+ /**
+ * 判断是否显示状态控制按钮
+ * 状态为0(未开始)、1(进行中)、5(已终止)时显示
+ */
+ const shouldShowControlButton = () => {
+ const stateNum = Number(state);
+ // 进行中(1)显示终止按钮,已终止(5)显示恢复按钮
+ return stateNum === 1 || stateNum === 5;
+ };
+
+ /**
+ * 获取状态控制按钮属性
+ */
+ const getControlButtonProps = () => {
+ const stateNum = Number(state);
+
+ if (stateNum === 1) {
+ return {
+ text: '终止',
+ style: 'terminate',
+ action: 'terminate'
+ };
+ } else if (stateNum === 5) {
+ return {
+ text: '恢复',
+ style: 'resume',
+ action: 'resume'
+ };
+ }
+
+ return null;
+ };
+
+ /**
+ * 处理状态控制按钮点击
+ */
+ const handleControlButtonClick = () => {
+ const buttonProps = getControlButtonProps();
+ if (!buttonProps) return;
+
+ setControlAction(buttonProps.action);
+ setControlConfirmVisible(true);
+ };
+
+ /**
+ * 处理状态控制确认
+ */
+ const handleControlConfirmOk = async () => {
+ if (!controlAction) return;
+
+ setControlLoading(true);
+ try {
+ const params = getMergedParams();
+ const actionCode = controlAction === 'terminate' ? 0 : 1;
+
+ if (!params.caseId) {
+ throw new Error('案件ID不能为空');
+ }
+
+ await ProcessAPIService.updateMediationState(params.caseId, {
+ action: actionCode,
+ userName: localStorage.getItem('userName') || '调解员',
+ remark: remark || ''
+ });
+
+ message.success(controlAction === 'terminate' ? '调解已终止' : '调解已恢复');
+ setControlConfirmVisible(false);
+ setRemark('');
+ setControlAction(null);
+
+ refreshData();
+ } catch (error) {
+ console.error('状态更新失败:', error);
+ message.error(error.message || '状态更新失败,请稍后重试');
+ } finally {
+ setControlLoading(false);
+ }
+ };
+
+ /**
+ * 处理状态控制取消
+ */
+ const handleControlConfirmCancel = () => {
+ setControlConfirmVisible(false);
+ setRemark('');
+ setControlAction(null);
+ };
+
+ // ==================== 人工接管逻辑 ====================
/**
* 处理接管API调用
@@ -178,6 +279,40 @@
};
/**
+ * 渲染状态控制按钮(终止/恢复)
+ */
+ const renderStateControlButton = () => {
+ if (!shouldShowControlButton()) return null;
+
+ const buttonProps = getControlButtonProps();
+ if (!buttonProps) return null;
+
+ const isTerminate = buttonProps.style === 'terminate';
+
+ return (
+ <button
+ className="floating-control-btn"
+ onClick={handleControlButtonClick}
+ disabled={controlLoading}
+ style={{
+ ...(isTerminate ? {
+ background: '#1A6FB8',
+ } : {
+ background: '#52c41a',
+ }),
+ ...(controlLoading ? { opacity: 0.6, cursor: 'not-allowed' } : {})
+ }}
+ >
+ {controlLoading ? (
+ <><i className="fas fa-spinner fa-spin"></i>处理中...</>
+ ) : (
+ <><i className={isTerminate ? "fas fa-pause-circle" : "fas fa-play-circle"}></i>{buttonProps.text}</>
+ )}
+ </button>
+ );
+ };
+
+ /**
* 渲染控制区域(按钮或印章)
*/
const renderControlAction = () => {
@@ -186,8 +321,18 @@
return <TakeoverStamp state={state} />;
}
- // 调解中:显示接管按钮
- return <TakeoverButton loading={takeoverLoading} onClick={handleTakeover} />;
+ // 已终止状态(5):只显示恢复按钮
+ if (Number(state) === PAUSED_STATE) {
+ return renderStateControlButton();
+ }
+
+ // 调解中(1):显示终止按钮和人工接管按钮
+ return (
+ <>
+ {renderStateControlButton()}
+ <TakeoverButton loading={takeoverLoading} onClick={handleTakeover} />
+ </>
+ );
};
return (
@@ -211,6 +356,34 @@
</div>
</div>
+ {/* 状态控制确认对话框(终止/恢复) */}
+ <Modal
+ title={controlAction === 'terminate' ? '确认终止调解' : '确认恢复调解'}
+ visible={controlConfirmVisible}
+ onOk={handleControlConfirmOk}
+ onCancel={handleControlConfirmCancel}
+ okText="确定"
+ cancelText="取消"
+ confirmLoading={controlLoading}
+ >
+ <p>
+ {controlAction === 'terminate'
+ ? '确定要终止当前AI调解流程吗?终止后调解将暂停,可在适当时机恢复。'
+ : '确定要恢复AI调解流程吗?恢复后将从当前位置继续调解。'}
+ </p>
+ <div style={{ marginTop: 15 }}>
+ <label style={{ display: 'block', marginBottom: 5, fontWeight: 500 }}>
+ 备注(可选):
+ </label>
+ <TextArea
+ value={remark}
+ onChange={(e) => setRemark(e.target.value)}
+ placeholder="请输入操作备注..."
+ rows={3}
+ />
+ </div>
+ </Modal>
+
{/* 人工接管确认对话框 */}
<Modal
title="人工接管确认"
diff --git a/web-app/src/components/dashboard/TabContainer.jsx b/web-app/src/components/dashboard/TabContainer.jsx
index 490f498..e732c80 100644
--- a/web-app/src/components/dashboard/TabContainer.jsx
+++ b/web-app/src/components/dashboard/TabContainer.jsx
@@ -154,26 +154,10 @@
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, refreshData } = useCaseData();
+ const { caseData } = 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) => {
@@ -362,114 +346,6 @@
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={{
@@ -564,81 +440,6 @@
</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>
</>
);
};
--
Gitblit v1.8.0