init: 羚牛车辆数据中心原型 + 部署配置
All checks were successful
ci/woodpecker/manual/woodpecker Pipeline was successful

- React 18 + Babel-in-browser SPA 原型,覆盖 8 个画板:
  实时地图 / 车辆详情 / 历史查询 / 轨迹回放 / 事件规则 / 通知中心 / ESG 碳减排 / 移动端
- 设计系统:IBM Plex Sans + JetBrains Mono,亮/暗双主题,羚牛绿 #007143
- 数据模型:12 + 40 辆车,TBOX (T) / JT808+1078 (JT) / 双源 (B)
- 部署:nginx 静态托管,Dockerfile + woodpecker.yml + docker-compose.yml
- 镜像:harbor.lnh2e.com/lingniu-v1/ln-vdc:<branch>-<VERSION>
- 容器端口 80,宿主映射 8112,含 /healthz 探活
This commit is contained in:
kkfluous
2026-04-28 15:12:32 +08:00
commit b2d0016a0d
59 changed files with 6938 additions and 0 deletions

74
components/icons.jsx Normal file
View File

@@ -0,0 +1,74 @@
// icons.jsx — tiny stroke icon set for the cockpit
const Icon = ({ name, size = 16, className = "", style = {} }) => {
const paths = {
map: <><path d="M9 3 3 5v13l6-2 6 2 6-2V3l-6 2Z"/><path d="M9 3v13M15 5v13"/></>,
car: <><path d="M3 12l2-5h14l2 5"/><rect x="2" y="12" width="20" height="6" rx="1"/><circle cx="7" cy="18" r="1.5"/><circle cx="17" cy="18" r="1.5"/></>,
history: <><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></>,
route: <><circle cx="6" cy="19" r="2"/><circle cx="18" cy="5" r="2"/><path d="M8 19h6a4 4 0 0 0 0-8h-4a4 4 0 0 1 0-8h6"/></>,
bell: <><path d="M6 8a6 6 0 0 1 12 0c0 7 3 7 3 9H3c0-2 3-2 3-9Z"/><path d="M10 21a2 2 0 0 0 4 0"/></>,
inbox: <><path d="M3 13h5l1 2h6l1-2h5"/><path d="M5 5h14l2 8v6H3v-6Z"/></>,
settings: <><circle cx="12" cy="12" r="3"/><path d="M19 12a7 7 0 0 0-.1-1.2l2-1.6-2-3.4-2.4.9a7 7 0 0 0-2-1.2L14 3h-4l-.5 2.5a7 7 0 0 0-2 1.2l-2.4-.9-2 3.4 2 1.6A7 7 0 0 0 5 12c0 .4 0 .8.1 1.2l-2 1.6 2 3.4 2.4-.9c.6.5 1.3.9 2 1.2L10 21h4l.5-2.5c.7-.3 1.4-.7 2-1.2l2.4.9 2-3.4-2-1.6c.1-.4.1-.8.1-1.2Z"/></>,
bolt: <><path d="M13 2 4 13h7l-1 9 9-11h-7l1-9Z"/></>,
fuel: <><path d="M3 21V5a2 2 0 0 1 2-2h7a2 2 0 0 1 2 2v16"/><path d="M3 21h11M14 8h2a2 2 0 0 1 2 2v6a2 2 0 0 0 4 0V8l-3-3"/></>,
h2: <><path d="M5 6v12M5 12h6M11 6v12"/><path d="M15 14a3 3 0 1 1 6 0c0 2-3 2-3 4h3"/></>,
gauge: <><path d="M21 12a9 9 0 1 0-15.5 6.2"/><path d="m12 12 5-3"/><circle cx="12" cy="12" r="1.4"/></>,
thermo: <><path d="M14 14V5a2 2 0 0 0-4 0v9a4 4 0 1 0 4 0Z"/></>,
tire: <><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="3"/><path d="M12 3v6M12 15v6M3 12h6M15 12h6M5.6 5.6l4.2 4.2M14.2 14.2l4.2 4.2M5.6 18.4l4.2-4.2M14.2 9.8l4.2-4.2"/></>,
play: <><path d="M6 4l14 8-14 8Z"/></>,
pause: <><path d="M7 4v16M17 4v16"/></>,
next: <><path d="M5 4v16l14-8Z"/></>,
prev: <><path d="M19 20V4L5 12Z"/></>,
chevron: <><path d="m9 6 6 6-6 6"/></>,
chevDown: <><path d="m6 9 6 6 6-6"/></>,
plus: <><path d="M12 5v14M5 12h14"/></>,
close: <><path d="M6 6l12 12M18 6 6 18"/></>,
search: <><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></>,
filter: <><path d="M3 5h18l-7 9v6l-4-2v-4Z"/></>,
download: <><path d="M12 4v12m-5-5 5 5 5-5M4 20h16"/></>,
refresh: <><path d="M4 12a8 8 0 0 1 14-5.3L21 9M21 4v5h-5M20 12a8 8 0 0 1-14 5.3L3 15M3 20v-5h5"/></>,
pin: <><path d="M12 22s7-7 7-12a7 7 0 0 0-14 0c0 5 7 12 7 12Z"/><circle cx="12" cy="10" r="2.5"/></>,
expand: <><path d="M4 9V4h5M20 9V4h-5M4 15v5h5M20 15v5h-5"/></>,
grip: <><circle cx="9" cy="6" r="1.2"/><circle cx="15" cy="6" r="1.2"/><circle cx="9" cy="12" r="1.2"/><circle cx="15" cy="12" r="1.2"/><circle cx="9" cy="18" r="1.2"/><circle cx="15" cy="18" r="1.2"/></>,
layers: <><path d="m12 3 9 5-9 5-9-5Z"/><path d="m3 13 9 5 9-5M3 18l9 5 9-5"/></>,
chart: <><path d="M4 4v16h16"/><path d="m8 14 3-4 3 3 5-7"/></>,
user: <><circle cx="12" cy="8" r="4"/><path d="M4 21a8 8 0 0 1 16 0"/></>,
shield: <><path d="M12 3 4 6v6c0 5 3.5 8 8 9 4.5-1 8-4 8-9V6Z"/></>,
flag: <><path d="M5 21V4M5 4h13l-3 5 3 5H5"/></>,
sliders: <><path d="M4 6h12M20 6h0M4 12h4M12 12h8M4 18h12M20 18h0"/><circle cx="18" cy="6" r="2"/><circle cx="10" cy="12" r="2"/><circle cx="18" cy="18" r="2"/></>,
edit: <><path d="M4 20h4l11-11-4-4L4 16Z"/></>,
trash: <><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13"/></>,
plug: <><path d="M9 2v6M15 2v6M7 8h10v3a5 5 0 0 1-10 0Z"/><path d="M12 16v6"/></>,
wifi: <><path d="M5 12a10 10 0 0 1 14 0M8 15a6 6 0 0 1 8 0M11 18h2"/></>,
sat: <><circle cx="12" cy="12" r="3"/><path d="M5 12a7 7 0 0 1 7-7M19 12a7 7 0 0 1-7 7M3 12a9 9 0 0 1 9-9M21 12a9 9 0 0 1-9 9"/></>,
lightning: <><path d="M13 2 4 14h7l-1 8 9-12h-7l1-8Z"/></>,
wrench: <><path d="M14 7a4 4 0 0 1 5 5l-2-1-2 2 1 2a4 4 0 0 1-5-5l-7 7 3 3 7-7"/></>,
truck: <><path d="M3 7h11v10H3zM14 10h4l3 3v4h-7"/><circle cx="7" cy="18" r="1.5"/><circle cx="17" cy="18" r="1.5"/></>,
list: <><path d="M4 6h16M4 12h16M4 18h16"/></>,
fullscreen: <><path d="M4 9V4h5M20 9V4h-5M4 15v5h5M20 15v5h-5"/></>,
timeline: <><path d="M3 12h18"/><circle cx="6" cy="12" r="2"/><circle cx="14" cy="12" r="2"/><circle cx="20" cy="12" r="2"/></>,
branch: <><path d="M6 3v18M18 21V9a4 4 0 0 0-4-4h-4"/><circle cx="6" cy="3" r="2"/><circle cx="6" cy="21" r="2"/><circle cx="18" cy="9" r="2"/></>,
bookmark: <><path d="M5 3h14v18l-7-5-7 5Z"/></>,
moon: <><path d="M21 13a9 9 0 1 1-10-10 7 7 0 0 0 10 10Z"/></>,
speed: <><circle cx="12" cy="13" r="9"/><path d="m12 13 5-3M12 4v2"/></>,
leaf: <><path d="M4 20c0-9 7-16 16-16 0 9-7 16-16 16Z"/><path d="M4 20c5-5 11-9 16-13"/></>,
cube: <><path d="m12 3 9 5v8l-9 5-9-5V8Z"/><path d="m3 8 9 5 9-5M12 13v9"/></>,
pulse: <><path d="M3 12h4l3-7 4 14 3-7h4"/></>,
mail: <><rect x="3" y="5" width="18" height="14" rx="2"/><path d="m3 7 9 6 9-6"/></>,
phone: <><path d="M5 4h4l2 5-3 2a11 11 0 0 0 5 5l2-3 5 2v4a2 2 0 0 1-2 2A17 17 0 0 1 3 6a2 2 0 0 1 2-2Z"/></>,
clipboard: <><rect x="6" y="4" width="12" height="17" rx="2"/><rect x="9" y="2" width="6" height="4" rx="1"/><path d="M9 11h6M9 15h4"/></>,
x: <><path d="M6 6l12 12M18 6 6 18"/></>,
sun: <><circle cx="12" cy="12" r="4"/><path d="M12 2v3M12 19v3M2 12h3M19 12h3M5 5l2 2M17 17l2 2M5 19l2-2M17 7l2-2"/></>,
};
return (
<svg
viewBox="0 0 24 24"
width={size} height={size}
fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"
className={className}
style={style}
aria-hidden="true"
>{paths[name] || null}</svg>
);
};
window.Icon = Icon;