/** * My Celestial Bodies Page (User Follow) * 我的天体页面 - 左侧是关注的天体列表,右侧是天体详情和事件 */ import { useState, useEffect } from 'react'; import { Row, Col, Card, List, Tag, Button, Empty, Descriptions, Table, Space } from 'antd'; import { StarFilled, RocketOutlined } from '@ant-design/icons'; import type { ColumnsType } from 'antd/es/table'; import { request } from '../../utils/request'; import { useToast } from '../../contexts/ToastContext'; interface CelestialBody { id: string; name: string; name_zh: string; type: string; is_active: boolean; followed_at?: string; } interface CelestialEvent { id: number; title: string; event_type: string; event_time: string; description: string; details: any; source: string; } export function MyCelestialBodies() { const [loading, setLoading] = useState(false); const [followedBodies, setFollowedBodies] = useState([]); const [selectedBody, setSelectedBody] = useState(null); const [bodyEvents, setBodyEvents] = useState([]); const [eventsLoading, setEventsLoading] = useState(false); const toast = useToast(); useEffect(() => { loadFollowedBodies(); }, []); const loadFollowedBodies = async () => { setLoading(true); try { const { data } = await request.get('/social/follows'); setFollowedBodies(data || []); // 如果有数据,默认选中第一个 if (data && data.length > 0) { handleSelectBody(data[0]); } } catch (error) { toast.error('加载关注列表失败'); } finally { setLoading(false); } }; const handleSelectBody = async (body: CelestialBody) => { setSelectedBody(body); setEventsLoading(true); try { const { data } = await request.get(`/events`, { params: { body_id: body.id, limit: 100 } }); setBodyEvents(data || []); } catch (error) { toast.error('加载天体事件失败'); setBodyEvents([]); } finally { setEventsLoading(false); } }; const handleUnfollow = async (bodyId: string) => { try { await request.delete(`/social/follow/${bodyId}`); toast.success('已取消关注'); // 重新加载列表 await loadFollowedBodies(); // 如果取消关注的是当前选中的天体,清空右侧显示 if (selectedBody?.id === bodyId) { setSelectedBody(null); setBodyEvents([]); } } catch (error) { toast.error('取消关注失败'); } }; const getBodyTypeLabel = (type: string) => { const labelMap: Record = { 'star': '恒星', 'planet': '行星', 'dwarf_planet': '矮行星', 'satellite': '卫星', 'comet': '彗星', 'asteroid': '小行星', 'probe': '探测器', }; return labelMap[type] || type; }; const getBodyTypeColor = (type: string) => { const colorMap: Record = { 'star': 'gold', 'planet': 'blue', 'dwarf_planet': 'cyan', 'satellite': 'geekblue', 'comet': 'purple', 'asteroid': 'volcano', 'probe': 'magenta', }; return colorMap[type] || 'default'; }; const getEventTypeLabel = (type: string) => { const labelMap: Record = { 'approach': '接近', 'close_approach': '近距离接近', 'eclipse': '食', 'conjunction': '合', 'opposition': '冲', 'transit': '凌', }; return labelMap[type] || type; }; const getEventTypeColor = (type: string) => { const colorMap: Record = { 'approach': 'blue', 'close_approach': 'magenta', 'eclipse': 'purple', 'conjunction': 'cyan', 'opposition': 'orange', 'transit': 'green', }; return colorMap[type] || 'default'; }; const eventColumns: ColumnsType = [ { title: '事件', dataIndex: 'title', key: 'title', ellipsis: true, width: '40%', }, { title: '类型', dataIndex: 'event_type', key: 'event_type', width: 200, render: (type) => ( {getEventTypeLabel(type)} ), filters: [ { text: '接近', value: 'approach' }, { text: '近距离接近', value: 'close_approach' }, { text: '食', value: 'eclipse' }, { text: '合', value: 'conjunction' }, { text: '冲', value: 'opposition' }, { text: '凌', value: 'transit' }, ], onFilter: (value, record) => record.event_type === value, }, { title: '时间', dataIndex: 'event_time', key: 'event_time', width: 180, render: (time) => new Date(time).toLocaleString('zh-CN'), sorter: (a, b) => new Date(a.event_time).getTime() - new Date(b.event_time).getTime(), }, ]; return ( {/* 左侧:关注的天体列表 */} 我的天体 {followedBodies.length} } extra={ } bordered={false} style={{ height: '100%', overflow: 'hidden' }} bodyStyle={{ height: 'calc(100% - 57px)', overflowY: 'auto', padding: 0 }} > {followedBodies.length === 0 && !loading ? (

在主页面点击天体,查看详情后可以关注

) : ( ( handleSelectBody(body)} style={{ cursor: 'pointer', backgroundColor: selectedBody?.id === body.id ? '#f0f5ff' : 'transparent', padding: '12px 16px', transition: 'background-color 0.3s', }} actions={[ , ]} > } title={ {body.name_zh || body.name} {getBodyTypeLabel(body.type)} } description={ body.followed_at ? `关注于 ${new Date(body.followed_at).toLocaleDateString('zh-CN')}` : body.name_zh ? body.name : undefined } /> )} /> )}
{/* 右侧:天体详情和事件 */} {selectedBody ? ( {/* 天体资料 */} {selectedBody.name_zh || selectedBody.name} {getBodyTypeLabel(selectedBody.type)} } bordered={false} > {selectedBody.id} {getBodyTypeLabel(selectedBody.type)} {selectedBody.name_zh || '-'} {selectedBody.name} {selectedBody.is_active ? '活跃' : '已归档'} {selectedBody.followed_at ? new Date(selectedBody.followed_at).toLocaleString('zh-CN') : '-'} {/* 天体事件列表 */} `共 ${total} 条`, }} locale={{ emptyText: ( ), }} expandable={{ expandedRowRender: (record) => (

描述: {record.description}

{record.details && (

详情: {JSON.stringify(record.details, null, 2)}

)}
), }} /> ) : ( )} ); }