Files
oneos-truck-date-prototype/羚牛车辆数据中心.html
kkfluous 58ec7e7f90
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
chore: 页面标题改为 羚牛车辆数据中心-简介
2026-04-28 15:17:08 +08:00

157 lines
7.0 KiB
HTML

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8"/>
<title>羚牛车辆数据中心-简介</title>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<link rel="stylesheet" href="styles/design-system.css?v=5"/>
<style>
html, body { margin: 0; padding: 0; height: 100%; overflow: hidden; }
body { font-family: "IBM Plex Sans", system-ui, sans-serif; background: var(--bg-0); }
#root { height: 100%; width: 100%; }
/* page transition */
@keyframes pageFade {
from { opacity: 0; transform: translateY(2px); }
to { opacity: 1; transform: translateY(0); }
}
/* responsive — hide artboard's built-in sidebar on mobile (drawer takes over) */
@media (max-width: 899px) {
.app > .sidebar:not(.mobile) { display: none !important; }
.app { flex-direction: column; }
.app > div:not(.sidebar) { width: 100%; }
/* shrink topbar on small screens */
.topbar { padding: 0 12px; gap: 8px; height: 52px; flex: 0 0 52px; }
.topbar .crumbs { font-size: 12px; }
.topbar .crumbs .muted:not(:nth-last-child(2)) { display: none; }
.topbar .crumbs .sep:not(:last-of-type) { display: none; }
.topbar .kpi-row { display: none; }
.topbar .search { max-width: none; flex: 1; }
.topbar .right .icon-btn:nth-child(-n+2) { display: none; }
.topbar .right .avatar { display: none; }
}
@media (max-width: 599px) {
.topbar .search { display: none; }
}
</style>
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
<script src="data/fleet.js"></script>
<script type="text/babel" src="design-canvas.jsx"></script>
<script type="text/babel" src="tweaks-panel.jsx"></script>
<script type="text/babel" src="components/icons.jsx"></script>
<script type="text/babel" src="app.jsx"></script>
<script type="text/babel" src="components/chrome.jsx"></script>
<script type="text/babel" src="components/charts.jsx"></script>
<script type="text/babel" src="components/map.jsx"></script>
<script type="text/babel" src="artboards/overview.jsx"></script>
<script type="text/babel" src="artboards/detail.jsx"></script>
<script type="text/babel" src="artboards/history.jsx"></script>
<script type="text/babel" src="artboards/playback.jsx"></script>
<script type="text/babel" src="artboards/alarm.jsx"></script>
<script type="text/babel" src="artboards/inbox.jsx"></script>
<script type="text/babel" src="artboards/esg.jsx"></script>
<script type="text/babel" src="artboards/variant-light.jsx"></script>
<script type="text/babel" src="artboards/variant-dense.jsx"></script>
<script type="text/babel" src="artboards/mobile.jsx"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"theme": "light",
"accentHue": 202,
"density": "comfortable",
"showHeatmap": false,
"mapStyle": "default",
"role": "admin"
}/*EDITMODE-END*/;
const RoleContext = React.createContext({ role: window.ROLES[0], setRole: () => {} });
window.useCurrentRole = () => React.useContext(RoleContext);
const ThemeContext = React.createContext({ theme: "dark", setTheme: () => {} });
window.useTheme = () => React.useContext(ThemeContext);
// Filter visible vehicles by role scope
window.useVisibleVehicles = () => {
const { role } = React.useContext(RoleContext);
return React.useMemo(() => {
if (!role || role.scope === "all" || role.scope === "ops" || role.scope === "finance") return window.VEHICLES;
if (role.scope === "dept") return window.VEHICLES.filter(v => v.dept === role.deptId);
return window.VEHICLES;
}, [role]);
};
const App = () => {
const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);
const role = (window.ROLES || []).find(r => r.id === tweaks.role) || window.ROLES[0];
const setRole = React.useCallback((id) => setTweak('role', id), [setTweak]);
const setTheme = React.useCallback((t) => setTweak('theme', t), [setTweak]);
React.useEffect(() => {
document.documentElement.dataset.theme = tweaks.theme || 'light';
}, [tweaks.theme]);
React.useEffect(() => {
if (tweaks.theme === 'dark') {
document.documentElement.style.setProperty('--accent', `oklch(0.74 0.170 ${tweaks.accentHue})`);
document.documentElement.style.setProperty('--accent-soft', `oklch(0.74 0.170 ${tweaks.accentHue} / 0.16)`);
document.documentElement.style.setProperty('--accent-glow', `oklch(0.74 0.170 ${tweaks.accentHue} / 0.32)`);
} else {
document.documentElement.style.removeProperty('--accent');
document.documentElement.style.removeProperty('--accent-soft');
document.documentElement.style.removeProperty('--accent-glow');
}
}, [tweaks.accentHue, tweaks.theme]);
return (
<>
<RoleContext.Provider value={{ role, setRole }}>
<ThemeContext.Provider value={{ theme: tweaks.theme, setTheme }}>
<RouterApp/>
</ThemeContext.Provider>
</RoleContext.Provider>
<TweaksPanel title="Tweaks">
<TweakSection title="登录身份(数据权限演示)">
<TweakSelect value={tweaks.role} options={(window.ROLES || []).map(r => ({value: r.id, label: r.name}))}
onChange={v => setTweak('role', v)}/>
<div style={{fontSize: 11, color: "var(--fg-3)", marginTop: 6, lineHeight: 1.5}}>
{role && role.desc}
</div>
</TweakSection>
<TweakSection title="主题">
<TweakRadio value={tweaks.theme} options={[
{value:"light", label:"亮·羚牛白"},
{value:"dark", label:"暗·驾驶舱"},
]} onChange={v => setTweak('theme', v)}/>
</TweakSection>
<TweakSection title="主色调(暗主题)">
<TweakSlider label="羚牛绿·色相" value={tweaks.accentHue} min={130} max={220} step={1}
onChange={v => setTweak('accentHue', v)}/>
</TweakSection>
<TweakSection title="信息密度">
<TweakRadio value={tweaks.density} options={[
{value:"comfortable", label:"舒适"},
{value:"compact", label:"紧凑"},
]} onChange={v => setTweak('density', v)}/>
</TweakSection>
<TweakSection title="地图">
<TweakToggle label="显示热力图" value={tweaks.showHeatmap} onChange={v => setTweak('showHeatmap', v)}/>
<TweakRadio value={tweaks.mapStyle} options={[
{value:"default", label:"标准"},
{value:"minimal", label:"极简"},
]} onChange={v => setTweak('mapStyle', v)}/>
</TweakSection>
</TweaksPanel>
</>
);
};
ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
</script>
</body>
</html>