feat(platform): add customer primary navigation path
This commit is contained in:
@@ -67,6 +67,14 @@ const navGroups = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const customerPrimaryPath = [
|
||||||
|
{ label: '车辆地图', page: 'map' as const },
|
||||||
|
{ label: '轨迹回放', page: 'history' as const },
|
||||||
|
{ label: '统计查询', page: 'mileage' as const },
|
||||||
|
{ label: '数据导出', page: 'history-query' as const },
|
||||||
|
{ label: '告警通知', page: 'alert-events' as const }
|
||||||
|
];
|
||||||
|
|
||||||
function linkHealthClassName(count: number | null) {
|
function linkHealthClassName(count: number | null) {
|
||||||
if (count == null) {
|
if (count == null) {
|
||||||
return '';
|
return '';
|
||||||
@@ -167,6 +175,25 @@ export function AppShell({
|
|||||||
<span>实时地图 / 轨迹统计 / 告警通知</span>
|
<span>实时地图 / 轨迹统计 / 告警通知</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="vp-customer-primary-path" aria-label="客户主线">
|
||||||
|
<div className="vp-customer-primary-path-head">
|
||||||
|
<strong>客户主线</strong>
|
||||||
|
<span>先看车,再查时间窗,最后导出和通知。</span>
|
||||||
|
</div>
|
||||||
|
<div className="vp-customer-primary-path-actions">
|
||||||
|
{customerPrimaryPath.map((item) => (
|
||||||
|
<button
|
||||||
|
key={item.page}
|
||||||
|
type="button"
|
||||||
|
className={selectedPage === item.page ? 'vp-customer-primary-path-action vp-customer-primary-path-action-active' : 'vp-customer-primary-path-action'}
|
||||||
|
onClick={() => onChange(item.page)}
|
||||||
|
aria-label={`客户主线 ${item.label}`}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="vp-nav-groups">
|
<div className="vp-nav-groups">
|
||||||
{navGroups.map((group) => (
|
{navGroups.map((group) => (
|
||||||
<div key={group.title} className="vp-nav-group">
|
<div key={group.title} className="vp-nav-group">
|
||||||
|
|||||||
@@ -69,6 +69,63 @@ body {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vp-customer-primary-path {
|
||||||
|
margin: 0 4px 14px;
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid rgba(22, 100, 255, 0.16);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #f8fbff;
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-customer-primary-path-head {
|
||||||
|
display: grid;
|
||||||
|
gap: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-customer-primary-path-head strong {
|
||||||
|
color: var(--vp-text);
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-customer-primary-path-head span {
|
||||||
|
color: var(--vp-text-muted);
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-customer-primary-path-actions {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-customer-primary-path-action {
|
||||||
|
min-height: 30px;
|
||||||
|
padding: 5px 8px;
|
||||||
|
border: 1px solid var(--vp-border);
|
||||||
|
border-radius: 7px;
|
||||||
|
background: #ffffff;
|
||||||
|
color: var(--vp-text-muted);
|
||||||
|
cursor: pointer;
|
||||||
|
font: inherit;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 18px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-customer-primary-path-action:hover,
|
||||||
|
.vp-customer-primary-path-action:focus-visible,
|
||||||
|
.vp-customer-primary-path-action-active {
|
||||||
|
border-color: rgba(22, 100, 255, 0.38);
|
||||||
|
background: #ffffff;
|
||||||
|
color: var(--vp-primary);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
.vp-nav-groups {
|
.vp-nav-groups {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
|||||||
@@ -22,6 +22,13 @@ test('renders vehicle platform shell', () => {
|
|||||||
render(<App />);
|
render(<App />);
|
||||||
expect(screen.getByText('车辆服务中台')).toBeInTheDocument();
|
expect(screen.getByText('车辆服务中台')).toBeInTheDocument();
|
||||||
expect(screen.getByText('实时地图 / 轨迹统计 / 告警通知')).toBeInTheDocument();
|
expect(screen.getByText('实时地图 / 轨迹统计 / 告警通知')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('客户主线')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('先看车,再查时间窗,最后导出和通知。')).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('button', { name: '客户主线 车辆地图' })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('button', { name: '客户主线 轨迹回放' })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('button', { name: '客户主线 统计查询' })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('button', { name: '客户主线 数据导出' })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('button', { name: '客户主线 告警通知' })).toBeInTheDocument();
|
||||||
expect(screen.getByText('实时运营')).toBeInTheDocument();
|
expect(screen.getByText('实时运营')).toBeInTheDocument();
|
||||||
expect(screen.getByText('地图看车、在线监控、异常优先')).toBeInTheDocument();
|
expect(screen.getByText('地图看车、在线监控、异常优先')).toBeInTheDocument();
|
||||||
expect(screen.getAllByText('车辆服务').length).toBeGreaterThanOrEqual(1);
|
expect(screen.getAllByText('车辆服务').length).toBeGreaterThanOrEqual(1);
|
||||||
|
|||||||
Reference in New Issue
Block a user