import { useState, useEffect, useRef } from 'react'; import '../styles/datascreen.css'; import Icon from '../components/Icons'; import ParkOverviewStrip from '../components/ParkOverviewStrip'; import ParkFeed from '../components/ParkFeed'; import { useParkSim } from '../utils/parkData'; /* ========================================================= 昆明市大学生创业园 · OPC 智能园区数字运营中心 —— 数据大屏(真实数据展示) --------------------------------------------------------- 数据口径(标准资料,多个权威来源): · 《昆明市大学生创业园运行情况统计表(2026年7月).xls》(官方统计) 实际入驻 38 家 / 可容纳 49 个 / 累计投入 560 万 / 当年累计带动就业 213 人 / 当年生产经营总额 252.15 万 / 当年税利 1.03 万 / 面积 3000㎡ · 《附件1.省级创业孵化载体相关情况简介材料》(省级绩效累计口径,截至2026年5月) 累计孵化 181 家 / 在孵 31 家 / 累计就业 1,529 人 / 累计经营收入 21,911.14 万 · 《昆明市创业园宣传册.pdf》 2009 年成立 / 2012 年省级创业园 / 民航路 229 号 / 四大孵化区域 · 《入驻企业信息表》39 家名录(含创始人/简介/场地安排) 测算口径(AI 平台业务锚点,非政府统计口径): · TOKEN:月均 200 亿 tokens → 日均 ≈ 6.67 亿,实时 ≈ 7,700 t/s,近30日累计 200 亿 · 工具调用:按每次调用约 15 万 tokens 折算 TOKEN/工具为基于真实锚点的测算演示,其余为真实统计值 ========================================================= */ /* ---------- 模型占比(TOKEN 构成演示分布) ---------- */ const MODELS = [ { name: 'DeepSeek', value: 38, color: '#4c8dff' }, { name: '通义千问', value: 21, color: '#22d3ee' }, { name: '智谱 GLM-4', value: 16, color: '#34d399' }, { name: '豆包', value: 12, color: '#fbbf24' }, { name: '讯飞星火', value: 8, color: '#a78bfa' }, { name: '其他', value: 5, color: '#64748b' }, ]; /* 工具调用占比(演示分布) */ const TOOL_RANK = [ { name: '文档生成', value: 34 }, { name: '数据查询', value: 27 }, { name: '图像创作', value: 16 }, { name: '代码执行', value: 13 }, { name: '语音合成', value: 10 }, ]; /* ---------- 工具函数 ---------- */ const fmtInt = (n) => (Number.isFinite(Number(n)) ? Math.round(Number(n)).toLocaleString('zh-CN') : '--'); /** 万/亿格式化:非有限数值(缺失/不一致口径)安全显示 '--',避免崩溃 */ const fmtWan = (n) => { const v = Number(n); if (!Number.isFinite(v)) return '--'; return (v >= 10000 ? (v / 10000).toFixed(2) + '亿' : v >= 100 ? v.toFixed(1) + '万' : v.toFixed(1)); }; /** 安全取数:缺失/非数值回退 0,供直接 .toFixed 调用点使用 */ const num = (v) => (Number.isFinite(Number(v)) ? Number(v) : 0); /* 数据源:后端 /api/dashboard/snapshot(离线时本地模拟兜底),见 utils/parkData.js */ /* ---------- 通用 Hooks ---------- */ /* 数字平滑滚动(rAF 缓动;进度钳制 [0,1],避免边缘环境时间戳异常导致发散) */ function useAnimatedNumber(target, duration = 900) { const [cur, setCur] = useState(target); const curRef = useRef(target); const raf = useRef(); useEffect(() => { const from = curRef.current; if (from === target) return; const t0 = performance.now(); cancelAnimationFrame(raf.current); const tick = (now) => { const p = Math.min(1, Math.max(0, (now - t0) / duration)); const e = 1 - Math.pow(1 - p, 3); const v = from + (target - from) * e; curRef.current = v; setCur(v); if (p < 1) raf.current = requestAnimationFrame(tick); }; raf.current = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf.current); }, [target, duration]); return cur; } /* ---------- 通用小组件 ---------- */ function CardHead({ title, right, icon }) { return (
{icon && } {title}
{right &&
{right}
}
); } function Pill({ children, tone = 'blue' }) { return {children}; } /* 面积图 + 折线(viewBox 高度动态匹配容器,满宽绘制、无留白错位) */ function AreaChart({ series, color, height = 100, unit = '', lastLabel }) { const W = 300, PL = 8, PR = 8, PT = 26, PB = 26; const ref = useRef(null); const [vh, setVh] = useState(height); // viewBox 高度(按容器宽高比计算) useEffect(() => { const el = ref.current; if (!el) return; const update = () => { const w = el.clientWidth, h = el.clientHeight; if (w > 0 && h > 0) setVh(Math.max(40, Math.round((W * h) / w))); }; update(); const ro = new ResizeObserver(update); ro.observe(el); return () => ro.disconnect(); }, []); const H = vh; const min = Math.min(...series), max = Math.max(...series); const lo = min * 0.9, hi = max * 1.08, span = hi - lo || 1; const px = (i) => PL + (i / (series.length - 1)) * (W - PL - PR); const py = (v) => H - PB - ((v - lo) / span) * (H - PT - PB); const line = series.map((v, i) => `${px(i).toFixed(1)},${py(v).toFixed(1)}`).join(' '); const area = `${line} ${px(series.length - 1).toFixed(1)},${(H - PB).toFixed(1)} ${px(0).toFixed(1)},${(H - PB).toFixed(1)}`; const gid = useRef('g' + Math.random().toString(36).slice(2, 8)).current; return ( {[0.25, 0.5, 0.75].map((t) => ( ))} 30 天前 今日 {lastLabel && ( {lastLabel} )} ); } /* 环形图 */ function Donut({ items, size = 148, thickness = 18, centerTop, centerBottom }) { const total = items.reduce((a, b) => a + b.value, 0); const R = (size - thickness) / 2; const C = 2 * Math.PI * R; const cx = size / 2; let acc = 0; return ( {items.map((it, i) => { const frac = it.value / total; const dash = Math.max(0.5, frac * C - 1.5); const off = -acc * C; acc += frac; return ( ); })} {centerTop} {centerBottom} ); } /* 横向排行条 */ function HBar({ items, color = '#4cc2ff', unit = '%' }) { const max = Math.max(...items.map((i) => i.value)); return (
{items.map((it, i) => (
{it.name}
{it.value}{unit}
))}
); } /* ========================================================= 面板组件 ========================================================= */ /* ---------- KPI 核心指标带(5 大必须指标) ---------- */ function KpiCard({ icon, label, value, unit, sub, tone }) { const v = useAnimatedNumber(value); return (
{label}
{tone === 'revenue' ? {fmtWan(v)} : {fmtInt(v)}} {unit && {unit}}
{sub}
); } /* ========================================================= 园区今日概览(标题右侧内联运行状态 + 五个指标一行) ┌──────────────────────────────────────────────┐ │ 园区今日概览 · [6 项运行状态内联] · 实时刷新 │ ├──────┬──────┬──────┬──────┬──────┤ │ TOKEN│ 营收 │ 入园 │ 工具 │ 就业 │ ← 五个卡片一行 └──────┴──────┴──────┴──────┴──────┘ (园区实时动态 → 整体页面右下方) ========================================================= */ function TodayOverview({ d }) { /* 园区运行状态 —— 内联在标题右侧,不作为独立卡片(真实数据) */ const status = [ { icon: 'gauge', label: '成立时间', value: `${d.park.founded} 年`, color: '#17b26a' }, { icon: 'trophy', label: '省级创业园', value: `${d.park.provinceLevel} 年认定`, color: '#2f6bff' }, { icon: 'building', label: '可容纳企业', value: `${d.projects.capacity} 个`, color: '#f79009' }, { icon: 'coin', label: '累计税利', value: `${fmtWan(d.revenue.tax)}万元`, color: '#8b5cf6' }, { icon: 'headset', label: '报名电话', value: d.park.phone, color: '#f04438' }, { icon: 'map-pin', label: '园区地址', value: d.park.address, color: '#0ea5e9' }, ]; return (
园区概览
{status.map((s) => ( {s.label} {s.value} ))}
{/* 五个指标卡片:必须一行展示 */}
); } /* ---------- 园区概览页脚:使用共享组件(与媒体播放页完全一致) ---------- */ /* ========================================================= 中部轮换大屏(一次展示一个面板,自动轮换,降低信息密度) ========================================================= */ /* ---------- TOKEN 消耗(大屏版) ---------- */ function TokenBig({ d }) { return (
今日消耗{fmtInt(d.token.today)} 万 tokens 近30日{fmtWan(d.token.total)} tokens 实时速率{d.token.rate} t/s
} />
首 token 平均 {num(d.live.firstToken).toFixed(1)}s 吞吐 {d.live.throughput} tok/s 缓存命中 {num(d.live.cacheHit).toFixed(1)}% 会话输入 {num(d.live.inputM).toFixed(1)}M / 输出 {num(d.live.outputM).toFixed(1)}M tok {d.live.rounds} 轮 · {d.live.steps} 步
{MODELS.map((m) => (
{m.name}
))}
{MODELS.map((m) => (
{m.value}%
))}
); } /* ---------- 工具调用(大屏版) ---------- */ function ToolsBig({ d }) { return (
今日调用{fmtInt(d.tools.today)} 次 累计调用{(num(d.tools.total) / 10000).toFixed(1)}万 次 成功率{d.tools.success}% 服务节点{d.tools.nodes} / {d.tools.nodes} 在线
} />
今日调用{fmtInt(d.tools.today)}次
累计调用{(num(d.tools.total) / 10000).toFixed(1)}万次
成功率{d.tools.success}%
AI 服务节点12/12 在线
热门工具调用占比
LLM 推理 15m27s 工具调用 9m56s 81 步 / 2 轮会话 每次调用 ≈ 15万 tokens
); } /* ---------- 入园项目(组视图 · 中密度) ---------- */ function ProjectsPanel({ d }) { return (
实际入驻{d.projects.inPark} 家 可容纳{d.projects.capacity} 个 累计投入{d.projects.invested} 万
} />
四大孵化区域(宣传册)
{d.zones.map((z) => (
{z.name} {z.period}
))}
入驻企业名录 ({d.companies.length} 家)
{[...d.companies, ...d.companies].map((c, i) => (
{c}
))}
); } /* ---------- 带动就业(组视图 · 中密度) ---------- */ function JobsPanel({ d }) { const mixTotal = d.industryMix.reduce((a, b) => a + b.value, 0); return (
累计带动就业{fmtInt(d.jobs.total)} 人 统计时间2026年7月 来源运行情况统计表
} />
园区企业行业比例 ({mixTotal} 家)
累计带动就业{fmtInt(d.jobs.total)}人
累计生产经营总额{d.revenue.total}万元
累计上缴税利{d.revenue.tax}万元
可容纳企业{d.projects.capacity}个
); } /* ---------- 营收总数(组视图 · 中密度) ---------- */ function RevenuePanel({ d }) { return (
累计生产经营总额{fmtWan(d.revenue.total)}元 累计税利{fmtWan(d.revenue.tax)}元 统计时间2026年7月
} />
累计生产经营总额{d.revenue.total}万元
累计上缴税利总额{d.revenue.tax}万元
带动就业{fmtInt(d.jobs.total)}人
实际入驻企业{d.projects.inPark}家
累计投入运营资金{d.projects.invested}万元
可容纳企业{d.projects.capacity}个
园区面积{d.park.area}㎡
成立时间{d.park.founded}年
免费办公空间 ≥20 ㎡ 一次性创业补贴 最高10万 担保贷款贴息 最高50万 科技计划经费 最高30万
单位负责人 李云鹏 {/* 填表人 李禹贤 填报时间 2026-07-31 */} 报名电话 {d.park.phone}
); } /* ---------- 中部轮换器:五项面板自由组织成两组,组间轮换 ---------- */ const ROTATE_MS = 10000; function MiddleRotator({ d }) { const views = [ { key: 'ai', label: 'OPC 智能资源', icon: 'cpu', node: (
), }, { key: 'park', label: '园区经营概况', icon: 'building', node: (
), }, ]; const [idx, setIdx] = useState(0); // 组间自动轮换:每次切换后重新计时;点击 Tab 也可切换 useEffect(() => { const iv = setInterval(() => setIdx((i) => (i + 1) % views.length), ROTATE_MS); return () => clearInterval(iv); }, [idx, views.length]); return (
{views.map((p, i) => ( ))}
{views[idx].node}
); } /* ---------- 实时动态(3 列 × 2 行,每几秒整体轮换加载) ---------- */ function LiveFeed({ feed, limit = 6 }) { const [page, setPage] = useState(0); const chunks = []; for (let i = 0; i < feed.length; i += limit) chunks.push(feed.slice(i, i + limit)); useEffect(() => { if (chunks.length <= 1) return; const iv = setInterval(() => setPage((p) => (p + 1) % chunks.length), 5000); return () => clearInterval(iv); }, [chunks.length]); const chunk = chunks[page % chunks.length] || []; return (
{chunk.map((e) => (
{e.text} {e.time}
))}
); } /* ========================================================= 页面 ========================================================= */ export default function DataScreen() { const d = useParkSim(); return (
{/* 园区今日概览(五个指标一行 + 标题右侧运行状态) */} {/* 中部大屏:五项内容自动轮换展示(降低信息密度) */} {/* 园区实时动态(底部整行 · 园区概览条上方 · 内容三列) */}
● LIVE} />
{/* 园区概览(保持在最底部) */}
); }