refine Semi UI telemetry evidence
This commit is contained in:
@@ -356,13 +356,18 @@ test('keeps protocol telemetry independent and explains mileage semantics', asyn
|
||||
vi.spyOn(api, 'vehicleRealtime').mockResolvedValue({ items: [{ ...initialRealtime, protocols: ['GB32960', 'JT808'] }], total: 1, limit: 1, offset: 0 });
|
||||
vi.spyOn(api, 'latestTelemetry').mockResolvedValue({
|
||||
vin: initialRealtime.vin,
|
||||
categories: [{ key: 'vehicle', label: '整车数据', count: 2 }],
|
||||
categories: [{ key: 'vehicle', label: '整车数据', count: 3 }],
|
||||
values: [
|
||||
{
|
||||
key: 'total_mileage_km', sourceField: 'gb32960.vehicle.total_mileage_km', label: '仪表盘总里程', unit: 'km',
|
||||
category: 'vehicle', valueType: 'numeric', value: 1200, protocol: 'GB32960', frameId: 'gb', deviceTime: '', serverTime: '2026-07-16 10:00:00',
|
||||
quality: 'good', qualityReason: '正常', freshnessSeconds: 1
|
||||
},
|
||||
{
|
||||
key: 'cell_voltages', sourceField: 'gb32960.extended.cell_voltages', label: '单体电压列表', unit: 'V',
|
||||
category: 'vehicle', valueType: 'array', value: [3.21, 3.22, 3.23, 3.24], protocol: 'GB32960', frameId: 'gb-array', deviceTime: '', serverTime: '2026-07-16 10:00:00',
|
||||
quality: 'good', qualityReason: '正常', freshnessSeconds: 1, sourceEndpoint: '10.0.0.1:8080'
|
||||
},
|
||||
{
|
||||
key: 'total_mileage_km', sourceField: 'jt808.location.total_mileage_km', label: 'GPS 总里程', unit: 'km',
|
||||
category: 'vehicle', valueType: 'numeric', value: 1180, protocol: 'JT808', frameId: 'jt', deviceTime: '', serverTime: '2026-07-16 10:00:00',
|
||||
@@ -383,6 +388,14 @@ test('keeps protocol telemetry independent and explains mileage semantics', asyn
|
||||
expect(document.querySelector('.v2-event-empty.semi-empty')).toBeInTheDocument();
|
||||
expect(screen.getByText('仪表盘总里程')).toBeInTheDocument();
|
||||
expect(screen.queryByText('GPS 总里程')).not.toBeInTheDocument();
|
||||
const complexValue = screen.getByRole('button', { name: '查看 单体电压列表 完整数据' });
|
||||
expect(complexValue).toHaveClass('semi-button', 'v2-telemetry-value-trigger');
|
||||
expect(complexValue).toHaveTextContent('4 个值');
|
||||
expect(complexValue).toHaveTextContent('3.21,3.22,3.23');
|
||||
fireEvent.click(complexValue);
|
||||
expect(await screen.findByRole('dialog', { name: '遥测字段完整数据' })).toBeInTheDocument();
|
||||
expect(document.querySelector('.v2-telemetry-value-detail pre')).toHaveTextContent('3.24');
|
||||
expect(document.querySelector('.v2-telemetry-value-detail > footer strong')).toHaveTextContent('10.0.0.1:8080');
|
||||
fireEvent.click(screen.getByRole('tab', { name: /JT\/T 808/ }));
|
||||
expect(screen.getByRole('tab', { name: /JT\/T 808/, selected: true })).toBeInTheDocument();
|
||||
expect(screen.getByText('GPS 总里程')).toBeInTheDocument();
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
IconAlarm, IconBox, IconCalendar, IconClock, IconCopy,
|
||||
IconChevronRight, IconMapPin, IconSearch, IconTickCircle
|
||||
} from '@douyinfe/semi-icons';
|
||||
import { Button, Card, Descriptions, Empty, Input, List, Select, Table, Tag } from '@douyinfe/semi-ui';
|
||||
import { Button, Card, Descriptions, Empty, Input, List, Select, SideSheet, Table, Tag } from '@douyinfe/semi-ui';
|
||||
import { FormEvent, lazy, Suspense, useEffect, useMemo, useState } from 'react';
|
||||
import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
||||
import { api } from '../../api/client';
|
||||
@@ -22,6 +22,7 @@ import { VehicleSourceEvidencePanel } from '../shared/VehicleSourceEvidencePanel
|
||||
import { WorkspacePanelHeader } from '../shared/WorkspacePanelHeader';
|
||||
import { monitorReturnFromParams, withMonitorReturn } from '../routing/monitorContext';
|
||||
import { useMobileLayout } from '../hooks/useMobileLayout';
|
||||
import { useSideSheetA11y } from '../hooks/useSideSheetA11y';
|
||||
|
||||
function fmt(value?: string) { return value?.trim() || '—'; }
|
||||
function metric(value: number | undefined, fallback = '—') { return typeof value === 'number' && Number.isFinite(value) ? formatZhNumber(value, 1) : fallback; }
|
||||
@@ -122,8 +123,72 @@ function TelemetryFieldCell({ item }: { item: LatestTelemetryValue }) {
|
||||
return <div className="v2-telemetry-field"><strong title={item.description}>{item.label}</strong><small title={item.sourceField}>{item.sourceField}</small></div>;
|
||||
}
|
||||
|
||||
function TelemetryValueCell({ item }: { item: LatestTelemetryValue }) {
|
||||
return <div className="v2-telemetry-value"><strong>{formatTelemetryValue(item.value, item.displayValue)}</strong>{item.unit ? <span>{item.unit}</span> : null}</div>;
|
||||
type TelemetryValuePresentation = {
|
||||
complex: boolean;
|
||||
preview: string;
|
||||
summary: string;
|
||||
};
|
||||
|
||||
function tryParseTelemetryJSON(value: string) {
|
||||
const trimmed = value.trim();
|
||||
if (!(trimmed.startsWith('[') && trimmed.endsWith(']')) && !(trimmed.startsWith('{') && trimmed.endsWith('}'))) return undefined;
|
||||
try { return JSON.parse(trimmed) as unknown; } catch { return undefined; }
|
||||
}
|
||||
|
||||
function telemetryValuePresentation(item: LatestTelemetryValue): TelemetryValuePresentation {
|
||||
const formatted = formatTelemetryValue(item.value, item.displayValue);
|
||||
const parsed = typeof item.value === 'string'
|
||||
? tryParseTelemetryJSON(item.value)
|
||||
: typeof item.displayValue === 'string'
|
||||
? tryParseTelemetryJSON(item.displayValue)
|
||||
: item.value;
|
||||
if (Array.isArray(parsed)) {
|
||||
return {
|
||||
complex: true,
|
||||
preview: parsed.slice(0, 3).map((value) => String(value)).join(',') || '空数组',
|
||||
summary: `${formatZhNumber(parsed.length)} 个值`
|
||||
};
|
||||
}
|
||||
if (parsed && typeof parsed === 'object') {
|
||||
const entries = Object.entries(parsed);
|
||||
return {
|
||||
complex: true,
|
||||
preview: entries.slice(0, 2).map(([key]) => key).join(',') || '空对象',
|
||||
summary: `${formatZhNumber(entries.length)} 个字段`
|
||||
};
|
||||
}
|
||||
const detail = String(formatted);
|
||||
return {
|
||||
complex: detail.length > 42,
|
||||
preview: detail.length > 54 ? `${detail.slice(0, 54)}…` : detail,
|
||||
summary: detail.length > 42 ? `${formatZhNumber(detail.length)} 个字符` : detail
|
||||
};
|
||||
}
|
||||
|
||||
function telemetryValueDetail(item: LatestTelemetryValue) {
|
||||
const parsed = typeof item.value === 'string'
|
||||
? tryParseTelemetryJSON(item.value)
|
||||
: typeof item.displayValue === 'string'
|
||||
? tryParseTelemetryJSON(item.displayValue)
|
||||
: item.value;
|
||||
if (parsed && typeof parsed === 'object') return JSON.stringify(parsed, null, 2);
|
||||
return String(formatTelemetryValue(item.value, item.displayValue));
|
||||
}
|
||||
|
||||
function TelemetryValueCell({ item, onInspect }: { item: LatestTelemetryValue; onInspect?: (item: LatestTelemetryValue) => void }) {
|
||||
const presentation = telemetryValuePresentation(item);
|
||||
if (presentation.complex) {
|
||||
return <div className="v2-telemetry-value is-complex"><Button
|
||||
className="v2-telemetry-value-trigger"
|
||||
theme="borderless"
|
||||
type="tertiary"
|
||||
aria-label={`查看 ${item.label} 完整数据`}
|
||||
onClick={() => onInspect?.(item)}
|
||||
>
|
||||
<span><strong>{presentation.summary}</strong><small>{presentation.preview}</small></span><IconChevronRight />
|
||||
</Button></div>;
|
||||
}
|
||||
return <div className="v2-telemetry-value"><strong>{presentation.summary}</strong>{item.unit ? <span>{item.unit}</span> : null}</div>;
|
||||
}
|
||||
|
||||
function TelemetryQualityCell({ item }: { item: LatestTelemetryValue }) {
|
||||
@@ -147,6 +212,11 @@ function TelemetryPanel({ data, pending, error }: { data?: LatestTelemetryRespon
|
||||
const mobileLayout = useMobileLayout();
|
||||
const [selectedProtocol, setSelectedProtocol] = useState('');
|
||||
const [selectedCategory, setSelectedCategory] = useState('vehicle');
|
||||
const [inspectedValue, setInspectedValue] = useState<LatestTelemetryValue>();
|
||||
const [copiedValue, setCopiedValue] = useState(false);
|
||||
const inspectedPresentation = useMemo(() => inspectedValue ? telemetryValuePresentation(inspectedValue) : undefined, [inspectedValue]);
|
||||
const inspectedDetail = useMemo(() => inspectedValue ? telemetryValueDetail(inspectedValue) : '', [inspectedValue]);
|
||||
useSideSheetA11y(Boolean(inspectedValue), '.v2-telemetry-value-sidesheet', 'v2-telemetry-value-detail', '遥测字段完整数据', '关闭遥测字段完整数据');
|
||||
const indexed = useMemo(() => {
|
||||
const valuesByProtocolCategory = new Map<string, LatestTelemetryResponse['values']>();
|
||||
const sources = new Map<string, { protocol: string; endpoint?: string }>();
|
||||
@@ -172,7 +242,7 @@ function TelemetryPanel({ data, pending, error }: { data?: LatestTelemetryRespon
|
||||
const protocolLabel = (protocol: string) => protocol === 'GB32960' ? 'GB/T 32960' : protocol === 'JT808' ? 'JT/T 808' : protocol === 'YUTONG_MQTT' ? '宇通 MQTT' : protocol;
|
||||
const columns = [
|
||||
{ title: '字段 / 协议映射', dataIndex: 'label', width: 260, render: (_: unknown, item: LatestTelemetryValue) => <TelemetryFieldCell item={item} /> },
|
||||
{ title: '当前值', dataIndex: 'value', width: 150, render: (_: unknown, item: LatestTelemetryValue) => <TelemetryValueCell item={item} /> },
|
||||
{ title: '当前值', dataIndex: 'value', width: 170, render: (_: unknown, item: LatestTelemetryValue) => <TelemetryValueCell item={item} onInspect={setInspectedValue} /> },
|
||||
{ title: '质量 / 新鲜度', dataIndex: 'quality', width: 170, render: (_: unknown, item: LatestTelemetryValue) => <TelemetryQualityCell item={item} /> },
|
||||
{ title: '设备 / 接收时间', dataIndex: 'deviceTime', width: 230, render: (_: unknown, item: LatestTelemetryValue) => <TelemetryTimeCell item={item} /> },
|
||||
{ title: '数据来源', dataIndex: 'protocol', width: 180, render: (_: unknown, item: LatestTelemetryValue) => <TelemetrySourceCell item={item} /> }
|
||||
@@ -185,7 +255,7 @@ function TelemetryPanel({ data, pending, error }: { data?: LatestTelemetryRespon
|
||||
? <Empty className="v2-telemetry-empty" title="暂无可展示字段" description={`该协议最近 ${data?.scannedFrames ?? 0} 帧没有可展示的标量遥测。`} />
|
||||
: mobileLayout
|
||||
? <List className="v2-telemetry-mobile-list" dataSource={visibleMetrics} split={false} renderItem={(item) => <List.Item className="v2-telemetry-mobile-item" key={telemetryRowKey(item)}>
|
||||
<header><TelemetryFieldCell item={item} /><TelemetryValueCell item={item} /></header>
|
||||
<header><TelemetryFieldCell item={item} /><TelemetryValueCell item={item} onInspect={setInspectedValue} /></header>
|
||||
<div><TelemetryQualityCell item={item} /><TelemetryTimeCell item={item} /></div>
|
||||
<TelemetrySourceCell item={item} />
|
||||
</List.Item>} />
|
||||
@@ -195,7 +265,7 @@ function TelemetryPanel({ data, pending, error }: { data?: LatestTelemetryRespon
|
||||
dataSource={visibleMetrics}
|
||||
rowKey={telemetryRowKey}
|
||||
pagination={false}
|
||||
scroll={{ x: 990 }}
|
||||
scroll={{ x: 1010 }}
|
||||
empty={null}
|
||||
/></div>;
|
||||
return <Card className="v2-record-card v2-telemetry-card" bodyStyle={{ padding: 0 }}>
|
||||
@@ -210,6 +280,42 @@ function TelemetryPanel({ data, pending, error }: { data?: LatestTelemetryRespon
|
||||
</div>
|
||||
{state}
|
||||
<footer><b>来源</b>{indexed.sources.map((source) => <span key={`${source.protocol}-${source.endpoint ?? ''}`} title={source.endpoint}>{source.protocol}</span>)}<small>扫描 {data?.scannedFrames ?? 0} 帧 · 截至 {formatTelemetryTime(data?.asOf)}</small></footer>
|
||||
<SideSheet
|
||||
className="v2-telemetry-value-sidesheet"
|
||||
visible={Boolean(inspectedValue)}
|
||||
width={520}
|
||||
title={inspectedValue ? <div className="v2-telemetry-value-title"><strong>{inspectedValue.label}</strong><span>{inspectedValue.sourceField}</span></div> : null}
|
||||
onCancel={() => { setInspectedValue(undefined); setCopiedValue(false); }}
|
||||
footer={<div className="v2-telemetry-value-footer"><span>完整值仅按需渲染,不影响实时遥测刷新。</span><Button theme="solid" onClick={() => { setInspectedValue(undefined); setCopiedValue(false); }}>完成</Button></div>}
|
||||
>
|
||||
{inspectedValue ? <div className="v2-telemetry-value-detail">
|
||||
<Descriptions
|
||||
className="v2-telemetry-value-meta"
|
||||
size="small"
|
||||
row
|
||||
data={[
|
||||
{ key: '协议', value: inspectedValue.protocol },
|
||||
{ key: '数据类型', value: inspectedValue.valueType || '—' },
|
||||
{ key: '单位', value: inspectedValue.unit || '—' },
|
||||
{ key: '质量', value: telemetryQualityLabel(inspectedValue.quality) },
|
||||
{ key: '设备时间', value: formatTelemetryTime(inspectedValue.deviceTime) },
|
||||
{ key: '接收时间', value: formatTelemetryTime(inspectedValue.serverTime) }
|
||||
]}
|
||||
/>
|
||||
<section>
|
||||
<header><div><strong>完整数据</strong><small>{inspectedPresentation?.summary}</small></div><Button
|
||||
theme="light"
|
||||
icon={<IconCopy />}
|
||||
onClick={() => {
|
||||
void navigator.clipboard?.writeText(inspectedDetail);
|
||||
setCopiedValue(true);
|
||||
}}
|
||||
>{copiedValue ? '已复制' : '复制完整值'}</Button></header>
|
||||
<pre>{inspectedDetail}</pre>
|
||||
</section>
|
||||
<footer><span>来源端点</span><strong>{inspectedValue.sourceEndpoint || '协议默认来源'}</strong></footer>
|
||||
</div> : null}
|
||||
</SideSheet>
|
||||
</Card>;
|
||||
}
|
||||
|
||||
|
||||
@@ -8427,6 +8427,40 @@ button, a { -webkit-tap-highlight-color: transparent; }
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.v2-telemetry-value span { color: #7d8b9e; font-size: 9px; }
|
||||
.v2-telemetry-value.is-complex { display: block; }
|
||||
.v2-telemetry-value-trigger.semi-button {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
min-height: 36px;
|
||||
justify-content: flex-start;
|
||||
border-radius: 7px;
|
||||
padding: 4px 6px;
|
||||
color: #315f9f;
|
||||
text-align: left;
|
||||
}
|
||||
.v2-telemetry-value-trigger .semi-button-content {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
grid-template-columns: minmax(0,1fr) auto;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
}
|
||||
.v2-telemetry-value-trigger .semi-button-content > span {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
gap: 3px;
|
||||
}
|
||||
.v2-telemetry-value-trigger .semi-button-content strong,
|
||||
.v2-telemetry-value-trigger .semi-button-content small {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.v2-telemetry-value-trigger .semi-button-content strong { color: #245f9f; font-size: 11px; }
|
||||
.v2-telemetry-value-trigger .semi-button-content small { color: #8492a4; font-size: 8px; font-weight: 500; }
|
||||
.v2-telemetry-value-trigger .semi-icon { flex: 0 0 auto; color: #7d9ac2; font-size: 12px; }
|
||||
.v2-telemetry-value-trigger:hover { background: #edf5ff; }
|
||||
.v2-telemetry-quality { justify-items: start; }
|
||||
.v2-telemetry-quality .semi-tag { min-height: 22px; border-radius: 999px; font-size: 9px; font-weight: 650; }
|
||||
.v2-telemetry-quality small,
|
||||
@@ -8453,6 +8487,87 @@ button, a { -webkit-tap-highlight-color: transparent; }
|
||||
.v2-telemetry-empty .semi-empty-description { color: #8492a4; font-size: 10px; }
|
||||
.v2-telemetry-mobile-list { display: none; }
|
||||
.v2-telemetry-card > .semi-card-body > footer { min-height: 40px; margin-top: 10px; }
|
||||
.v2-telemetry-value-sidesheet .semi-sidesheet-inner { max-width: 100vw; }
|
||||
.v2-telemetry-value-sidesheet .semi-sidesheet-header { border-bottom: 1px solid #e3e9f1; padding: 18px 20px 15px; }
|
||||
.v2-telemetry-value-title { display: grid; min-width: 0; gap: 4px; }
|
||||
.v2-telemetry-value-title strong {
|
||||
overflow: hidden;
|
||||
color: #243a53;
|
||||
font-size: 16px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.v2-telemetry-value-title span {
|
||||
overflow: hidden;
|
||||
color: #8391a3;
|
||||
font: 9px ui-monospace,SFMono-Regular,Menlo,monospace;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.v2-telemetry-value-sidesheet .semi-sidesheet-body { background: #f6f8fb; padding: 16px; }
|
||||
.v2-telemetry-value-detail { display: grid; gap: 12px; }
|
||||
.v2-telemetry-value-meta.semi-descriptions {
|
||||
border: 1px solid #dfe6ef;
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
padding: 12px 14px;
|
||||
}
|
||||
.v2-telemetry-value-meta .semi-descriptions-item { min-width: 130px; padding-block: 5px; }
|
||||
.v2-telemetry-value-meta .semi-descriptions-key { color: #8a97a8; font-size: 9px; }
|
||||
.v2-telemetry-value-meta .semi-descriptions-value { color: #3d5067; font-size: 10px; font-weight: 650; }
|
||||
.v2-telemetry-value-detail > section {
|
||||
overflow: hidden;
|
||||
border: 1px solid #dfe6ef;
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
}
|
||||
.v2-telemetry-value-detail > section > header {
|
||||
display: flex;
|
||||
min-height: 54px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
border-bottom: 1px solid #e4e9f0;
|
||||
padding: 9px 12px 9px 15px;
|
||||
}
|
||||
.v2-telemetry-value-detail > section > header > div { display: grid; min-width: 0; gap: 3px; }
|
||||
.v2-telemetry-value-detail > section > header strong { color: #30445c; font-size: 12px; }
|
||||
.v2-telemetry-value-detail > section > header small { color: #8a97a8; font-size: 9px; }
|
||||
.v2-telemetry-value-detail > section > header .semi-button { flex: 0 0 auto; }
|
||||
.v2-telemetry-value-detail pre {
|
||||
max-height: min(54vh,560px);
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
background: #f8fafc;
|
||||
padding: 15px;
|
||||
color: #31465e;
|
||||
font: 10px/1.7 ui-monospace,SFMono-Regular,Menlo,monospace;
|
||||
tab-size: 2;
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.v2-telemetry-value-detail > footer {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
grid-template-columns: auto minmax(0,1fr);
|
||||
align-items: center;
|
||||
gap: 9px;
|
||||
border: 1px solid #dfe6ef;
|
||||
border-radius: 9px;
|
||||
background: #fff;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
.v2-telemetry-value-detail > footer span { color: #8a97a8; font-size: 9px; }
|
||||
.v2-telemetry-value-detail > footer strong {
|
||||
overflow: hidden;
|
||||
color: #52667d;
|
||||
font: 9px ui-monospace,SFMono-Regular,Menlo,monospace;
|
||||
text-align: right;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.v2-telemetry-value-footer { display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 14px; }
|
||||
.v2-telemetry-value-footer span { color: #7e8d9f; font-size: 9px; }
|
||||
|
||||
@media (max-width: 680px) {
|
||||
.v2-record-grid { grid-template-rows: none; }
|
||||
@@ -8483,6 +8598,8 @@ button, a { -webkit-tap-highlight-color: transparent; }
|
||||
}
|
||||
.v2-telemetry-mobile-item > header .v2-telemetry-value { justify-content: end; }
|
||||
.v2-telemetry-mobile-item > header .v2-telemetry-value strong { font-size: 13px; }
|
||||
.v2-telemetry-mobile-item > header .v2-telemetry-value.is-complex { width: min(142px,42vw); }
|
||||
.v2-telemetry-mobile-item > header .v2-telemetry-value-trigger.semi-button { padding: 2px 4px; }
|
||||
.v2-telemetry-mobile-item > div {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0,.8fr) minmax(0,1.2fr);
|
||||
@@ -8495,6 +8612,15 @@ button, a { -webkit-tap-highlight-color: transparent; }
|
||||
.v2-telemetry-mobile-item > .v2-telemetry-source small { min-width: 0; }
|
||||
.v2-telemetry-card > .semi-card-body > footer { flex-wrap: wrap; gap: 5px; margin: 3px 9px 9px; }
|
||||
.v2-telemetry-card > .semi-card-body > footer small { width: 100%; margin: 3px 0 0; }
|
||||
.v2-telemetry-value-sidesheet .semi-sidesheet-inner { width: 100vw !important; }
|
||||
.v2-telemetry-value-sidesheet .semi-sidesheet-header { padding: 15px 16px 13px; }
|
||||
.v2-telemetry-value-sidesheet .semi-sidesheet-body { padding: 12px; }
|
||||
.v2-telemetry-value-meta .semi-descriptions-item { min-width: 108px; }
|
||||
.v2-telemetry-value-detail > section > header { align-items: flex-start; flex-direction: column; }
|
||||
.v2-telemetry-value-detail > section > header .semi-button { width: 100%; justify-content: center; }
|
||||
.v2-telemetry-value-detail pre { max-height: 46vh; font-size: 9px; }
|
||||
.v2-telemetry-value-footer { align-items: stretch; flex-direction: column; }
|
||||
.v2-telemetry-value-footer .semi-button { width: 100%; justify-content: center; }
|
||||
}
|
||||
|
||||
/* Semi UI track evidence: one list language for stops, events and source provenance. */
|
||||
|
||||
Reference in New Issue
Block a user