Compare commits
2 Commits
843690cc71
...
270bb163eb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
270bb163eb | ||
|
|
2c09fd7cf0 |
@@ -60,6 +60,17 @@ stat-writer 已支持在累计里程缺失时,用有速度证据的相邻 GPS
|
||||
|
||||
03:56 新版本 `mileage-gps-candidate-reconcile-202607200350` 完成清理型正式回补:写入 452 个来源聚合,`subthresholdGPSCandidatesFound=1`、`subthresholdGPSCandidatesDeleted=1`。沪A56988F 当天阈值下候选及错误最终投影均已删除;剩余被选中的 GPS 候选最小值为 `0.131 km`,低于 `0.1 km` 的选中候选为 0。清理后该车显式暴露为“JT808 当日缺少新鲜总里程字段”的源数据缺口,统计管道缺口仍为 0;系统保留该缺口,不用 4 米静止漂移构造业务里程。
|
||||
|
||||
## 7 月 20 日 05:40 再次复核
|
||||
|
||||
针对“轨迹回放有里程、今日里程没有计算”的再次反馈,按 `2026-07-20 00:00:00` 至 `05:40:00` 重新核对生产轨迹、实时总里程证据、来源候选和最终日里程:
|
||||
|
||||
| 车牌 | 今日轨迹证据 | 今日统计 | 处理结论 |
|
||||
| --- | --- | --- | --- |
|
||||
| 沪A01559F | 宇通 260 个源点、259 个有效点;最高/平均速度均为 0;坐标区间累计 0 km | 宇通 `stationary_carry_forward` 0 km;仪表累计总里程 14,097 km 的最新证据仍是 7 月 19 日 21:23:59 | 已有显式 0 km 统计,无可信正向里程可补 |
|
||||
| 沪A62013F | 今日 0 个源点;7 月 19 日 520 个源点最高速度仍为 0,区间里程 0 km | 7 月 19 日宇通 0 km;累计总里程 24,245 km | 今日没有上报,昨日 0 km 正确,禁止把累计值补成日里程 |
|
||||
|
||||
同一时点全量诊断覆盖 480 个活跃“协议 × 车辆”项,`pipeline_issue_total=0`;仅有 1 个赛格 JT808 项缺少新鲜总里程,属于上游源数据问题,与这两辆车无关。轨迹页点位卡片同步将“累计里程”明确为“累计总里程”,并说明它不是所选时间窗里程或今日里程,避免把点位累计值误读为区间结果。
|
||||
|
||||
## 限制与后续监控
|
||||
|
||||
- GPS 估算只在仪表里程缺失或停滞时兜底,不替代 GB32960、宇通仪表里程或 JT808 设备里程。
|
||||
|
||||
@@ -150,7 +150,7 @@ test('clears old alert rows and inspector evidence when the event scope changes'
|
||||
expect(screen.getByLabelText('告警中心工作区').querySelector('.v2-workspace-command-icon.semi-avatar')).toBeInTheDocument();
|
||||
expect(screen.getByRole('heading', { level: 5, name: '告警处置工作台' })).toBeInTheDocument();
|
||||
expect(screen.getByText('核验事件证据、治理规则并追踪通知状态')).toBeInTheDocument();
|
||||
expect(screen.getByText('可处置事件')).toBeInTheDocument();
|
||||
expect(screen.getByText('具备处置权限')).toBeInTheDocument();
|
||||
expect(screen.queryByRole('heading', { level: 2, name: '告警中心' })).not.toBeInTheDocument();
|
||||
expect(view.container.querySelector('.v2-alert-inspector')).not.toBeInTheDocument();
|
||||
expect(view.container.querySelector('.v2-alert-workspace')).not.toHaveClass('is-inspector-open');
|
||||
@@ -326,7 +326,7 @@ test('uses one focused Semi bottom SideSheet for the complete mobile event filte
|
||||
render(<QueryClientProvider client={client}><MemoryRouter future={ROUTER_FUTURE} initialEntries={['/alerts']}><AlertsPage /></MemoryRouter></QueryClientProvider>);
|
||||
|
||||
await screen.findByText('当前筛选条件没有告警事件');
|
||||
const trigger = screen.getByRole('button', { name: /修改事件筛选/ });
|
||||
const trigger = screen.getByRole('button', { name: '打开告警筛选:全部告警' });
|
||||
expect(trigger).toHaveAttribute('aria-expanded', 'false');
|
||||
fireEvent.click(trigger);
|
||||
|
||||
@@ -346,7 +346,7 @@ test('uses one focused Semi bottom SideSheet for the complete mobile event filte
|
||||
fireEvent.click(within(dialog).getByRole('button', { name: '应用并查询' }));
|
||||
|
||||
await waitFor(() => expect(mocks.alertEventsV2).toHaveBeenLastCalledWith(expect.objectContaining({ protocol: 'JT808' }), expect.anything()));
|
||||
expect(screen.getByRole('button', { name: /修改事件筛选:已启用 1 项/ })).toHaveAttribute('aria-expanded', 'false');
|
||||
expect(screen.getByRole('button', { name: '打开告警筛选:已启用 1 项' })).toHaveAttribute('aria-expanded', 'false');
|
||||
});
|
||||
|
||||
test('keeps mobile rule selection focused and opens editing in a Semi bottom SideSheet', async () => {
|
||||
|
||||
@@ -284,12 +284,13 @@ function EventWorkspace({ filters, draft, setDraft, setFilters, rules, unread, e
|
||||
return <>
|
||||
{mobileLayout ? <div className="v2-alert-mobile-discovery">
|
||||
<MobileFilterToggle
|
||||
title="事件筛选"
|
||||
title="筛选告警"
|
||||
summary={activeFilterCount ? `已启用 ${activeFilterCount} 项` : '全部告警'}
|
||||
expanded={mobileFiltersOpen}
|
||||
controls="v2-alert-mobile-filters"
|
||||
expandedLabel="关闭"
|
||||
collapsedLabel="修改"
|
||||
collapsedLabel="筛选"
|
||||
ariaLabel={`${mobileFiltersOpen ? '关闭' : '打开'}告警筛选:${activeFilterCount ? `已启用 ${activeFilterCount} 项` : '全部告警'}`}
|
||||
onToggle={() => setFiltersCollapsed((value) => !value)}
|
||||
/>
|
||||
<MobileFilterSheet
|
||||
@@ -640,7 +641,7 @@ export default function AlertsPage() {
|
||||
description="核验事件证据、治理规则并追踪通知状态"
|
||||
status={unread ? `${unread.toLocaleString('zh-CN')} 条未读` : '通知已读'}
|
||||
statusColor={unread ? 'orange' : 'green'}
|
||||
meta={<Typography.Text type="tertiary">{operator ? '可处置事件' : '只读查看'}</Typography.Text>}
|
||||
meta={<Typography.Text type="tertiary">{operator ? '具备处置权限' : '仅限查看'}</Typography.Text>}
|
||||
actions={<SegmentedTabs className="v2-alert-tabs" variant="filled" ariaLabel="告警中心分类" value={activeTab} items={tabs} onChange={setTab} />}
|
||||
/>
|
||||
{activeTab !== 'notifications' && rules.isError ? <InlineError message={rules.error.message} onRetry={() => rules.refetch()} /> : null}
|
||||
|
||||
@@ -197,6 +197,8 @@ test('renders a map-first replay workspace and connects stop, event, and panel i
|
||||
expect(view.container.querySelector('.v2-track-current-card.semi-card')).toBeInTheDocument();
|
||||
expect(view.container.querySelector('.v2-track-current-coverage')).toBeInTheDocument();
|
||||
expect(view.container.querySelectorAll('.v2-track-current-metrics > span')).toHaveLength(4);
|
||||
expect(view.container.querySelector('.v2-track-current-metrics')).toHaveTextContent('累计总里程100 km');
|
||||
expect(view.container.querySelector('.v2-track-current-metrics > span:last-child')).toHaveAttribute('title', '当前轨迹点设备上报的累计总里程,不是所选时间窗里程或今日里程');
|
||||
expect(view.container.querySelector('.v2-track-current-evidence')).toHaveTextContent('JT/T 808时间窗完整3 → 3 点报警 —');
|
||||
expect(view.container.querySelector('.v2-track-current-evidence .v2-track-protocol-tag')).toHaveClass('semi-tag-cyan-light');
|
||||
expect(view.container.querySelector('.v2-track-coverage-float')).not.toBeInTheDocument();
|
||||
|
||||
@@ -515,7 +515,7 @@ export default function TrackPage() {
|
||||
<span><small>速度</small><strong>{number(current?.speedKmh ?? 0)}<em> km/h</em></strong></span>
|
||||
<span><small>方向</small><strong>{direction(current?.directionDeg)}</strong></span>
|
||||
<span><small>SOC</small><strong>{current?.socAvailable ? `${number(current.socPercent)}%` : '—'}</strong></span>
|
||||
<span><small>累计里程</small><strong>{trackPointMileageAvailable(current) ? number(current?.totalMileageKm ?? 0) : '—'}{trackPointMileageAvailable(current) ? <em> km</em> : null}</strong></span>
|
||||
<span title="当前轨迹点设备上报的累计总里程,不是所选时间窗里程或今日里程"><small>累计总里程</small><strong>{trackPointMileageAvailable(current) ? number(current?.totalMileageKm ?? 0) : '—'}{trackPointMileageAvailable(current) ? <em> km</em> : null}</strong></span>
|
||||
</div>
|
||||
<div className={`v2-track-current-evidence v2-track-current-coverage${track.coverage.complete ? '' : ' is-warning'}`}>
|
||||
<ProtocolTag className="v2-track-protocol-tag" protocol={current?.protocol} compact />
|
||||
|
||||
@@ -9431,6 +9431,81 @@
|
||||
padding-inline: 7px;
|
||||
}
|
||||
|
||||
@media (min-width: 681px) {
|
||||
.v2-alert-notification-list {
|
||||
padding: 0 10px 10px;
|
||||
}
|
||||
|
||||
.v2-alert-notification-cards.semi-card-group {
|
||||
overflow: hidden;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
gap: 0;
|
||||
border: 1px solid #e1e8f0;
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.v2-alert-notification-card.semi-card {
|
||||
display: grid;
|
||||
min-height: 64px;
|
||||
grid-template-columns: minmax(248px, .72fr) minmax(0, 1.7fr);
|
||||
border: 0;
|
||||
border-bottom: 1px solid #e8edf3;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
contain-intrinsic-size: auto 64px;
|
||||
transition: background-color .16s ease;
|
||||
}
|
||||
|
||||
.v2-alert-notification-card.semi-card:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.v2-alert-notification-card.semi-card:hover {
|
||||
background: #f8fbff;
|
||||
}
|
||||
|
||||
.v2-alert-notification-card.is-unread.semi-card {
|
||||
box-shadow: inset 3px 0 #2f78e8;
|
||||
}
|
||||
|
||||
.v2-alert-notification-card > .semi-card-header {
|
||||
min-height: 64px;
|
||||
border-right: 1px solid #edf1f5;
|
||||
border-bottom: 0;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.v2-alert-notification-card > .semi-card-body {
|
||||
display: grid;
|
||||
min-height: 64px;
|
||||
grid-template-columns: minmax(0, 1fr) 138px;
|
||||
}
|
||||
|
||||
.v2-alert-notification-content {
|
||||
display: -webkit-box;
|
||||
min-height: 64px;
|
||||
overflow: hidden;
|
||||
align-content: center;
|
||||
margin: 0;
|
||||
padding: 12px 14px;
|
||||
line-height: 1.45;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
.v2-alert-notification-card > .semi-card-body > footer {
|
||||
min-height: 64px;
|
||||
justify-content: center;
|
||||
align-items: flex-end;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
border-top: 0;
|
||||
border-left: 1px solid #edf1f5;
|
||||
padding: 7px 9px;
|
||||
}
|
||||
}
|
||||
|
||||
.v2-alert-notification-pagination {
|
||||
display: flex;
|
||||
min-height: 48px;
|
||||
|
||||
Reference in New Issue
Block a user