@@ -140,17 +138,20 @@ export function VehicleSourceEvidencePanel({
{expanded && !mobileLayout ? evidenceBody : null}
- {mobileLayout ? 位置与里程来源{description}
}
- footer={null}
+ ariaLabel="位置与里程来源证据"
+ closeLabel="关闭来源证据"
+ title="位置与里程来源"
+ description={description}
+ badge={sourceCount ? `${sourceCount} 个来源` : '按需读取'}
+ badgeColor={sourceCount ? 'blue' : 'grey'}
onCancel={() => setExpanded(false)}
>
{evidenceBody}
- : null}
+ : null}
>;
}
diff --git a/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceDetailSideSheet.test.tsx b/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceDetailSideSheet.test.tsx
new file mode 100644
index 00000000..76580f99
--- /dev/null
+++ b/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceDetailSideSheet.test.tsx
@@ -0,0 +1,29 @@
+import { cleanup, fireEvent, render, screen } from '@testing-library/react';
+import { afterEach, expect, test, vi } from 'vitest';
+import { WorkspaceDetailSideSheet } from './WorkspaceDetailSideSheet';
+
+afterEach(cleanup);
+
+test('renders one accessible Semi detail sheet with shared identity and status', () => {
+ const onCancel = vi.fn();
+ render(
+ 详情内容
+ );
+
+ const dialog = screen.getByRole('dialog', { name: '车辆证据详情' });
+ expect(dialog.closest('.semi-sidesheet')).toHaveClass('v2-workspace-detail-sidesheet', 'test-detail-sheet');
+ expect(screen.getByText('粤A00001 · VIN-001')).toBeInTheDocument();
+ expect(screen.getByText('状态正常').closest('.semi-tag')).toHaveClass('semi-tag-green-light');
+ fireEvent.click(screen.getByRole('button', { name: '关闭证据详情' }));
+ expect(onCancel).toHaveBeenCalledTimes(1);
+});
diff --git a/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceDetailSideSheet.tsx b/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceDetailSideSheet.tsx
new file mode 100644
index 00000000..94c95bcf
--- /dev/null
+++ b/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceDetailSideSheet.tsx
@@ -0,0 +1,55 @@
+import type { ReactNode } from 'react';
+import {
+ WorkspaceSideSheet,
+ type WorkspaceSideSheetBadgeColor
+} from './WorkspaceSideSheet';
+
+type WorkspaceDetailSideSheetProps = {
+ className?: string;
+ visible: boolean;
+ ariaLabel: string;
+ closeLabel?: string;
+ title: ReactNode;
+ description: ReactNode;
+ badge?: ReactNode;
+ badgeColor?: WorkspaceSideSheetBadgeColor;
+ placement?: 'left' | 'right' | 'top' | 'bottom';
+ width?: string | number;
+ height?: string | number;
+ onCancel: () => void;
+ children: ReactNode;
+};
+
+export function WorkspaceDetailSideSheet({
+ className,
+ visible,
+ ariaLabel,
+ closeLabel,
+ title,
+ description,
+ badge,
+ badgeColor,
+ placement,
+ width,
+ height,
+ onCancel,
+ children
+}: WorkspaceDetailSideSheetProps) {
+ return
+ {children}
+ ;
+}
diff --git a/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceSideSheet.tsx b/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceSideSheet.tsx
index 394a7bad..30165ff4 100644
--- a/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceSideSheet.tsx
+++ b/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceSideSheet.tsx
@@ -17,15 +17,18 @@ export type WorkspaceSideSheetSummaryItem = {
tone?: 'neutral' | 'primary' | 'success' | 'warning' | 'danger';
};
-type WorkspaceSideSheetProps = {
+export type WorkspaceSideSheetBadgeColor = 'blue' | 'green' | 'orange' | 'red' | 'grey';
+
+export type WorkspaceSideSheetProps = {
className?: string;
+ variant?: 'config' | 'detail';
visible: boolean;
ariaLabel: string;
closeLabel?: string;
title: ReactNode;
description: ReactNode;
badge?: ReactNode;
- badgeColor?: 'blue' | 'green' | 'orange' | 'red' | 'grey';
+ badgeColor?: WorkspaceSideSheetBadgeColor;
placement?: 'left' | 'right' | 'top' | 'bottom';
width?: string | number;
height?: string | number;
@@ -39,6 +42,7 @@ type WorkspaceSideSheetProps = {
export function WorkspaceSideSheet({
className = '',
+ variant = 'config',
visible,
ariaLabel,
closeLabel = `关闭${ariaLabel}`,
@@ -57,7 +61,7 @@ export function WorkspaceSideSheet({
children
}: WorkspaceSideSheetProps) {
const sheetId = useId();
- const sheetClassName = ['v2-workspace-config-sidesheet', className].filter(Boolean).join(' ');
+ const sheetClassName = [`v2-workspace-${variant}-sidesheet`, className].filter(Boolean).join(' ');
const hasFooter = Boolean(footerNote || secondaryActions.length || primaryAction);
useLayoutEffect(() => {
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 000965a4..b258dbbb 100644
--- a/vehicle-data-platform/apps/web/src/v2/styles/workspace.css
+++ b/vehicle-data-platform/apps/web/src/v2/styles/workspace.css
@@ -20806,6 +20806,72 @@
box-shadow: 0 7px 18px rgba(18, 104, 243, .18);
}
+/*
+ * Shared Semi UI detail SideSheet: preserve each page's evidence content while
+ * keeping object identity, status, surface depth and scrolling consistent.
+ */
+.v2-workspace-detail-sidesheet .semi-sidesheet-inner {
+ overflow: hidden;
+ border-left: 1px solid #d9e3ef;
+ background: #f4f7fb;
+ box-shadow: -22px 0 64px rgba(24, 43, 68, .16);
+}
+
+.v2-workspace-detail-sidesheet .semi-sidesheet-header {
+ min-height: 78px;
+ border-bottom: 1px solid #dfe7f0;
+ background:
+ radial-gradient(circle at 10% 0%, rgba(18, 104, 243, .075), transparent 36%),
+ linear-gradient(180deg, #fff 0%, #fbfcfe 100%);
+ padding: 13px 18px;
+}
+
+.v2-workspace-detail-sidesheet .semi-sidesheet-title {
+ min-width: 0;
+}
+
+.v2-workspace-detail-sidesheet .v2-workspace-config-title strong {
+ font-size: 18px;
+}
+
+.v2-workspace-detail-sidesheet .semi-sidesheet-close {
+ width: 36px;
+ height: 36px;
+ border-radius: 9px;
+}
+
+.v2-workspace-detail-sidesheet .semi-sidesheet-body {
+ min-height: 0;
+ overflow: hidden;
+ background: #f4f7fb;
+ padding: 0;
+}
+
+.v2-workspace-detail-sidesheet .v2-workspace-config-content {
+ padding: 9px;
+ scrollbar-gutter: stable;
+}
+
+.v2-workspace-detail-sidesheet .v2-workspace-config-content > .semi-card {
+ width: 100%;
+ min-height: 100%;
+ border-radius: 11px;
+}
+
+.v2-alert-detail-sidesheet .v2-workspace-config-content,
+.v2-source-evidence-sidesheet .v2-workspace-config-content {
+ padding: 0;
+}
+
+.v2-history-detail-sidesheet .v2-workspace-config-content,
+.v2-access-detail-sidesheet .v2-workspace-config-content {
+ padding: 8px;
+}
+
+.v2-source-evidence-sidesheet .v2-source-evidence-sheet-body > .v2-source-evidence-body {
+ min-height: 100%;
+}
+
.v2-history-column-sidesheet .v2-workspace-config-content {
overflow: hidden;
padding: 0;
@@ -20872,6 +20938,49 @@
}
@media (max-width: 680px) {
+ .v2-workspace-detail-sidesheet.semi-sidesheet-bottom .semi-sidesheet-inner {
+ width: 100% !important;
+ max-width: 100%;
+ border: 1px solid #d8e2ed;
+ border-bottom: 0;
+ border-radius: 18px 18px 0 0;
+ box-shadow: 0 -18px 48px rgba(23, 43, 68, .17);
+ padding-bottom: env(safe-area-inset-bottom);
+ }
+
+ .v2-workspace-detail-sidesheet.semi-sidesheet-bottom .semi-sidesheet-header {
+ min-height: 70px;
+ padding: 10px 12px 10px 14px;
+ }
+
+ .v2-workspace-detail-sidesheet .v2-workspace-config-title {
+ gap: 8px;
+ }
+
+ .v2-workspace-detail-sidesheet .v2-workspace-config-title strong {
+ font-size: 16px;
+ }
+
+ .v2-workspace-detail-sidesheet .v2-workspace-config-title small {
+ max-width: 250px;
+ font-size: 9px;
+ }
+
+ .v2-workspace-detail-sidesheet .v2-workspace-config-title > .semi-tag {
+ min-height: 23px;
+ padding-inline: 8px;
+ font-size: 8px;
+ }
+
+ .v2-workspace-detail-sidesheet .v2-workspace-config-content {
+ padding: 8px;
+ }
+
+ .v2-alert-detail-sidesheet .v2-workspace-config-content,
+ .v2-source-evidence-sidesheet .v2-workspace-config-content {
+ padding: 0;
+ }
+
.v2-workspace-config-sidesheet.semi-sidesheet-bottom .semi-sidesheet-inner {
border: 1px solid #d8e2ed;
border-bottom: 0;