({ value: item.name, label: `${ruleLabel(item.name)} · ${item.count}` })) ?? [])
]} />
-
- {(page?.total ?? 0).toLocaleString('zh-CN')} 个符合条件的问题
- 按严重程度、最近发现时间排序
-
{issues.isError ?
issues.refetch()} /> : null}
{
expect(workspaceMetricRailSource).toContain("import { Card, CardGroup } from '@douyinfe/semi-ui'");
expect(workspaceMetricRailSource).toContain('
{
expect(reconciliationSource).toContain(' {
expect(view.container.querySelectorAll('.test-card.semi-card')).toHaveLength(3);
expect(screen.getByLabelText('车辆数:20;当前授权范围')).toHaveClass('is-vehicles');
});
+
+ it('renders a list-first queue variant inside one Semi card', () => {
+ const view = render(当前筛选 1,347 条}
+ />);
+
+ const rail = view.container.querySelector('.v2-workspace-metric-rail');
+ expect(rail).toHaveClass('semi-card', 'is-queue', 'queue-metrics');
+ const list = screen.getByRole('list', { name: '处置队列概览' });
+ expect(within(list).getAllByRole('listitem')).toHaveLength(4);
+ expect(within(list).getByText('待复核').closest('[role="listitem"]')).toHaveClass('is-primary');
+ expect(within(list).getByText('SLA 超时').closest('[role="listitem"]')).toHaveClass('is-danger');
+ expect(within(list).getByText('已恢复').closest('[role="listitem"]')).toHaveClass('is-secondary', 'is-success');
+ expect(view.container.querySelector('.v2-workspace-metric-context')).toHaveTextContent('当前筛选 1,347 条');
+ expect(view.container.querySelectorAll('.semi-card')).toHaveLength(1);
+ });
});
diff --git a/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceMetricRail.tsx b/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceMetricRail.tsx
index bb91569f..d4d00525 100644
--- a/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceMetricRail.tsx
+++ b/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceMetricRail.tsx
@@ -8,15 +8,15 @@ export type WorkspaceMetricRailItem = {
className?: string;
};
-export function WorkspaceMetricRail({
- ariaLabel,
- primary,
- secondary,
- className,
- cardClassName,
- secondaryClassName,
- primaryValueClassName
-}: {
+export type WorkspaceQueueMetricRailItem = {
+ label: string;
+ value: ReactNode;
+ note?: ReactNode;
+ tone?: 'neutral' | 'primary' | 'success' | 'warning' | 'danger';
+ emphasis?: 'primary' | 'secondary';
+};
+
+type WorkspaceSummaryMetricRailProps = {
ariaLabel: string;
primary: WorkspaceMetricRailItem;
secondary: WorkspaceMetricRailItem[];
@@ -24,7 +24,47 @@ export function WorkspaceMetricRail({
cardClassName: string;
secondaryClassName: string;
primaryValueClassName?: string;
-}) {
+};
+
+type WorkspaceQueueMetricRailProps = {
+ variant: 'queue';
+ ariaLabel: string;
+ className: string;
+ items: WorkspaceQueueMetricRailItem[];
+ context?: ReactNode;
+};
+
+export function WorkspaceMetricRail(props: WorkspaceSummaryMetricRailProps | WorkspaceQueueMetricRailProps) {
+ if ('items' in props) {
+ return
+
+ {props.items.map((item, index) => {
+ const toneClassName = `is-${item.tone ?? 'neutral'}`;
+ const emphasisClassName = `is-${item.emphasis ?? (index < 2 ? 'primary' : 'secondary')}`;
+ return
+ {item.label}
+ {item.value}
+ {item.note ? {item.note} : null}
+ ;
+ })}
+
+ {props.context ? {props.context}
: null}
+ ;
+ }
+
+ const {
+ ariaLabel,
+ primary,
+ secondary,
+ className,
+ cardClassName,
+ secondaryClassName,
+ primaryValueClassName
+ } = props;
const cardClass = (item: WorkspaceMetricRailItem, primaryCard = false) => [
cardClassName,
primaryCard ? 'is-primary' : '',
diff --git a/vehicle-data-platform/apps/web/src/v2/styles/workspace.css b/vehicle-data-platform/apps/web/src/v2/styles/workspace.css
index 9c508fa5..68ee6608 100644
--- a/vehicle-data-platform/apps/web/src/v2/styles/workspace.css
+++ b/vehicle-data-platform/apps/web/src/v2/styles/workspace.css
@@ -16180,6 +16180,207 @@
min-width: 908px;
}
+/*
+ * Shared Semi UI queue metrics.
+ * Keep operational evidence in one compact rail so the result list remains
+ * the visual and interaction focus.
+ */
+.v2-workspace-metric-rail.is-queue.semi-card {
+ overflow: hidden;
+ border: 0;
+ border-bottom: 1px solid #e1e8f0;
+ border-radius: 0;
+ background: #fff;
+ box-shadow: none;
+}
+
+.v2-workspace-metric-rail.is-queue > .semi-card-body {
+ display: grid;
+ min-height: 58px;
+ grid-template-columns: minmax(0, 1fr) 218px;
+}
+
+.v2-workspace-metric-list {
+ display: grid;
+ min-width: 0;
+ grid-template-columns: 1.15fr 1.15fr .9fr .9fr;
+}
+
+.v2-workspace-metric-list > span {
+ display: grid;
+ min-width: 0;
+ min-height: 58px;
+ grid-template-columns: auto minmax(0, 1fr);
+ grid-template-rows: auto auto;
+ align-content: center;
+ column-gap: 10px;
+ padding: 7px 13px;
+}
+
+.v2-workspace-metric-list > span + span {
+ border-left: 1px solid #e5ebf2;
+}
+
+.v2-workspace-metric-list > span > small {
+ grid-column: 1;
+ color: #78879a;
+ font-size: 9px;
+ font-weight: 650;
+ line-height: 14px;
+ white-space: nowrap;
+}
+
+.v2-workspace-metric-list > span > strong {
+ grid-column: 1;
+ color: #243950;
+ font-size: 21px;
+ font-variant-numeric: tabular-nums;
+ line-height: 24px;
+ white-space: nowrap;
+}
+
+.v2-workspace-metric-list > span > em {
+ align-self: center;
+ grid-column: 2;
+ grid-row: 1 / 3;
+ overflow: hidden;
+ color: #8a97a8;
+ font-size: 9px;
+ font-style: normal;
+ line-height: 14px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.v2-workspace-metric-list > span.is-secondary > strong {
+ font-size: 17px;
+}
+
+.v2-workspace-metric-list > span.is-primary {
+ background: linear-gradient(145deg, #edf5ff 0%, #f8fbff 100%);
+ box-shadow: inset 0 -3px #1f6feb;
+}
+
+.v2-workspace-metric-list > span.is-primary > :is(small, strong) {
+ color: #155fc5;
+}
+
+.v2-workspace-metric-list > span.is-danger {
+ background: linear-gradient(145deg, #fff 0%, #fff8f7 100%);
+ box-shadow: inset 0 -3px #e2554f;
+}
+
+.v2-workspace-metric-list > span.is-danger > :is(small, strong) {
+ color: #c2413d;
+}
+
+.v2-workspace-metric-list > span.is-success > :is(small, strong) {
+ color: #148459;
+}
+
+.v2-workspace-metric-list > span.is-warning > :is(small, strong) {
+ color: #a76512;
+}
+
+.v2-workspace-metric-context {
+ display: flex;
+ min-width: 0;
+ align-items: center;
+ border-left: 1px solid #e5ebf2;
+ background: #fbfcfe;
+ padding: 7px 13px;
+}
+
+.v2-workspace-metric-context > span {
+ display: grid;
+ min-width: 0;
+ grid-template-columns: auto 1fr;
+ align-items: baseline;
+ gap: 1px 6px;
+}
+
+.v2-workspace-metric-context small {
+ color: #8491a2;
+ font-size: 9px;
+}
+
+.v2-workspace-metric-context strong {
+ color: #2f5f9b;
+ font-size: 18px;
+ font-variant-numeric: tabular-nums;
+}
+
+.v2-workspace-metric-context em {
+ grid-column: 1 / -1;
+ overflow: hidden;
+ color: #929eac;
+ font-size: 8px;
+ font-style: normal;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.v2-reconcile-metric-rail {
+ flex: 0 0 auto;
+}
+
+@media (min-width: 681px) {
+ .v2-reconcile-heading.v2-workspace-panel-header {
+ min-height: 62px;
+ padding-block: 9px;
+ }
+
+ .v2-reconcile-toolbar {
+ padding-block: 8px;
+ }
+}
+
+@media (max-width: 900px) {
+ .v2-workspace-metric-rail.is-queue > .semi-card-body {
+ grid-template-columns: minmax(0, 1fr) 180px;
+ }
+
+ .v2-workspace-metric-list > span {
+ column-gap: 6px;
+ padding-inline: 9px;
+ }
+}
+
+@media (max-width: 680px) {
+ .v2-workspace-metric-rail.is-queue > .semi-card-body {
+ min-height: 56px;
+ grid-template-columns: minmax(0, 1fr);
+ }
+
+ .v2-workspace-metric-list {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+
+ .v2-workspace-metric-list > span {
+ min-height: 56px;
+ grid-template-columns: auto minmax(0, 1fr);
+ column-gap: 8px;
+ padding: 6px 12px;
+ }
+
+ .v2-workspace-metric-list > span.is-secondary {
+ display: none;
+ }
+
+ .v2-workspace-metric-list > span > strong {
+ font-size: 19px;
+ line-height: 22px;
+ }
+
+ .v2-workspace-metric-list > span > em {
+ font-size: 8px;
+ }
+
+ .v2-workspace-metric-context {
+ display: none;
+ }
+}
+
.v2-reconcile-issue-cell > span {
display: flex;
min-width: 0;
@@ -21899,10 +22100,35 @@
padding: 8px;
}
+ .v2-reconcile-detail-sidesheet .v2-workspace-config-summary {
+ min-height: 104px;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+
+ .v2-reconcile-detail-sidesheet .v2-workspace-config-summary > span:last-child {
+ min-height: 38px;
+ grid-column: 1 / -1;
+ grid-template-columns: auto auto minmax(0, 1fr);
+ align-items: baseline;
+ align-content: center;
+ column-gap: 8px;
+ padding-block: 6px;
+ }
+
+ .v2-reconcile-detail-sidesheet .v2-workspace-config-summary > span:last-child :is(small, strong, em) {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
.v2-reconcile-detail-sidesheet .v2-workspace-config-summary > span:first-child strong,
.v2-reconcile-detail-sidesheet .v2-workspace-config-summary > span:nth-child(2) strong {
font-size: 11px;
}
+
+ .v2-reconcile-detail-sidesheet .v2-workspace-config-summary > span:last-child strong {
+ font-size: 10px;
+ }
}
@media (max-width: 900px) and (max-height: 480px) and (orientation: landscape) {