158 lines
6.1 KiB
JavaScript
158 lines
6.1 KiB
JavaScript
import React from 'react';
|
||
import { formatDateTime } from '../../utils/formatters';
|
||
|
||
export default function ExplainEventsSection({
|
||
explainTimeline,
|
||
isOpen,
|
||
onToggle,
|
||
availableEventDates,
|
||
selectedEventDate,
|
||
onSelectEventDate,
|
||
eventCategoryCounts,
|
||
activeEventCategory,
|
||
onSelectEventCategory,
|
||
eventCategoryMeta,
|
||
visibleExplainEvents,
|
||
}) {
|
||
return (
|
||
<div className="section">
|
||
<div className="section-header">
|
||
<h2 className="section-title">关键事件时间线</h2>
|
||
<div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
|
||
<div style={{ fontSize: 11, color: '#666666' }}>
|
||
图上点击事件点可切换对应日期
|
||
</div>
|
||
<button
|
||
onClick={onToggle}
|
||
style={{
|
||
border: '1px solid #111111',
|
||
background: isOpen ? '#111111' : '#ffffff',
|
||
color: isOpen ? '#ffffff' : '#111111',
|
||
padding: '7px 10px',
|
||
fontFamily: 'inherit',
|
||
fontSize: 11,
|
||
fontWeight: 700,
|
||
cursor: 'pointer'
|
||
}}
|
||
>
|
||
{isOpen ? '收起关键事件' : `展开关键事件 ${explainTimeline.length}`}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{explainTimeline.length === 0 ? (
|
||
<div className="empty-state">当前还没有可以串起来看的关键事件。</div>
|
||
) : !isOpen ? (
|
||
<div className="empty-state">关键事件默认收起,需要时再展开查看和筛选。</div>
|
||
) : (
|
||
<div style={{ display: 'grid', gap: 14 }}>
|
||
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
|
||
{availableEventDates.map((dateKey) => {
|
||
const isActive = dateKey === selectedEventDate;
|
||
return (
|
||
<button
|
||
key={dateKey}
|
||
onClick={() => onSelectEventDate(dateKey)}
|
||
style={{
|
||
border: '1px solid #111111',
|
||
background: isActive ? '#111111' : '#ffffff',
|
||
color: isActive ? '#ffffff' : '#111111',
|
||
padding: '7px 10px',
|
||
fontFamily: 'inherit',
|
||
fontSize: 11,
|
||
fontWeight: 700,
|
||
cursor: 'pointer'
|
||
}}
|
||
>
|
||
{dateKey}
|
||
</button>
|
||
);
|
||
})}
|
||
</div>
|
||
|
||
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
|
||
{Object.entries(eventCategoryMeta)
|
||
.filter(([key]) => (eventCategoryCounts[key] || 0) > 0 || key === 'all')
|
||
.map(([key, meta]) => {
|
||
const isActive = key === activeEventCategory;
|
||
return (
|
||
<button
|
||
key={key}
|
||
onClick={() => onSelectEventCategory(key)}
|
||
style={{
|
||
border: `1px solid ${meta.color}`,
|
||
background: isActive ? meta.color : '#ffffff',
|
||
color: isActive ? '#ffffff' : meta.color,
|
||
padding: '8px 10px',
|
||
fontFamily: 'inherit',
|
||
fontSize: 11,
|
||
fontWeight: 700,
|
||
cursor: 'pointer'
|
||
}}
|
||
>
|
||
{meta.label} {eventCategoryCounts[key] || 0}
|
||
</button>
|
||
);
|
||
})}
|
||
</div>
|
||
|
||
{visibleExplainEvents.length === 0 ? (
|
||
<div className="empty-state">当前日期下没有符合筛选条件的事件</div>
|
||
) : (
|
||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(320px, 1fr))', gap: 16 }}>
|
||
{visibleExplainEvents.map((event) => {
|
||
const accent = event.tone === 'positive' ? '#00C853' : event.tone === 'negative' ? '#FF1744' : '#000000';
|
||
const categoryMeta = eventCategoryMeta[event.category] || eventCategoryMeta.other;
|
||
return (
|
||
<div
|
||
key={event.id}
|
||
style={{
|
||
border: '1px solid #000000',
|
||
background: '#ffffff',
|
||
padding: 14,
|
||
minHeight: 180
|
||
}}
|
||
>
|
||
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 12, marginBottom: 8 }}>
|
||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
|
||
<span style={{
|
||
display: 'inline-flex',
|
||
padding: '2px 6px',
|
||
border: `1px solid ${categoryMeta.color}`,
|
||
color: categoryMeta.color,
|
||
fontSize: 10,
|
||
fontWeight: 700
|
||
}}>
|
||
{categoryMeta.label}
|
||
</span>
|
||
<strong style={{ fontSize: 13 }}>{event.title}</strong>
|
||
</div>
|
||
<span style={{ fontSize: 10, color: '#666666', whiteSpace: 'nowrap' }}>
|
||
{formatDateTime(event.timestamp)}
|
||
</span>
|
||
</div>
|
||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
|
||
<span style={{
|
||
width: 8,
|
||
height: 8,
|
||
borderRadius: '50%',
|
||
background: accent
|
||
}} />
|
||
<span style={{ fontSize: 10, color: '#666666', textTransform: 'uppercase', letterSpacing: 0.6 }}>
|
||
{event.meta}
|
||
</span>
|
||
</div>
|
||
<div style={{ fontSize: 12, lineHeight: 1.7, color: '#000000', whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
|
||
{event.body}
|
||
</div>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|