157 lines
7.0 KiB
HTML
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>
|