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 (
);
}
/* 环形图 */
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 (
);
}
/* 横向排行条 */
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) => (
))}
);
}
/* ---------- 实时动态(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 (
{/* 园区今日概览(五个指标一行 + 标题右侧运行状态) */}
{/* 中部大屏:五项内容自动轮换展示(降低信息密度) */}
{/* 园区实时动态(底部整行 · 园区概览条上方 · 内容三列) */}
{/* 园区概览(保持在最底部) */}
);
}