feat(web): simplify vehicle workspace with Apple-inspired design

This commit is contained in:
lingniu
2026-09-17 18:48:48 +08:00
parent f7e7176f88
commit 39d9cb5403
29 changed files with 1612 additions and 169 deletions
@@ -7,6 +7,10 @@ import './v2/styles/workspace.css';
import './v2/styles/foundation.css';
import './v2/styles/experience.css';
import './v2/styles/event-center.css';
import './v2/styles/apple-workspace.css';
import './v2/styles/apple-data-pages.css';
import './v2/styles/apple-governance.css';
import './v2/styles/apple-map-pages.css';
if ('scrollRestoration' in window.history) {
window.history.scrollRestoration = 'manual';
@@ -115,10 +115,8 @@ test('opens contextual help for the active module and closes it without navigati
expect(screen.getByRole('dialog', { name: '全局监控' })).toHaveTextContent('筛选会自动生效');
expect(document.querySelector('.v2-help-sheet')).toHaveClass('v2-workspace-help-sidesheet');
const helpSummary = screen.getByRole('list', { name: '全局监控摘要' });
expect(helpSummary).toHaveTextContent('页面目标快速完成');
expect(helpSummary).toHaveTextContent('建议步骤3 项');
expect(helpSummary).toHaveTextContent('快捷关闭Esc');
expect(screen.getByRole('dialog', { name: '全局监控' })).toHaveTextContent('查看全车队实时位置、状态和最新上报。');
expect(document.querySelectorAll('.v2-help-panel ol > li')).toHaveLength(3);
expect(document.querySelector('.v2-help-overview.semi-card')).toBeInTheDocument();
expect(screen.getByText('建议操作')).toBeInTheDocument();
expect(help).toHaveAttribute('aria-expanded', 'true');
@@ -86,18 +86,12 @@ function ContextHelp({ section, visible, mobile, onClose }: { section: string; v
title={title}
description="当前页面帮助与推荐操作"
badge={`${content.tips.length} 项建议`}
summaryItems={[
{ label: '页面目标', value: '快速完成', detail: '保留当前任务状态', tone: 'primary' },
{ label: '建议步骤', value: `${content.tips.length}`, detail: '按顺序执行' },
{ label: '快捷关闭', value: 'Esc', detail: '随时返回页面' }
]}
footerNote="关闭帮助不会改变当前筛选、地图或任务状态"
primaryAction={{ label: '返回当前页面', onClick: onClose }}
onCancel={onClose}
>
<div className="v2-help-panel">
<Card className="v2-help-overview" bodyStyle={{ padding: 0 }}>
<Tag color="blue"></Tag>
<Text type="secondary">{content.summary}</Text>
</Card>
<div className="v2-help-section-label"><Text strong></Text><Text type="tertiary" size="small">{content.tips.length} </Text></div>
@@ -127,7 +127,6 @@ test('removes old access rows immediately when the vehicle filter scope changes'
expect(await screen.findByText('旧接入车牌')).toBeInTheDocument();
expect(screen.getByRole('heading', { name: '接入健康工作台', level: 5 })).toBeInTheDocument();
expect(screen.getByLabelText('接入管理操作')).toHaveClass('v2-workspace-command-bar', 'v2-access-command-bar', 'has-identity');
expect(screen.getByLabelText('接入管理操作')).toHaveTextContent('来源治理');
expect(screen.getByLabelText('接入管理操作').querySelector('.v2-workspace-command-icon.semi-avatar')).toBeInTheDocument();
expect(view.container.querySelector('.v2-access-discovery-shell')).toBeInTheDocument();
for (const className of ['v2-access-filter-card-v3', 'v2-access-table-v3']) {
@@ -601,7 +601,6 @@ export default function AccessPage() {
<WorkspaceCommandBar
className="v2-access-command-bar"
ariaLabel="接入管理操作"
eyebrow="来源治理"
icon={<IconConnectionPoint1 />}
title="接入健康工作台"
description={mobileLayout
@@ -679,7 +678,6 @@ export default function AccessPage() {
<div className="v2-access-workspace-v3">
<Card className="v2-access-table-v3" bodyStyle={{ padding: 0 }}>
<WorkspacePanelHeader
eyebrow="接入差异"
tone="primary"
title="接入差异清单"
description={`当前范围:${scopeLabels[criteria.connectionState] || '全部主车辆'} · 缺席协议仅表示当前未发现`}
@@ -1395,6 +1395,7 @@ test.each([false, true])('displays native GB32960 alarms without automation (mob
expect(screen.getByLabelText('具体告警信息')).toHaveTextContent('绝缘报警');
expect(screen.getByLabelText('具体告警信息')).toHaveTextContent('驱动电机温度报警');
expect(screen.getByLabelText('具体告警信息')).toHaveTextContent('bit20、bit21');
if (!mobile) expect(screen.getByText('车辆原生告警 · GB/T 32960')).toBeInTheDocument();
expect(screen.getByLabelText('事件执行轨迹')).toHaveTextContent('车辆原生告警入库');
expect(screen.getByLabelText('事件执行轨迹')).not.toHaveTextContent('匹配自动化');
});
@@ -342,7 +342,7 @@ const AlertEventTable = memo(function AlertEventTable({ rows, selectedID, compac
{ title: '协议', dataIndex: 'protocol', className: 'is-protocol', width: 98, render: (value: string) => <ProtocolTag className="v2-alert-protocol-tag" protocol={value} compact /> },
{ title: '发生时间', dataIndex: 'triggeredAt', className: 'is-triggered-at', width: 124, render: (value: string) => <AlertTime value={value} /> },
{ title: '事件来源 / 自动化', dataIndex: 'ruleName', className: 'is-rule', width: 154, render: (value: string, event: AlertEvent) => <span className="v2-alert-event-rule" title={value}>{isNativeAlarm(event) ? '车辆原生告警' : value}</span> },
{ title: '执行状态', dataIndex: 'status', className: 'is-status', width: 92, render: (_: AlertEvent['status'], event: AlertEvent) => <EventExecutionTag event={event} /> },
{ title: '执行状态', dataIndex: 'status', className: 'is-status', width: 92, fixed: 'right' as const, render: (_: AlertEvent['status'], event: AlertEvent) => <EventExecutionTag event={event} /> },
];
return compact
? allColumns.map((column) => ({ ...column, width: COMPACT_ALERT_COLUMN_WIDTHS[column.dataIndex] ?? column.width }))
@@ -352,6 +352,7 @@ const AlertEventTable = memo(function AlertEventTable({ rows, selectedID, compac
return <Table
className={`v2-alert-event-table${compact ? ' is-compact' : ''}`}
columns={columns}
scroll={{ x: columns.reduce((width, column) => width + column.width, 0) }}
dataSource={rows}
rowKey="id"
pagination={false}
@@ -378,7 +379,7 @@ function EventInspector({ event, loading = false, detailError, note, acting, act
</Card>;
const normalized = toVehicleEvent(event);
return <Card className={`v2-alert-inspector${sheet ? ' is-sheet' : ''}`} bodyStyle={{ padding: 0 }}>
{!sheet ? <WorkspacePanelHeader className="v2-alert-inspector-heading" title="事件详情" description={normalized.type} actions={<><span className="v2-alert-inspector-status"><EventExecutionTag event={event} /></span><Button className="v2-alert-inspector-close" theme="borderless" icon={<IconClose />} aria-label="关闭事件详情" onClick={onClose} /></>} /> : null}
{!sheet ? <WorkspacePanelHeader className="v2-alert-inspector-heading" title="事件详情" description={isNativeAlarm(event) ? "车辆原生告警 · GB/T 32960" : `${normalized.categoryLabel} · 自动化事件`} actions={<><span className="v2-alert-inspector-status"><EventExecutionTag event={event} /></span><Button className="v2-alert-inspector-close" theme="borderless" icon={<IconClose />} aria-label="关闭事件详情" onClick={onClose} /></>} /> : null}
<div className="v2-alert-inspector-content">
{detailError ? <InlineError message={`最新事件状态同步失败:${detailError}`} onRetry={onRetry} /> : null}
<Card className="v2-alert-detail-card v2-alert-focus-card" bodyStyle={{ padding: 0 }} aria-label="事件摘要">
@@ -392,7 +393,7 @@ function EventInspector({ event, loading = false, detailError, note, acting, act
<span><small></small><strong><AlertTime value={normalized.occurredAt} /></strong></span>
<span><small>{isNativeAlarm(event) ? '事件来源' : '匹配自动化'}</small><strong>{isNativeAlarm(event) ? '车辆原生告警' : `v${event.ruleVersion}`}</strong></span>
</div>
{isNativeAlarm(event) ? <section aria-label="32960 原生告警字段">
{isNativeAlarm(event) ? <section className="v2-native-alarm-fields" aria-label="32960 原生告警字段">
<div className="v2-native-alarm-meaning" aria-label="具体告警信息">
<strong></strong>
{event.nativeAlarmNames?.length ? <ul>{event.nativeAlarmNames.map((name) => <li key={name}>{name}</li>)}</ul> : <p>{event.ruleName}</p>}
@@ -1245,7 +1246,7 @@ function RulesWorkspace({ page, rulesReady, rulesLoading, rulesError, onRetryRul
<Button className={`v2-alert-rule-item${selectedRule?.id === rule.id ? ' is-selected' : ''}`} theme="borderless" type="tertiary" aria-pressed={selectedRule?.id === rule.id} aria-haspopup={mobileLayout ? 'dialog' : undefined} aria-expanded={mobileLayout ? (mobileDetailOpen || editorOpen) && selectedID === rule.id : selectedRule?.id === rule.id} onClick={() => mobileLayout ? openMobileRuleDetail(rule) : selectRule(rule)}>
<span className="v2-alert-rule-copy"><span><strong>{rule.name}</strong><Tag color={rule.archivedAt ? 'amber' : rule.enabled ? 'green' : 'grey'} type="light" size="small">{rule.archivedAt ? '已归档' : rule.enabled ? '运行中' : '已停用'}</Tag></span><small>{automationSource(rule)} · {ruleCondition(rule, catalogLabels, catalogUnits)}</small></span>
</Button>
{rule.archivedAt ? <Tag className="v2-alert-automation-readonly" color="grey" type="light" size="small"></Tag> : <Switch className="v2-alert-automation-switch" checked={rule.enabled} loading={toggle.isPending} aria-label={`${rule.enabled ? '停用' : '启用'}自动化:${rule.name}`} onChange={() => toggle.mutate(rule)} />}
{rule.archivedAt ? <Tag className="v2-alert-automation-readonly" color="grey" type="light" size="small"></Tag> : <label className="v2-alert-automation-switch-target"><Switch className="v2-alert-automation-switch" checked={rule.enabled} loading={toggle.isPending} aria-label={`${rule.enabled ? '停用' : '启用'}自动化:${rule.name}`} onChange={() => toggle.mutate(rule)} /></label>}
<small className="v2-alert-automation-version">v{rule.version}</small>
</article>)}
{rulesLoading ? <PanelLoading compact title="正在检索自动化" description="从完整规则库读取当前页。" /> : rulesError ? <PanelError compact title="自动化检索失败" description={rulesError} action={<Button theme="solid" type="primary" onClick={onRetryRules}></Button>} /> : !filteredRules.length ? <PanelEmpty className="v2-alert-rule-empty" tone="primary" icon={lifecycle === 'archived' ? <IconBox /> : <IconSearch />} title={search || protocolFilter || statusFilter !== 'all' ? '没有匹配的自动化' : lifecycle === 'archived' ? '暂无审计归档' : '暂无自动化'} description={search || protocolFilter || statusFilter !== 'all' ? '换一个名称、协议或状态筛选试试。' : lifecycle === 'archived' ? '停用规则可在详情中带原因归档,并在这里恢复。' : '创建第一条事件自动化。'} /> : null}
@@ -577,10 +577,7 @@ test('clears stale rows, charts, and evidence as soon as the history scope chang
expect(view.container.querySelector(`.${className}.semi-card`)).toBeInTheDocument();
}
expect(view.container.querySelector('.v2-history-discovery-shell')).toContainElement(screen.getByLabelText('历史数据查询操作'));
expect(screen.getByLabelText('历史数据查询操作')).toHaveClass('has-identity');
expect(screen.getByLabelText('历史数据查询操作')).toHaveTextContent('历史证据');
expect(screen.getByLabelText('历史数据查询操作').querySelector('.v2-workspace-command-icon.semi-avatar')).toBeInTheDocument();
expect(screen.getByLabelText('历史数据查询操作')).toHaveTextContent('遥测证据检索');
expect(screen.getByLabelText('历史数据查询操作')).toHaveTextContent('查询条件');
expect(screen.getByLabelText('历史数据查询操作')).toHaveTextContent('07-16 00:00 07-16 05:00');
expect(screen.getByLabelText('历史数据查询操作')).toHaveTextContent('位置数据');
expect(screen.getByLabelText('历史数据查询操作')).toHaveTextContent('全部来源');
@@ -1894,10 +1894,8 @@ export default function HistoryPage() {
<WorkspaceCommandBar
className="v2-history-command-bar"
ariaLabel="历史数据查询操作"
eyebrow="历史证据"
icon={<IconHistory />}
title="遥测证据检索"
description="按车辆、时间和协议核验可追溯的历史数据"
title="查询条件"
description="选择车辆和时间,查询历史上报"
status={keywords.length ? `${keywords.length} 辆 · ${(result?.total ?? 0).toLocaleString('zh-CN')}` : '等待查询'}
statusColor={keywords.length ? 'blue' : 'grey'}
meta={keywords.length ? <span className="v2-history-applied-scope" aria-label="已应用查询范围">
@@ -2029,8 +2027,6 @@ export default function HistoryPage() {
<HistoryTrend response={seriesQuery.data} category={criteria.category} loading={seriesQuery.isFetching} error={seriesQuery.isError ? (seriesQuery.error instanceof Error ? seriesQuery.error.message : '未知错误') : undefined} hasMetrics={Boolean(seriesMetricKey)} expanded={trendExpanded} onToggle={() => setTrendExpanded((value) => !value)} onRetry={() => { void seriesQuery.refetch(); }} />
<Card className={`v2-history-table-card is-${density}`} bodyStyle={{ padding: 0 }}>
<WorkspacePanelHeader
eyebrow="证据结果"
tone="primary"
title="历史明细"
description={keywords.length ? `${(result?.total ?? 0).toLocaleString('zh-CN')} 条记录 · ${resultSummary?.vehicleCount ?? 0} 辆车辆 · ${summarySources}` : '选择车辆和时间范围后查询'}
meta={<span className="v2-history-result-meta"><Tag color={activeRawSegment ? 'cyan' : activeFieldViewName.includes('未保存') ? 'orange' : 'blue'} type="light" size="small">{activeRawSegment ? `${rawSegments.find((segment) => segment.key === activeRawSegment)?.label} · ${displayMetrics.length - 2} 字段` : `${activeFieldViewName} · ${visibleMetrics.length} / ${allMetrics.length} 字段`}</Tag>{resultSummary ? <span className="v2-history-query-duration"> {resultSummary.queryDurationMs} ms</span> : null}</span>}
@@ -2096,15 +2092,9 @@ export default function HistoryPage() {
/>
: <WorkspaceEmptyGuide
className="v2-history-empty"
eyebrow={mobileLayout ? undefined : '从一个明确的车辆范围开始'}
icon={<IconSearch />}
title="选择车辆,开始核验历史证据"
description="一次最多查询 5 台车辆,结果会保留协议来源、质量说明与 RAW 证据。"
steps={mobileLayout ? undefined : [
{ icon: <IconSearch />, title: '选择车辆', description: '支持车牌、VIN 与批量粘贴' },
{ icon: <IconCalendar />, title: '确认时间', description: '按上海时区精确到分钟' },
{ icon: <IconMapPin />, title: '核验数据', description: '位置、RAW 与日里程统一检索' }
]}
action={<Button className="v2-history-empty-action" theme="solid" icon={<IconSearch />} aria-label="选择车辆并查询" onClick={() => setFiltersCollapsed(false)}></Button>}
/>
: null}
@@ -620,7 +620,7 @@ export default function MonitorPage() {
{mode === 'map' ? <section className={`v2-monitor-workspace${selected && detailOpen ? ' is-detail-open' : ''}${selected && !detailOpen ? ' is-detail-collapsed' : ''}`}>
<div className="v2-vehicle-rail">
<header><strong></strong><span>{batchSearchPending ? '查询中' : `${formatNumber(vehicles.data?.total ?? visibleRows.length)}`}</span></header>
<div className={`v2-rail-search${batchMatch.missing.length && !batchSearchPending ? ' has-missing' : ''}`} title={searchTerms.length > 1 ? batchStatusTitle : undefined}><IconSearch /><span>{searchTerms.length > 1 ? batchStatus : deferredKeyword ? `正在筛选“${deferredKeyword}` : '最新上报排序'}</span></div>
<div role="status" className={`v2-rail-search${batchMatch.missing.length && !batchSearchPending ? ' has-missing' : ''}`} title={searchTerms.length > 1 ? batchStatusTitle : undefined}><span>{searchTerms.length > 1 ? batchStatus : deferredKeyword ? `正在筛选“${deferredKeyword}` : '最新上报优先'}</span></div>
<div className="v2-vehicle-scroll">
{vehicles.isLoading || batchSearchPending ? <div className="v2-list-loading"><span className="v2-spinner" />{batchSearchPending ? `正在查找 ${searchTerms.length} 辆车` : '加载车辆'}</div> : null}
{!vehicles.isLoading && !vehicles.isError && !batchSearchPending && visibleRows.length === 0 ? <EmptyState /> : null}
@@ -79,7 +79,6 @@ test('renders reconciliation queue, loads evidence on demand and records review
expect(await screen.findByText('质量问题队列')).toBeInTheDocument();
expect(screen.getByRole('heading', { name: '运行与数据质量', level: 5 })).toBeInTheDocument();
expect(screen.getByLabelText('运维质量操作')).toHaveClass('v2-workspace-command-bar', 'v2-ops-command-bar', 'has-identity');
expect(screen.getByLabelText('运维质量操作')).toHaveTextContent('运行治理');
expect(screen.getByLabelText('运维质量操作').querySelector('.v2-workspace-command-icon.semi-avatar')).toBeInTheDocument();
expect(screen.getByRole('button', { name: '刷新全局证据' })).toHaveClass('v2-workspace-mobile-tool-button', 'is-muted');
const navigation = screen.getByRole('navigation', { name: '运维质量视图' });
@@ -294,7 +293,11 @@ test('renders only the compact mobile reconciliation surface and defers secondar
expect(toolbar).not.toHaveClass('is-mobile-collapsed');
expect(screen.queryByText('近 30 天趋势')).not.toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: '30 天趋势' }));
fireEvent.click(screen.getByRole('button', { name: '质量问题更多操作' }));
expect(await screen.findByRole('menuitem', { name: '批量处置' })).toBeInTheDocument();
expect(screen.getByRole('menuitem', { name: /导出当前筛选/ })).toBeInTheDocument();
expect(screen.getByRole('menuitem', { name: /刷新结果/ })).toBeInTheDocument();
fireEvent.click(screen.getByRole('menuitem', { name: /30 天趋势/ }));
expect(await screen.findByRole('dialog', { name: '30 天差异趋势' })).toBeInTheDocument();
expect(document.querySelector('.v2-reconcile-trend-sidesheet')).toHaveClass('semi-sidesheet-bottom', 'v2-workspace-detail-sidesheet');
expect(document.querySelector('.v2-reconcile-trend-sidesheet .semi-sidesheet-inner')).toHaveStyle({ height: 'min(72dvh, 620px)' });
@@ -598,7 +598,6 @@ export default function OperationsPage() {
<WorkspaceCommandBar
className="v2-ops-command-bar"
ariaLabel="运维质量操作"
eyebrow="运行治理"
icon={<IconPulse />}
tone="health"
title="运行与数据质量"
@@ -634,7 +633,6 @@ export default function OperationsPage() {
{readiness.isError ? <InlineError message={readiness.error instanceof Error ? readiness.error.message : '协议来源就绪度读取失败'} onRetry={() => readiness.refetch()} /> : null}
<Card className="v2-ops-overview" bodyStyle={{ padding: 0 }}>
<WorkspacePanelHeader
eyebrow="运行快照"
tone="health"
title="运行概览"
description="关键链路、基础设施与车辆覆盖的同一健康快照"
@@ -1,5 +1,5 @@
import { IconAlertTriangle, IconBox, IconChevronDown, IconChevronRight, IconChevronUp, IconClose, IconDownload, IconHistory, IconRefresh, IconSearch, IconTickCircle } from '@douyinfe/semi-icons';
import { Button, Card, Checkbox, Input, RadioGroup, Select, Table, Tag, TextArea, Toast } from '@douyinfe/semi-ui';
import { IconAlertTriangle, IconBox, IconChevronDown, IconChevronRight, IconChevronUp, IconClose, IconDownload, IconHistory, IconMore, IconRefresh, IconSearch, IconTickCircle } from '@douyinfe/semi-icons';
import { Button, Card, Checkbox, Dropdown, Input, RadioGroup, Select, Table, Tag, TextArea, Toast } from '@douyinfe/semi-ui';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useCallback, useDeferredValue, useEffect, useId, useMemo, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
@@ -615,6 +615,7 @@ function ReconciliationBatchReview({ issues, visible, mobileLayout, onCancel, on
export default function ReconciliationCenter() {
const mobileLayout = useMobileLayout();
const [mobileActionsOpen, setMobileActionsOpen] = useState(false);
const [searchParams, setSearchParams] = useSearchParams();
const initialScope = searchParams.get('reconcileScope') === 'archived' ? 'archived' : 'current';
const defaultPageSize = mobileLayout ? MOBILE_PAGE_SIZE : DESKTOP_PAGE_SIZE;
@@ -843,12 +844,23 @@ export default function ReconciliationCenter() {
return <><Card className="v2-reconcile-center" bodyStyle={{ padding: 0 }}>
<WorkspacePanelHeader
className="v2-reconcile-heading"
eyebrow="自动对账"
tone="health"
title="质量问题队列"
description="自动发现并合并重复问题;复核人员基于原始证据形成结论,不直接改写数据。"
meta="每日自动对账"
actions={<>
actions={mobileLayout ? <Dropdown
trigger="click"
position="bottomRight"
visible={mobileActionsOpen}
onVisibleChange={setMobileActionsOpen}
render={<Dropdown.Menu>
{!batchMode && scope === 'current' ? <Dropdown.Item onClick={() => { setMobileActionsOpen(false); enterBatchMode('operate'); }}></Dropdown.Item> : null}
{!batchMode && administrator ? <Dropdown.Item icon={scope === 'archived' ? <IconRefresh /> : <IconBox />} onClick={() => { setMobileActionsOpen(false); enterBatchMode('lifecycle'); }}>{scope === 'archived' ? '批量恢复' : '批量归档'}</Dropdown.Item> : null}
{!batchMode ? <Dropdown.Item icon={<IconDownload />} disabled={!page?.total || exportIssues.isPending} onClick={() => { setMobileActionsOpen(false); exportIssues.mutate(); }}>{exportIssues.isPending ? '正在导出…' : '导出当前筛选'}</Dropdown.Item> : null}
{scope === 'current' ? <Dropdown.Item icon={<IconHistory />} onClick={() => { setMobileActionsOpen(false); toggleTrend(); }}>30 </Dropdown.Item> : null}
<Dropdown.Item icon={<IconRefresh />} disabled={summary.isFetching || issues.isFetching} onClick={() => { setMobileActionsOpen(false); refresh(); }}></Dropdown.Item>
</Dropdown.Menu>}
><Button className="v2-reconcile-mobile-more" theme="borderless" icon={<IconMore />} aria-haspopup="menu" aria-expanded={mobileActionsOpen} aria-label="质量问题更多操作"></Button></Dropdown> : <>
{!batchMode && scope === 'current' ? <Button className="v2-reconcile-batch-entry v2-workspace-mobile-tool-button is-muted" theme="light" aria-label="进入批量处置" onClick={() => enterBatchMode('operate')}>{mobileLayout ? '批量' : '批量处置'}</Button> : null}
{!batchMode && administrator ? <Button className="v2-workspace-mobile-tool-button is-muted" theme="light" type="tertiary" icon={scope === 'archived' ? <IconRefresh /> : <IconBox />} aria-label={scope === 'archived' ? '进入批量恢复' : '进入批量归档'} onClick={() => enterBatchMode('lifecycle')}>{mobileLayout ? scope === 'archived' ? '恢复' : '归档' : scope === 'archived' ? '批量恢复' : '批量归档'}</Button> : null}
{!batchMode ? <Button className="v2-reconcile-export v2-workspace-mobile-tool-button is-muted" theme="light" icon={<IconDownload />} aria-label="导出当前筛选" loading={exportIssues.isPending} disabled={!page?.total} onClick={() => exportIssues.mutate()}>{mobileLayout ? '导出' : '导出当前筛选'}</Button> : null}
@@ -1349,8 +1349,6 @@ export default function StatisticsPage() {
</div>
<Card className="v2-mileage-results" bodyStyle={{ padding: 0 }}>
<WorkspacePanelHeader
eyebrow={metricView === 'mileage' ? '每日里程' : '每日氢耗'}
tone={metricView === 'mileage' ? 'primary' : 'health'}
title={metricView === 'mileage' ? '车辆每日里程' : '车辆每日氢耗'}
description={`${criteria.dateFrom}${criteria.dateTo}`}
meta={<span className="v2-workspace-result-meta"><Tag color="blue" type="light" size="small">{hasVehicles ? `${totalVehicles}` : `${displayVehicles.length} / ${totalVehicles}`}</Tag><span>{dates.length} </span></span>}
@@ -1370,6 +1368,6 @@ export default function StatisticsPage() {
<footer>{totalVehicles > PAGE_SIZE ? <TablePagination page={page} totalPages={totalPages} info={`${hasVehicles ? '已选择' : '共'} ${totalVehicles.toLocaleString('zh-CN')} 辆 · 每页 ${PAGE_SIZE}${exportFeedback ? ` · ${exportFeedback}` : ''}`} disabled={mileage.isFetching || (!hasVehicles && fleetVehicles.isFetching)} onPageChange={setPage} /> : <span className="v2-table-pagination-info">{hasVehicles ? '已选择' : '共'} {totalVehicles.toLocaleString('zh-CN')} {exportFeedback ? ` · ${exportFeedback}` : ''}</span>}</footer>
</Card>
<HydrogenEvidenceSheet target={hydrogenEvidenceTarget} onClose={() => setHydrogenEvidenceTarget(undefined)} />
<footer className="v2-mileage-evidence"><span>{statistics.data?.asOf || '—'}</span><span>{criteria.sources.filter((source) => source.enabled).map((source) => source.protocol).join(' ')}</span><span> 1 · </span></footer>
<footer className="v2-mileage-evidence"><span>{statistics.data?.asOf || '—'}</span><span>{criteria.sources.filter((source) => source.enabled).map((source) => source.protocol).join(' ')}</span><span></span></footer>
</div>;
}
@@ -160,6 +160,9 @@ test('explains an empty selected-vehicle time window without asking to select th
expect(screen.getByText('车辆已选定,可调整时间范围或数据来源后重新查询。')).toBeInTheDocument();
expect(screen.queryByText('先选择车辆,再开始轨迹回放')).not.toBeInTheDocument();
expect(screen.getByRole('button', { name: '修改查询' })).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: '修改查询' }));
expect(screen.getByRole('textbox', { name: '搜索轨迹车辆' })).toHaveValue('LTEST000000000001');
expect(screen.getByRole('button', { name: /查询轨迹/ })).toBeEnabled();
});
test('distinguishes a failed vehicle lookup from an empty result and retries in place', async () => {
@@ -456,8 +459,8 @@ test('keeps one clear mobile query entry before a vehicle is selected', async ()
expect(screen.queryByRole('button', { name: '打开轨迹详情' })).not.toBeInTheDocument();
expect(screen.queryByRole('toolbar', { name: '地图轨迹工具' })).not.toBeInTheDocument();
expect(screen.queryByRole('slider', { name: '轨迹播放进度' })).not.toBeInTheDocument();
expect(screen.getByRole('slider', { name: '轨迹播放进度,等待查询结果' })).toBeDisabled();
expect(view.container.querySelector('.v2-track-playback-dock.is-empty')).toBeInTheDocument();
expect(screen.queryByRole('slider', { name: '轨迹播放进度,等待查询结果' })).not.toBeInTheDocument();
expect(view.container.querySelector('.v2-track-playback-dock')).not.toBeInTheDocument();
expect(view.container.querySelector('.v2-workspace-empty-guide-eyebrow')).not.toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: /选择车辆/ }));
expect(await screen.findByRole('dialog', { name: '轨迹查询与明细' })).toBeInTheDocument();
@@ -135,7 +135,7 @@ function trackSummaryItems(track?: TrackPlaybackResponse): WorkspaceQueueMetricR
return [
{
label: distanceLabel,
value: track ? `${number(track.summary.distanceKm)} km` : '—',
value: track ? <>{number(track.summary.distanceKm)}<small className="v2-track-distance-unit"> km</small></> : '—',
note: track ? formatDuration(track.summary.durationSeconds) : '等待查询',
tone: 'primary',
emphasis: 'primary'
@@ -291,18 +291,18 @@ const TrackRail = memo(function TrackRail({ draft, applied, track, summaryItems,
], []).map((preset) => ({ ...preset, active: activePreset === preset.key }));
const compactAppliedWindow = compactTrackWindowLabel(applied.dateFrom, applied.dateTo);
const appliedSource = applied.protocol ? protocolDisplayLabel(applied.protocol) : '自动来源';
const contextTitle = queryCollapsed ? track?.plate || applied.keyword || '轨迹查询' : '轨迹查询';
const contextTitle = queryCollapsed ? track?.plate || applied.keyword || '查询条件' : '查询条件';
return <aside className="v2-track-rail">
<Card className="v2-track-rail-shell" bodyStyle={{ padding: 0 }}>
<section className={`v2-track-query-panel${queryCollapsed ? ' is-collapsed' : ''}${showCommandBar ? '' : ' is-command-hidden'}`}>
{showCommandBar ? <WorkspaceCommandBar
className="v2-track-query-header"
ariaLabel="轨迹查询任务栏"
eyebrow="轨迹回放"
icon={<IconRoute />}
eyebrow={track ? '轨迹回放' : undefined}
icon={track ? <IconRoute /> : undefined}
title={contextTitle}
description={queryCollapsed ? `${compactAppliedWindow} · ${appliedSource}` : '最长支持连续 7 天'}
status={loading ? '读取中' : track ? undefined : '等待查询'}
description={queryCollapsed ? `${compactAppliedWindow} · ${appliedSource}` : '最长 7 天'}
status={loading ? '读取中' : undefined}
statusColor={loading ? 'blue' : track ? 'green' : 'grey'}
actions={<>
<Button
@@ -335,7 +335,7 @@ const TrackRail = memo(function TrackRail({ draft, applied, track, summaryItems,
</form> : null}
</section>
<section className="v2-track-rail-result">
{track ? <section className="v2-track-rail-result">
<WorkspaceMetricRail
variant="queue"
ariaLabel="轨迹行程摘要"
@@ -345,12 +345,11 @@ const TrackRail = memo(function TrackRail({ draft, applied, track, summaryItems,
{track ? <TrackCoverageNotice track={track} onModify={onToggleQuery} onOverview={() => onTab('overview')} /> : null}
<SegmentedTabs className="v2-track-rail-tabs" ariaLabel="轨迹明细分类" value={tab} onChange={onTab} items={[{ key: 'stops', label: '停留点', count: track?.stops.length ?? 0 }, { key: 'events', label: '事件点', count: track?.events.length ?? 0 }, { key: 'overview', label: '概览' }]} />
<div className="v2-track-rail-scroll">
{!track ? <PanelEmpty className="v2-track-rail-empty" compact tone="primary" icon={<IconMapPin />} title="选择车辆后查询轨迹" description="停留点、轨迹事件与行程证据会在这里统一呈现。" /> : null}
{track && tab === 'stops' ? <List className="v2-track-evidence-list v2-track-stop-list">{track.stops.map((stop, index) => <List.Item className="v2-track-evidence-item" key={`${stop.startTime}-${index}`}><Button theme="borderless" type="tertiary" className={`v2-track-list-action${activeStopIndexes.includes(index) ? ' is-active' : ''}`} onClick={() => onSelectIndex(stop.sampledIndex)}><i>{index + 1}</i><span><strong>{dateTime(stop.startTime)}</strong><small> {formatDuration(stop.durationSeconds)} · {stop.pointCount} </small></span><em>{time(stop.endTime)}</em></Button></List.Item>)}{!track.stops.length ? <PanelEmpty className="v2-track-list-empty" compact icon={<IconMapPin />} title="当前没有停留点" description="时间窗内没有超过 3 分钟的连续停留。" /> : null}</List> : null}
{track && tab === 'events' ? <List className="v2-track-evidence-list v2-track-event-list">{track.events.map((event, index) => { const sampled = sampledEventIndex(event, track.points.length, track.summary.pointCount); return <List.Item className="v2-track-evidence-item" key={`${event.type}-${event.time}-${index}`}><Button theme="borderless" type="tertiary" className={`v2-track-list-action${activeEventIndexes.includes(index) ? ' is-active' : ''}`} onClick={() => onSelectIndex(sampled)}><i className={`is-${eventTone(event.type)}`}>{index + 1}</i><span><strong>{event.title}</strong><small>{dateTime(event.time)}</small></span><em>{number(event.speedKmh, 0)} km/h</em></Button></List.Item>; })}{!track.events.length ? <PanelEmpty className="v2-track-list-empty" compact icon={<IconMapPin />} title="当前没有事件点" description="时间窗内没有识别到启停、急加速或异常间隔。" /> : null}</List> : null}
{track && tab === 'overview' ? <OverviewPanel track={track} /> : null}
</div>
</section>
</section> : <p className="v2-track-query-hint"></p>}
</Card>
</aside>;
});
@@ -479,6 +478,11 @@ export default function TrackPage() {
}, [mobileLayout, selectIndex]);
const collapseRail = useCallback(() => setRailCollapsed(true), []);
const toggleQuery = useCallback(() => setQueryCollapsed((value) => !value), []);
const openEmptyQuery = useCallback(() => {
setQueryCollapsed(false);
setRailCollapsed(false);
window.requestAnimationFrame(() => document.querySelector<HTMLInputElement>('.v2-track-query input')?.focus());
}, []);
const togglePlayback = () => {
if (points.length < 2) return;
if (!playing && boundedIndex >= points.length - 1) setActiveIndex(0);
@@ -601,12 +605,7 @@ export default function TrackPage() {
icon={<IconMapPin />}
title={emptyTrackTitle}
description={emptyTrackDescription}
steps={emptyTrackHasVehicle ? undefined : [
{ icon: <IconSearch />, title: '选择车辆', description: '车牌或 VIN 精确定位' },
{ icon: <IconMapPin />, title: '确认来源', description: '自动选择最佳定位来源' },
{ icon: <IconPlay />, title: '播放核验', description: '逐点查看速度、里程与事件' }
]}
action={<Button theme="solid" icon={<IconSearch />} aria-label={emptyTrackHasVehicle ? '修改查询' : '选择车辆'} onClick={() => setRailCollapsed(false)}>{emptyTrackHasVehicle ? '修改查询' : '选择车辆'}</Button>}
action={<Button theme="solid" icon={<IconSearch />} aria-label={emptyTrackHasVehicle ? '修改查询' : '选择车辆'} onClick={openEmptyQuery}>{emptyTrackHasVehicle ? '修改查询' : '选择车辆'}</Button>}
/></Card>}
{query.isFetching ? <PanelLoading className="v2-track-loading" compact title="正在生成轨迹" description="正在校验来源覆盖并整理停留与事件证据。" /> : null}
@@ -628,17 +627,6 @@ export default function TrackPage() {
<label><span id="track-playback-speed-label"></span><Select aria-labelledby="track-playback-speed-label" value={playbackSpeed} onChange={(value) => setPlaybackSpeed(Number(value) as PlaybackSpeed)} optionList={speedOptions.map((speed) => ({ value: speed, label: `${speed}×` }))} /></label>
<Button theme="borderless" aria-label="回到起点" icon={<IconRefresh />} onClick={() => selectIndex(0)} disabled={!boundedIndex} />
</div>
</Card> : mobileLayout ? <Card className="v2-track-playback-dock is-empty" bodyStyle={{ padding: 0 }} aria-label="轨迹播放控制,等待查询结果">
<div className="v2-track-dock-progress">
<Slider className="v2-track-progress-slider" aria-label="轨迹播放进度,等待查询结果" min={0} max={1} value={0} disabled showBoundary={false} />
<div className="v2-track-progress-meta"><time>00:00:00</time><span className="v2-track-progress-status"></span><time>00:00:00</time></div>
</div>
<div className="v2-track-dock-controls">
<Button theme="borderless" aria-label="上一个轨迹点" icon={<IconChevronLeft />} disabled />
<Button theme="solid" className="is-primary" aria-label="开始轨迹播放" icon={<IconPlay />} disabled><span className="v2-track-playback-primary-label"></span></Button>
<Button theme="borderless" aria-label="下一个轨迹点" icon={<IconChevronRight />} disabled />
<label><span id="track-empty-playback-speed-label"></span><Select aria-labelledby="track-empty-playback-speed-label" value={1} disabled optionList={[{ value: 1, label: '1×' }]} /></label>
</div>
</Card> : null}
</section>
</div>;
@@ -158,7 +158,6 @@ test('deduplicates vehicle candidates by VIN and renders granted vehicles plate
const customer = await screen.findByRole('button', { name: /选择客户 华东客户/ });
expect(screen.getByLabelText('账号管理操作')).toHaveClass('has-identity');
expect(screen.getByLabelText('账号管理操作')).toHaveTextContent('访问治理');
expect(screen.getByLabelText('账号管理操作').querySelector('.v2-workspace-command-icon.semi-avatar')).toBeInTheDocument();
expect(screen.getByLabelText('账号管理操作')).toHaveTextContent('客户访问治理');
expect(screen.getByLabelText('账号管理操作')).toHaveTextContent('变更最多 30 秒生效');
@@ -170,7 +169,6 @@ test('deduplicates vehicle candidates by VIN and renders granted vehicles plate
expect(view.container.querySelector('.v2-user-list.semi-card')).toBeInTheDocument();
expect(view.container.querySelector('.v2-user-filter-panel.v2-workspace-filter-panel.semi-card')).toBeInTheDocument();
expect(view.container.querySelector('.v2-customer-list.semi-list')).toBeInTheDocument();
expect(screen.getByRole('row', { name: '客户账号目录列' })).toHaveTextContent('客户账号菜单权限车辆权限最近登录访问状态');
const directoryMetrics = screen.getByRole('list', { name: '客户权限目录统计' });
expect(directoryMetrics.closest('.v2-user-directory-metric-rail')).toHaveClass('v2-workspace-metric-rail', 'is-queue');
expect(directoryMetrics.querySelectorAll(':scope > [role="listitem"]')).toHaveLength(4);
@@ -754,7 +754,6 @@ export default function UsersPage() {
<WorkspaceCommandBar
className="v2-user-command-bar"
ariaLabel="账号管理操作"
eyebrow="访问治理"
icon={<IconUserGroup />}
tone="authority"
title="客户访问治理"
@@ -901,7 +900,7 @@ export default function UsersPage() {
description="创建客户账号后,可在这里配置菜单和车辆范围。"
action={<Button theme="solid" icon={<IconPlus />} aria-label="创建第一个客户账号" onClick={() => startCreate()}></Button>}
/> : <>
{visibleCustomers.length ? <div className="v2-user-directory-columns" role="row" aria-label="客户账号目录列">
{visibleCustomers.length ? <div className="v2-user-directory-columns" aria-hidden="true">
<span></span>
<span></span>
<span></span>
@@ -945,7 +944,7 @@ export default function UsersPage() {
>
<Avatar className="v2-user-avatar" color={user.status === 'enabled' ? 'light-blue' : 'grey'} shape="square" size="small">{user.displayName.slice(0, 1)}</Avatar>
<span className="v2-user-list-identity">
<span className="v2-user-list-name"><b>{user.displayName}</b><span className="v2-user-provider-hint" title={authProvider.title}><Tag className="v2-user-provider-tag" color={authProvider.color} type="light" size="small">{authProvider.label}</Tag></span></span>
<span className="v2-user-list-name"><b title={user.displayName}>{user.displayName}</b><span className="v2-user-provider-hint" title={authProvider.title}><Tag className="v2-user-provider-tag" color={authProvider.color} type="light" size="small">{authProvider.label}</Tag></span></span>
<span className="v2-user-list-secondary">
<small>@{user.username}{user.customerRef ? ` · ${user.customerRef}` : ''}</small>
<span className="v2-user-list-access-summary" aria-hidden="true"><b>{user.menuKeys.length}</b> · <b>{user.vehicles.length}</b> </span>
@@ -357,15 +357,10 @@ function VehicleRecordNavigation({ onOpenSources }: { onOpenSources: () => void
['archive', '车辆主档', <IconBox />]
] as const;
return <Card className="v2-record-card v2-vehicle-record-nav" bodyStyle={{ padding: 0 }}>
<WorkspacePanelHeader
variant="compact"
title="车辆视图"
description="位置、事件、遥测"
actions={<nav className="v2-vehicle-section-nav" aria-label="单车详情导航">
<nav className="v2-vehicle-section-nav" aria-label="单车详情导航">
<VehicleSourceEvidenceButton onClick={onOpenSources} />
{navigationItems.map(([key, label, icon]) => <Button key={key} size="small" theme="borderless" type="tertiary" icon={icon} aria-label={`跳转到${label}`} onClick={() => jumpToSection(key)}>{label}</Button>)}
</nav>}
/>
</nav>
</Card>;
}
@@ -466,8 +461,6 @@ function VehicleRecord({ detail, liveRealtime, telemetry, telemetryPending, tele
<WorkspaceCommandBar
className="v2-vehicle-command-header v2-vehicle-record-command"
ariaLabel="单车任务栏"
eyebrow="车辆数字档案"
icon={<IconBox />}
title={<span className="v2-identity-primary">
<span className="v2-plate">{fmt(identity?.plate || realtime?.plate)}</span>
<Tag className={`v2-online-label ${realtime?.online ? 'is-online' : ''}`} color={realtime?.online ? 'green' : 'grey'} size="small">{realtime?.online ? '在线' : '离线'}</Tag>
@@ -508,7 +501,7 @@ function VehicleRecord({ detail, liveRealtime, telemetry, telemetryPending, tele
<Card className="v2-single-map-card" bodyStyle={{ padding: 0 }}>
<WorkspacePanelHeader
title="实时位置"
description="平滑移动"
description="车辆最新有效位置"
meta={realtime?.online ? '实时跟随' : '保留最后位置'}
/>
<FleetMap vehicles={mapVehicles} selectedVin={hasLocation ? detail.vin : undefined} onSelect={() => undefined} />
@@ -316,9 +316,8 @@ export default function VehicleSearchWorkspace() {
{!mobileLayout ? <WorkspaceCommandBar
className="v2-vehicle-command-bar"
ariaLabel="查车操作"
icon={<IconSearch />}
title="查找车辆"
description="车牌、VIN 或手机号快速查车"
description="车牌、VIN 或终端手机号搜索"
status={candidates.isPending ? '正在读取授权范围' : vehicleScope}
statusColor={candidates.isError ? 'red' : 'blue'}
actions={<div className="v2-vehicle-command-actions">
@@ -90,19 +90,19 @@ describe('V2 production entry', () => {
expect(corePageSources[name]).toContain("from '../shared/WorkspaceCommandBar'");
expect(corePageSources[name]).toContain('<WorkspaceCommandBar');
}
for (const [name, eyebrow, tone] of [
['AccessPage', '接入差异', 'primary'],
['HistoryPage', '证据结果', 'primary'],
['OperationsPage', '运行快照', 'health']
// Preserve the shared section hierarchy without requiring decorative eyebrows.
for (const [name, title] of [
['AccessPage', '接入差异清单'],
['HistoryPage', '历史明细'],
['OperationsPage', '运行概览']
]) {
expect(corePageSources[name]).toContain(`eyebrow="${eyebrow}"`);
expect(corePageSources[name]).toContain(`tone="${tone}"`);
expect(corePageSources[name]).toContain('<WorkspacePanelHeader');
expect(corePageSources[name]).toContain(`title="${title}"`);
}
expect(corePageSources.StatisticsPage).toContain("eyebrow={metricView === 'mileage' ? '每日里程' : '每日氢耗'}");
expect(corePageSources.StatisticsPage).toContain("tone={metricView === 'mileage' ? 'primary' : 'health'}");
expect(corePageSources.StatisticsPage).toContain("title={metricView === 'mileage' ? '车辆每日里程' : '车辆每日氢耗'}");
expect(corePageSources.AlertsPage).toContain('aria-label="事件执行状态"');
expect(corePageSources.AlertsPage).toContain('aria-label="事件中心工作区"');
expect(reconciliationSource).toContain('eyebrow="自动对账"');
expect(reconciliationSource).toContain('<WorkspacePanelHeader');
expect(reconciliationSource).toContain('tone="health"');
expect(workspaceStyles).toContain('.v2-workspace-panel-header.has-eyebrow');
expect(workspaceStyles).toContain('.v2-workspace-panel-eyebrow.semi-typography');
@@ -482,7 +482,7 @@ describe('V2 production entry', () => {
expect(corePageSources.TrackPage).toContain('<Card className="v2-track-playback-dock"');
expect(corePageSources.TrackPage).toContain('<Card className="v2-track-overview-section v2-track-overview-card"');
expect(corePageSources.TrackPage).toContain('<Descriptions className="v2-track-overview-descriptions"');
expect(corePageSources.TrackPage).toContain('<PanelEmpty className="v2-track-rail-empty"');
expect(corePageSources.TrackPage).toContain('title={emptyTrackTitle}');
expect(corePageSources.TrackPage).toContain('<List className="v2-track-evidence-list v2-track-stop-list"');
expect(corePageSources.TrackPage).toContain('<List className="v2-track-evidence-list v2-track-event-list"');
expect(corePageSources.TrackPage).toContain('<List.Item className="v2-track-evidence-item"');
@@ -39,8 +39,8 @@ export function WorkspacePanelHeader({
<header className={headerClassName}>
<div className="v2-workspace-panel-copy">
{eyebrow != null ? <Text className="v2-workspace-panel-eyebrow">{eyebrow}</Text> : null}
<Title heading={5}>{title}</Title>
{description ? <Text type="tertiary">{description}</Text> : null}
<Title className="v2-workspace-panel-title" heading={5}>{title}</Title>
{description ? <Text className="v2-workspace-panel-description" type="tertiary">{description}</Text> : null}
</div>
{meta || actions ? <div className={trailingClassName}>
{meta ? <span className="v2-workspace-panel-meta">{meta}</span> : null}
@@ -0,0 +1,442 @@
/* Data workspaces: quiet grouping, readable records, explicit primary actions.
* Loaded after legacy page styles; rules stay within the three data workspaces.
*/
.v2-vehicle-directory-v3.v2-vehicle-search-page,
.v2-history-page,
.v2-mileage-page,
.v2-vehicle-record-page.v2-vehicle-record-v3 {
gap: 20px;
}
.v2-vehicle-directory-v3 > .v2-vehicle-discovery-shell,
.v2-history-page > .v2-history-discovery-shell,
.v2-mileage-page .v2-mileage-discovery-shell {
border: 1px solid var(--v2-color-border);
border-radius: 16px;
background: var(--v2-color-surface);
box-shadow: none;
}
.v2-vehicle-directory-v3 .v2-vehicle-command-bar.v2-workspace-command-bar,
.v2-history-page .v2-history-command-bar.v2-workspace-command-bar {
min-height: 66px;
margin: 0;
padding: 16px 20px;
border: 0;
border-bottom: 1px solid var(--v2-color-border);
border-radius: 16px 16px 0 0;
background: transparent;
box-shadow: none;
}
.v2-vehicle-directory-v3 .v2-workspace-command-copy,
.v2-history-page .v2-workspace-command-copy {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 5px;
}
.v2-vehicle-directory-v3 .v2-workspace-command-title.semi-typography,
.v2-history-page .v2-workspace-command-title.semi-typography {
font-size: 17px;
font-weight: 600;
letter-spacing: -.02em;
}
.v2-vehicle-directory-v3 .v2-workspace-command-description.semi-typography,
.v2-history-page .v2-workspace-command-description.semi-typography {
color: var(--v2-color-text-secondary);
font-size: 12px;
line-height: 1.5;
}
.v2-vehicle-directory-v3 .v2-vehicle-search-form,
.v2-history-page .v2-history-toolbar,
.v2-mileage-page .v2-mileage-filter {
gap: 12px;
padding: 16px 20px;
background: transparent;
}
.v2-vehicle-directory-v3 .v2-vehicle-business-filter-grid {
gap: 16px;
margin-top: 4px;
}
.v2-vehicle-directory-v3 .v2-vehicle-business-filter-grid label > span,
.v2-history-page .v2-history-toolbar label > span,
.v2-mileage-page .v2-mileage-filter label > span {
color: var(--v2-color-text-secondary);
font-size: 12px;
line-height: 1.5;
}
.v2-vehicle-directory-v3 > .v2-vehicle-recent-card.semi-card,
.v2-history-page .v2-history-table-card.semi-card,
.v2-mileage-page .v2-mileage-results.semi-card {
border: 1px solid var(--v2-color-border);
border-radius: 16px;
background: var(--v2-color-surface);
box-shadow: none;
}
.v2-vehicle-directory-v3 .v2-vehicle-directory-context h5 {
font-size: 15px;
font-weight: 600;
}
.v2-vehicle-directory-v3 .v2-vehicle-directory-context small,
.v2-vehicle-directory-v3 .v2-vehicle-directory-status small {
color: var(--v2-color-text-secondary);
font-size: 12px;
line-height: 1.5;
}
.v2-vehicle-directory-v3 .v2-vehicle-directory-table .semi-table-thead > tr > th,
.v2-history-page .v2-history-data-table .semi-table-thead > tr > th,
.v2-mileage-page .v2-mileage-table .semi-table-thead > tr > th {
padding: 12px 16px;
border-color: var(--v2-color-border);
background: var(--v2-color-surface-subtle);
color: var(--v2-color-text-secondary);
font-size: 12px;
font-weight: 500;
}
.v2-vehicle-directory-v3 .v2-vehicle-directory-table .semi-table-tbody > tr > td {
height: 68px;
padding: 13px 16px;
border-bottom-color: var(--v2-color-border);
box-shadow: none;
font-size: 14px;
}
.v2-vehicle-directory-v3 .v2-vehicle-directory-table .semi-table-row::before {
display: none;
}
.v2-vehicle-directory-v3 .v2-vehicle-directory-identity strong,
.v2-vehicle-directory-v3 .v2-vehicle-directory-profile strong {
color: var(--v2-color-text);
font-size: 14px;
font-weight: 600;
}
.v2-vehicle-directory-v3 .v2-vehicle-directory-identity small,
.v2-vehicle-directory-v3 .v2-vehicle-directory-profile small {
margin-top: 5px;
color: var(--v2-color-text-secondary);
font-size: 12px;
letter-spacing: .01em;
}
.v2-vehicle-directory-v3 .v2-vehicle-directory-open {
font-size: 13px;
font-weight: 500;
}
.v2-history-page .v2-history-table-card > .semi-card-body > .v2-workspace-panel-header,
.v2-mileage-page .v2-mileage-results > .semi-card-body > .v2-workspace-panel-header {
min-height: 72px;
padding: 16px 20px;
background: transparent;
box-shadow: none;
}
.v2-history-page .v2-history-empty.v2-workspace-empty-guide {
min-height: 320px;
padding: 56px 24px;
border: 0;
background: transparent;
box-shadow: none;
}
.v2-history-page .v2-history-empty .semi-empty-title {
font-size: 20px;
font-weight: 600;
}
.v2-history-page .v2-history-empty .semi-empty-description {
margin-top: 12px;
color: var(--v2-color-text-secondary);
font-size: 14px;
line-height: 1.7;
}
.v2-history-page .v2-history-empty .v2-workspace-empty-guide-icon {
border: 0;
border-radius: 16px;
background: var(--v2-color-surface-subtle);
box-shadow: none;
}
.v2-history-page .v2-history-data-table .semi-table-tbody > tr > td,
.v2-mileage-page .v2-mileage-table .semi-table-tbody > tr > td {
min-height: 52px;
padding: 15px 16px;
font-size: 13px;
font-variant-numeric: tabular-nums;
}
.v2-mileage-page .v2-mileage-table .semi-table-tbody > tr > td strong,
.v2-mileage-page .v2-mileage-table .semi-table-tbody > tr > td span {
font-size: 13px;
font-weight: 500;
}
.v2-mileage-page .v2-mileage-table .semi-table-tbody > tr > td.is-total strong,
.v2-mileage-page .v2-mileage-table .semi-table-tbody > tr > td.is-plate strong {
font-weight: 600;
}
.v2-mileage-page .v2-mileage-evidence {
gap: 16px;
color: var(--v2-color-text-secondary);
font-size: 12px;
line-height: 1.6;
}
.v2-vehicle-record-v3 .v2-identity-primary .v2-plate {
color: var(--v2-color-text);
font-size: 26px;
font-weight: 600;
letter-spacing: -.03em;
}
.v2-vehicle-record-v3 .v2-identity-vin b {
font-size: 13px;
font-weight: 400;
letter-spacing: .02em;
}
@media (max-width: 1100px) and (min-width: 681px) {
.v2-vehicle-directory-v3 .v2-vehicle-command-bar.v2-workspace-command-bar,
.v2-history-page .v2-history-command-bar.v2-workspace-command-bar {
flex-wrap: wrap;
}
.v2-vehicle-directory-v3 .v2-workspace-command-actions,
.v2-history-page .v2-workspace-command-actions {
flex-wrap: wrap;
justify-content: flex-start;
}
}
@media (max-width: 680px) {
.v2-vehicle-directory-v3.v2-vehicle-search-page,
.v2-history-page,
.v2-mileage-page,
.v2-vehicle-record-page.v2-vehicle-record-v3 {
gap: 12px;
}
.v2-history-page .v2-history-command-bar.v2-workspace-command-bar,
.v2-history-page .v2-history-table-card > .semi-card-body > .v2-workspace-panel-header,
.v2-mileage-page .v2-mileage-results > .semi-card-body > .v2-workspace-panel-header {
min-height: 60px;
padding: 12px 14px;
}
.v2-history-page .v2-history-empty.v2-workspace-empty-guide {
min-height: 280px;
padding: 36px 18px;
}
.v2-history-page .v2-history-empty .semi-empty-title {
font-size: 18px;
}
.v2-mileage-page .v2-mileage-evidence {
gap: 6px;
font-size: 11px;
}
}
/* Keep search suggestions outside the rounded surface without clipping them. */
.v2-vehicle-directory-v3 .v2-vehicle-query-panel.v2-workspace-filter-panel.semi-card,
.v2-vehicle-directory-v3 .v2-vehicle-query-panel > .semi-card-body,
.v2-vehicle-directory-v3 .v2-vehicle-search-form {
border: 0;
border-radius: 0 0 16px 16px;
background: transparent;
box-shadow: none;
}
.v2-vehicle-directory-v3 .v2-vehicle-directory-table .semi-table-tbody > .semi-table-row.is-online > .semi-table-row-cell:first-child,
.v2-vehicle-directory-v3 .v2-vehicle-directory-table .semi-table-tbody > .semi-table-row.is-offline > .semi-table-row-cell:first-child {
box-shadow: none;
}
.v2-vehicle-directory-v3 .v2-vehicle-directory-open {
flex-wrap: nowrap;
flex-shrink: 0;
gap: 5px;
padding-inline: 4px;
white-space: nowrap;
word-break: keep-all;
}
.v2-vehicle-directory-v3 .v2-vehicle-directory-open > svg {
flex: 0 0 auto;
}
@media (min-width: 681px) {
.v2-vehicle-directory-v3 .v2-vehicle-directory-metric-rail.is-queue.has-context > .semi-card-body {
display: grid;
grid-template-columns: minmax(300px, 1.15fr) minmax(0, 1.5fr);
}
.v2-vehicle-directory-v3 .v2-vehicle-directory-metric-rail .v2-workspace-metric-context {
order: -1;
padding: 14px 16px;
border: 0;
border-right: 1px solid var(--v2-color-border);
}
.v2-vehicle-directory-v3 .v2-vehicle-directory-context {
flex-wrap: wrap;
gap: 8px 14px;
}
.v2-vehicle-directory-v3 .v2-vehicle-directory-context > span:first-child {
flex: 1 1 180px;
}
.v2-vehicle-directory-v3 .v2-vehicle-directory-metric-rail .v2-workspace-metric-list {
min-width: 0;
border: 0;
}
}
.v2-vehicle-record-v3 .v2-vehicle-record-nav > .semi-card-body > .v2-vehicle-section-nav {
display: flex;
justify-content: flex-start;
gap: 6px;
overflow-x: auto;
padding: 10px 14px;
border: 0;
background: transparent;
}
.v2-vehicle-record-v3 .v2-vehicle-section-nav .semi-button {
min-height: 38px;
flex: 0 0 auto;
font-size: 13px;
}
.v2-vehicle-record-v3 .v2-event-list .v2-event-row > div > strong {
font-size: 14px;
font-weight: 600;
}
.v2-vehicle-record-v3 .v2-event-list .v2-event-row > div > p,
.v2-vehicle-record-v3 .v2-event-list .v2-event-row > time,
.v2-vehicle-record-v3 .v2-live-report-meta,
.v2-vehicle-record-v3 .v2-map-update-time {
font-size: 12px;
line-height: 1.5;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-meta small,
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-vin small,
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-meta strong,
.v2-vehicle-record-v3.is-mobile-layout .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-meta small,
.v2-vehicle-record-v3.is-mobile-layout .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-meta strong {
font-size: 12px;
line-height: 1.5;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-actions > .semi-button,
.v2-vehicle-record-v3.is-mobile-layout .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-actions > .semi-button {
min-height: 38px;
font-size: 13px;
}
.v2-content.is-section-vehicles .v2-monitor-return > a,
.v2-content.is-section-vehicles .v2-monitor-return > span,
.v2-content.is-section-vehicles .v2-monitor-return > em {
font-size: 13px;
line-height: 1.5;
}
@media (min-width: 681px) {
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command {
min-height: 100px;
padding: 0;
grid-template-columns: minmax(235px, .7fr) minmax(0, 2fr);
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-workspace-command-copy {
display: flex;
min-height: 100px;
flex-direction: column;
align-items: flex-start;
justify-content: center;
gap: 9px;
padding: 16px 20px;
border-right: 0;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-workspace-command-actions {
grid-template-columns: minmax(220px, .85fr) minmax(270px, 1.15fr);
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-primary {
justify-content: flex-start;
padding: 0;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-plate {
border: 0;
background: transparent;
box-shadow: none;
padding: 0;
font-size: 24px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-actions {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
align-content: center;
gap: 6px;
padding: 12px 16px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-actions > .semi-button {
width: auto;
flex: 0 0 auto;
padding-inline: 10px;
}
}
@media (max-width: 680px) {
.v2-vehicle-directory-v3 .v2-vehicle-mobile-scope-row {
grid-template-columns: minmax(0, 1fr);
gap: 6px;
min-width: 0;
}
.v2-vehicle-directory-v3 .v2-vehicle-mobile-scope-row > .v2-mobile-filter-toggle.semi-button {
width: 100%;
min-width: 0;
min-height: 56px;
}
.v2-vehicle-directory-v3 .v2-vehicle-mobile-scope-row .v2-mobile-filter-toggle-content {
width: 100%;
min-width: 0;
grid-template-columns: 38px minmax(0, 1fr) auto;
}
.v2-vehicle-directory-v3 .v2-vehicle-mobile-scope-row > .v2-vehicle-mobile-sync.semi-button {
width: auto;
min-width: 118px;
min-height: 44px;
justify-self: end;
padding: 10px 14px;
font-size: 13px;
white-space: nowrap;
}
}
@@ -0,0 +1,419 @@
/* Governance workspaces: readable evidence, quiet grouping, one row per entity.
* Keep this after the legacy workspace layers. Prefixing the user selectors also
* protects the directory from its asynchronously loaded users.css stylesheet.
*/
.v2-user-admin,
.v2-access-page-v3,
.v2-ops-page,
.v2-event-center {
font-variant-numeric: tabular-nums;
}
.v2-user-admin .v2-user-discovery-shell,
.v2-access-page-v3 .v2-access-discovery-shell,
.v2-ops-page .v2-ops-command-bar {
background: var(--v2-color-surface);
box-shadow: none;
}
.v2-user-admin .v2-user-command-bar,
.v2-access-page-v3 .v2-access-command-bar,
.v2-ops-page .v2-ops-command-bar {
background: transparent;
border-radius: 0;
}
/* Account identity and every fact share one column definition with the header. */
.v2-user-admin .v2-user-list {
--v2-user-row-columns: 36px minmax(190px, 2fr) minmax(80px, .55fr) minmax(90px, .6fr) minmax(164px, 1fr) minmax(96px, .6fr);
}
.v2-user-admin .v2-customer-list .semi-list-items {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 0;
border: 0;
border-radius: 0;
}
.v2-user-admin .v2-user-directory-columns,
.v2-user-admin .v2-customer-list .v2-user-list-item .semi-button-content {
display: grid;
grid-template-columns: var(--v2-user-row-columns);
gap: 12px;
align-items: center;
}
.v2-user-admin .v2-user-directory-columns {
min-height: 42px;
padding: 0 20px;
color: var(--v2-color-text-secondary);
font-size: 12px;
background: var(--v2-color-surface-subtle);
}
.v2-user-admin .v2-user-directory-columns > span:first-child { grid-column: 1 / 3; }
.v2-user-admin .v2-user-directory-columns > span:last-child { text-align: right; }
.v2-user-admin .v2-customer-list .v2-user-list-row.semi-list-item {
width: 100%;
min-width: 0;
min-height: 72px;
background: var(--v2-color-surface);
box-shadow: none;
border-bottom: 1px solid var(--v2-color-border);
}
.v2-user-admin .v2-customer-list .v2-user-list-row.semi-list-item:hover {
background: var(--v2-color-surface-subtle);
box-shadow: none;
}
.v2-user-admin .v2-customer-list .v2-user-list-row.is-active.semi-list-item {
background: var(--v2-color-primary-soft, #edf4ff);
box-shadow: inset 3px 0 var(--v2-color-primary);
}
.v2-user-admin .v2-customer-list .v2-user-list-item.semi-button {
width: 100%;
min-height: 72px;
padding: 12px 20px;
white-space: normal;
}
.v2-user-admin .v2-user-list-name > b { font-size: 14px; font-weight: 600; }
.v2-user-admin .v2-user-list-secondary > small { font-size: 12px; color: var(--v2-color-text-secondary); }
.v2-user-admin .v2-user-list-facts b,
.v2-user-admin .v2-user-list-facts.is-login b { font-size: 13px; font-weight: 500; color: var(--v2-color-text); }
.v2-user-admin .v2-user-list-facts.is-menu,
.v2-user-admin .v2-user-list-facts.is-vehicle { padding: 0; border: 0; background: transparent; }
.v2-user-admin .v2-user-provider-tag.semi-tag,
.v2-user-admin .v2-user-status-tag.semi-tag { font-size: 12px; height: auto; min-height: 24px; line-height: 20px; }
.v2-user-admin .v2-user-avatar.semi-avatar { border: 0; background: #f0f1f3; color: var(--v2-color-text-secondary); }
.v2-user-admin .v2-customer-list-scroll { scrollbar-gutter: stable; }
/* Protocol evidence reads as aligned text; colour remains on the actual state. */
.v2-access-page-v3 .v2-access-protocol-cell {
border: 0;
border-radius: 0;
background: transparent;
box-shadow: none;
padding: 4px 0;
min-height: 62px;
}
.v2-access-page-v3 .v2-access-primary-cell > strong,
.v2-access-page-v3 .v2-access-protocol-cell strong,
.v2-access-page-v3 .v2-access-protocol-cell time { font-size: 14px; font-weight: 500; }
.v2-access-page-v3 .v2-access-primary-cell > span,
.v2-access-page-v3 .v2-access-protocol-cell small,
.v2-access-page-v3 .v2-access-connection small { font-size: 12px; color: var(--v2-color-text-secondary); line-height: 1.5; }
.v2-access-page-v3 .v2-access-connection p { font-size: 13px; }
.v2-access-page-v3 .v2-access-connection-tag.semi-tag { font-size: 12px; min-height: 24px; }
.v2-access-page-v3 .v2-access-semi-table .semi-table-row > .semi-table-row-cell:first-child { box-shadow: none; }
.v2-access-page-v3 .v2-access-semi-table .semi-table-row > .semi-table-row-cell:first-child::before { display: none; }
.v2-access-page-v3 .v2-access-semi-table .semi-table-row { box-shadow: none; }
.v2-access-page-v3 .v2-access-semi-table .semi-table-tbody > .semi-table-row:is(.is-state-healthy, .is-state-degraded, .is-state-incomplete, .is-state-offline, .is-state-not_connected, .is-selected) > .semi-table-row-cell:first-child { box-shadow: none; }
.v2-access-page-v3 .v2-access-status-tabs { height: auto; overflow: visible; }
.v2-access-page-v3 .v2-access-metric-rail.is-queue,
.v2-access-page-v3 .v2-access-metric-rail.is-queue > .semi-card-body,
.v2-access-page-v3 .v2-access-metric-rail .v2-workspace-metric-list { height: auto; min-height: 80px; }
.v2-access-page-v3 .v2-access-metric-rail .v2-workspace-metric-list > span,
.v2-access-page-v3 .v2-access-metric-rail .v2-workspace-metric-action.semi-button {
height: auto;
min-height: 80px;
overflow: visible;
}
/* Action metrics contain a button, unlike plain metrics' three text nodes.
* Do not place that button into the legacy 14px/24px text grid: its 80px
* content otherwise starts in the first short track and spills below the rail.
*/
.v2-access-page-v3 .v2-access-metric-rail .v2-workspace-metric-list > span.is-action {
display: block;
grid-template-columns: none;
grid-template-rows: none;
padding: 0;
}
.v2-access-page-v3 .v2-access-metric-rail .v2-workspace-metric-action.semi-button {
display: block;
padding: 0;
}
.v2-access-page-v3 .v2-access-metric-rail .v2-workspace-metric-list > span:not(.is-action),
.v2-access-page-v3 .v2-access-metric-rail .v2-workspace-metric-action .semi-button-content {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
grid-template-rows: auto auto;
align-content: center;
align-items: center;
height: auto;
min-height: 80px;
gap: 4px 12px;
padding: 12px 16px;
overflow: visible;
}
.v2-access-page-v3 .v2-access-metric-rail .v2-workspace-metric-list small { grid-column: 1 / -1; grid-row: 1; font-size: 12px; line-height: 1.5; }
.v2-access-page-v3 .v2-access-metric-rail .v2-workspace-metric-list strong { grid-column: 1; grid-row: 2; font-size: 24px; line-height: 1.2; }
.v2-access-page-v3 .v2-access-metric-rail .v2-workspace-metric-list em { grid-column: 2; grid-row: 2; font-size: 12px; line-height: 1.5; white-space: normal; }
/* The event itself is primary. The existing native alarm title stays intact. */
.v2-event-center .v2-event-identity > span {
width: 28px;
height: 28px;
flex-basis: 28px;
border: 0;
border-radius: 7px;
background: var(--v2-color-surface-subtle);
color: var(--v2-color-text-secondary);
box-shadow: none;
}
.v2-event-center .v2-event-identity strong { font-size: 14px; line-height: 1.5; font-weight: 600; }
.v2-event-center .v2-event-identity small,
.v2-event-center .v2-alert-event-vehicle small { font-size: 12px; line-height: 1.5; color: var(--v2-color-text-secondary); }
.v2-event-center .v2-alert-event-vehicle strong,
.v2-event-center .v2-alert-event-rule,
.v2-event-center .v2-alert-time { font-size: 13px; line-height: 1.5; font-weight: 500; }
.v2-event-center .v2-event-execution.semi-tag { min-height: 24px; height: auto; font-size: 12px; line-height: 20px; }
.v2-event-center .v2-alert-event-table .semi-table-row > .semi-table-row-cell { border-right: 0; }
.v2-event-center .v2-alert-event-table .semi-table-row > .semi-table-row-cell:first-child { box-shadow: none; }
.v2-event-center .v2-alert-event-table .semi-table-row > .semi-table-row-cell:first-child::before { display: none; }
/* Let Semi's table body own horizontal scrolling. The legacy 970px minimum on
* its outer wrapper put the sticky right edge beyond the visible viewport. */
.v2-event-center .v2-alert-workspace .v2-alert-table-scroll .v2-alert-event-table.semi-table-wrapper {
min-width: 0;
width: 100%;
max-width: 100%;
}
.v2-event-center .v2-alert-event-table .is-status { position: sticky; right: 0; z-index: 2; background: var(--v2-color-surface); box-shadow: -1px 0 var(--v2-color-border); }
.v2-event-center .v2-alert-event-table .semi-table-thead .is-status { z-index: 3; background: var(--v2-color-surface-subtle); }
.v2-event-center .v2-alert-event-table .is-selected .is-status { background: var(--v2-color-primary-soft); }
.v2-event-center .v2-event-status-tabs { gap: 4px; padding: 8px 12px; }
.v2-event-center .v2-event-status-tabs .semi-button { min-height: 38px; padding-inline: 12px; font-size: 13px; }
.v2-event-center .v2-event-status-tabs strong { font-size: 13px; font-weight: 600; }
.v2-event-center .v2-alert-detail-card { border-color: var(--v2-color-border); background: var(--v2-color-surface); box-shadow: none; }
.v2-event-center .v2-alert-automation-row .v2-alert-rule-item.semi-button,
.v2-event-center .v2-alert-automation-row .v2-alert-rule-item.semi-button:hover,
.v2-event-center .v2-alert-automation-row .v2-alert-rule-item.is-selected.semi-button {
border: 0;
border-radius: 0;
background: transparent;
box-shadow: none;
transform: none;
}
.v2-event-center .v2-alert-automation-row.is-selected { background: var(--v2-color-primary-soft); box-shadow: none; }
.v2-event-center .v2-alert-automation-row .v2-alert-rule-copy strong { font-size: 14px; line-height: 1.5; }
.v2-event-center .v2-alert-automation-row .v2-alert-rule-copy small { font-size: 12px; line-height: 1.5; }
.v2-event-center .v2-alert-automation-row .v2-alert-automation-icon { background: var(--v2-color-surface-subtle); color: var(--v2-color-text-secondary); box-shadow: none; }
.v2-event-center .v2-alert-automation-switch-target { grid-area: switch; display: grid; place-items: center; min-width: 44px; min-height: 44px; cursor: pointer; }
.v2-event-center .v2-alert-automation-switch-target .v2-alert-automation-switch { grid-area: auto; }
.v2-event-center .v2-alert-automation-version { font-size: 12px; }
.v2-alert-inspector .v2-native-alarm-fields { padding: 16px; }
.v2-alert-inspector .v2-native-alarm-meaning > strong { font-size: 14px; font-weight: 600; }
.v2-alert-inspector .v2-native-alarm-meaning ul { margin: 10px 0; padding-left: 20px; font-size: 14px; line-height: 1.7; }
.v2-alert-inspector .v2-native-alarm-meaning p,
.v2-alert-inspector .v2-native-alarm-fields > p { font-size: 12px; color: var(--v2-color-text-secondary); line-height: 1.6; }
.v2-alert-inspector .v2-alert-descriptions .semi-descriptions-item-key { font-size: 12px; line-height: 1.6; }
.v2-alert-inspector .v2-alert-descriptions .semi-descriptions-item-value { font-size: 13px; line-height: 1.6; }
:is(.v2-event-center, .v2-alert-detail-sidesheet) .v2-alert-inspector .v2-alert-focus-card .v2-alert-evidence {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 0;
min-height: 0;
height: auto;
border: 0;
background: transparent;
padding: 12px 16px;
}
:is(.v2-event-center, .v2-alert-detail-sidesheet) .v2-alert-inspector .v2-alert-focus-card .v2-alert-evidence > .v2-alert-evidence-value.semi-card {
min-height: 0;
height: auto;
border: 0;
border-bottom: 1px solid var(--v2-color-border);
border-radius: 0;
background: transparent;
box-shadow: none;
}
:is(.v2-event-center, .v2-alert-detail-sidesheet) .v2-alert-inspector .v2-alert-focus-card .v2-alert-evidence > .v2-alert-evidence-value > .semi-card-body {
display: grid;
grid-template-columns: 76px minmax(0, 1fr);
align-items: center;
gap: 12px;
height: auto;
min-height: 44px;
padding: 10px 0;
text-align: left;
}
:is(.v2-event-center, .v2-alert-detail-sidesheet) .v2-alert-inspector .v2-alert-focus-card .v2-alert-evidence small { font-size: 12px; line-height: 1.5; }
:is(.v2-event-center, .v2-alert-detail-sidesheet) .v2-alert-inspector .v2-alert-focus-card .v2-alert-evidence strong { font-size: 14px; line-height: 1.5; white-space: normal; overflow-wrap: anywhere; }
.v2-event-center .v2-alert-inspector .v2-alert-inspector-close.semi-button { width: 36px; height: 36px; min-width: 36px; min-height: 36px; padding: 8px; }
.v2-event-center .v2-alert-inspector .v2-alert-inspector-close :is(.semi-icon, svg) { width: 16px; height: 16px; font-size: 16px; }
/* Quality queues and diagnostic evidence share the same reading size. */
.v2-ops-page .v2-reconcile-cell > strong,
.v2-ops-page .v2-reconcile-cell > b,
.v2-ops-page .v2-source-cell > strong { font-size: 14px; line-height: 1.5; font-weight: 600; }
.v2-ops-page .v2-reconcile-cell small,
.v2-ops-page .v2-source-cell small { font-size: 12px; line-height: 1.5; color: var(--v2-color-text-secondary); }
.v2-ops-page .v2-reconcile-table .semi-table-row-cell,
.v2-ops-page .v2-source-table .semi-table-row-cell { padding-block: 14px; }
.v2-ops-page .v2-ops-link-card,
.v2-ops-page .v2-ops-source-card,
.v2-ops-page .v2-source-recommendation { background: var(--v2-color-surface); box-shadow: none; border-color: var(--v2-color-border); }
.v2-ops-page .v2-ops-source-evidence p,
.v2-ops-page .v2-ops-source-action p,
.v2-ops-page .v2-ops-link-card p { font-size: 13px; line-height: 1.6; color: var(--v2-color-text-secondary); }
.v2-ops-page .v2-ops-source-evidence > strong,
.v2-ops-page .v2-ops-source-action > strong,
.v2-ops-page .v2-ops-link-status strong { font-size: 14px; font-weight: 600; }
/* Portal-mounted inspectors need their own scope. */
:is(.v2-user-editor-sidesheet, .v2-user-grant-sidesheet, .v2-user-import-sidesheet, .v2-access-detail-sidesheet, .v2-access-governance-sidesheet, .v2-reconcile-detail-sidesheet, .v2-source-policy-sidesheet) .v2-workspace-config-summary > span::before {
display: none;
}
:is(.v2-user-editor-sidesheet, .v2-user-grant-sidesheet, .v2-user-import-sidesheet, .v2-access-detail-sidesheet, .v2-access-governance-sidesheet, .v2-reconcile-detail-sidesheet, .v2-source-policy-sidesheet) .v2-workspace-config-summary > span {
background: transparent;
box-shadow: none;
border-left: 0;
}
:is(.v2-user-editor-sidesheet, .v2-user-grant-sidesheet, .v2-user-import-sidesheet, .v2-access-detail-sidesheet, .v2-access-governance-sidesheet, .v2-reconcile-detail-sidesheet, .v2-source-policy-sidesheet) .v2-workspace-config-summary > span:is(.is-primary, .is-success, .is-warning, .is-danger) {
background: transparent;
box-shadow: none;
border-left: 0;
}
:is(.v2-user-editor-sidesheet, .v2-user-grant-sidesheet, .v2-user-import-sidesheet, .v2-access-detail-sidesheet, .v2-access-governance-sidesheet, .v2-reconcile-detail-sidesheet, .v2-source-policy-sidesheet) .v2-workspace-config-summary > span :is(small, em) {
font-size: 12px;
line-height: 1.5;
color: var(--v2-color-text-secondary);
}
:is(.v2-user-editor-sidesheet, .v2-user-grant-sidesheet, .v2-user-import-sidesheet, .v2-access-detail-sidesheet, .v2-access-governance-sidesheet, .v2-reconcile-detail-sidesheet, .v2-source-policy-sidesheet) .v2-workspace-config-summary > span > strong {
font-size: 14px;
line-height: 1.5;
color: var(--v2-color-text);
}
.v2-user-editor-sidesheet .v2-workspace-config-content .v2-user-editor-form :is(small, em),
.v2-user-editor-sidesheet .v2-user-editor-switcher small {
font-size: 12px;
line-height: 1.5;
color: var(--v2-color-text-secondary);
}
.v2-user-editor-sidesheet .v2-workspace-config-content .v2-user-editor-form :is(strong, b) {
font-size: 14px;
line-height: 1.5;
font-weight: 600;
}
.v2-user-editor-sidesheet .v2-user-editor-tabs > .semi-tabs-content {
background: var(--v2-color-surface);
padding: 0;
}
.v2-user-editor-sidesheet .v2-user-editor-form .v2-user-editor-section.semi-card {
border: 0;
border-radius: 0;
box-shadow: none;
}
.v2-user-editor-sidesheet .v2-user-editor-form :is(.v2-user-vehicle-composer, .v2-user-identity-overview, .v2-user-menu-overview, .v2-user-identity-source, .v2-assigned-vins) {
border: 0;
border-radius: 0;
background: transparent;
box-shadow: none;
}
.v2-user-editor-sidesheet .v2-user-vehicle-composer-heading {
background: transparent;
border: 0;
padding: 0 0 12px;
}
.v2-user-editor-sidesheet .v2-user-vehicle-composer .v2-vehicle-permission-tools { padding: 0 0 16px; }
.v2-user-editor-sidesheet .v2-assigned-toolbar { border-top: 1px solid var(--v2-color-border); padding-top: 16px; }
.v2-user-editor-sidesheet .v2-assigned-vins > .v2-assigned-vehicle-card.semi-card { padding: 16px 0; }
.v2-user-editor-sidesheet .v2-assigned-vehicle-card .v2-assigned-vehicle-actions .semi-button { font-size: 12px; min-height: 36px; }
.v2-user-editor-sidesheet .v2-assigned-vehicle-card .v2-assigned-vehicle-interval :is(small, strong) { font-size: 12px; line-height: 1.5; }
.v2-user-editor-sidesheet .v2-user-editor-form .v2-user-field-group { border: 0; border-radius: 0; box-shadow: none; }
.v2-user-editor-sidesheet .v2-user-editor-form .v2-user-field-group + .v2-user-field-group { border-top: 1px solid var(--v2-color-border); padding-top: 20px; }
:is(.v2-access-detail-sidesheet, .v2-access-governance-sidesheet, .v2-reconcile-detail-sidesheet, .v2-source-policy-sidesheet) .v2-workspace-config-content :is(small, em) {
font-size: 12px;
line-height: 1.5;
}
:is(.v2-access-detail-sidesheet, .v2-access-governance-sidesheet, .v2-reconcile-detail-sidesheet, .v2-source-policy-sidesheet) .v2-workspace-config-content :is(strong, b) {
font-size: 14px;
line-height: 1.5;
}
.v2-access-detail-sidesheet .v2-access-inspector-v3.semi-card,
.v2-reconcile-detail-sidesheet .v2-reconcile-detail.semi-card {
border: 0;
background: transparent;
box-shadow: none;
}
.v2-access-detail-sidesheet .v2-access-inspector-v3 .v2-access-protocol-detail.semi-card,
.v2-reconcile-detail-sidesheet .v2-reconcile-detail .v2-reconcile-evidence.semi-card {
border: 0;
border-top: 1px solid var(--v2-color-border);
border-radius: 0;
box-shadow: none;
background: transparent;
}
:is(.v2-access-detail-sidesheet, .v2-access-governance-sidesheet, .v2-alert-detail-sidesheet, .v2-reconcile-detail-sidesheet, .v2-source-policy-sidesheet) .semi-descriptions-item-key {
font-size: 12px;
color: var(--v2-color-text-secondary);
line-height: 1.6;
}
:is(.v2-access-detail-sidesheet, .v2-access-governance-sidesheet, .v2-alert-detail-sidesheet, .v2-reconcile-detail-sidesheet, .v2-source-policy-sidesheet) .semi-descriptions-item-value {
font-size: 14px;
line-height: 1.6;
}
@media (min-width: 681px) and (max-width: 1180px) {
.v2-access-page-v3 .v2-access-filter-actions { display: flex; justify-content: flex-end; grid-column: 1 / -1; }
.v2-access-page-v3 .v2-access-filter-actions > .semi-button { width: auto; min-width: 80px; flex: 0 0 auto; }
.v2-ops-page .v2-reconcile-heading.v2-workspace-panel-header { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 12px; }
.v2-ops-page .v2-reconcile-heading .v2-workspace-panel-copy { width: 100%; flex: 1 0 100%; min-width: 0; }
.v2-ops-page .v2-reconcile-heading .v2-workspace-panel-description { max-width: 100%; white-space: normal; }
.v2-ops-page .v2-reconcile-heading .v2-workspace-panel-actions { width: 100%; flex: 1 0 100%; justify-content: flex-end; flex-wrap: wrap; }
.v2-user-admin .v2-user-list { --v2-user-row-columns: 32px minmax(150px, 1fr) 76px 84px 100px; }
.v2-user-admin .v2-user-directory-columns > span:nth-child(4),
.v2-user-admin .v2-user-list-facts.is-login { display: none; }
.v2-user-admin .v2-customer-list .v2-user-list-item.semi-button,
.v2-user-admin .v2-user-directory-columns { padding-inline: 14px; }
}
@media (max-width: 680px) {
.v2-ops-page .v2-reconcile-heading.v2-workspace-panel-header { display: flex; flex-direction: row; align-items: center; padding: 12px 16px; min-height: 64px; }
.v2-ops-page .v2-reconcile-heading .v2-workspace-panel-actions { width: auto; flex: 0 0 auto; }
.v2-ops-page .v2-reconcile-mobile-more.semi-button { min-height: 44px; font-size: 12px; }
.v2-user-admin .v2-user-list { --v2-user-row-columns: 34px minmax(0, 1fr) auto; }
.v2-user-admin .v2-user-directory-columns { display: none; }
.v2-user-admin .v2-customer-list .v2-user-list-item .semi-button-content { gap: 10px; }
.v2-user-admin .v2-customer-list .v2-user-list-item.semi-button { padding: 12px; min-height: 76px; }
.v2-user-admin .v2-user-list-facts { display: none; }
.v2-user-admin .v2-user-list-name > b { white-space: normal; overflow-wrap: anywhere; font-size: 14px; }
.v2-user-admin .v2-user-list-secondary { display: grid; gap: 3px; }
.v2-user-admin .v2-user-list-access-summary { font-size: 12px; }
.v2-user-admin .v2-user-list-trailing { min-width: 0; }
.v2-user-admin .v2-user-status-tag.semi-tag { font-size: 11px; }
.v2-event-center .v2-alert-mobile-card-content > header > strong { font-size: 14px; white-space: normal; }
.v2-event-center .v2-alert-mobile-facts,
.v2-access-page-v3 .v2-access-mobile-card-content > p { font-size: 12px; line-height: 1.5; }
.v2-event-center .v2-event-status-tabs { padding: 6px 8px; gap: 2px; }
.v2-event-center .v2-event-status-tabs .semi-button { padding-inline: 10px; }
.v2-access-page-v3 .v2-access-protocol-cell { min-height: 58px; }
.v2-access-page-v3 .v2-access-mobile-card { box-shadow: none; border-left: 0; }
.v2-access-page-v3 .v2-access-mobile-card::before,
.v2-access-page-v3 .v2-access-mobile-card .v2-access-mobile-action::before { display: none; }
.v2-access-page-v3 .v2-access-mobile-list .v2-access-protocol-cell {
display: grid;
grid-template-columns: minmax(0, 1fr);
grid-template-rows: auto auto auto;
align-content: start;
gap: 6px;
padding: 10px 7px;
min-height: 94px;
}
.v2-access-page-v3 .v2-access-mobile-list .v2-access-protocol-cell > :is(b, span, strong) {
grid-column: 1;
justify-self: start;
font-size: 12px;
line-height: 1.4;
white-space: nowrap;
overflow: visible;
}
.v2-access-page-v3 .v2-access-mobile-list .v2-access-protocol-cell time { font-size: 12px; line-height: 1.4; }
.v2-access-page-v3 .v2-access-mobile-list .v2-access-protocol-cell > span i { width: 5px; height: 5px; }
}
@@ -0,0 +1,241 @@
/* Quiet map chrome; playback and data controls keep their existing behavior. */
.v2-monitor-page .v2-monitor-workspace,
.v2-track-page .v2-track-stage,
.v2-track-page .v2-track-rail-shell.semi-card {
border-color: rgba(60, 60, 67, .13);
border-radius: 16px;
box-shadow: none;
}
.v2-monitor-page .v2-vehicle-rail { background: #fff; border-right-color: rgba(60, 60, 67, .12); }
.v2-monitor-page .v2-vehicle-rail > header { padding: 17px 18px 9px; }
.v2-monitor-page .v2-vehicle-rail > header strong { color: #1d1d1f; font-size: 15px; font-weight: 600; }
.v2-monitor-page .v2-rail-search {
min-height: 28px;
height: auto;
margin: 0 18px 8px;
padding: 0;
border: 0;
border-radius: 0;
color: #6e6e73;
background: transparent;
box-shadow: none;
font-size: 12px;
line-height: 20px;
}
.v2-monitor-page .v2-vehicle-row.semi-button {
width: calc(100% - 16px);
margin: 2px 8px;
padding: 14px 10px;
border: 0;
border-radius: 10px;
background: transparent;
box-shadow: none;
}
.v2-monitor-page .v2-vehicle-row.semi-button:hover { background: #f5f5f7; }
.v2-monitor-page .v2-vehicle-row.semi-button.is-selected { background: #eaf3ff; box-shadow: none; }
.v2-monitor-page .v2-vehicle-identity strong { color: #1d1d1f; font-size: 14px; font-weight: 600; }
.v2-monitor-page .v2-vehicle-identity small,
.v2-monitor-page .v2-vehicle-motion > small { color: #6e6e73; font-size: 11px; }
.v2-monitor-page .v2-map-control-button.semi-button {
min-height: 48px;
border: 1px solid rgba(60, 60, 67, .12);
border-radius: 12px;
background: rgba(255, 255, 255, .96);
box-shadow: 0 3px 16px rgba(0, 0, 0, .08);
}
.v2-monitor-page .v2-map-control-copy strong { color: #1d1d1f; font-size: 12px; font-weight: 600; }
.v2-monitor-page .v2-map-control-copy small { color: #6e6e73; font-size: 10px; }
.v2-monitor-page .v2-map-legend {
min-height: 36px;
border-color: rgba(60, 60, 67, .12);
border-radius: 10px;
background: rgba(255, 255, 255, .96);
box-shadow: 0 3px 16px rgba(0, 0, 0, .06);
}
.v2-monitor-page .v2-event-strip.semi-card { border-color: rgba(60, 60, 67, .1); border-radius: 12px; box-shadow: none; }
.v2-track-page { background: #f5f5f7; }
.v2-track-page .v2-track-stage,
.v2-track-page .v2-track-map { background: #eef0f2; }
.v2-track-query-hint { margin: 0; padding: 18px; color: #6e6e73; font-size: 12px; line-height: 1.7; }
.v2-track-query-panel .v2-track-query-header.v2-workspace-command-bar { background: #fff; box-shadow: none; }
.v2-track-query { gap: 18px; padding: 18px; }
.v2-track-query > label > span { color: #6e6e73; font-size: 12px; font-weight: 500; }
.v2-track-page .v2-track-empty-state.semi-card {
width: min(520px, calc(100% - 48px));
border: 0;
border-radius: 20px;
background: transparent;
box-shadow: none;
}
.v2-track-page .v2-track-empty-state .v2-workspace-empty-guide,
.v2-track-page .v2-track-mobile-empty .v2-workspace-empty-guide { border: 0; background: transparent; box-shadow: none; }
.v2-track-page .v2-track-empty-state .v2-workspace-empty-guide::before,
.v2-track-page .v2-track-mobile-empty .v2-workspace-empty-guide::before { display: none; }
.v2-track-page .v2-track-empty-state .semi-empty-title { color: #1d1d1f; font-size: 22px; font-weight: 600; letter-spacing: -.025em; line-height: 1.4; }
.v2-track-page .v2-track-empty-state .semi-empty-description { color: #6e6e73; font-size: 13px; line-height: 1.7; }
.v2-track-page .v2-track-current-card.semi-card {
border-color: rgba(60, 60, 67, .12);
border-radius: 14px;
background: rgba(255, 255, 255, .97);
box-shadow: 0 6px 24px rgba(0, 0, 0, .09);
}
.v2-track-page .v2-track-stage-tools {
padding: 4px;
border: 1px solid rgba(60, 60, 67, .12);
border-radius: 12px;
background: rgba(255, 255, 255, .97);
box-shadow: 0 3px 16px rgba(0, 0, 0, .08);
}
.v2-track-page .v2-track-stage-tools .semi-button { min-height: 36px; border-radius: 8px; font-size: 12px; font-weight: 500; }
.v2-track-page .v2-track-stage-tools .semi-button.is-active { color: #007aff; background: #eaf3ff; }
.v2-track-page .v2-track-playback-dock.semi-card { border-color: rgba(60, 60, 67, .13); background: rgba(255, 255, 255, .98); box-shadow: 0 -3px 20px rgba(0, 0, 0, .06); }
.v2-track-page .v2-track-progress-meta { color: #6e6e73; font-size: 12px; font-variant-numeric: tabular-nums; }
.v2-track-page .v2-track-progress-meta time { color: #1d1d1f; font-weight: 500; }
.v2-track-page .v2-track-dock-controls .semi-button { min-width: 40px; height: 40px; border-radius: 10px; }
.v2-track-page .v2-track-dock-controls .semi-button.is-primary { min-width: 64px; background: #007aff; box-shadow: none; }
@media (max-width: 960px) {
.v2-monitor-page .v2-map-control-button.semi-button,
.v2-monitor-page .v2-monitor-mobile-card .semi-button,
.v2-track-page .v2-track-stage-tools .semi-button,
.v2-track-page .v2-track-dock-controls .semi-button,
.v2-track-page .v2-track-dock-controls .semi-select,
.v2-track-detail-sidesheet .v2-track-query .semi-button,
.v2-track-detail-sidesheet .v2-track-query .semi-input-wrapper,
.v2-track-detail-sidesheet .v2-track-query .semi-select,
.v2-track-detail-sidesheet .v2-track-list-action.semi-button { min-height: 44px; }
.v2-track-page .v2-track-dock-controls .semi-button { min-width: 44px; }
.v2-track-page .v2-track-progress-slider.semi-slider-wrapper { min-height: 44px; display: flex; align-items: center; }
.v2-track-page .v2-track-mobile-query-launcher.semi-button { min-height: 60px; border-radius: 14px; background: #fff; box-shadow: 0 3px 18px rgba(0, 0, 0, .08); }
.v2-track-page .v2-track-mobile-empty { bottom: 24px; }
.v2-track-page .v2-track-mobile-empty .semi-empty-title { color: #1d1d1f; font-size: 20px; font-weight: 600; }
}
/* A narrow evidence rail must show complete dates and complete metric values. */
.v2-track-date-time-picker .semi-datepicker-range-input {
display: grid;
grid-template-columns: minmax(0, 1fr) 28px;
gap: 0;
height: auto;
min-height: 88px;
padding: 0 6px 0 10px;
border: 1px solid rgba(60, 60, 67, .15);
border-radius: 10px;
background: #fff;
}
.v2-track-date-time-picker .semi-datepicker-range-input-wrapper {
display: flex;
align-items: center;
width: 100%;
min-width: 0;
height: 44px;
grid-column: 1;
padding: 0;
}
.v2-track-date-time-picker .semi-datepicker-range-input-wrapper-start { grid-row: 1; }
.v2-track-date-time-picker .semi-datepicker-range-input-wrapper-end { grid-row: 2; border-top: 1px solid rgba(60, 60, 67, .09); }
.v2-track-date-time-picker .semi-datepicker-range-input-wrapper::before { flex: 0 0 36px; color: #6e6e73; font-size: 12px; }
.v2-track-date-time-picker .semi-datepicker-range-input-wrapper-start::before { content: '开始'; }
.v2-track-date-time-picker .semi-datepicker-range-input-wrapper-end::before { content: '结束'; }
.v2-track-date-time-picker .semi-datepicker-range-input-wrapper .semi-input-wrapper {
flex: 1;
min-width: 0;
border: 0;
border-radius: 0;
background: transparent;
box-shadow: none;
}
.v2-track-date-time-picker .semi-datepicker-range-input-wrapper .semi-input {
padding-left: 0;
padding-right: 0;
font-size: 14px;
font-variant-numeric: tabular-nums;
}
.v2-track-date-time-picker .semi-datepicker-range-input-separator { display: none; }
.v2-track-date-time-picker .semi-datepicker-range-input-suffix { grid-column: 2; grid-row: 1 / 3; align-self: center; width: 28px; margin: 0; }
.v2-track-result-metric-rail .v2-workspace-metric-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.v2-track-result-metric-rail .v2-workspace-metric-list > span { padding: 12px 16px; border-bottom: 1px solid rgba(60, 60, 67, .09); }
.v2-track-result-metric-rail .v2-workspace-metric-list > span > strong,
.v2-track-result-metric-rail .v2-workspace-metric-list > span.is-secondary > strong {
overflow: visible;
white-space: normal;
overflow-wrap: anywhere;
text-overflow: clip;
font-variant-numeric: tabular-nums;
}
.v2-track-distance-unit { font-size: 12px; font-weight: 500; color: #6e6e73; }
.v2-track-page .v2-track-current-card .v2-track-current-metrics small,
.v2-track-page .v2-track-current-card .v2-track-current-metrics em,
.v2-track-page .v2-track-current-card .v2-track-current-evidence em,
.v2-track-page .v2-track-current-card .v2-track-current-evidence small,
.v2-track-page .v2-track-current-card > .semi-card-body > p { color: #6e6e73; font-size: 12px; }
.v2-track-page .v2-track-current-card .v2-track-current-metrics strong {
overflow: visible;
white-space: normal;
overflow-wrap: anywhere;
text-overflow: clip;
}
.v2-track-page .v2-track-current-card .v2-track-current-evidence { flex-wrap: wrap; gap: 6px 10px; }
.v2-track-result-metric-rail .v2-workspace-metric-list > span > small,
.v2-track-result-metric-rail .v2-workspace-metric-list > span > em { color: #6e6e73; font-size: 12px; line-height: 1.5; white-space: normal; }
.v2-track-page .v2-track-current-card > .semi-card-body > p.is-address-error,
.v2-track-page .v2-track-current-card > .semi-card-body > p.is-address-error > span { color: #6e6e73; font-size: 12px; }
.v2-track-page .v2-track-current-card > .semi-card-body > .v2-track-current-metrics small > b { color: #6e6e73; font-size: 11px; }
@media (max-width: 960px) {
.v2-track-page .v2-track-current-card > .semi-card-body > .v2-track-current-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.v2-track-page .v2-track-current-card > .semi-card-body > .v2-track-current-metrics > span { padding: 8px 12px; }
.v2-track-page .v2-track-current-card > .semi-card-body > .v2-track-current-metrics > span:nth-child(3) { border-left: 0; }
.v2-track-page .v2-track-current-card > .semi-card-body > .v2-track-current-metrics > span:nth-child(n+3) { border-top: 1px solid rgba(60, 60, 67, .09); }
.v2-track-page .v2-track-current-card > .semi-card-body > .v2-track-current-metrics strong,
.v2-track-page .v2-track-current-card > .semi-card-body > .v2-track-current-metrics > span:last-child strong { font-size: 16px; overflow: visible; text-overflow: clip; white-space: normal; }
.v2-track-page .v2-track-current-card > .semi-card-body > .v2-track-current-metrics small,
.v2-track-page .v2-track-current-card > .semi-card-body > .v2-track-current-metrics em { font-size: 12px; }
}
/* Let the map consume the space left by controls inside the mobile shell. */
@media (max-width: 760px) {
.v2-monitor-page.is-map-mode {
height: 100%;
min-height: 0;
overflow-x: hidden;
overflow-y: auto;
}
.v2-monitor-page.is-map-mode > .v2-filterbar,
.v2-monitor-page.is-map-mode > .v2-monitor-summary-rail,
.v2-monitor-page.is-map-mode > .v2-event-strip { flex: 0 0 auto; }
.v2-monitor-page.is-map-mode > .v2-monitor-workspace,
.v2-monitor-page.is-map-mode > .v2-monitor-workspace.is-detail-open,
.v2-monitor-page.is-map-mode > .v2-monitor-workspace.is-detail-collapsed {
flex: 1 1 0;
min-height: 0;
grid-template-columns: minmax(0, 1fr);
grid-template-rows: minmax(0, 1fr);
}
.v2-monitor-page.is-map-mode .v2-fleet-map { min-height: 0; height: 100%; }
}
.v2-track-query-panel .v2-track-query-header.v2-workspace-command-bar,
.v2-track-query-panel .v2-track-query-header.has-identity.v2-workspace-command-bar {
display: flex;
flex-wrap: wrap;
gap: 8px 10px;
}
.v2-track-query-header .v2-workspace-command-copy,
.v2-track-query-header.has-identity .v2-workspace-command-copy {
flex: 1 1 130px;
min-width: 90px;
}
.v2-track-query-header .v2-workspace-command-actions,
.v2-track-query-header.has-identity .v2-workspace-command-actions {
flex: 0 0 auto;
margin-left: auto;
}
.v2-track-query-header .v2-workspace-command-stack { min-width: 90px; }
.v2-track-query-header .v2-workspace-command-title,
.v2-track-query-header.has-identity .v2-workspace-command-title,
.v2-track-query-header .v2-workspace-command-description,
.v2-track-query-header.has-identity .v2-workspace-command-description {
overflow: visible;
text-overflow: clip;
white-space: normal;
overflow-wrap: anywhere;
}
@@ -0,0 +1,317 @@
/* Shared desktop workspace: restrained chrome, readable content, native controls.
* Keep page-specific layouts in apple-*-pages.css. Overlays also live outside
* the shell, so their selectors deliberately use the workspace component class.
*/
body {
--semi-color-primary: var(--v2-color-primary);
--semi-color-primary-hover: var(--v2-color-primary-hover);
--semi-color-primary-active: #005bb5;
--semi-color-primary-light-default: var(--v2-color-primary-soft);
--semi-color-primary-light-hover: #deebfa;
--semi-color-primary-light-active: #d3e4f8;
--semi-color-border: var(--v2-color-border);
--semi-color-bg-0: var(--v2-color-canvas);
--semi-color-text-0: var(--v2-color-text);
--semi-color-text-1: #3a3a3c;
--semi-color-text-2: var(--v2-color-text-secondary);
--semi-color-text-3: var(--v2-color-text-tertiary);
--v2-control-border: var(--v2-color-border-strong);
--v2-control-bg-muted: #f5f5f7;
--v2-control-text-muted: var(--v2-color-text-secondary);
--v2-control-height-mobile: 44px;
--v2-control-height-touch: 44px;
}
.v2-shell .v2-sidebar {
width: var(--v2-sidebar-width);
background: #f0f0f2;
border-right: 1px solid rgba(0,0,0,.08);
}
.v2-shell .v2-sidebar.is-collapsed { width: var(--v2-sidebar-width-compact); }
.v2-shell .v2-main { margin-left: var(--v2-sidebar-width); }
.v2-shell .v2-sidebar.is-collapsed + .v2-main { margin-left: var(--v2-sidebar-width-compact); }
.v2-shell .v2-brand { height: 72px; padding-inline: 24px; border-bottom: 0; }
.v2-shell .v2-brand-logo { width: 146px; }
.v2-shell .v2-navigation.semi-navigation { background: transparent; padding: 12px; }
.v2-shell .v2-navigation.semi-navigation .semi-navigation-item {
min-height: 42px;
border-radius: 8px;
color: #49494d;
box-shadow: none;
background: transparent;
}
.v2-shell .v2-navigation.semi-navigation .semi-navigation-item-text {
font-size: 14px;
font-weight: 500;
}
.v2-shell .v2-navigation.semi-navigation .semi-navigation-item-icon {
background: transparent;
color: #636366;
box-shadow: none;
border-radius: 0;
}
.v2-shell .v2-navigation.semi-navigation .semi-navigation-item:hover { background: #e7e7ea; }
.v2-shell .v2-navigation.semi-navigation .semi-navigation-item-selected {
background: #dfe5ed;
color: #1d1d1f;
box-shadow: none;
}
.v2-shell .v2-navigation.semi-navigation .semi-navigation-item-selected .semi-navigation-item-icon {
background: transparent;
color: var(--v2-color-primary);
box-shadow: none;
}
.v2-shell .v2-navigation.semi-navigation .semi-navigation-item.v2-nav-group-start::before {
color: #76767b;
font-size: 12px;
font-weight: 500;
letter-spacing: 0;
}
.v2-shell .v2-collapse.semi-button { border-top-color: rgba(0,0,0,.06); color: #636366; }
.v2-shell .v2-topbar {
height: 72px;
background: rgba(250,250,252,.94);
border-bottom: 1px solid rgba(0,0,0,.08);
padding-inline: 24px;
backdrop-filter: blur(20px);
}
.v2-shell .v2-topbar-title h4.semi-typography {
color: var(--v2-color-text);
font-size: 22px;
font-weight: 650;
letter-spacing: -.025em;
}
.v2-shell .v2-topbar-title .v2-topbar-description.semi-typography { color: #76767b; font-size: 13px; }
.v2-shell .v2-topbar-actions { gap: 8px; }
.v2-shell .v2-topbar-actions > .v2-help-trigger.semi-button {
min-width: 40px;
border: 0;
background: transparent;
color: #636366;
box-shadow: none;
}
.v2-shell .v2-current-user .semi-tag { display: none; }
.v2-shell .v2-current-user b { font-weight: 500; }
.v2-shell .v2-current-user .semi-avatar { background: #e3e3e8; color: #48484a; }
.v2-shell .v2-topbar-actions .semi-button:hover { background: #ededf0; }
/* Common toolbars contain a single title and an optional supporting sentence. */
.v2-shell .v2-content .v2-workspace-command-bar {
min-height: 66px;
margin-inline: 0;
border: 0;
border-bottom: 1px solid var(--v2-color-border);
border-radius: 0;
background: var(--v2-color-surface);
padding: 14px 20px;
box-shadow: none;
gap: 16px;
}
.v2-shell .v2-content .v2-workspace-command-bar::before,
.v2-shell .v2-content .v2-workspace-panel-header::before { display: none; }
.v2-shell .v2-content .v2-workspace-command-copy { gap: 12px; }
.v2-shell .v2-content .v2-workspace-command-icon {
width: 30px;
height: 30px;
flex-basis: 30px;
border: 0;
background: transparent;
color: #636366;
box-shadow: none;
}
.v2-shell .v2-content .v2-workspace-command-eyebrow { display: none; }
.v2-shell .v2-content .v2-workspace-command-title.semi-typography {
color: var(--v2-color-text);
font-size: 17px;
font-weight: 600;
line-height: 1.4;
letter-spacing: -.015em;
}
.v2-shell .v2-content .v2-workspace-command-description.semi-typography {
margin-top: 3px;
color: var(--v2-color-text-secondary);
font-size: 12px;
line-height: 1.5;
}
.v2-shell .v2-content .v2-workspace-command-meta { color: #76767b; font-size: 12px; }
.v2-shell .v2-content .v2-workspace-command-actions { gap: 10px; }
.v2-shell .v2-content .v2-workspace-panel-header {
min-height: 60px;
padding: 14px 20px;
border-left: 0;
border-bottom: 1px solid var(--v2-color-border);
background: #fff;
box-shadow: none;
gap: 16px;
}
.v2-shell .v2-content .v2-workspace-panel-header .v2-workspace-panel-title.semi-typography {
font-size: 16px;
font-weight: 600;
color: var(--v2-color-text);
}
.v2-shell .v2-content .v2-workspace-panel-header .v2-workspace-panel-eyebrow { display: none; }
.v2-shell .v2-content .v2-workspace-panel-header .v2-workspace-panel-description.semi-typography {
color: var(--v2-color-text-secondary);
font-size: 12px;
line-height: 1.5;
}
/* Controls share geometry without changing their native disabled/pressed state. */
.v2-shell .semi-button { font-size: 13px; font-weight: 500; border-radius: 8px; }
.v2-shell .semi-input-wrapper,
.v2-shell .semi-select { border-radius: 8px; background: #fff; }
.v2-shell .semi-input,
.v2-shell .semi-select-selection { font-size: 14px; color: var(--v2-color-text); }
.v2-shell .semi-input::placeholder { color: #76767b; }
.v2-shell .semi-tag { font-size: 12px; font-weight: 500; letter-spacing: 0; }
.v2-shell .v2-content .semi-table-thead > tr > th {
background: #f8f8fa;
color: #636366;
font-size: 12px;
font-weight: 500;
border-bottom-color: var(--v2-color-border);
}
.v2-shell .v2-content .semi-table-tbody > tr > td {
font-size: 14px;
border-bottom-color: #efeff1;
}
.v2-shell .v2-content .semi-table-tbody > tr:hover > td { background: #f5f7fa; }
.v2-shell .v2-content .v2-segmented-tabs.is-filled {
gap: 2px;
border: 0;
border-radius: 9px;
background: #eeeef0;
padding: 3px;
box-shadow: none;
}
.v2-shell .v2-content .v2-segmented-tabs > .semi-button {
min-height: 34px;
font-size: 13px;
font-weight: 500;
color: #636366;
}
.v2-shell .v2-content .v2-segmented-tabs.is-filled > .semi-button.is-active {
background: #fff;
color: #1d1d1f;
box-shadow: 0 1px 3px rgba(0,0,0,.13);
border-radius: 7px;
}
.v2-shell .v2-content .v2-segmented-tab-count {
background: rgba(0,0,0,.05);
color: inherit;
font-size: 12px;
font-weight: 500;
}
.v2-shell .v2-content .v2-workspace-metric-list > span {
border-color: var(--v2-color-border);
background: #fff;
box-shadow: none;
}
.v2-shell .v2-content .v2-workspace-metric-list > span > small,
.v2-shell .v2-content .v2-workspace-metric-action > small { font-size: 12px; color: #636366; font-weight: 500; }
.v2-shell .v2-content .v2-workspace-metric-list > span > strong,
.v2-shell .v2-content .v2-workspace-metric-action > strong { font-size: 24px; font-weight: 600; letter-spacing: -.025em; font-variant-numeric: tabular-nums; }
.v2-shell .v2-content .v2-workspace-metric-list > span > em,
.v2-shell .v2-content .v2-workspace-metric-action > em { color: #76767b; font-size: 12px; line-height: 1.5; }
.v2-shell .v2-content .v2-workspace-metric-list > span.is-active { background: #f0f5fc; }
/* Sheets and dialogs are the only elevated content surfaces. */
body [data-workspace-sheet-id] .semi-sidesheet-inner {
background: #fff;
box-shadow: -12px 0 48px rgba(0,0,0,.12);
}
body [data-workspace-sheet-id] .semi-sidesheet-header {
border-bottom: 1px solid var(--v2-color-border);
background: #fafafa;
padding: 20px 24px;
}
body [data-workspace-sheet-id] .v2-workspace-config-title { gap: 12px; }
body [data-workspace-sheet-id] .v2-workspace-config-title-icon {
border: 0;
background: #eeeef0;
color: #636366;
box-shadow: none;
}
body [data-workspace-sheet-id] .v2-workspace-config-title strong { color: #1d1d1f; font-size: 20px; font-weight: 600; }
body [data-workspace-sheet-id] .v2-workspace-config-title small { color: #636366; font-size: 13px; line-height: 1.5; }
body [data-workspace-sheet-id] .v2-workspace-config-body { background: #fff; }
body [data-workspace-sheet-id] .v2-workspace-config-content { font-size: 14px; }
body [data-workspace-sheet-id] .v2-workspace-config-footer { background: #fafafa; gap: 16px; padding: 16px 24px; }
body [data-workspace-sheet-id] .v2-workspace-config-footer > span { color: #636366; font-size: 12px; }
body [data-workspace-sheet-id] .semi-button { min-height: 36px; font-size: 13px; border-radius: 8px; }
body [data-workspace-sheet-id] .semi-sidesheet-close { min-width: 36px; min-height: 36px; border-radius: 50%; }
body .v2-help-panel li { background: #f5f5f7; color: #3a3a3c; font-size: 14px; padding: 16px; }
body .v2-help-panel > footer { font-size: 12px; }
body .v2-help-overview.semi-card { border: 0; background: transparent; box-shadow: none; }
body .v2-help-overview .semi-typography { color: #636366; font-size: 14px; line-height: 1.6; }
body .v2-account-dropdown { border-color: #e5e5e7; border-radius: 12px; box-shadow: 0 12px 40px rgba(0,0,0,.16); }
body .v2-account-dropdown-profile { background: #f5f5f7; }
body .v2-account-dropdown-profile strong { font-size: 14px; }
body .v2-account-dropdown-profile small,
body .v2-account-dropdown-profile em { font-size: 12px; color: #636366; }
:where(.v2-shell, [data-workspace-sheet-id]) :is(button, a, input, [role="button"], [role="tab"]):focus-visible {
outline: 2px solid var(--v2-color-primary);
outline-offset: 2px;
}
@media (max-width: 1100px) and (min-width: 681px) {
.v2-shell .v2-main { margin-left: var(--v2-sidebar-width-compact); }
.v2-shell .v2-topbar-description { display: none; }
}
@media (max-width: 680px) {
.v2-shell .v2-main { margin-left: 0; }
.v2-shell .v2-topbar { height: 60px; padding-inline: 16px; }
.v2-shell .v2-topbar-title h4.semi-typography { font-size: 20px; }
.v2-shell .v2-topbar-description { display: none; }
.v2-shell .v2-topbar-actions { gap: 0; }
.v2-shell .v2-help-trigger-label { display: none; }
.v2-shell .v2-topbar-actions > .semi-button { min-height: 44px; }
.v2-shell .v2-content .v2-workspace-command-bar,
.v2-shell .v2-content .v2-workspace-panel-header { padding: 12px 16px; gap: 10px; }
.v2-shell .v2-content .v2-workspace-command-title.semi-typography { font-size: 16px; }
.v2-shell .v2-content .v2-workspace-command-actions { flex-wrap: wrap; }
.v2-shell .v2-content .v2-workspace-command-description.semi-typography { font-size: 12px; }
.v2-shell .v2-content .v2-workspace-metric-list > span > strong,
.v2-shell .v2-content .v2-workspace-metric-action > strong { font-size: 21px; }
.v2-shell .v2-content .semi-button:not(.semi-button-size-small),
.v2-shell .v2-content .semi-input-wrapper,
.v2-shell .v2-content .semi-select,
body [data-workspace-sheet-id] .semi-button { min-height: 44px; }
.v2-shell .v2-content .v2-segmented-tabs > .semi-button { min-height: 40px; }
.v2-shell .v2-mobile-navigation { background: rgba(250,250,252,.96); backdrop-filter: blur(20px); border-color: #e5e5e7; }
body [data-workspace-sheet-id] .semi-sidesheet-header { padding: 18px 16px; }
body [data-workspace-sheet-id] .v2-workspace-config-footer { padding: 12px 16px; }
}
@media (prefers-reduced-motion: reduce) {
.v2-shell *, [data-workspace-sheet-id] * { transition-duration: 0s !important; animation-duration: 0s !important; }
}
.v2-shell .v2-navigation.semi-navigation .semi-navigation-item-selected::after { display: none; }
.v2-shell .v2-topbar-title > .v2-topbar-heading > .v2-topbar-page-title.semi-typography { font-size: 22px; font-weight: 650; }
body .v2-workspace-help-sidesheet .v2-help-overview.semi-card { min-height: 0; }
body .v2-workspace-help-sidesheet .v2-help-overview > .semi-card-body { padding: 8px 2px 20px; }
/* Long titles must leave room for the close control at narrow widths. */
body [data-workspace-sheet-id] .semi-sidesheet-header { display: flex; align-items: flex-start; gap: 12px; }
body [data-workspace-sheet-id] .semi-sidesheet-title { flex: 1; min-width: 0; }
body [data-workspace-sheet-id] .v2-workspace-config-title { min-width: 0; }
body [data-workspace-sheet-id] .v2-workspace-config-title > span { min-width: 0; flex: 1; }
body [data-workspace-sheet-id] .v2-workspace-config-title small { white-space: normal; overflow-wrap: anywhere; }
body [data-workspace-sheet-id] .semi-sidesheet-close { flex: 0 0 36px; margin: 0; }
body [data-workspace-sheet-id] .v2-workspace-config-summary > span { background: #fafafa; box-shadow: none; border-color: #e5e5e7; }
body [data-workspace-sheet-id] .v2-workspace-config-summary small,
body [data-workspace-sheet-id] .v2-workspace-config-summary em { font-size: 12px; color: #636366; line-height: 1.5; }
body [data-workspace-sheet-id] .v2-workspace-config-summary strong { font-size: 16px; font-weight: 600; }
@media (max-width: 680px) {
body [data-workspace-sheet-id] .v2-workspace-config-title-icon { display: none; }
body [data-workspace-sheet-id] .semi-sidesheet-close { flex-basis: 44px; min-width: 44px; }
.v2-shell .v2-topbar-title > .v2-topbar-heading > .v2-topbar-page-title.semi-typography { font-size: 20px; }
}
body .v2-mobile-more-section > header strong { font-size: 14px; }
body .v2-mobile-more-section > header span,
body .v2-mobile-more-section nav a > span { font-size: 12px; color: #636366; }
body .v2-mobile-more-section nav a strong { font-size: 14px; }
body .v2-mobile-more-section nav a :is(small, em) { font-size: 12px; color: #636366; }
.v2-shell .semi-input:focus-visible,
body [data-workspace-sheet-id] .semi-input:focus-visible { outline: none; box-shadow: none; }
+17 -17
View File
@@ -10,24 +10,24 @@
color-scheme: light;
/* Color */
--v2-color-canvas: #f6f8fb;
--v2-color-canvas: #f5f5f7;
--v2-color-surface: #ffffff;
--v2-color-surface-subtle: #fafbfd;
--v2-color-text: #172033;
--v2-color-text-secondary: #66758a;
--v2-color-text-tertiary: #8a96a7;
--v2-color-border: #dfe5ed;
--v2-color-border-strong: #cfd7e2;
--v2-color-primary: #1467e8;
--v2-color-primary-hover: #0f5dcc;
--v2-color-primary-soft: #edf4ff;
--v2-color-success: #15965f;
--v2-color-danger: #dc3f3f;
--v2-color-warning: #d98900;
--v2-color-surface-subtle: #fafafa;
--v2-color-text: #1d1d1f;
--v2-color-text-secondary: #636366;
--v2-color-text-tertiary: #76767b;
--v2-color-border: #e5e5e7;
--v2-color-border-strong: #c7c7cc;
--v2-color-primary: #0071e3;
--v2-color-primary-hover: #0064cc;
--v2-color-primary-soft: #eaf3ff;
--v2-color-success: #248a3d;
--v2-color-danger: #d70015;
--v2-color-warning: #a15c00;
/* Typography */
--v2-font-sans: Inter, "SF Pro Text", "SF Pro Display", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
--v2-type-caption: 11px;
--v2-font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
--v2-type-caption: 12px;
--v2-type-meta: 12px;
--v2-type-control: 14px;
--v2-type-body: 14px;
@@ -46,14 +46,14 @@
--v2-space-7: 28px;
--v2-space-8: 32px;
--v2-radius-control: 8px;
--v2-radius-surface: 10px;
--v2-radius-surface: 12px;
--v2-control-height: 40px;
--v2-control-height-large: 44px;
--v2-touch-target: 44px;
--v2-sidebar-width: 228px;
--v2-sidebar-width-compact: 72px;
--v2-page-gutter: clamp(18px, 1.7vw, 28px);
--v2-page-gap: 12px;
--v2-page-gap: 16px;
/* Motion */
--v2-motion-fast: 140ms;
@@ -5,29 +5,10 @@
* keeps one visual hierarchy: identity, menu access, then vehicle scope.
*/
.v2-user-admin,
.v2-user-editor-sidesheet {
--v2-color-canvas: #f6f8fb;
--v2-color-surface-subtle: #fafbfd;
--v2-color-text: #172033;
--v2-color-text-secondary: #66758a;
--v2-color-text-tertiary: #8a96a7;
--v2-color-border: #dfe5ed;
--v2-color-primary: #1467e8;
--v2-color-warning: #d98900;
--v2-radius-surface: 10px;
--v2-motion-fast: 140ms;
}
/* Batch account import keeps upload, server preflight, repair and retry in one wide task surface. */
.v2-user-import-sidesheet {
--v2-color-text: #172033;
--v2-color-text-secondary: #66758a;
--v2-color-border: #dfe5ed;
}
/* Colours, typography and motion inherit the shared foundation. */
.v2-user-import-sidesheet .v2-workspace-config-content {
background: #f6f8fb;
background: var(--v2-color-canvas);
}
.v2-user-import-workspace {
@@ -70,7 +51,7 @@
.v2-user-import-row-result small,
.v2-user-import-upload small {
color: var(--v2-color-text-secondary);
font-size: 11px;
font-size: 12px;
}
.v2-user-import-upload {
@@ -106,14 +87,14 @@
.v2-user-import-format span {
color: var(--v2-color-text-secondary);
font-size: 10px;
font-size: 12px;
font-weight: 650;
}
.v2-user-import-format code {
overflow-x: auto;
color: #36506f;
font-size: 10px;
font-size: 12px;
white-space: nowrap;
}
@@ -123,7 +104,7 @@
background: #fff4f3;
padding: 9px 14px;
color: #b42318;
font-size: 11px;
font-size: 12px;
}
.v2-user-import-list {
@@ -161,7 +142,7 @@
border-radius: 6px;
background: #eef3f9;
color: #5b6b80;
font-size: 10px;
font-size: 12px;
font-weight: 700;
}
@@ -211,7 +192,7 @@
.v2-user-import-repair-fields label > span {
color: var(--v2-color-text-secondary);
font-size: 11px;
font-size: 12px;
font-weight: 650;
}
@@ -370,7 +351,7 @@
.v2-user-directory-metric-rail .v2-workspace-metric-list > span > small {
color: var(--v2-color-text-secondary);
font-size: 11px;
font-size: 12px;
}
.v2-user-directory-metric-rail .v2-workspace-metric-list > span > strong {
@@ -381,7 +362,7 @@
.v2-user-directory-metric-rail .v2-workspace-metric-list > span > em {
color: var(--v2-color-text-tertiary);
font-size: 10px;
font-size: 12px;
}
.v2-user-directory-body {
@@ -418,7 +399,7 @@
.v2-user-route-notice small {
color: var(--v2-color-text-secondary);
font-size: 11px;
font-size: 12px;
}
.v2-user-directory-columns {
@@ -429,7 +410,7 @@
background: var(--v2-color-surface-subtle);
padding: 0 16px;
color: var(--v2-color-text-tertiary);
font-size: 11px;
font-size: 12px;
font-weight: 600;
}
@@ -520,7 +501,7 @@
.v2-user-provider-tag.semi-tag {
min-height: 20px;
border-radius: 5px;
font-size: 10px;
font-size: 12px;
}
.v2-user-list-secondary {
@@ -533,7 +514,7 @@
.v2-user-list-secondary > small {
overflow: hidden;
color: var(--v2-color-text-tertiary);
font-size: 11px;
font-size: 12px;
text-overflow: ellipsis;
white-space: nowrap;
}
@@ -555,7 +536,7 @@
.v2-user-list-facts > b > i {
color: var(--v2-color-text-tertiary);
font-size: 11px;
font-size: 12px;
font-style: normal;
font-weight: 500;
}
@@ -580,7 +561,7 @@
min-width: 64px;
min-height: 24px;
border-radius: 999px;
font-size: 10px;
font-size: 12px;
font-weight: 650;
}
@@ -652,7 +633,7 @@
.v2-user-editor-switcher small {
overflow: hidden;
color: var(--v2-color-text-tertiary);
font-size: 10px;
font-size: 12px;
text-overflow: ellipsis;
white-space: nowrap;
}
@@ -687,7 +668,7 @@
.v2-user-change-preview small {
color: #9b7249;
font-size: 10px;
font-size: 12px;
}
.v2-user-change-preview > div {
@@ -730,7 +711,7 @@
border: 1px solid #d9e2ee;
border-radius: 50%;
color: var(--v2-color-text-tertiary);
font-size: 10px;
font-size: 12px;
font-style: normal;
font-weight: 700;
}
@@ -815,7 +796,7 @@
.v2-user-identity-source > p,
.v2-user-identity-source > div small {
color: var(--v2-color-text-secondary);
font-size: 10px;
font-size: 12px;
line-height: 1.55;
}
@@ -843,7 +824,7 @@
.v2-user-identity-source > div strong {
overflow: hidden;
color: #30445d;
font-size: 11px;
font-size: 12px;
font-weight: 680;
text-overflow: ellipsis;
white-space: nowrap;
@@ -929,7 +910,7 @@
.v2-user-vehicle-composer-heading small {
color: var(--v2-color-text-tertiary);
font-size: 10px;
font-size: 12px;
}
.v2-user-vehicle-composer .v2-vehicle-permission-tools {
@@ -1057,7 +1038,7 @@
}
.v2-user-directory-glance {
font-size: 11px;
font-size: 12px;
}
.v2-user-directory-glance b {
@@ -1086,7 +1067,7 @@
}
.v2-user-directory-metric-rail .v2-workspace-metric-list > span > em {
font-size: 9px;
font-size: 12px;
}
.v2-user-directory-body,
@@ -1170,7 +1151,7 @@
overflow: hidden;
max-width: 47%;
flex: 0 1 auto;
font-size: 10px;
font-size: 12px;
text-overflow: ellipsis;
white-space: nowrap;
}
@@ -1186,7 +1167,7 @@
flex: 0 0 auto;
align-items: center;
color: var(--v2-color-text-secondary);
font-size: 10px;
font-size: 12px;
font-weight: 520;
white-space: nowrap;
}
@@ -1204,7 +1185,7 @@
.v2-user-status-tag.semi-tag {
min-width: 40px;
min-height: 22px;
font-size: 9px;
font-size: 12px;
}
.v2-user-config-label {
@@ -1246,11 +1227,11 @@
.v2-user-editor-sidesheet .v2-workspace-config-summary > span > small,
.v2-user-editor-sidesheet .v2-workspace-config-summary > span > em {
font-size: 9px;
font-size: 12px;
}
.v2-user-editor-sidesheet .v2-workspace-config-summary > span > strong {
font-size: 11px;
font-size: 12px;
}
.v2-user-change-preview {
@@ -1269,7 +1250,7 @@
}
.v2-user-change-preview .semi-tag {
font-size: 9px;
font-size: 12px;
}
.v2-user-editor-switcher {
@@ -1294,7 +1275,7 @@
.v2-user-editor-sidesheet .v2-user-editor-tabs > .semi-tabs-bar .semi-tabs-tab {
min-height: 49px;
padding-inline: 8px;
font-size: 11px;
font-size: 12px;
}
.v2-user-tab-label {