From d31819515e4aac228f26e7cbb92c89e0f520e8ac Mon Sep 17 00:00:00 2001
From: tony.cheng <chengmingwei_1984122@126.com>
Date: Mon, 09 Feb 2026 18:08:01 +0800
Subject: [PATCH] feat: 实现智能外呼自动触发与状态监控功能
---
web-app/src/components/dashboard/TabContainer.jsx | 1254 +++++++++++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 1,113 insertions(+), 141 deletions(-)
diff --git a/web-app/src/components/dashboard/TabContainer.jsx b/web-app/src/components/dashboard/TabContainer.jsx
index 2b42c5f..b16768d 100644
--- a/web-app/src/components/dashboard/TabContainer.jsx
+++ b/web-app/src/components/dashboard/TabContainer.jsx
@@ -1,21 +1,33 @@
-import React, { useState, useEffect } from 'react';
+import React, { useState, useEffect, useRef } from 'react';
import { useCaseData } from '../../contexts/CaseDataContext';
import { formatDuration, formatSuccessRate, formatRoundCount } from '../../utils/stateTranslator';
import ProcessAPIService from '../../services/ProcessAPIService';
-import { message, Spin } from 'antd';
+import EvidenceAPIService from '../../services/EvidenceAPIService';
+import MediationAgreementAPIService from '../../services/MediationAgreementAPIService';
+import { getMergedParams } from '../../utils/urlParams';
+import { message, Spin, Tag, Modal, Button, Input, Image } from 'antd';
+
+const { TextArea } = Input;
/**
* 选项卡容器组件 - 4个选项卡
*/
const TabContainer = () => {
const [activeTab, setActiveTab] = useState('mediation-data-board');
+ // 证据材料汇总Tab的审核状态badge
+ const [evidenceBadge, setEvidenceBadge] = useState(null);
const tabs = [
{ key: 'mediation-data-board', label: '调解分析', icon: 'fa-chart-line' },
{ key: 'mediation-board', label: 'AI调解实时看板', icon: 'fa-tv' },
- { key: 'evidence-board', label: '证据材料汇总', icon: 'fa-file-alt', badge: '待审核' },
+ { key: 'evidence-board', label: '证据材料汇总', icon: 'fa-file-alt', badge: evidenceBadge },
{ key: 'agreement-section', label: '调解协议', icon: 'fa-file-contract', badge: '待确认' },
];
+
+ // 更新证据材料汇总Tab的badge状态
+ const handleEvidenceStatusChange = (status) => {
+ setEvidenceBadge(status);
+ };
return (
<div className="tab-container">
@@ -51,7 +63,7 @@
{/* 证据材料汇总 */}
<div className={`tab-pane ${activeTab === 'evidence-board' ? 'active' : ''}`}>
<div className="tab-content-area">
- <EvidenceBoard />
+ <EvidenceBoard onStatusChange={handleEvidenceStatusChange} />
</div>
</div>
@@ -426,20 +438,316 @@
/**
* 证据材料汇总
*/
-const EvidenceBoard = () => {
- const applicantEvidence = [
- { name: '劳动合同', desc: '2023年8月1日-2026年1月31日,约定月工资¥14,000', time: '2026-01-15 09:35', status: 'verified' },
- { name: '2026年1-3月工资条', desc: '显示工资发放记录,1月后无发放记录', time: '2026-01-15 09:36', status: 'verified' },
- { name: '2026年第一季度考勤记录', desc: '显示全勤,无请假缺勤记录', time: '2026-01-15 09:37', status: 'verified' },
- { name: '绩效考评表', desc: '第一季度绩效评分85分,达到奖金发放标准', time: '2026-01-15 09:38', status: 'verified' },
- { name: '解除劳动合同通知书', desc: '公司2026年1月15日单方面解除合同', time: '2026-01-15 09:45', status: 'pending' },
- ];
+const EvidenceBoard = ({ onStatusChange }) => {
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+ // 审核弹窗状态
+ 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);
+ const [evidenceImages, setEvidenceImages] = useState([]);
+ const [platformUrl, setPlatformUrl] = useState('');
- const respondentEvidence = [
- { name: '2026年1-3月考勤异常记录', desc: '显示李晓明1月有2天事假,应扣款¥1,333', time: '2026-01-15 09:50', status: 'pending' },
- { name: '第一季度销售业绩报表', desc: '显示李晓明未完成销售KPI指标(完成率87%)', time: '2026-01-15 09:52', status: 'pending' },
- { name: '员工离职申请表', desc: '李晓明2026年1月10日提交的辞职申请', time: '2026-01-15 09:55', status: 'pending' },
- ];
+ // 计算整体审核状态(综合申请人和被申请人所有材料)
+ const calculateOverallTabStatus = (applicantList, respondentList) => {
+ const allMaterials = [...applicantList, ...respondentList];
+ if (!allMaterials || allMaterials.length === 0) return null;
+
+ // 存在驳回状态 -> 显示"驳回"
+ const hasRejected = allMaterials.some(m => m.audit_state === -2);
+ if (hasRejected) return '驳回';
+
+ // 存在待审核状态(0或-2) -> 显示"待审核"
+ const hasPending = allMaterials.some(m => m.audit_state === 0 || m.audit_state === -2);
+ if (hasPending) return '待审核';
+
+ // 所有材料都已审核 -> 显示"已审核"
+ return '已审核';
+ };
+
+ // 加载数据
+ const loadData = async () => {
+ // 使用getMergedParams获取参数(URL参数优先,默认值兜底)
+ const params = getMergedParams();
+ const caseId = params.caseId;
+ const 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);
+ }
+ };
+
+ // 组件挂载时加载数据
+ useEffect(() => {
+ loadData();
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
+
+ // 计算区域内整体审核状态(用于卡片标题旁的Tag显示)
+ const calculateOverallStatus = (materials) => {
+ if (!materials || materials.length === 0) return { text: '无数据', color: 'default' };
+ const hasRejected = materials.some(m => m.audit_state === -2);
+ if (hasRejected) return { text: '驳回', color: 'red' };
+ const hasPending = materials.some(m => m.audit_state === 0 || m.audit_state === -2);
+ if (hasPending) return { text: '待审核', color: 'orange' };
+ return { text: '已审核', color: 'green' };
+ };
+
+ const applicantStatus = calculateOverallStatus(applicantMaterials);
+ const respondentStatus = calculateOverallStatus(respondentMaterials);
+
+ // Loading状态
+ if (loading) {
+ return (
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: 300 }}>
+ <Spin size="large" tip="加载证据材料中..." />
+ </div>
+ );
+ }
+
+ // 错误状态
+ if (error) {
+ return (
+ <div style={{ textAlign: 'center', padding: 40, color: '#ff4d4f' }}>
+ <div style={{ fontSize: '1.2rem', marginBottom: 10 }}>
+ <i className="fas fa-exclamation-circle"></i> 数据加载失败
+ </div>
+ <div>{error}</div>
+ <button onClick={loadData} style={{
+ marginTop: 15, padding: '8px 16px', backgroundColor: '#1890ff',
+ color: 'white', border: 'none', borderRadius: 4, cursor: 'pointer'
+ }}>重新加载</button>
+ </div>
+ );
+ }
+
+ // 获取材料审核状态样式
+ const getAuditStatusStyle = (auditState) => {
+ if (auditState === 1) {
+ // 已审核 - 绿色边框
+ return {
+ color: '#52c41a',
+ background: '#f6ffed',
+ border: '1px solid #b7eb8f',
+ borderRadius: 4,
+ padding: '2px 10px',
+ fontSize: '0.8rem',
+ fontWeight: 500,
+ };
+ }
+ // 待审核 - 橙色边框
+ return {
+ color: '#fa8c16',
+ background: '#fff7e6',
+ border: '1px solid #ffd591',
+ borderRadius: 4,
+ padding: '2px 10px',
+ fontSize: '0.8rem',
+ fontWeight: 500,
+ };
+ };
+
+ // 打开审核弹窗
+ const handleOpenAuditModal = async (item, type) => {
+ // 获取参数
+ const params = getMergedParams();
+ const caseId = params.caseId;
+
+ // 设置当前审核项
+ const auditItem = {
+ ...item,
+ personType: type,
+ per_type: type === 'applicant' ? '15_020008-1' : '15_020008-2'
+ };
+ setCurrentAuditItem(auditItem);
+ setAuditRemark('');
+ setAuditModalVisible(true);
+
+ // 获取platform_url
+ try {
+ const caseDataTimeline = JSON.parse(localStorage.getItem('case_data_timeline') || '{}');
+ const pUrl = caseDataTimeline?.process_config?.platform_url || '';
+ setPlatformUrl(pUrl);
+ } catch (e) {
+ console.warn('获取platform_url失败:', e);
+ }
+
+ // 调用API获取弹窗数据
+ setModalDataLoading(true);
+ setPersonInfo(null);
+ setEvidenceImages([]);
+
+ try {
+ // 并行调用两个API
+ const [personInfoRes, evidenceListRes] = await Promise.all([
+ EvidenceAPIService.getPersonInfo({
+ case_id: caseId,
+ evidence_type: item.evidence_type,
+ per_type: auditItem.per_type
+ }),
+ EvidenceAPIService.getEvidenceListByPerson({
+ case_id: caseId,
+ evidence_type: item.evidence_type,
+ per_type: auditItem.per_type,
+ person_id: item.person_id
+ })
+ ]);
+
+ console.log('getPersonInfo response:', personInfoRes);
+ console.log('getEvidenceListByPerson response:', evidenceListRes);
+
+ // 设置当事人信息
+ if (personInfoRes?.data) {
+ setPersonInfo(personInfoRes.data);
+ }
+
+ // 设置图片列表
+ if (evidenceListRes?.data && Array.isArray(evidenceListRes.data)) {
+ setEvidenceImages(evidenceListRes.data);
+ }
+ } catch (err) {
+ console.error('加载弹窗数据失败:', err);
+ message.error('加载材料详情失败');
+ } finally {
+ setModalDataLoading(false);
+ }
+ };
+
+ // 关闭审核弹窗
+ const handleCloseAuditModal = () => {
+ setAuditModalVisible(false);
+ setCurrentAuditItem(null);
+ setAuditRemark('');
+ setPersonInfo(null);
+ setEvidenceImages([]);
+ };
+
+ // 审核通过
+ const handleAuditPass = async () => {
+ if (!currentAuditItem) return;
+
+ const params = getMergedParams();
+ const caseId = params.caseId;
+
+ setAuditLoading(true);
+ try {
+ // 获取文件ID列表
+ const fileIdList = evidenceImages.map(img => img.file_id || img.id).filter(Boolean);
+
+ await EvidenceAPIService.auditEvidence({
+ case_id: caseId,
+ evidence_type: currentAuditItem.evidence_type,
+ person_id: currentAuditItem.person_id,
+ file_id_list: fileIdList,
+ audit_user: '当前用户', // TODO: 从登录信息获取
+ audit_state: 1,
+ audit_remark: ''
+ });
+
+ message.success('审核通过!');
+ handleCloseAuditModal();
+ // 重新加载数据
+ loadData();
+ } catch (err) {
+ console.error('审核失败:', err);
+ message.error('审核失败,请重试');
+ } finally {
+ setAuditLoading(false);
+ }
+ };
+
+ // 退回补充
+ const handleAuditReturn = async () => {
+ if (!currentAuditItem) return;
+ if (!auditRemark.trim()) {
+ message.warning('请填写退回意见');
+ return;
+ }
+
+ const params = getMergedParams();
+ const caseId = params.caseId;
+
+ setAuditLoading(true);
+ try {
+ // 获取文件ID列表
+ const fileIdList = evidenceImages.map(img => img.file_id || img.id).filter(Boolean);
+
+ await EvidenceAPIService.auditEvidence({
+ case_id: caseId,
+ evidence_type: currentAuditItem.evidence_type,
+ person_id: currentAuditItem.person_id,
+ file_id_list: fileIdList,
+ audit_user: '当前用户', // TODO: 从登录信息获取
+ audit_state: 2,
+ audit_remark: auditRemark.trim()
+ });
+
+ message.success('材料已退回,等待补充提交');
+ setReturnModalVisible(false);
+ handleCloseAuditModal();
+ loadData();
+ } catch (err) {
+ console.error('退回失败:', err);
+ message.error('退回失败,请重试');
+ } finally {
+ setAuditLoading(false);
+ }
+ };
+
+ // 打开退回弹窗
+ const handleOpenReturnModal = () => {
+ setAuditRemark('');
+ setReturnModalVisible(true);
+ };
+
+ // 关闭退回弹窗
+ const handleCloseReturnModal = () => {
+ setReturnModalVisible(false);
+ setAuditRemark('');
+ };
const renderEvidenceItem = (item, type) => (
<div key={item.name} className="evidence-item" style={{
@@ -451,27 +759,52 @@
justifyContent: 'space-between',
alignItems: 'flex-start',
marginBottom: 10,
+ position: 'relative',
}}>
- <div style={{ flex: 1 }}>
+ <div style={{ flex: 1, paddingRight: 70 }}>
<div style={{ fontWeight: 600, fontSize: '0.9rem', marginBottom: 4, color: 'var(--dark-color)' }}>{item.name}</div>
- <div style={{ fontSize: '0.8rem', color: 'var(--gray-color)', lineHeight: 1.4 }}>{item.desc}</div>
+ <div style={{ fontSize: '0.8rem', color: 'var(--gray-color)', lineHeight: 1.4 }}>{item.result}</div>
<div style={{ fontSize: '0.75rem', color: 'var(--gray-color)', marginTop: 4, display: 'flex', alignItems: 'center', gap: 5 }}>
<i className="far fa-clock"></i>
- <span>上传时间:{item.time}</span>
+ <span>上传时间:{item.update_time || item.create_time}</span>
</div>
+ {/* 待审核时显示审核按钮 */}
+ {item.audit_state !== 1 && (
+ <button
+ onClick={() => handleOpenAuditModal(item, type)}
+ style={{
+ marginTop: 8,
+ padding: '4px 12px',
+ fontSize: '0.75rem',
+ background: '#1A6FB8',
+ color: 'white',
+ border: 'none',
+ borderRadius: 4,
+ cursor: 'pointer',
+ fontWeight: 600,
+ transition: 'all 0.2s ease',
+ }}
+ onMouseOver={(e) => {
+ e.target.style.background = '#0d4a8a';
+ e.target.style.transform = 'translateY(-1px)';
+ }}
+ onMouseOut={(e) => {
+ e.target.style.background = '#1A6FB8';
+ e.target.style.transform = 'translateY(0)';
+ }}
+ >
+ 审核
+ </button>
+ )}
</div>
- <div style={{ marginLeft: 10 }}>
- <span style={{
- fontSize: '0.75rem',
- padding: '3px 8px',
- borderRadius: 12,
- fontWeight: 600,
- background: item.status === 'verified' ? '#d4edda' : '#fff3cd',
- color: item.status === 'verified' ? '#155724' : '#856404',
- border: `1px solid ${item.status === 'verified' ? '#c3e6cb' : '#ffeaa7'}`,
- }}>
- {item.status === 'verified' ? '已审核' : '待审核'}
- </span>
+ {/* 右上角审核状态标签 */}
+ <div style={{
+ position: 'absolute',
+ top: 12,
+ right: 12,
+ ...getAuditStatusStyle(item.audit_state)
+ }}>
+ {item.audit_state === 1 ? '已审核' : '待审核'}
</div>
</div>
);
@@ -491,10 +824,15 @@
<i className="fas fa-user-tie" style={{ color: 'var(--primary-color)', marginRight: 8 }}></i>
申请人材料
</div>
- <div style={{ fontSize: '0.85rem', color: 'var(--gray-color)', marginLeft: 'auto' }}>{applicantEvidence.length}份材料</div>
+ <Tag color={applicantStatus.color}>{applicantStatus.text}</Tag>
+ <div style={{ fontSize: '0.85rem', color: 'var(--gray-color)', marginLeft: 'auto' }}>{applicantMaterials.length}份材料</div>
</div>
- <div>
- {applicantEvidence.map((item) => renderEvidenceItem(item, 'applicant'))}
+ <div style={{ maxHeight: 400, overflowY: 'auto' }}>
+ {applicantMaterials.length > 0 ? (
+ applicantMaterials.map((item) => renderEvidenceItem(item, 'applicant'))
+ ) : (
+ <div style={{ textAlign: 'center', padding: 40, color: 'var(--gray-color)' }}>暂无材料</div>
+ )}
</div>
</div>
@@ -511,12 +849,395 @@
<i className="fas fa-building" style={{ color: '#e9c46a', marginRight: 8 }}></i>
被申请人材料
</div>
- <div style={{ fontSize: '0.85rem', color: 'var(--gray-color)', marginLeft: 'auto' }}>{respondentEvidence.length}份材料</div>
+ <Tag color={respondentStatus.color}>{respondentStatus.text}</Tag>
+ <div style={{ fontSize: '0.85rem', color: 'var(--gray-color)', marginLeft: 'auto' }}>{respondentMaterials.length}份材料</div>
</div>
- <div>
- {respondentEvidence.map((item) => renderEvidenceItem(item, 'respondent'))}
+ <div style={{ maxHeight: 400, overflowY: 'auto' }}>
+ {respondentMaterials.length > 0 ? (
+ respondentMaterials.map((item) => renderEvidenceItem(item, 'respondent'))
+ ) : (
+ <div style={{ textAlign: 'center', padding: 40, color: 'var(--gray-color)' }}>暂无材料</div>
+ )}
</div>
</div>
+
+ {/* 证据材料审查弹窗 */}
+ <Modal
+ title={null}
+ visible={auditModalVisible}
+ onCancel={handleCloseAuditModal}
+ footer={null}
+ width={900}
+ centered
+ destroyOnClose
+ bodyStyle={{ padding: 0 }}
+ className="doc-audit-modal"
+ >
+ {currentAuditItem && (
+ <div style={{ background: '#f5f7fa' }}>
+ {/* 头部 - 蓝色渐变 */}
+ <div style={{
+ background: 'linear-gradient(135deg, #1A6FB8 0%, #0d4a8a 100%)',
+ color: 'white',
+ padding: '20px 30px',
+ }}>
+ <div style={{ fontSize: 20, fontWeight: 600, marginBottom: 8 }}>证据材料审查</div>
+ <div style={{ fontSize: 14, opacity: 0.9 }}>
+ {currentAuditItem.personType === 'applicant' ? '申请人' : '被申请人'}提交的证据材料
+ </div>
+ </div>
+
+ {/* 主体内容 */}
+ <div style={{ padding: '20px 30px', background: 'white', margin: 0 }}>
+ {/* 审核说明 tip-box */}
+ <div style={{
+ background: '#f0f8ff',
+ borderRadius: 8,
+ padding: 16,
+ marginBottom: 24,
+ border: '1px solid #d9eafb',
+ }}>
+ <div style={{ color: '#1A6FB8', marginBottom: 8, fontSize: 15, fontWeight: 600 }}>审核说明</div>
+ <div style={{ color: '#555', fontSize: 14 }}>
+ 请仔细核对申请人提交的材料,确保材料真实、完整、有效。如有疑问或需要补充材料,请使用"退回补充"功能。
+ </div>
+ </div>
+
+ {/* 材料基本信息 */}
+ <div style={{ marginBottom: 24 }}>
+ <div style={{
+ color: '#1A6FB8',
+ fontSize: 16,
+ fontWeight: 600,
+ marginBottom: 16,
+ display: 'flex',
+ alignItems: 'center',
+ }}>
+ <span style={{
+ display: 'inline-block',
+ width: 4,
+ height: 16,
+ background: '#1A6FB8',
+ marginRight: 10,
+ borderRadius: 2,
+ }}></span>
+ 材料基本信息
+ </div>
+ {modalDataLoading ? (
+ <div style={{ textAlign: 'center', padding: 20 }}>
+ <Spin tip="加载中..." />
+ </div>
+ ) : (
+ <div style={{
+ display: 'grid',
+ gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))',
+ gap: 16,
+ }}>
+ <div style={{ display: 'flex', marginBottom: 12 }}>
+ <span style={{ fontWeight: 500, color: '#666', minWidth: 80 }}>提交人:</span>
+ <span style={{ color: '#222', fontWeight: 500 }}>
+ {personInfo
+ ? `${personInfo.per_class_name || (currentAuditItem.personType === 'applicant' ? '申请人' : '被申请人')}-${personInfo.true_name || ''}`
+ : (currentAuditItem.personType === 'applicant' ? '申请方' : '被申请方')}
+ </span>
+ </div>
+ <div style={{ display: 'flex', marginBottom: 12 }}>
+ <span style={{ fontWeight: 500, color: '#666', minWidth: 80 }}>材料类型:</span>
+ <span style={{ color: '#222', fontWeight: 500 }}>
+ {currentAuditItem.evidence_type_name || currentAuditItem.name || '证据材料'}
+ </span>
+ </div>
+ <div style={{ display: 'flex', marginBottom: 12 }}>
+ <span style={{ fontWeight: 500, color: '#666', minWidth: 80 }}>材料数量:</span>
+ <span style={{ color: '#222', fontWeight: 500 }}>
+ {personInfo?.total_count ? `${personInfo.total_count}份` : `${evidenceImages.length || 1}份`}
+ </span>
+ </div>
+ <div style={{ display: 'flex', marginBottom: 12 }}>
+ <span style={{ fontWeight: 500, color: '#666', minWidth: 80 }}>提交时间:</span>
+ <span style={{ color: '#222', fontWeight: 500 }}>
+ {personInfo?.submit_time || currentAuditItem.update_time || currentAuditItem.create_time}
+ </span>
+ </div>
+ </div>
+ )}
+ </div>
+
+ {/* 材料说明 */}
+ <div style={{ marginBottom: 24 }}>
+ <div style={{
+ color: '#1A6FB8',
+ fontSize: 16,
+ fontWeight: 600,
+ marginBottom: 16,
+ display: 'flex',
+ alignItems: 'center',
+ }}>
+ <span style={{
+ display: 'inline-block',
+ width: 4,
+ height: 16,
+ background: '#1A6FB8',
+ marginRight: 10,
+ borderRadius: 2,
+ }}></span>
+ 材料说明
+ </div>
+ <div style={{
+ border: '1px solid #e8e8e8',
+ borderRadius: 6,
+ padding: 16,
+ background: '#fafafa',
+ minHeight: 100,
+ fontSize: 14,
+ lineHeight: 1.7,
+ }}>
+ <p style={{ marginBottom: 8 }}>
+ <strong>{currentAuditItem.name}</strong>
+ </p>
+ <p style={{ margin: 0, color: '#555' }}>
+ {currentAuditItem.result || '暂无详细说明'}
+ </p>
+ </div>
+ </div>
+
+ {/* 材料清单 */}
+ <div style={{ marginBottom: 24 }}>
+ <div style={{
+ color: '#1A6FB8',
+ fontSize: 16,
+ fontWeight: 600,
+ marginBottom: 16,
+ display: 'flex',
+ alignItems: 'center',
+ }}>
+ <span style={{
+ display: 'inline-block',
+ width: 4,
+ height: 16,
+ background: '#1A6FB8',
+ marginRight: 10,
+ borderRadius: 2,
+ }}></span>
+ 材料清单
+ </div>
+ <table style={{
+ width: '100%',
+ borderCollapse: 'collapse',
+ borderRadius: 6,
+ overflow: 'hidden',
+ boxShadow: '0 1px 2px 0 rgba(0, 0, 0, 0.03)',
+ border: '1px solid #f0f0f0',
+ }}>
+ <thead>
+ <tr>
+ <th style={{
+ background: '#fafafa',
+ color: '#333',
+ fontWeight: 600,
+ textAlign: 'left',
+ padding: 16,
+ borderBottom: '1px solid #f0f0f0',
+ fontSize: 14,
+ width: '40%',
+ }}>材料信息</th>
+ <th style={{
+ background: '#fafafa',
+ color: '#333',
+ fontWeight: 600,
+ textAlign: 'left',
+ padding: 16,
+ borderBottom: '1px solid #f0f0f0',
+ fontSize: 14,
+ width: '60%',
+ }}>材料预览</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td style={{ padding: 16, borderBottom: '1px solid #f5f5f5', verticalAlign: 'top', fontSize: 14 }}>
+ <div style={{ fontWeight: 600, color: '#333', marginBottom: 6 }}>{currentAuditItem.name}</div>
+ <div style={{ fontSize: 13, color: '#666', marginBottom: 4 }}>
+ 材料类型:{currentAuditItem.evidence_type_name || '证据材料'}
+ </div>
+ <div style={{ fontSize: 13, color: '#666', marginBottom: 4 }}>
+ 文件格式:{personInfo?.suffix || 'PDF/图片'}
+ </div>
+ <div style={{ fontSize: 13, color: '#666', marginBottom: 4 }}>
+ 上传时间:{personInfo?.submit_time || currentAuditItem.update_time || currentAuditItem.create_time}
+ </div>
+ {personInfo?.total_file_size && (
+ <div style={{ fontSize: 13, color: '#666', marginBottom: 4 }}>
+ 文件大小:{personInfo.total_file_size}MB
+ </div>
+ )}
+ </td>
+ <td style={{ padding: 16, borderBottom: '1px solid #f5f5f5', verticalAlign: 'top', fontSize: 14 }}>
+ {modalDataLoading ? (
+ <div style={{ textAlign: 'center', padding: 20 }}>
+ <Spin size="small" />
+ </div>
+ ) : evidenceImages.length > 0 ? (
+ <>
+ <Image.PreviewGroup>
+ <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
+ {evidenceImages.map((img, index) => {
+ const imgUrl = img.show_url
+ ? (platformUrl ? `${platformUrl}/${img.show_url}` : img.show_url)
+ : '';
+ return (
+ <div
+ key={img.file_id || img.id || index}
+ style={{
+ width: 100,
+ height: 80,
+ borderRadius: 4,
+ overflow: 'hidden',
+ border: '1px solid #e8e8e8',
+ cursor: 'pointer',
+ transition: 'all 0.2s',
+ }}
+ >
+ <Image
+ src={imgUrl}
+ alt={img.file_name || `材料${index + 1}`}
+ style={{ width: '100%', height: '100%', objectFit: 'cover' }}
+ fallback="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjgwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxMDAiIGhlaWdodD0iODAiIGZpbGw9IiNmNWY1ZjUiLz48dGV4dCB4PSI1MCIgeT0iNDAiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIxMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzk5OSI+5Zu+54mH6aKE6KeIPC90ZXh0Pjwvc3ZnPg=="
+ preview={{
+ mask: <span style={{ fontSize: 12 }}>预览</span>
+ }}
+ />
+ </div>
+ );
+ })}
+ </div>
+ </Image.PreviewGroup>
+ <div style={{ fontSize: 13, color: '#666', marginTop: 8 }}>共{evidenceImages.length}个文件</div>
+ </>
+ ) : (
+ <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap' }}>
+ <div style={{
+ width: 100,
+ height: 80,
+ borderRadius: 4,
+ overflow: 'hidden',
+ border: '1px solid #e8e8e8',
+ background: 'linear-gradient(135deg, #f5f5f5 0%, #e8e8e8 100%)',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ color: '#999',
+ fontSize: 13,
+ fontWeight: 500,
+ }}>
+ 暂无图片
+ </div>
+ </div>
+ )}
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ {/* 操作按钮 */}
+ <div style={{
+ display: 'flex',
+ justifyContent: 'center',
+ gap: 16,
+ marginTop: 32,
+ paddingTop: 24,
+ borderTop: '1px solid #f0f0f0',
+ }}>
+ <Button
+ type="primary"
+ size="large"
+ loading={auditLoading}
+ onClick={handleAuditPass}
+ style={{
+ background: '#1A6FB8',
+ borderColor: '#1A6FB8',
+ minWidth: 120,
+ height: 40,
+ fontWeight: 500,
+ boxShadow: '0 2px 0 rgba(0, 0, 0, 0.045)',
+ }}
+ >
+ 审核通过
+ </Button>
+ <Button
+ size="large"
+ loading={auditLoading}
+ onClick={handleOpenReturnModal}
+ style={{
+ background: 'white',
+ color: '#1A6FB8',
+ borderColor: '#1A6FB8',
+ minWidth: 120,
+ height: 40,
+ fontWeight: 500,
+ boxShadow: '0 2px 0 rgba(0, 0, 0, 0.015)',
+ }}
+ >
+ 退回补充
+ </Button>
+ </div>
+ </div>
+ </div>
+ )}
+ </Modal>
+
+ {/* 退回补充子弹窗 */}
+ <Modal
+ title={
+ <div style={{ background: '#1A6FB8', margin: '-20px -24px', padding: '16px 24px', color: 'white' }}>
+ <span style={{ fontSize: 16, fontWeight: 600 }}>退回补充材料</span>
+ </div>
+ }
+ visible={returnModalVisible}
+ onCancel={handleCloseReturnModal}
+ footer={null}
+ width={480}
+ centered
+ destroyOnClose
+ bodyStyle={{ paddingTop: 24 }}
+ >
+ <div style={{ marginBottom: 20 }}>
+ <label style={{ display: 'block', fontWeight: 500, color: '#333', marginBottom: 8, fontSize: 14 }}>
+ 退回意见:
+ </label>
+ <TextArea
+ value={auditRemark}
+ onChange={(e) => setAuditRemark(e.target.value)}
+ placeholder="请详细说明需要补充的材料内容及原因..."
+ rows={5}
+ style={{
+ borderRadius: 4,
+ resize: 'vertical',
+ }}
+ />
+ </div>
+ <div style={{
+ background: '#fafafa',
+ margin: '0 -24px -24px',
+ padding: '16px 24px',
+ display: 'flex',
+ justifyContent: 'flex-end',
+ gap: 12,
+ }}>
+ <Button onClick={handleCloseReturnModal} style={{ padding: '6px 16px' }}>
+ 取消
+ </Button>
+ <Button
+ type="primary"
+ loading={auditLoading}
+ onClick={handleAuditReturn}
+ style={{ background: '#1A6FB8', borderColor: '#1A6FB8', padding: '6px 16px' }}
+ >
+ 确认退回
+ </Button>
+ </div>
+ </Modal>
</div>
);
};
@@ -525,6 +1246,193 @@
* 调解协议
*/
const AgreementSection = () => {
+ const { caseData } = useCaseData();
+ const [agreementContent, setAgreementContent] = useState('');
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [editModalVisible, setEditModalVisible] = useState(false);
+ const [editContent, setEditContent] = useState('');
+ const [editLoading, setEditLoading] = useState(false);
+ const [actionLoading, setActionLoading] = useState({
+ confirm: false,
+ download: false,
+ regenerate: false,
+ });
+ const loadedRef = useRef(false);
+
+ // 获取 caseId
+ const caseId = caseData?.caseId || getMergedParams().caseId;
+
+ // 处理协议内容展示(纯文本,处理换行)
+ const renderAgreementContent = (content) => {
+ if (!content) return null;
+ // 处理 \n 换行,简单过滤 Markdown 符号
+ const lines = content.split('\n');
+ return lines.map((line, index) => {
+ // 简单过滤 ** 符号,保留文本
+ const cleanLine = line.replace(/\*\*/g, '');
+ if (!cleanLine.trim()) {
+ return <br key={index} />;
+ }
+ return (
+ <p key={index} style={{ margin: '8px 0', lineHeight: 1.6 }}>
+ {cleanLine}
+ </p>
+ );
+ });
+ };
+
+ // 首次加载协议内容
+ 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);
+ }
+ };
+
+ // 组件挂载时加载协议
+ 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, confirm: true }));
+ try {
+ await MediationAgreementAPIService.confirmAgreement(caseId, 'mediator');
+ message.success('协议确认成功!');
+ } catch (err) {
+ console.error('确认协议失败:', err);
+ message.error('确认协议失败,请稍后重试');
+ } finally {
+ setActionLoading(prev => ({ ...prev, confirm: false }));
+ }
+ };
+
+ // 下载协议
+ const handleDownloadAgreement = async () => {
+ if (!caseId) return;
+ setActionLoading(prev => ({ ...prev, download: true }));
+ try {
+ await MediationAgreementAPIService.downloadAgreement(caseId);
+ message.success('协议下载成功!');
+ } catch (err) {
+ console.error('下载协议失败:', err);
+ message.error('下载协议失败,请稍后重试');
+ } finally {
+ setActionLoading(prev => ({ ...prev, download: false }));
+ }
+ };
+
+ // 重新生成协议
+ const handleRegenerateAgreement = async () => {
+ if (!caseId) return;
+ setActionLoading(prev => ({ ...prev, regenerate: true }));
+ try {
+ const response = await MediationAgreementAPIService.generateAgreement(caseId);
+ if (response?.data?.agreeContent) {
+ setAgreementContent(response.data.agreeContent);
+ message.success('协议重新生成成功!');
+ }
+ } catch (err) {
+ console.error('重新生成协议失败:', err);
+ message.error('重新生成协议失败,请稍后重试');
+ } finally {
+ setActionLoading(prev => ({ ...prev, regenerate: false }));
+ }
+ };
+
+ // 打开修改弹窗
+ const handleOpenEditModal = async () => {
+ if (!caseId) return;
+ setEditModalVisible(true);
+ setEditLoading(true);
+ try {
+ const response = await MediationAgreementAPIService.getAgreementDetail(caseId);
+ if (response?.data?.agreeContent) {
+ setEditContent(response.data.agreeContent);
+ }
+ } catch (err) {
+ console.error('获取协议内容失败:', err);
+ message.error('获取协议内容失败');
+ } finally {
+ setEditLoading(false);
+ }
+ };
+
+ // 关闭修改弹窗
+ const handleCloseEditModal = () => {
+ setEditModalVisible(false);
+ setEditContent('');
+ };
+
+ // 保存修改
+ const handleSaveEdit = async () => {
+ if (!caseId || !editContent.trim()) {
+ message.warning('协议内容不能为空');
+ return;
+ }
+ setEditLoading(true);
+ try {
+ await MediationAgreementAPIService.updateAgreement(caseId, editContent);
+ message.success('协议修改保存成功!');
+ handleCloseEditModal();
+ // 刷新父页面协议内容
+ const response = await MediationAgreementAPIService.generateAgreement(caseId);
+ if (response?.data?.agreeContent) {
+ setAgreementContent(response.data.agreeContent);
+ }
+ } catch (err) {
+ console.error('保存协议失败:', err);
+ message.error('保存协议失败,请稍后重试');
+ } finally {
+ setEditLoading(false);
+ }
+ };
+
+ // Loading 状态
+ if (loading) {
+ return (
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: 300 }}>
+ <Spin size="large" tip="正在生成调解协议..." />
+ </div>
+ );
+ }
+
+ // 错误状态
+ if (error) {
+ return (
+ <div style={{ textAlign: 'center', padding: 40, color: '#ff4d4f' }}>
+ <div style={{ fontSize: '1.2rem', marginBottom: 10 }}>
+ <i className="fas fa-exclamation-circle"></i> {error}
+ </div>
+ <button onClick={loadAgreement} style={{
+ marginTop: 15, padding: '8px 16px', backgroundColor: '#1890ff',
+ color: 'white', border: 'none', borderRadius: 4, cursor: 'pointer'
+ }}>重新加载</button>
+ </div>
+ );
+ }
+
return (
<div style={{
background: '#f8f9fa',
@@ -533,56 +1441,15 @@
maxHeight: 450,
overflowY: 'auto',
}}>
+ {/* 协议内容展示区域 */}
<div style={{ lineHeight: 1.5, fontSize: '0.9rem' }}>
- <div style={{ textAlign: 'center', color: 'var(--secondary-color)', fontSize: '1.2rem', marginBottom: 20, fontWeight: 700 }}>
- 李晓明与广东好又多贸易有限公司劳动争议调解协议书
- </div>
-
- <p><strong>甲方(申请人):</strong>李晓明</p>
- <p><strong>乙方(被申请人):</strong>广东好又多贸易有限公司</p>
-
- <p style={{ textIndent: '2em', marginTop: 15 }}>
- 甲方与乙方因劳动报酬支付问题发生争议,双方于2026年1月15日向"云小调"劳动争议AI调解智能体申请调解。经AI调解员调解,双方本着平等自愿、互谅互让的原则,达成如下调解协议:
- </p>
-
- <h3 style={{ color: 'var(--secondary-color)', margin: '15px 0 8px', fontSize: '1rem' }}>一、争议事项</h3>
- <p style={{ textIndent: '2em' }}>甲方主张乙方拖欠2026年1月至3月共三个月工资、2026年第一季度绩效奖金以及解除劳动合同经济补偿金,合计人民币52,800元。</p>
-
- <h3 style={{ color: 'var(--secondary-color)', margin: '15px 0 8px', fontSize: '1rem' }}>二、调解结果</h3>
- <p style={{ textIndent: '2em' }}>经调解,双方达成一致意见如下:</p>
- <ul style={{ paddingLeft: 30, marginBottom: 12 }}>
- <li style={{ marginBottom: 6 }}>乙方同意向甲方支付劳动报酬共计人民币肆万肆仟元整(¥44,000)</li>
- <li style={{ marginBottom: 6 }}>支付方式:分两期支付
- <ul style={{ paddingLeft: 20 }}>
- <li>第一期:人民币贰万贰仟元整(¥22,000),于2026年1月30日前支付</li>
- <li>第二期:人民币贰万贰仟元整(¥22,000),于2026年2月28日前支付</li>
- </ul>
- </li>
- <li style={{ marginBottom: 6 }}>支付账户:甲方指定的银行账户(账户信息另行提供)</li>
- </ul>
-
- <h3 style={{ color: 'var(--secondary-color)', margin: '15px 0 8px', fontSize: '1rem' }}>三、双方权利义务</h3>
- <ul style={{ paddingLeft: 30, marginBottom: 12 }}>
- <li style={{ marginBottom: 6 }}>乙方应按约定时间足额支付上述款项</li>
- <li style={{ marginBottom: 6 }}>甲方收到全部款项后,双方劳动关系正式解除</li>
- <li style={{ marginBottom: 6 }}>双方互不追究其他法律责任,本协议履行完毕后,争议事项一次性了结</li>
- </ul>
-
- {/* 签名区域 */}
- <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 20, paddingTop: 15, borderTop: '1px solid var(--border-color)' }}>
- <div style={{ textAlign: 'center', width: '45%' }}>
- <div style={{ borderBottom: '1px solid var(--dark-color)', padding: '15px 0 5px', marginBottom: 5 }}></div>
- <div>甲方(申请人):李晓明</div>
- <div style={{ fontSize: '0.8rem', color: 'var(--gray-color)' }}>签字/盖章</div>
- <div style={{ fontSize: '0.8rem', color: 'var(--gray-color)', marginTop: 5 }}>日期:2026年1月15日</div>
+ {agreementContent ? (
+ renderAgreementContent(agreementContent)
+ ) : (
+ <div style={{ textAlign: 'center', color: 'var(--gray-color)', padding: 40 }}>
+ 暂无协议内容
</div>
- <div style={{ textAlign: 'center', width: '45%' }}>
- <div style={{ borderBottom: '1px solid var(--dark-color)', padding: '15px 0 5px', marginBottom: 5 }}></div>
- <div>乙方(被申请人):广东好又多贸易有限公司</div>
- <div style={{ fontSize: '0.8rem', color: 'var(--gray-color)' }}>签字/盖章</div>
- <div style={{ fontSize: '0.8rem', color: 'var(--gray-color)', marginTop: 5 }}>日期:2026年1月15日</div>
- </div>
- </div>
+ )}
</div>
{/* 操作按钮 */}
@@ -597,71 +1464,176 @@
justifyContent: 'center',
gap: 12,
}}>
- <button style={{
- padding: '10px 20px',
- border: '2px solid #c3e6cb',
- borderRadius: 'var(--border-radius)',
- fontWeight: 600,
- fontSize: '0.9rem',
- cursor: 'pointer',
- display: 'flex',
- alignItems: 'center',
- gap: 6,
- background: '#d4edda',
- color: '#155724',
- }}>
- <i className="fas fa-check-circle"></i>
+ <button
+ onClick={handleConfirmAgreement}
+ disabled={actionLoading.confirm}
+ style={{
+ padding: '10px 20px',
+ border: '2px solid #c3e6cb',
+ borderRadius: 'var(--border-radius)',
+ fontWeight: 600,
+ fontSize: '0.9rem',
+ cursor: actionLoading.confirm ? 'not-allowed' : 'pointer',
+ display: 'flex',
+ alignItems: 'center',
+ gap: 6,
+ background: '#d4edda',
+ color: '#155724',
+ opacity: actionLoading.confirm ? 0.6 : 1,
+ }}>
+ <i className={actionLoading.confirm ? "fas fa-spinner fa-spin" : "fas fa-check-circle"}></i>
确认协议
</button>
- <button style={{
- padding: '10px 20px',
- border: '2px solid #ffeaa7',
- borderRadius: 'var(--border-radius)',
- fontWeight: 600,
- fontSize: '0.9rem',
- cursor: 'pointer',
- display: 'flex',
- alignItems: 'center',
- gap: 6,
- background: '#fff3cd',
- color: '#856404',
- }}>
+ <button
+ onClick={handleOpenEditModal}
+ style={{
+ padding: '10px 20px',
+ border: '2px solid #ffeaa7',
+ borderRadius: 'var(--border-radius)',
+ fontWeight: 600,
+ fontSize: '0.9rem',
+ cursor: 'pointer',
+ display: 'flex',
+ alignItems: 'center',
+ gap: 6,
+ background: '#fff3cd',
+ color: '#856404',
+ }}>
<i className="fas fa-edit"></i>
修改协议
</button>
- <button style={{
- padding: '10px 20px',
- border: '2px solid #bbdefb',
- borderRadius: 'var(--border-radius)',
- fontWeight: 600,
- fontSize: '0.9rem',
- cursor: 'pointer',
- display: 'flex',
- alignItems: 'center',
- gap: 6,
- background: '#e3f2fd',
- color: '#1565c0',
- }}>
- <i className="fas fa-download"></i>
+ <button
+ onClick={handleDownloadAgreement}
+ disabled={actionLoading.download}
+ style={{
+ padding: '10px 20px',
+ border: '2px solid #bbdefb',
+ borderRadius: 'var(--border-radius)',
+ fontWeight: 600,
+ fontSize: '0.9rem',
+ cursor: actionLoading.download ? 'not-allowed' : 'pointer',
+ display: 'flex',
+ alignItems: 'center',
+ gap: 6,
+ background: '#e3f2fd',
+ color: '#1565c0',
+ opacity: actionLoading.download ? 0.6 : 1,
+ }}>
+ <i className={actionLoading.download ? "fas fa-spinner fa-spin" : "fas fa-download"}></i>
下载协议
</button>
- <button style={{
- padding: '10px 20px',
- border: '2px solid #bee5eb',
- borderRadius: 'var(--border-radius)',
- fontWeight: 600,
- fontSize: '0.9rem',
- cursor: 'pointer',
- display: 'flex',
- alignItems: 'center',
- gap: 6,
- background: '#d1ecf1',
- color: '#0c5460',
- }}>
- <i className="fas fa-redo"></i>
+ <button
+ onClick={handleRegenerateAgreement}
+ disabled={actionLoading.regenerate}
+ style={{
+ padding: '10px 20px',
+ border: '2px solid #bee5eb',
+ borderRadius: 'var(--border-radius)',
+ fontWeight: 600,
+ fontSize: '0.9rem',
+ cursor: actionLoading.regenerate ? 'not-allowed' : 'pointer',
+ display: 'flex',
+ alignItems: 'center',
+ gap: 6,
+ background: '#d1ecf1',
+ color: '#0c5460',
+ opacity: actionLoading.regenerate ? 0.6 : 1,
+ }}>
+ <i className={actionLoading.regenerate ? "fas fa-spinner fa-spin" : "fas fa-redo"}></i>
重新生成
</button>
</div>
+
+ {/* 修改调解协议书弹窗 */}
+ <Modal
+ title={null}
+ visible={editModalVisible}
+ onCancel={handleCloseEditModal}
+ footer={null}
+ width={1000}
+ centered
+ destroyOnClose
+ bodyStyle={{ padding: 0 }}
+ >
+ <div style={{ background: '#f5f7fa' }}>
+ {/* 头部 */}
+ <div style={{
+ background: '#1A6FB8',
+ color: 'white',
+ padding: '25px 30px',
+ borderBottom: '5px solid #0d4a8a',
+ }}>
+ <div style={{ fontSize: 24, fontWeight: 600, marginBottom: 10 }}>在线修改调解协议书</div>
+ <div style={{ fontSize: 16, opacity: 0.9 }}>编辑协议内容</div>
+ </div>
+
+ {/* 主体内容 */}
+ <div style={{ padding: '25px 30px', background: 'white' }}>
+ {/* 编辑提示 */}
+ <div style={{
+ fontSize: 14,
+ color: '#666',
+ marginBottom: 15,
+ padding: 10,
+ background: '#f0f8ff',
+ borderRadius: 5,
+ borderLeft: '3px solid #1A6FB8',
+ }}>
+ <strong>编辑提示:</strong>下方文本框中的协议内容可直接编辑修改。编辑完成后,请点击底部的"保存修改"按钮。
+ </div>
+
+ {/* 协议编辑区域 */}
+ <div style={{ marginBottom: 25 }}>
+ {editLoading ? (
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: 400 }}>
+ <Spin size="large" tip="加载协议内容..." />
+ </div>
+ ) : (
+ <TextArea
+ value={editContent}
+ onChange={(e) => setEditContent(e.target.value)}
+ style={{
+ border: '1px solid #d0e3ff',
+ borderRadius: 8,
+ minHeight: 500,
+ padding: 20,
+ fontSize: 16,
+ lineHeight: 1.8,
+ background: '#f9fafc',
+ }}
+ placeholder="请输入协议内容..."
+ />
+ )}
+ </div>
+
+ {/* 操作按钮 */}
+ <div style={{
+ display: 'flex',
+ justifyContent: 'flex-end',
+ marginTop: 30,
+ paddingTop: 20,
+ borderTop: '1px solid #eaeaea',
+ }}>
+ <Button
+ type="primary"
+ size="large"
+ loading={editLoading}
+ onClick={handleSaveEdit}
+ style={{
+ padding: '12px 36px',
+ height: 'auto',
+ fontWeight: 600,
+ fontSize: 16,
+ background: '#1A6FB8',
+ borderColor: '#1A6FB8',
+ }}
+ >
+ 保存修改
+ </Button>
+ </div>
+ </div>
+ </div>
+ </Modal>
</div>
);
};
--
Gitblit v1.8.0