Files
lingniu-vehicle-ingest/vehicle-data-platform/apps/web/src/layout/AppShell.tsx

125 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Button, Input, Nav, Space, Tag, Toast } from '@douyinfe/semi-ui';
import {
IconActivity,
IconBarChartHStroked,
IconHistogram,
IconHome,
IconMapPin,
IconSearch,
IconServer,
IconSetting
} from '@douyinfe/semi-icons';
import type { ReactNode } from 'react';
import { useState } from 'react';
import type { VehicleSourceConsistency, VehicleServiceStatus } from '../api/types';
import { isAMapConfigured } from '../config/appConfig';
export type PageKey = 'dashboard' | 'vehicles' | 'realtime' | 'detail' | 'history' | 'mileage' | 'quality';
const navItems = [
{ itemKey: 'dashboard', text: '运营总览', icon: <IconHome /> },
{ itemKey: 'vehicles', text: '车辆中心', icon: <IconServer /> },
{ itemKey: 'realtime', text: '实时监控', icon: <IconActivity /> },
{ itemKey: 'detail', text: '车辆档案', icon: <IconSetting /> },
{ itemKey: 'history', text: '轨迹回放', icon: <IconMapPin /> },
{ itemKey: 'mileage', text: '统计分析', icon: <IconBarChartHStroked /> },
{ itemKey: 'quality', text: '告警通知', icon: <IconHistogram /> }
];
function linkHealthClassName(count: number | null) {
if (count == null) {
return '';
}
return count > 0 ? 'vp-link-health-button-warning' : 'vp-link-health-button-ok';
}
export function AppShell({
activePage,
linkIssueCount,
currentVehicleStatus,
currentVehicleLabel,
currentVehicleConsistency,
onChange,
onVehicleSearch,
children
}: {
activePage: PageKey;
linkIssueCount: number | null;
currentVehicleStatus?: VehicleServiceStatus;
currentVehicleLabel?: string;
currentVehicleConsistency?: VehicleSourceConsistency;
onChange: (page: PageKey) => void;
onVehicleSearch: (keyword: string) => void | Promise<void>;
children: ReactNode;
}) {
const [keyword, setKeyword] = useState('');
const [searching, setSearching] = useState(false);
const amapConfigured = isAMapConfigured();
const search = () => {
const value = keyword.trim();
if (!value) {
Toast.warning('请输入 VIN / 车牌 / 手机号');
return;
}
setSearching(true);
Promise.resolve(onVehicleSearch(value)).finally(() => setSearching(false));
};
return (
<div className="vp-app">
<div className="vp-shell">
<aside className="vp-sidebar">
<div className="vp-brand">
<span className="vp-brand-mark" />
<span></span>
</div>
<Nav
selectedKeys={[activePage]}
items={navItems}
onSelect={({ itemKey }) => onChange(itemKey as PageKey)}
style={{ maxWidth: '100%' }}
/>
</aside>
<main className="vp-main">
<header className="vp-topbar">
<Space spacing={12}>
<Input
prefix={<IconSearch />}
placeholder="搜索 VIN / 车牌 / 手机号"
value={keyword}
onChange={setKeyword}
onEnterPress={search}
style={{ width: 320 }}
/>
<Button theme="solid" type="primary" loading={searching} onClick={search}></Button>
</Space>
<Space spacing={12}>
{currentVehicleLabel ? <Tag color="grey">{currentVehicleLabel}</Tag> : null}
{currentVehicleStatus ? (
<Tag color={currentVehicleStatus.severity === 'ok' ? 'green' : currentVehicleStatus.severity === 'error' ? 'red' : 'orange'}>
{currentVehicleStatus.title}
</Tag>
) : null}
{currentVehicleConsistency ? (
<Tag color={currentVehicleConsistency.severity === 'ok' ? 'green' : currentVehicleConsistency.severity === 'error' ? 'red' : 'orange'}>
{currentVehicleConsistency.title || `${currentVehicleConsistency.onlineSourceCount}/${currentVehicleConsistency.sourceCount} 来源`}
</Tag>
) : null}
<Tag color="blue"></Tag>
<Tag color="grey"> / </Tag>
<Tag color={amapConfigured ? 'green' : 'orange'}>{amapConfigured ? '地图就绪' : '地图待配置'}</Tag>
<Button size="small" onClick={() => onChange('realtime')}></Button>
<Button size="small" onClick={() => onChange('history')}></Button>
<Button size="small" className={linkHealthClassName(linkIssueCount)} onClick={() => onChange('quality')}>
{linkIssueCount == null ? '链路监控' : linkIssueCount > 0 ? `链路 ${linkIssueCount} 项关注` : '链路正常'}
</Button>
</Space>
</header>
{children}
</main>
</div>
</div>
);
}