feat(web): converge workspace identity themes

This commit is contained in:
lingniu
2026-07-20 02:56:13 +08:00
parent 0417056eeb
commit dfa2782db6
7 changed files with 165 additions and 142 deletions

View File

@@ -633,6 +633,7 @@ export default function AlertsPage() {
ariaLabel="告警中心工作区"
eyebrow="主动告警"
icon={<IconAlarm />}
tone="warning"
title="告警处置工作台"
description="核验事件证据、治理规则并追踪通知状态"
status={unread ? `${unread.toLocaleString('zh-CN')} 条未读` : '通知已读'}

View File

@@ -536,6 +536,7 @@ export default function OperationsPage() {
ariaLabel="运维质量操作"
eyebrow="运行治理"
icon={<IconPulse />}
tone="health"
title="运行与数据质量"
description="集中处理自动对账问题,按需诊断单车来源,并核对服务与协议健康;所有结论均可追溯。"
status={data?.runtime.dataMode === 'production' ? '生产模式' : '状态待确认'}

View File

@@ -433,6 +433,7 @@ export default function UsersPage() {
ariaLabel="账号管理操作"
eyebrow="访问治理"
icon={<IconUserGroup />}
tone="authority"
title="客户访问治理"
description="菜单与车辆按最小权限开放,变更最多 30 秒生效"
status={`${customers.length} 个客户账号${attentionCustomerCount ? ` · ${attentionCustomerCount} 待完善` : ' · 权限就绪'}`}

View File

@@ -31,8 +31,24 @@ test('renders a Semi identity treatment for primary workspaces', () => {
description="车牌、VIN 或手机号快速查车"
/>);
expect(screen.getByLabelText('车辆目录操作')).toHaveClass('has-identity');
expect(screen.getByLabelText('车辆目录操作')).toHaveClass('has-identity', 'is-primary');
expect(screen.getByText('车辆目录')).toHaveClass('v2-workspace-command-eyebrow');
expect(screen.getByRole('heading', { name: '车辆快速定位', level: 5 })).toHaveClass('v2-workspace-command-title');
expect(view.container.querySelector('.v2-workspace-command-icon')).toHaveClass('semi-avatar');
});
test('exposes the semantic identity tone without coupling it to the status color', () => {
render(<WorkspaceCommandBar
ariaLabel="告警处置操作"
eyebrow="主动告警"
icon={<IconSearch />}
tone="warning"
title="告警处置工作台"
description="核验事件证据"
status="通知已读"
statusColor="green"
/>);
expect(screen.getByLabelText('告警处置操作')).toHaveClass('has-identity', 'is-warning');
expect(screen.getByText('通知已读').closest('.semi-tag')).toHaveClass('semi-tag-green-light');
});

View File

@@ -3,11 +3,14 @@ import type { ReactNode } from 'react';
const { Text, Title } = Typography;
export type WorkspaceCommandTone = 'primary' | 'warning' | 'authority' | 'health';
export function WorkspaceCommandBar({
title,
description,
eyebrow,
icon,
tone = 'primary',
status,
statusColor = 'blue',
meta,
@@ -19,6 +22,7 @@ export function WorkspaceCommandBar({
description: ReactNode;
eyebrow?: ReactNode;
icon?: ReactNode;
tone?: WorkspaceCommandTone;
status?: string;
statusColor?: React.ComponentProps<typeof Tag>['color'];
meta?: ReactNode;
@@ -27,7 +31,12 @@ export function WorkspaceCommandBar({
ariaLabel?: string;
}) {
const hasIdentity = eyebrow != null || icon != null;
const rootClassName = ['v2-workspace-command-bar', hasIdentity ? 'has-identity' : '', className].filter(Boolean).join(' ');
const rootClassName = [
'v2-workspace-command-bar',
hasIdentity ? 'has-identity' : '',
hasIdentity ? `is-${tone}` : '',
className
].filter(Boolean).join(' ');
return (
<header className={rootClassName} aria-label={ariaLabel}>

View File

@@ -26583,6 +26583,64 @@
* Give high-value workspaces one shared visual grammar: a semantic Semi
* avatar, a quiet eyebrow, a strong task title and supporting context.
*/
.v2-workspace-command-bar.has-identity {
--v2-identity-accent: var(--v2-blue);
--v2-identity-border: rgba(18, 104, 243, .12);
--v2-identity-icon-start: #edf5ff;
--v2-identity-icon-end: #e4efff;
--v2-identity-glow: rgba(18, 104, 243, .1);
--v2-identity-surface: #fbfdff;
--v2-identity-eyebrow: #71839a;
--v2-identity-title: #263a52;
--v2-identity-description: #8290a3;
--v2-identity-tag-border: rgba(18, 104, 243, .08);
--v2-identity-tag-background: #eef5ff;
--v2-identity-tag-color: #245da8;
--v2-identity-shadow: rgba(18, 104, 243, .08);
}
.v2-workspace-command-bar.has-identity.is-warning {
--v2-identity-accent: #d97706;
--v2-identity-border: rgba(230, 126, 34, .13);
--v2-identity-icon-start: #fff6e8;
--v2-identity-icon-end: #ffefd7;
--v2-identity-glow: rgba(245, 158, 11, .1);
--v2-identity-surface: #fffdf9;
--v2-identity-eyebrow: #9a7b52;
--v2-identity-tag-border: rgba(217, 119, 6, .1);
--v2-identity-tag-background: #fff6e8;
--v2-identity-tag-color: #b86505;
--v2-identity-shadow: rgba(217, 119, 6, .07);
}
.v2-workspace-command-bar.has-identity.is-authority {
--v2-identity-accent: #6656c8;
--v2-identity-border: rgba(109, 91, 208, .13);
--v2-identity-icon-start: #f5f2ff;
--v2-identity-icon-end: #eee9ff;
--v2-identity-glow: rgba(109, 91, 208, .1);
--v2-identity-surface: #fdfcff;
--v2-identity-eyebrow: #8177af;
--v2-identity-tag-border: rgba(109, 91, 208, .1);
--v2-identity-tag-background: #f4f1ff;
--v2-identity-tag-color: #6656c8;
--v2-identity-shadow: rgba(109, 91, 208, .07);
}
.v2-workspace-command-bar.has-identity.is-health {
--v2-identity-accent: #0f8f68;
--v2-identity-border: rgba(16, 148, 110, .13);
--v2-identity-icon-start: #edfbf6;
--v2-identity-icon-end: #e3f7ef;
--v2-identity-glow: rgba(16, 148, 110, .1);
--v2-identity-surface: #fbfffd;
--v2-identity-eyebrow: #5f8e7d;
--v2-identity-tag-border: rgba(16, 148, 110, .1);
--v2-identity-tag-background: #eaf8f3;
--v2-identity-tag-color: #0f7d5e;
--v2-identity-shadow: rgba(16, 148, 110, .07);
}
.v2-workspace-command-bar.has-identity .v2-workspace-command-copy {
display: flex;
min-width: 0;
@@ -26594,11 +26652,11 @@
width: 36px;
height: 36px;
flex: 0 0 36px;
border: 1px solid rgba(18, 104, 243, .12);
border: 1px solid var(--v2-identity-border);
border-radius: 10px;
background: linear-gradient(145deg, #edf5ff 0%, #e4efff 100%);
color: var(--v2-blue);
box-shadow: inset 0 1px rgba(255, 255, 255, .86), 0 5px 14px rgba(18, 104, 243, .08);
background: linear-gradient(145deg, var(--v2-identity-icon-start) 0%, var(--v2-identity-icon-end) 100%);
color: var(--v2-identity-accent);
box-shadow: inset 0 1px rgba(255, 255, 255, .86), 0 5px 14px var(--v2-identity-shadow);
}
.v2-workspace-command-stack {
@@ -26609,7 +26667,7 @@
.v2-workspace-command-eyebrow.semi-typography {
overflow: hidden;
color: #7d8c9f;
color: var(--v2-identity-eyebrow);
font-size: 8px;
font-weight: 700;
letter-spacing: .08em;
@@ -26620,7 +26678,7 @@
.v2-workspace-command-stack > .v2-workspace-command-title.semi-typography {
overflow: hidden;
color: #21374f;
color: var(--v2-identity-title);
font-size: 14px;
font-weight: 750;
letter-spacing: -.015em;
@@ -26631,7 +26689,7 @@
.v2-workspace-command-stack > .v2-workspace-command-description.semi-typography {
overflow: hidden;
color: #7b8a9d;
color: var(--v2-identity-description);
font-size: 9px;
font-weight: 450;
line-height: 13px;
@@ -26639,20 +26697,20 @@
white-space: nowrap;
}
.v2-workspace-command-bar.has-identity .v2-workspace-command-actions > .semi-tag {
border-color: var(--v2-identity-tag-border);
background: var(--v2-identity-tag-background);
color: var(--v2-identity-tag-color);
}
.v2-vehicle-discovery-shell > .v2-vehicle-command-bar.has-identity {
min-height: 58px;
background:
radial-gradient(circle at 88% -80%, rgba(18, 104, 243, .1), transparent 44%),
linear-gradient(105deg, #fff 0%, #fbfdff 100%);
radial-gradient(circle at 88% -80%, var(--v2-identity-glow), transparent 44%),
linear-gradient(105deg, #fff 0%, var(--v2-identity-surface) 100%);
padding: 8px 10px 8px 12px;
}
.v2-vehicle-discovery-shell > .v2-vehicle-command-bar.has-identity .v2-workspace-command-actions > .semi-tag {
border-color: rgba(18, 104, 243, .08);
background: #eef5ff;
color: #245da8;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.has-identity .v2-workspace-command-copy {
display: flex;
min-height: 94px;
@@ -26759,8 +26817,8 @@
.v2-track-query-panel .v2-track-query-header.has-identity.v2-workspace-command-bar {
min-height: 62px;
background:
radial-gradient(circle at 12% -80%, rgba(18, 104, 243, .12), transparent 52%),
linear-gradient(108deg, #fff 0%, #fbfdff 100%);
radial-gradient(circle at 12% -80%, var(--v2-identity-glow), transparent 52%),
linear-gradient(108deg, #fff 0%, var(--v2-identity-surface) 100%);
padding: 8px 7px 8px 10px;
}
@@ -26782,20 +26840,17 @@
}
.v2-track-query-header.has-identity .v2-workspace-command-eyebrow {
color: #71839a;
font-size: 7px;
line-height: 9px;
}
.v2-track-query-header.has-identity .v2-workspace-command-title {
color: #263a52;
font-size: 14px;
font-weight: 760;
line-height: 17px;
}
.v2-track-query-header.has-identity .v2-workspace-command-description {
color: #8492a4;
font-size: 8px;
line-height: 11px;
}
@@ -26805,17 +26860,11 @@
gap: 2px;
}
.v2-track-query-header.has-identity .v2-workspace-command-actions > .semi-tag {
border-color: rgba(18, 104, 243, .08);
background: #eef5ff;
color: #245da8;
}
.v2-history-discovery-shell > .v2-history-command-bar.has-identity {
min-height: 58px;
background:
radial-gradient(circle at 91% -100%, rgba(18, 104, 243, .1), transparent 42%),
linear-gradient(105deg, #fff 0%, #fbfdff 100%);
radial-gradient(circle at 91% -100%, var(--v2-identity-glow), transparent 42%),
linear-gradient(105deg, #fff 0%, var(--v2-identity-surface) 100%);
padding: 8px 10px 8px 12px;
}
@@ -26836,24 +26885,16 @@
}
.v2-history-command-bar.has-identity .v2-workspace-command-title {
color: #263a52;
font-size: 14px;
font-weight: 760;
line-height: 18px;
}
.v2-history-command-bar.has-identity .v2-workspace-command-description {
color: #8290a3;
font-size: 9px;
line-height: 12px;
}
.v2-history-command-bar.has-identity .v2-workspace-command-actions > .semi-tag {
border-color: rgba(18, 104, 243, .08);
background: #eef5ff;
color: #245da8;
}
@media (min-width: 681px) and (max-width: 980px) {
.v2-history-command-bar.has-identity .v2-workspace-command-copy {
min-width: 190px;
@@ -26895,8 +26936,8 @@
.v2-alert-command-bar.has-identity.v2-workspace-command-bar {
min-height: 60px;
background:
radial-gradient(circle at 8% -90%, rgba(245, 158, 11, .1), transparent 48%),
linear-gradient(108deg, #fff 0%, #fffdf9 52%, #fbfdff 100%);
radial-gradient(circle at 8% -90%, var(--v2-identity-glow), transparent 48%),
linear-gradient(108deg, #fff 0%, var(--v2-identity-surface) 52%, #fbfdff 100%);
padding: 7px 8px 7px 11px;
}
@@ -26910,11 +26951,7 @@
width: 34px;
height: 34px;
flex-basis: 34px;
border-color: rgba(230, 126, 34, .13);
border-radius: 10px;
background: linear-gradient(145deg, #fff6e8 0%, #ffefd7 100%);
color: #d97706;
box-shadow: inset 0 1px rgba(255, 255, 255, .9), 0 5px 14px rgba(217, 119, 6, .07);
}
.v2-alert-command-bar.has-identity .v2-workspace-command-stack {
@@ -26922,13 +26959,11 @@
}
.v2-alert-command-bar.has-identity .v2-workspace-command-eyebrow {
color: #9a7b52;
font-size: 7px;
line-height: 10px;
}
.v2-alert-command-bar.has-identity .v2-workspace-command-title {
color: #334155;
font-size: 14px;
font-weight: 760;
line-height: 18px;
@@ -26936,22 +26971,15 @@
.v2-alert-command-bar.has-identity .v2-workspace-command-description {
max-width: 280px;
color: #8793a3;
font-size: 9px;
line-height: 12px;
}
.v2-alert-command-bar.has-identity .v2-workspace-command-actions > .semi-tag {
border-color: rgba(217, 119, 6, .1);
background: #fff6e8;
color: #b86505;
}
.v2-access-discovery-shell > .v2-access-command-bar.has-identity {
min-height: 58px;
background:
radial-gradient(circle at 91% -100%, rgba(18, 104, 243, .1), transparent 43%),
linear-gradient(105deg, #fff 0%, #fbfdff 100%);
radial-gradient(circle at 91% -100%, var(--v2-identity-glow), transparent 43%),
linear-gradient(105deg, #fff 0%, var(--v2-identity-surface) 100%);
padding: 8px 10px 8px 12px;
}
@@ -26972,24 +27000,16 @@
}
.v2-access-command-bar.has-identity .v2-workspace-command-title {
color: #263a52;
font-size: 14px;
font-weight: 760;
line-height: 18px;
}
.v2-access-command-bar.has-identity .v2-workspace-command-description {
color: #8290a3;
font-size: 9px;
line-height: 12px;
}
.v2-access-command-bar.has-identity .v2-workspace-command-actions > .semi-tag {
border-color: rgba(18, 104, 243, .08);
background: #eef5ff;
color: #245da8;
}
@media (min-width: 681px) and (max-width: 980px) {
.v2-alert-command-bar.has-identity .v2-workspace-command-copy {
max-width: 250px;
@@ -27058,8 +27078,8 @@
.v2-user-discovery-shell > .v2-user-command-bar.has-identity {
min-height: 58px;
background:
radial-gradient(circle at 9% -100%, rgba(109, 91, 208, .1), transparent 46%),
linear-gradient(106deg, #fff 0%, #fdfcff 52%, #fbfdff 100%);
radial-gradient(circle at 9% -100%, var(--v2-identity-glow), transparent 46%),
linear-gradient(106deg, #fff 0%, var(--v2-identity-surface) 52%, #fbfdff 100%);
padding: 8px 10px 8px 12px;
}
@@ -27071,11 +27091,7 @@
width: 34px;
height: 34px;
flex-basis: 34px;
border-color: rgba(109, 91, 208, .13);
border-radius: 10px;
background: linear-gradient(145deg, #f5f2ff 0%, #eee9ff 100%);
color: #6656c8;
box-shadow: inset 0 1px rgba(255, 255, 255, .9), 0 5px 14px rgba(109, 91, 208, .07);
}
.v2-user-command-bar.has-identity .v2-workspace-command-stack {
@@ -27083,35 +27099,26 @@
}
.v2-user-command-bar.has-identity .v2-workspace-command-eyebrow {
color: #8177af;
font-size: 7px;
line-height: 10px;
}
.v2-user-command-bar.has-identity .v2-workspace-command-title {
color: #334155;
font-size: 14px;
font-weight: 760;
line-height: 18px;
}
.v2-user-command-bar.has-identity .v2-workspace-command-description {
color: #8793a3;
font-size: 9px;
line-height: 12px;
}
.v2-user-command-bar.has-identity .v2-workspace-command-actions > .semi-tag {
border-color: rgba(109, 91, 208, .1);
background: #f4f1ff;
color: #6656c8;
}
.v2-ops-page > .v2-ops-command-bar.has-identity {
min-height: 60px;
background:
radial-gradient(circle at 8% -100%, rgba(16, 148, 110, .1), transparent 47%),
linear-gradient(108deg, #fff 0%, #fbfffd 52%, #fbfdff 100%);
radial-gradient(circle at 8% -100%, var(--v2-identity-glow), transparent 47%),
linear-gradient(108deg, #fff 0%, var(--v2-identity-surface) 52%, #fbfdff 100%);
padding: 8px 10px 8px 12px;
}
@@ -27123,11 +27130,7 @@
width: 34px;
height: 34px;
flex-basis: 34px;
border-color: rgba(16, 148, 110, .13);
border-radius: 10px;
background: linear-gradient(145deg, #edfbf6 0%, #e3f7ef 100%);
color: #0f8f68;
box-shadow: inset 0 1px rgba(255, 255, 255, .9), 0 5px 14px rgba(16, 148, 110, .07);
}
.v2-ops-command-bar.has-identity .v2-workspace-command-stack {
@@ -27135,13 +27138,11 @@
}
.v2-ops-command-bar.has-identity .v2-workspace-command-eyebrow {
color: #5f8e7d;
font-size: 7px;
line-height: 10px;
}
.v2-ops-command-bar.has-identity .v2-workspace-command-title {
color: #334155;
font-size: 14px;
font-weight: 760;
line-height: 18px;
@@ -27149,17 +27150,10 @@
.v2-ops-command-bar.has-identity .v2-workspace-command-description {
max-width: 520px;
color: #8793a3;
font-size: 9px;
line-height: 12px;
}
.v2-ops-command-bar.has-identity .v2-workspace-command-actions > .semi-tag {
border-color: rgba(16, 148, 110, .1);
background: #eaf8f3;
color: #0f7d5e;
}
@media (min-width: 681px) and (max-width: 980px) {
.v2-user-command-bar.has-identity .v2-workspace-command-copy,
.v2-ops-command-bar.has-identity .v2-workspace-command-copy {