import { useState, useEffect } from 'react' import { Row, Col, Card, Avatar, Tooltip, Button } from 'antd' import { BarChartOutlined, EyeOutlined, CalendarOutlined, FireOutlined, PictureOutlined, } from '@ant-design/icons' import useAuthStore from '../../stores/authStore' import { listEpisodes } from '../../services/episodeService' import { listTargets } from '../../services/yearlyTargetService' import './Dashboard.css' /** * 收视份额颜色判定(极易写反,禁止凭直觉): * audience_share > stretch_target → 🔴 红(优秀) * base_target ≤ audience_share ≤ stretch_target → 🔵 蓝(达标) * audience_share < base_target → 🟢 绿(待提升) * 判色取该期 air_date 所属年份的 yearly_targets 行(不是当前年)。 */ function getShareColor(share, targets, airYear) { const target = targets.find(t => t.year === airYear) if (!target) return '#999' if (share > target.stretch_target) return '#cf1322' // 红=优秀 if (share >= target.base_target) return '#6b8e6b' // 蓝=达标 return '#d4a017' // 绿=待提升 } function getBarHeight(share) { return Math.round((share / 1.0) * 180) } function getShortTitle(title) { return title.length > 12 ? title.slice(0, 12) + '...' : title } function Dashboard() { const { user } = useAuthStore() const [episodes, setEpisodes] = useState([]) const [targets, setTargets] = useState([]) const [loading, setLoading] = useState(true) useEffect(() => { Promise.all([ listEpisodes(9), listTargets(), ]).then(([epData, tgtData]) => { // 按 air_date 倒序取前 9 const sorted = (epData || []).sort((a, b) => new Date(b.air_date) - new Date(a.air_date)).slice(0, 9) setEpisodes(sorted) setTargets(tgtData || []) setLoading(false) }).catch(() => { setLoading(false) }) }, []) // 显示最近有份额数据的期次(最多9个,不够用真实数据补位) const hasShare = episodes.filter(e => e.audience_share != null) const displayEpisodes = hasShare.length >= 5 ? hasShare.slice(0, 9) : episodes.slice(0, Math.max(9, hasShare.length || 5)) const bestEpisode = [...displayEpisodes].filter(e => e.audience_share != null).sort((a, b) => b.audience_share - a.audience_share)[0] const showChangeCoverBtn = user?.role === 'zhipianren' || user?.role === 'zebian' return (
{/* 顶部 Banner */}

本月收视最佳

{bestEpisode ? (

第 {bestEpisode.episode_number} 期 {bestEpisode.program_name} · 收视份额 {bestEpisode.audience_share}

) : (

暂无收视数据

)}
{/* 题图渐变占位 */}
{showChangeCoverBtn && ( )} 敬请期待
{/* KPI Cards */}
{displayEpisodes.filter(e => e.audience_share).length || '--'} 近 9 期已录
{bestEpisode ? (bestEpisode.audience_share * 100).toFixed(1) + '%' : '--'} 最佳收视份额
{targets.length || '--'} 年度目标条数
{/* 三卡片区域 */} {/* 热点雷达 */}

热点雷达 · Phase 4c

编导个人首页核心功能,Phase 4c 实施
{/* 近 9 期收视柱图 */}
{displayEpisodes.map((ep) => { const airYear = new Date(ep.air_date).getFullYear() const color = ep.audience_share != null ? getShareColor(ep.audience_share, targets, airYear) : '#ccc' return (
{getShortTitle(ep.program_name)}
{(ep.editor_name_snapshot || '?')[0]} {ep.editor_name_snapshot || '未知'}
) })}
红=优秀 蓝=达标 绿=待提升
{/* 排播计划 */}

排播计划 · Phase 4b

甘特图排期使用 frappe-gantt 实现,Phase 4b 开发
) } export default Dashboard