feat: build vehicle data platform and production pipeline
This commit is contained in:
88
vehicle-data-platform/apps/web/src/v2/layout/AppShell.tsx
Normal file
88
vehicle-data-platform/apps/web/src/v2/layout/AppShell.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import {
|
||||
IconAlarm,
|
||||
IconBarChartHStroked,
|
||||
IconBox,
|
||||
IconChevronLeft,
|
||||
IconHelpCircle,
|
||||
IconHome,
|
||||
IconMapPin,
|
||||
IconSearch,
|
||||
IconSetting,
|
||||
IconUser,
|
||||
IconExit
|
||||
} from '@douyinfe/semi-icons';
|
||||
import { useState } from 'react';
|
||||
import { NavLink, Outlet, useLocation } from 'react-router-dom';
|
||||
import { usePlatformSession } from '../auth/AuthGate';
|
||||
|
||||
const navigation = [
|
||||
{ to: '/monitor', label: '全局监控', icon: IconHome },
|
||||
{ to: '/vehicles', label: '车辆查询', icon: IconSearch },
|
||||
{ to: '/tracks', label: '轨迹回放', icon: IconMapPin },
|
||||
{ to: '/history', label: '历史数据', icon: IconBarChartHStroked },
|
||||
{ to: '/alerts', label: '告警中心', icon: IconAlarm },
|
||||
{ to: '/access', label: '接入管理', icon: IconBox }
|
||||
];
|
||||
|
||||
const pageNames: Record<string, string> = {
|
||||
monitor: '全局监控',
|
||||
vehicles: '车辆查询',
|
||||
tracks: '轨迹回放',
|
||||
history: '历史数据',
|
||||
alerts: '告警中心',
|
||||
access: '接入管理',
|
||||
operations: '运维质量'
|
||||
};
|
||||
|
||||
export function AppShell() {
|
||||
const location = useLocation();
|
||||
const section = location.pathname.split('/')[1] || 'monitor';
|
||||
const { session, logout } = usePlatformSession();
|
||||
const roleLabel = { viewer: '只读', operator: '处置员', admin: '管理员' }[session.role];
|
||||
|
||||
return (
|
||||
<div className="v2-shell">
|
||||
<Sidebar />
|
||||
<div className="v2-main">
|
||||
<header className="v2-topbar">
|
||||
<h1>{pageNames[section] ?? '车辆数据中台'}</h1>
|
||||
<div className="v2-topbar-actions">
|
||||
<button type="button" aria-label="帮助"><IconHelpCircle /></button>
|
||||
<span className="v2-current-user"><IconUser /><b>{session.name}</b><small>{roleLabel}</small></span>
|
||||
<button type="button" aria-label="退出登录" title="退出登录" onClick={logout}><IconExit /></button>
|
||||
</div>
|
||||
</header>
|
||||
<main className="v2-content"><Outlet /></main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Sidebar() {
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
|
||||
return (
|
||||
<aside className={`v2-sidebar${collapsed ? ' is-collapsed' : ''}`}>
|
||||
<div className="v2-brand">
|
||||
<span className="v2-brand-mark"><IconBox size="large" /></span>
|
||||
<strong>车辆数据中台</strong>
|
||||
</div>
|
||||
<nav className="v2-navigation" aria-label="主导航">
|
||||
{navigation.map(({ to, label, icon: Icon }) => (
|
||||
<NavLink key={to} to={to} className={({ isActive }) => `v2-nav-item ${isActive ? 'is-active' : ''}`}>
|
||||
<Icon size="large" />
|
||||
<span className="v2-nav-label">{label}</span>
|
||||
</NavLink>
|
||||
))}
|
||||
</nav>
|
||||
<NavLink to="/operations" className={({ isActive }) => `v2-nav-item v2-nav-operations ${isActive ? 'is-active' : ''}`}>
|
||||
<IconSetting size="large" />
|
||||
<span className="v2-nav-label">运维质量</span>
|
||||
</NavLink>
|
||||
<button className="v2-collapse" type="button" onClick={() => setCollapsed((value) => !value)} aria-label={collapsed ? '展开侧栏' : '收起侧栏'}>
|
||||
<IconChevronLeft />
|
||||
<span>收起</span>
|
||||
</button>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user