import React, { useState } from 'react';
|
import { useCaseData } from '../../contexts/CaseDataContext';
|
import { formatDuration, formatSuccessRate, formatRoundCount } from '../../utils/stateTranslator';
|
|
/**
|
* 选项卡容器组件 - 4个选项卡
|
*/
|
const TabContainer = () => {
|
const [activeTab, setActiveTab] = useState('mediation-data-board');
|
|
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: 'agreement-section', label: '调解协议', icon: 'fa-file-contract', badge: '待确认' },
|
];
|
|
return (
|
<div className="tab-container">
|
<div className="tab-header">
|
{tabs.map((tab) => (
|
<button
|
key={tab.key}
|
className={`tab-btn ${activeTab === tab.key ? 'active' : ''}`}
|
onClick={() => setActiveTab(tab.key)}
|
>
|
<i className={`fas ${tab.icon}`}></i>
|
{tab.label}
|
{tab.badge && <span className="tab-badge">{tab.badge}</span>}
|
</button>
|
))}
|
</div>
|
|
<div className="tab-content">
|
{/* 调解数据看板 */}
|
<div className={`tab-pane ${activeTab === 'mediation-data-board' ? 'active' : ''}`}>
|
<div className="tab-content-area">
|
<MediationDataBoard />
|
</div>
|
</div>
|
|
{/* AI调解实时看板 */}
|
<div className={`tab-pane ${activeTab === 'mediation-board' ? 'active' : ''}`}>
|
<div className="tab-content-area">
|
<MediationBoard />
|
</div>
|
</div>
|
|
{/* 证据材料汇总 */}
|
<div className={`tab-pane ${activeTab === 'evidence-board' ? 'active' : ''}`}>
|
<div className="tab-content-area">
|
<EvidenceBoard />
|
</div>
|
</div>
|
|
{/* 调解协议 */}
|
<div className={`tab-pane ${activeTab === 'agreement-section' ? 'active' : ''}`}>
|
<div className="tab-content-area">
|
<AgreementSection />
|
</div>
|
</div>
|
</div>
|
</div>
|
);
|
};
|
|
/**
|
* 调解数据看板
|
*/
|
const MediationDataBoard = () => {
|
const { caseData } = useCaseData();
|
const timeline = caseData || {};
|
|
// 从 timeline 获取数据
|
const gapContent = timeline.result || '暂无分歧分析';
|
const updateTime = formatDuration(timeline.before_duration);
|
const successRate = formatSuccessRate(timeline.mediation?.success_rate);
|
const roundCount = formatRoundCount(timeline.mediation?.mediation_count);
|
|
return (
|
<div className="mediation-metrics">
|
{/* 左侧:诉求差距分析 */}
|
<div className="metric-card">
|
<div className="metric-title">
|
<i className="fas fa-exclamation-circle"></i>
|
<span>诉求差距分析</span>
|
</div>
|
<div className="metric-content">
|
<div className="gap-analysis">
|
<div className="gap-title">
|
<i className="fas fa-exclamation-circle"></i>
|
主要分歧点
|
</div>
|
<div className="gap-update-time">{updateTime}</div>
|
<div className="gap-content">
|
{gapContent}
|
</div>
|
</div>
|
</div>
|
</div>
|
|
{/* 右侧:调解数据 */}
|
<div className="metric-card">
|
<div className="metric-title">
|
<i className="fas fa-exchange-alt"></i>
|
<span>调解数据</span>
|
</div>
|
<div className="metric-content">
|
<div className="success-metric">
|
<div className="success-value">{successRate}</div>
|
<div className="success-label">预计调解成功概率</div>
|
<div className="success-change">
|
|
{/* <i className="fas fa-arrow-up"></i><span>较{updateTime} +8%</span> */}
|
</div>
|
<div style={{ marginTop: 15, fontSize: '0.9rem', color: 'var(--gray-color)' }}>
|
协商沟通:<span style={{ color: 'var(--dark-color)', fontWeight: 600 }}>{roundCount}</span>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
);
|
};
|
|
/**
|
* AI调解实时看板
|
*/
|
const MediationBoard = () => {
|
const boardItems = [
|
{
|
avatar: 'ai',
|
name: 'AI调解员',
|
time: '09:30:05',
|
content: '已分别联系李晓明(申请人)和广东好又多贸易有限公司(被申请人)。双方均表示愿意接受调解,希望通过协商解决劳动争议/拖欠、克扣工资纠纷。',
|
tags: [{ text: '意愿调查', type: 'tag-type-1' }, { text: '初步接触', type: 'tag-type-2' }],
|
},
|
{
|
avatar: 'applicant',
|
name: '申请人(李晓明)',
|
time: '09:35:22',
|
content: '我在好又多公司担任销售经理3年,公司拖欠我3个月工资共¥42,000,还克扣了季度绩效奖金¥10,800。另外,公司单方面解除劳动合同,应支付经济补偿金¥12,000。我要求公司支付总共¥52,800。',
|
tags: [{ text: '诉求表达', type: 'tag-type-3' }, { text: '情绪激动', type: 'tag-type-5' }],
|
},
|
{
|
avatar: 'respondent',
|
name: '被申请人(好又多公司)',
|
time: '09:40:15',
|
content: '公司确实遇到了资金周转困难,拖欠工资问题承认。但李晓明主张的金额有误:1) 3个月工资应为¥35,000(含请假扣款);2) 绩效奖金因未完成KPI指标不应发放;3) 是李晓明主动提出辞职,不应支付经济补偿金。公司最多支付¥38,500。',
|
tags: [{ text: '诉求表达', type: 'tag-type-3' }],
|
},
|
];
|
|
const getAvatarClass = (avatar) => {
|
const map = {
|
ai: 'avatar-ai',
|
applicant: 'avatar-applicant',
|
respondent: 'avatar-respondent',
|
mediator: 'avatar-mediator',
|
};
|
return map[avatar] || 'avatar-ai';
|
};
|
|
const getContentClass = (avatar) => {
|
const map = {
|
ai: 'content-ai',
|
applicant: 'content-applicant',
|
respondent: 'content-respondent',
|
mediator: 'content-mediator',
|
};
|
return map[avatar] || 'content-ai';
|
};
|
|
const getAvatarContent = (avatar) => {
|
if (avatar === 'ai') return <i className="fas fa-robot"></i>;
|
if (avatar === 'applicant') return '李';
|
if (avatar === 'respondent') return '好';
|
if (avatar === 'mediator') return '调';
|
return <i className="fas fa-robot"></i>;
|
};
|
|
return (
|
<>
|
<div className="mediation-summary" style={{
|
background: '#f0f7ff',
|
borderRadius: 'var(--border-radius)',
|
padding: '12px 16px',
|
marginBottom: 15,
|
borderLeft: '4px solid var(--primary-color)',
|
fontSize: '0.9rem',
|
color: 'var(--dark-color)',
|
}}>
|
<div style={{ fontWeight: 600, color: 'var(--secondary-color)', marginBottom: 5, display: 'flex', alignItems: 'center', gap: 8 }}>
|
<i className="fas fa-chart-bar"></i>
|
沟通情况总结
|
</div>
|
<div style={{ lineHeight: 1.5 }}>
|
截至目前,AI调解员已与申请双方进行协商沟通6轮,期间申请人补充材料2次,被申请人补充材料2次,双方对调解方案表现出积极协商态度。
|
</div>
|
</div>
|
|
<div className="board-container" style={{ maxHeight: 450, overflowY: 'auto' }}>
|
{boardItems.map((item, index) => (
|
<div key={index} className="board-item" style={{
|
background: '#f8f9fa',
|
borderRadius: 'var(--border-radius)',
|
padding: 16,
|
marginBottom: 12,
|
border: '1px solid #e9ecef',
|
}}>
|
<div className="item-header" style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10 }}>
|
<div className={`item-avatar ${getAvatarClass(item.avatar)}`} style={{
|
width: 32,
|
height: 32,
|
borderRadius: '50%',
|
display: 'flex',
|
justifyContent: 'center',
|
alignItems: 'center',
|
color: 'white',
|
fontWeight: 600,
|
fontSize: '0.9rem',
|
background: item.avatar === 'ai' ? 'linear-gradient(135deg, var(--primary-color), var(--secondary-color))' :
|
item.avatar === 'applicant' ? 'linear-gradient(135deg, var(--primary-color), #0d4a8a)' :
|
item.avatar === 'respondent' ? 'linear-gradient(135deg, #e9c46a, #e76f51)' :
|
'linear-gradient(135deg, #7209b7, #3a0ca3)',
|
}}>
|
{getAvatarContent(item.avatar)}
|
</div>
|
<div className="item-source">
|
<div style={{ fontWeight: 600, fontSize: '0.95rem', color: 'var(--dark-color)', marginBottom: 2 }}>{item.name}</div>
|
<div style={{ fontSize: '0.8rem', color: 'var(--gray-color)', display: 'flex', alignItems: 'center', gap: 6 }}>
|
<i className="far fa-clock"></i>
|
<span>{item.time}</span>
|
</div>
|
</div>
|
</div>
|
<div className={`item-content ${getContentClass(item.avatar)}`} style={{
|
background: 'white',
|
borderRadius: 8,
|
padding: 12,
|
marginBottom: 10,
|
lineHeight: 1.5,
|
fontSize: '0.9rem',
|
borderLeft: item.avatar === 'respondent' ? '3px solid #e9c46a' : '3px solid var(--primary-color)',
|
}}>
|
{item.content}
|
</div>
|
<div className="item-tags" style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
|
{item.tags.map((tag, i) => (
|
<span key={i} className={`item-tag ${tag.type}`} style={{
|
display: 'inline-block',
|
padding: '3px 8px',
|
borderRadius: 20,
|
fontSize: '0.75rem',
|
fontWeight: 600,
|
background: tag.type === 'tag-type-1' ? '#e3f2fd' :
|
tag.type === 'tag-type-2' ? '#e8f5e9' :
|
tag.type === 'tag-type-3' ? '#fff3e0' :
|
tag.type === 'tag-type-5' ? '#ffebee' : '#e3f2fd',
|
color: tag.type === 'tag-type-1' ? '#1565c0' :
|
tag.type === 'tag-type-2' ? '#2e7d32' :
|
tag.type === 'tag-type-3' ? '#ef6c00' :
|
tag.type === 'tag-type-5' ? '#c62828' : '#1565c0',
|
border: `1px solid ${tag.type === 'tag-type-1' ? '#bbdefb' :
|
tag.type === 'tag-type-2' ? '#c8e6c9' :
|
tag.type === 'tag-type-3' ? '#ffe0b2' :
|
tag.type === 'tag-type-5' ? '#ffcdd2' : '#bbdefb'}`,
|
}}>
|
{tag.text}
|
</span>
|
))}
|
</div>
|
</div>
|
))}
|
</div>
|
</>
|
);
|
};
|
|
/**
|
* 证据材料汇总
|
*/
|
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 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 renderEvidenceItem = (item, type) => (
|
<div key={item.name} className="evidence-item" style={{
|
background: 'white',
|
borderRadius: 6,
|
padding: 12,
|
borderLeft: type === 'applicant' ? '3px solid var(--primary-color)' : '3px solid #e9c46a',
|
display: 'flex',
|
justifyContent: 'space-between',
|
alignItems: 'flex-start',
|
marginBottom: 10,
|
}}>
|
<div style={{ flex: 1 }}>
|
<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.75rem', color: 'var(--gray-color)', marginTop: 4, display: 'flex', alignItems: 'center', gap: 5 }}>
|
<i className="far fa-clock"></i>
|
<span>上传时间:{item.time}</span>
|
</div>
|
</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>
|
</div>
|
);
|
|
return (
|
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 20 }}>
|
{/* 申请人证据材料 */}
|
<div style={{
|
background: '#f8f9fa',
|
borderRadius: 'var(--border-radius)',
|
padding: 18,
|
borderTop: '4px solid var(--primary-color)',
|
minHeight: 300,
|
}}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
|
<div style={{ fontWeight: 600, fontSize: '1.05rem', color: 'var(--dark-color)' }}>
|
<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>
|
</div>
|
<div>
|
{applicantEvidence.map((item) => renderEvidenceItem(item, 'applicant'))}
|
</div>
|
</div>
|
|
{/* 被申请人证据材料 */}
|
<div style={{
|
background: '#f8f9fa',
|
borderRadius: 'var(--border-radius)',
|
padding: 18,
|
borderTop: '4px solid #e9c46a',
|
minHeight: 300,
|
}}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
|
<div style={{ fontWeight: 600, fontSize: '1.05rem', color: 'var(--dark-color)' }}>
|
<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>
|
</div>
|
<div>
|
{respondentEvidence.map((item) => renderEvidenceItem(item, 'respondent'))}
|
</div>
|
</div>
|
</div>
|
);
|
};
|
|
/**
|
* 调解协议
|
*/
|
const AgreementSection = () => {
|
return (
|
<div style={{
|
background: '#f8f9fa',
|
borderRadius: 'var(--border-radius)',
|
padding: 18,
|
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>
|
</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>
|
|
{/* 操作按钮 */}
|
<div style={{
|
position: 'sticky',
|
bottom: 0,
|
background: 'rgba(255, 255, 255, 0.95)',
|
padding: '15px 0',
|
marginTop: 20,
|
borderTop: '1px solid var(--border-color)',
|
display: 'flex',
|
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>
|
<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',
|
}}>
|
<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>
|
<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>
|
</div>
|
</div>
|
);
|
};
|
|
export default TabContainer;
|