import { useEffect, useMemo, useState } from 'react' import { Download, FilePlus2, MessageSquarePlus, ThumbsUp, Star, TriangleAlert } from 'lucide-react' import { Button } from '@/components/ui/button' import { Checkbox } from '@/components/ui/checkbox' import { useMonitorTasks, useReport } from '@/hooks/useMonitor' import { useCurrentPlatform } from '@/hooks/usePlatform' import { UnwiredPlatformNotice } from '@/components/layout/UnwiredPlatformNotice' import { monitorApi } from '@/lib/api' import { formatCount } from '@/lib/monitorFormat' import type { MetricKey, ReportRow } from '@/types/monitor' import { StatTile } from './StatTile' /** Local-date ISO string; toISOString() would shift across the UTC boundary. */ function toISODate(value: Date): string { const pad = (n: number) => String(n).padStart(2, '0') return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}` } const METRIC_COLUMNS: Array<{ key: MetricKey; label: string }> = [ { key: 'liked_count_delta', label: '点赞 Δ' }, { key: 'comment_count_delta', label: '评论 Δ' }, { key: 'collected_count_delta', label: '收藏 Δ' }, { key: 'share_count_delta', label: '分享 Δ' }, ] /** Neutral for zero, since most rows are zero and colouring them all is noise. */ function DeltaCell({ value }: { value: number }) { if (value === 0) return — return ( 0 ? 'text-cyber-neon-green' : 'text-cyber-neon-pink'}> {value > 0 ? '+' : ''} {formatCount(value)} ) } export function ReportView() { const { isWired, isLoading: platformLoading } = useCurrentPlatform() const { data: tasks } = useMonitorTasks() const [selected, setSelected] = useState(null) const [endDate, setEndDate] = useState(() => toISODate(new Date())) const [startDate, setStartDate] = useState(() => { const d = new Date() d.setDate(d.getDate() - 6) return toISODate(d) }) // Default to every task once the list arrives; the user narrows from there. useEffect(() => { if (tasks && selected === null) { setSelected(tasks.map((task) => task.id)) } }, [tasks, selected]) const activeTaskIds = selected ?? [] const { data: report, isLoading } = useReport(activeTaskIds, startDate, endDate) const allSelected = useMemo( () => Boolean(tasks?.length) && activeTaskIds.length === tasks?.length, [tasks, activeTaskIds], ) const toggleTask = (id: number) => { setSelected((current) => { const base = current ?? [] return base.includes(id) ? base.filter((value) => value !== id) : [...base, id] }) } const applyPreset = (days: number) => { const end = new Date() const start = new Date() start.setDate(start.getDate() - (days - 1)) setStartDate(toISODate(start)) setEndDate(toISODate(end)) } if (!platformLoading && !isWired) { return } const totals = report?.totals const partialLabels = (report?.partial_metrics ?? []) .map((field) => report?.metric_labels?.[field] ?? field) .join('、') return (
{/* Filters */}
统计范围
setStartDate(event.target.value)} className="rounded-md border border-cyber-border-DEFAULT bg-cyber-bg-tertiary px-2 py-1 font-mono text-[11px] text-cyber-text-primary outline-none focus-visible:border-cyber-neon-cyan/50" /> 至 setEndDate(event.target.value)} className="rounded-md border border-cyber-border-DEFAULT bg-cyber-bg-tertiary px-2 py-1 font-mono text-[11px] text-cyber-text-primary outline-none focus-visible:border-cyber-neon-cyan/50" />
{[7, 30, 90].map((days) => ( ))}
参与统计的任务(已选{' '} {activeTaskIds.length} 个)
{tasks?.length ? (
{tasks.map((task) => { const checked = activeTaskIds.includes(task.id) return ( ) })}
) : (

还没有监控任务,先去「监控」页建一个。

)}
{/* Headline totals */}
{/* Daily breakdown */}
{activeTaskIds.length === 0 ? (

请至少选择一个任务

) : isLoading ? (

统计中…

) : ( {METRIC_COLUMNS.map((column) => ( ))} {report?.rows.map((row: ReportRow) => ( {METRIC_COLUMNS.map((column) => ( ))} ))} {report && ( {METRIC_COLUMNS.map((column) => ( ))} )}
日期 新增作品 新增评论 {column.label}
{row.date} 0 ? 'text-cyber-neon-green' : 'text-cyber-text-muted'}> {row.new_notes || '—'} 0 ? 'text-cyber-neon-green' : 'text-cyber-text-muted'} > {row.new_comments || '—'}
合计 {report.totals.new_notes} {report.totals.new_comments} {report.totals[column.key] > 0 ? '+' : ''} {formatCount(report.totals[column.key])}
)}
{/* Honest caveats, stated where the numbers are read. */}
{partialLabels && (

{partialLabels} 存在无法解析的数值,这些增量未计入统计,实际增幅会更高。

)}

「增量」= 当日末值 − 当日之前最后一次采到的值;作品首次出现的那天从 0 起算, 所以新增作品的全部点赞都计在其首次发现日。
「新增评论」受接口限制只覆盖平台默认排序的前 N 条,沉底的新评论不计入。

) }