diff --git a/vehicle-data-platform/apps/web/src/v2/pages/UsersPage.test.tsx b/vehicle-data-platform/apps/web/src/v2/pages/UsersPage.test.tsx
index 89d215c7..945818d4 100644
--- a/vehicle-data-platform/apps/web/src/v2/pages/UsersPage.test.tsx
+++ b/vehicle-data-platform/apps/web/src/v2/pages/UsersPage.test.tsx
@@ -47,12 +47,17 @@ test('deduplicates vehicle candidates by VIN and renders granted vehicles plate
expect(screen.getByLabelText('账号管理操作')).toHaveTextContent('客户访问治理');
expect(screen.getByLabelText('账号管理操作')).toHaveTextContent('变更最多 30 秒生效');
expect(view.container.querySelector('.v2-page-heading')).not.toBeInTheDocument();
+ expect(view.container.querySelector('.v2-user-discovery-shell')).toContainElement(screen.getByLabelText('账号管理操作'));
expect(screen.getByRole('heading', { name: '账号范围', level: 5 })).toBeInTheDocument();
expect(screen.getByText('按客户名称、登录账号、客户标识与启停状态查找')).toBeInTheDocument();
expect(screen.getAllByText('1 / 1 个账号')).toHaveLength(2);
expect(view.container.querySelector('.v2-user-list.semi-card')).toBeInTheDocument();
expect(view.container.querySelector('.v2-user-filter-panel.v2-workspace-filter-panel.semi-card')).toBeInTheDocument();
expect(view.container.querySelector('.v2-customer-list.semi-list')).toBeInTheDocument();
+ expect(screen.getByRole('row', { name: '客户账号目录列' })).toHaveTextContent('客户账号菜单权限车辆权限最近登录状态');
+ expect(screen.getByText('启用 1')).toBeInTheDocument();
+ expect(screen.getByText('车辆授权 1')).toBeInTheDocument();
+ expect(view.container.querySelector('.v2-user-directory-metrics')).not.toBeInTheDocument();
expect(customer).toHaveClass('semi-button', 'v2-user-list-item');
expect(customer.closest('.semi-list-item')).toHaveClass('v2-user-list-row');
expect(customer).toHaveAttribute('aria-pressed', 'false');
@@ -235,7 +240,8 @@ test('filters the customer directory by status and exposes the active result sco
await waitFor(() => expect(screen.queryByRole('button', { name: /选择客户 华东客户/ })).not.toBeInTheDocument());
expect(screen.getByRole('button', { name: /选择客户 西部客户/ })).toBeInTheDocument();
expect(screen.getAllByText('1 / 2 个账号')).toHaveLength(2);
- expect(screen.getByText('启用账号').nextSibling).toHaveTextContent('0');
+ expect(screen.getByText('启用 0')).toBeInTheDocument();
+ expect(screen.getByText('车辆授权 0')).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: '清空账号筛选' }));
await waitFor(() => expect(screen.getByRole('button', { name: /选择客户 华东客户/ })).toBeInTheDocument());
diff --git a/vehicle-data-platform/apps/web/src/v2/pages/UsersPage.tsx b/vehicle-data-platform/apps/web/src/v2/pages/UsersPage.tsx
index 21dcc6ff..186cd0c4 100644
--- a/vehicle-data-platform/apps/web/src/v2/pages/UsersPage.tsx
+++ b/vehicle-data-platform/apps/web/src/v2/pages/UsersPage.tsx
@@ -256,75 +256,83 @@ export default function UsersPage() {
};
return
-
本地账号 · 可扩展外部身份源}
- actions={} aria-label="新建客户账号" onClick={startCreate}>新建客户账号}
- />
- setFiltersCollapsed((value) => !value)}
- >
-
-
-
- }
- aria-label="清空账号筛选"
- disabled={!customerKeyword.trim() && customerStatus === 'all'}
- onClick={() => {
- setCustomerKeyword('');
- setCustomerStatus('all');
- }}
- >
- 清空
-
-
-
+
{visibleCustomers.length} / {customers.length} 个账号}
+ meta={
+ {visibleCustomers.length} / {customers.length} 个账号
+ 启用 {enabledCustomers}
+ 车辆授权 {grantedVehicles}
+ }
/>
-
- 启用账号{enabledCustomers}
- 停用账号{customers.length - enabledCustomers}
- 车辆授权{grantedVehicles}
-
{users.isPending ?
: customers.length === 0 ?
创建第一个客户账号} /> : <>
+ {visibleCustomers.length ?
+ 客户账号
+ 菜单权限
+ 车辆权限
+ 最近登录
+ 状态
+
: null}
selectCustomer(user)}
>
{user.displayName.slice(0, 1)}
- {user.displayName}@{user.username}
- 菜单权限{user.menuKeys.length} 项
- 车辆权限{user.vehicles.length} 辆
+ {user.displayName}@{user.username}{user.customerRef ? ` · ${user.customerRef}` : ''}
+ 菜单权限{user.menuKeys.length} 项
+ 车辆权限{user.vehicles.length} 辆
最近登录{formatTime(user.lastLoginAt)}
{user.status === 'enabled' ? '启用' : '停用'}
diff --git a/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts b/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts
index 882191d5..05c68122 100644
--- a/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts
+++ b/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts
@@ -237,7 +237,9 @@ describe('V2 production entry', () => {
expect(corePageSources.UsersPage).toContain(' .v2-workspace-command-bar {
+ width: 100%;
+ min-height: 50px;
+ border: 0;
+ border-bottom: 1px solid #e6ecf3;
+ border-radius: 14px 14px 0 0;
+ box-shadow: none;
+}
+
+.v2-user-discovery-shell > .v2-user-filter-panel.v2-workspace-filter-panel.semi-card {
+ width: 100%;
+ overflow: visible;
+ border: 0;
+ border-radius: 0 0 14px 14px;
+ box-shadow: none;
+}
+
+.v2-user-discovery-shell .v2-user-filter-form {
+ min-height: 54px;
+ border-radius: 0 0 14px 14px;
+ background: #fbfcfe;
+}
+
+.v2-user-admin-grid {
+ min-height: 0;
+ flex: 0 0 auto;
+ align-items: start;
+}
+
+.v2-user-list.semi-card {
+ height: auto;
+ min-height: 0;
+ flex: 0 0 auto;
+ border-radius: 12px;
+ box-shadow: 0 8px 26px rgba(31, 50, 76, .05);
+}
+
+.v2-user-list > .semi-card-body {
+ height: auto;
+ min-height: 0;
+}
+
+.v2-user-directory-header.v2-workspace-panel-header {
+ min-height: 58px;
+ padding: 9px 14px;
+}
+
+.v2-user-directory-summary {
+ display: flex;
+ min-width: 0;
+ align-items: center;
+ gap: 12px;
+}
+
+.v2-user-directory-summary > .semi-tag {
+ min-height: 24px;
+ flex: 0 0 auto;
+ border-radius: 999px;
+ padding-inline: 9px;
+ font-size: 10px;
+ font-weight: 650;
+}
+
+.v2-user-directory-summary > .semi-typography {
+ position: relative;
+ margin: 0;
+ padding-left: 12px;
+ color: #718197;
+ font-size: 10px;
+ font-weight: 600;
+ white-space: nowrap;
+}
+
+.v2-user-directory-summary > .semi-typography::before {
+ position: absolute;
+ top: 50%;
+ left: 0;
+ width: 1px;
+ height: 14px;
+ background: #e0e7f0;
+ content: '';
+ transform: translateY(-50%);
+}
+
+.v2-user-directory-body {
+ display: block;
+ min-height: 0;
+ flex: 0 0 auto;
+ padding: 0 10px 10px;
+}
+
+.v2-user-directory-columns {
+ display: grid;
+ min-height: 34px;
+ grid-template-columns: 36px minmax(170px, 1.45fr) minmax(70px, .45fr) minmax(70px, .45fr) minmax(150px, .9fr) auto;
+ align-items: center;
+ gap: 10px;
+ border-bottom: 1px solid #e6ecf3;
+ padding: 0 10px;
+ color: #8a97a8;
+ font-size: 9px;
+ font-weight: 650;
+ letter-spacing: .02em;
+}
+
+.v2-user-directory-columns > span:first-child {
+ grid-column: 1 / 3;
+}
+
+.v2-user-directory-columns > span:last-child {
+ min-width: 72px;
+ text-align: right;
+}
+
+.v2-customer-list-scroll {
+ flex: 0 0 auto;
+ overflow: visible;
+ padding-top: 7px;
+}
+
+.v2-customer-list .semi-list-items {
+ gap: 6px;
+}
+
+.v2-customer-list .v2-user-list-row.semi-list-item {
+ min-height: 64px;
+ border: 1px solid #e5ebf2;
+ border-radius: 9px;
+ background: #fbfcfe;
+}
+
+.v2-customer-list .v2-user-list-row.semi-list-item:hover {
+ border-color: #cfdceb;
+ background: #fff;
+ box-shadow: 0 7px 18px rgba(35, 57, 84, .055);
+ transform: translateY(-1px);
+}
+
+.v2-customer-list .v2-user-list-row.is-active.semi-list-item {
+ border-color: #a9c9f7;
+ background: linear-gradient(110deg, #edf5ff 0%, #f9fbff 100%);
+ box-shadow: inset 3px 0 var(--v2-blue), 0 7px 18px rgba(18, 104, 243, .07);
+}
+
+.v2-customer-list .v2-user-list-item.semi-button {
+ min-height: 64px;
+ padding: 8px 10px;
+}
+
+.v2-customer-list .v2-user-list-item .semi-button-content {
+ grid-template-columns: 36px minmax(170px, 1.45fr) minmax(70px, .45fr) minmax(70px, .45fr) minmax(150px, .9fr) auto;
+ gap: 10px;
+}
+
+.v2-user-avatar.semi-avatar {
+ width: 36px;
+ height: 36px;
+ border: 1px solid rgba(18, 104, 243, .08);
+ border-radius: 9px;
+ font-size: 13px;
+ font-weight: 750;
+}
+
+.v2-user-list-identity {
+ gap: 3px;
+}
+
+.v2-user-list-identity b {
+ color: #253a52;
+ font-size: 13px;
+ font-weight: 720;
+}
+
+.v2-user-list-identity small {
+ overflow: hidden;
+ color: #8390a1;
+ font-size: 9px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.v2-user-list-facts {
+ display: block;
+}
+
+.v2-user-list-facts small {
+ display: none;
+}
+
+.v2-user-list-facts b {
+ color: #4c6077;
+ font-size: 11px;
+ font-weight: 650;
+}
+
+.v2-user-list-facts.is-login b {
+ color: #6d7d91;
+ font-size: 10px;
+ font-variant-numeric: tabular-nums;
+}
+
+.v2-user-list-trailing {
+ min-width: 72px;
+ justify-content: flex-end;
+ gap: 7px;
+}
+
+.v2-user-list-trailing > svg {
+ color: #9aa7b7;
+ font-size: 14px;
+}
+
+.v2-user-status-tag.semi-tag {
+ min-width: 42px;
+ justify-content: center;
+ border-radius: 999px;
+ font-size: 9px;
+ font-weight: 700;
+}
+
+.v2-user-list-loading,
+.v2-user-list-empty.semi-empty {
+ min-height: 280px;
+}
+
+.v2-user-filter-empty.semi-empty {
+ min-height: 230px;
+}
+
+@media (max-width: 980px) and (min-width: 681px) {
+ .v2-user-directory-columns,
+ .v2-customer-list .v2-user-list-item .semi-button-content {
+ grid-template-columns: 36px minmax(150px, 1fr) 70px 70px auto;
+ }
+
+ .v2-user-directory-columns > span:nth-child(4) {
+ display: none;
+ }
+}
+
+@media (max-width: 680px) {
+ .v2-user-admin {
+ height: 100%;
+ gap: 7px;
+ overflow-x: hidden;
+ overflow-y: auto;
+ padding: 7px 7px 12px;
+ scrollbar-gutter: auto;
+ -webkit-overflow-scrolling: touch;
+ }
+
+ .v2-user-discovery-shell {
+ display: contents;
+ }
+
+ .v2-user-discovery-shell > .v2-workspace-command-bar {
+ min-height: 48px;
+ border: 1px solid #dce6f2;
+ border-radius: 10px;
+ background: linear-gradient(105deg, #fff 0%, #f8fbff 100%);
+ box-shadow: 0 6px 20px rgba(31, 50, 76, .035);
+ }
+
+ .v2-user-discovery-shell > .v2-user-filter-panel.v2-workspace-filter-panel.semi-card {
+ border: 1px solid #dce6f2;
+ border-radius: 10px;
+ box-shadow: var(--v2-shadow);
+ }
+
+ .v2-user-discovery-shell .v2-user-filter-form {
+ border-radius: 0 0 10px 10px;
+ background: #fff;
+ }
+
+ .v2-user-admin-grid {
+ flex: 0 0 auto;
+ overflow: visible;
+ }
+
+ .v2-user-list.semi-card {
+ height: auto;
+ overflow: hidden;
+ border-radius: 10px;
+ }
+
+ .v2-user-directory-header.v2-workspace-panel-header {
+ min-height: 50px;
+ padding: 7px 10px;
+ }
+
+ .v2-user-directory-summary {
+ gap: 0;
+ }
+
+ .v2-user-directory-summary > .semi-typography {
+ display: none;
+ }
+
+ .v2-user-directory-body {
+ padding: 0 7px 7px;
+ }
+
+ .v2-user-directory-columns {
+ display: none;
+ }
+
+ .v2-customer-list-scroll {
+ overflow: visible;
+ padding-top: 7px;
+ }
+
+ .v2-customer-list .v2-user-list-row.semi-list-item {
+ min-height: 74px;
+ }
+
+ .v2-customer-list .v2-user-list-item.semi-button {
+ min-height: 74px;
+ padding: 8px;
+ }
+
+ .v2-user-list-facts {
+ display: grid;
+ gap: 2px;
+ }
+
+ .v2-user-list-facts small {
+ display: block;
+ }
+
+ .v2-user-list-facts b {
+ font-size: 10px;
+ }
+
+ .v2-user-list-trailing {
+ min-width: 0;
+ }
+
+ .v2-user-list-loading,
+ .v2-user-list-empty.semi-empty,
+ .v2-user-filter-empty.semi-empty {
+ min-height: 190px;
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .v2-customer-list .v2-user-list-row.semi-list-item {
+ transition: none;
+ }
+}
+
/* Semi UI workspace migration: global monitor command bar and fleet overview. */
.v2-filterbar.semi-card {
border-color: #dce5ef;