feat: unify governance evidence workbenches

This commit is contained in:
lingniu
2026-07-19 06:51:23 +08:00
parent f2fe5404d9
commit 3b5a66b687
6 changed files with 249 additions and 79 deletions

View File

@@ -1,5 +1,5 @@
import { IconChevronDown, IconChevronRight, IconClose, IconDownload, IconMore, IconRefresh, IconSave, IconSearch, IconSetting } from '@douyinfe/semi-icons';
import { Button, Card, CardGroup, Collapse, Descriptions, Dropdown, Empty, Input, Select, SideSheet, Spin, Table, Tag, Typography } from '@douyinfe/semi-ui';
import { Button, Card, CardGroup, Collapse, Descriptions, Dropdown, Empty, Input, Select, Spin, Table, Tag, Typography } from '@douyinfe/semi-ui';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { FormEvent, KeyboardEvent, memo, useCallback, useEffect, useMemo, useState } from 'react';
import { Link, useSearchParams } from 'react-router-dom';
@@ -15,6 +15,7 @@ import { WorkspaceCommandBar } from '../shared/WorkspaceCommandBar';
import { WorkspaceDetailSideSheet } from '../shared/WorkspaceDetailSideSheet';
import { WorkspaceFilterPanel } from '../shared/WorkspaceFilterPanel';
import { WorkspacePanelHeader } from '../shared/WorkspacePanelHeader';
import { WorkspaceSideSheet } from '../shared/WorkspaceSideSheet';
import { detailTriggerRow } from '../shared/detailTriggerRow';
import { usePlatformSession } from '../auth/AuthGate';
import { canAdminister } from '../auth/session';
@@ -356,7 +357,6 @@ export default function AccessPage() {
}, [mobileLayout]);
const rows = vehiclesQuery.data?.items ?? []; const selected = rows.find((row) => row.vin === selectedVIN);
const mobileFiltersOpen = mobileLayout && !filtersCollapsed;
useSideSheetA11y(editable && governanceOpen, '.v2-access-governance-sidesheet', 'v2-access-governance', '接入治理配置', '关闭接入治理配置');
useSideSheetA11y(mobileFiltersOpen, '.v2-access-mobile-filter-sidesheet', 'v2-access-mobile-filters', '接入车辆筛选', '关闭接入车辆筛选');
const syncURL = (filters: Filters) => { const next = new URLSearchParams(); Object.entries(filters).forEach(([key, value]) => { if (value) next.set(key, value); }); setSearchParams(next, { replace: true }); };
const apply = (next: Filters) => { setDraft(next); setCriteria(next); setOffset(0); setSelectedVIN(''); syncURL(next); };
@@ -505,28 +505,49 @@ export default function AccessPage() {
>
{selected ? <VehicleInspector key={selected.vin} row={selected} onClose={() => setSelectedVIN('')} sheet mobile={mobileLayout} /> : null}
</WorkspaceDetailSideSheet>
{editable ? <SideSheet
{editable ? <WorkspaceSideSheet
className="v2-access-governance-sidesheet"
variant="config"
visible={governanceOpen}
aria-label="接入治理配置"
ariaLabel="接入治理配置"
closeLabel="关闭接入治理配置"
placement={mobileLayout ? 'bottom' : 'right'}
width={mobileLayout ? undefined : 560}
height={mobileLayout ? 'min(86dvh, 760px)' : undefined}
title={<div className="v2-access-sheet-title"><strong></strong><span>线</span></div>}
height={mobileLayout ? 'min(88dvh, 780px)' : undefined}
title="接入治理工作台"
description="核对待绑定来源,统一维护协议在线判定阈值"
badge={(unresolvedQuery.data?.total ?? 0) > 0 ? `${unresolvedQuery.data?.total.toLocaleString('zh-CN')} 项待办` : '治理正常'}
badgeColor={(unresolvedQuery.data?.total ?? 0) > 0 ? 'orange' : 'green'}
summaryItems={[
{
label: '待绑定来源',
value: (unresolvedQuery.data?.total ?? 0).toLocaleString('zh-CN'),
detail: (unresolvedQuery.data?.total ?? 0) > 0 ? '需要核对权威 VIN' : '当前没有身份待办',
tone: (unresolvedQuery.data?.total ?? 0) > 0 ? 'warning' : 'success'
},
{
label: '阈值版本',
value: `v${thresholdQuery.data?.version ?? '—'}`,
detail: '在线与延迟统一口径',
tone: 'primary'
},
{
label: '主车辆口径',
value: (summary?.totalVehicles ?? 0).toLocaleString('zh-CN'),
detail: '仅统计权威主车辆',
tone: 'neutral'
}
]}
footerNote="阈值保存后即时生效;关闭工作台不会丢失已保存配置"
primaryAction={{ label: '完成', onClick: () => setGovernanceOpen(false) }}
onCancel={() => setGovernanceOpen(false)}
footer={<Button theme="solid" onClick={() => setGovernanceOpen(false)}></Button>}
>
{governanceOpen ? <div className="v2-access-governance">
<Card className="v2-access-governance-summary" bodyStyle={{ padding: 0 }}>
<div className={(unresolvedQuery.data?.total ?? 0) > 0 ? 'is-attention' : ''}><small></small><strong>{(unresolvedQuery.data?.total ?? 0).toLocaleString('zh-CN')}</strong><span>{(unresolvedQuery.data?.total ?? 0) > 0 ? '优先处理' : '暂无待办'}</span></div>
<div><small></small><strong>v{thresholdQuery.data?.version ?? '—'}</strong></div>
<div><small></small><strong>{(summary?.totalVehicles ?? 0).toLocaleString('zh-CN')}</strong></div>
</Card>
{unresolvedQuery.isError ? <InlineError message={unresolvedQuery.error instanceof Error ? unresolvedQuery.error.message : '待绑定身份读取失败'} onRetry={() => unresolvedQuery.refetch()} /> : null}
<IdentityQueue items={unresolvedQuery.data?.items ?? []} total={unresolvedQuery.data?.total ?? 0} />
{thresholdQuery.isError ? <InlineError message={thresholdQuery.error instanceof Error ? thresholdQuery.error.message : '接入阈值读取失败'} onRetry={() => thresholdQuery.refetch()} /> : null}
<ThresholdSettings config={thresholdQuery.data} draft={thresholdDraft} editable saving={updateThreshold.isPending} error={updateThreshold.error instanceof Error ? updateThreshold.error.message : undefined} onChange={setThresholdDraft} onSave={() => thresholdDraft && updateThreshold.mutate(thresholdDraft)} />
</div> : null}
</SideSheet> : null}
</WorkspaceSideSheet> : null}
</div>;
}