refactor(ui): refine semi account governance
This commit is contained in:
@@ -65,6 +65,7 @@ test('deduplicates vehicle candidates by VIN and renders granted vehicles plate
|
||||
expect(directoryMetrics).toHaveTextContent('车辆授权1当前范围合计');
|
||||
expect(customer).toHaveClass('semi-button', 'v2-user-list-item');
|
||||
expect(customer.closest('.semi-list-item')).toHaveClass('v2-user-list-row');
|
||||
expect(customer.querySelector('.v2-user-provider-tag')).toHaveTextContent('本地');
|
||||
expect(customer).toHaveAttribute('aria-pressed', 'false');
|
||||
expect(customer).toHaveAttribute('aria-expanded', 'false');
|
||||
expect(document.querySelector('.v2-user-editor-sidesheet')).not.toBeInTheDocument();
|
||||
|
||||
@@ -113,6 +113,12 @@ function accessBadgeColor(color: CustomerAccessState['color']): WorkspaceSideShe
|
||||
return color;
|
||||
}
|
||||
|
||||
function authProviderMeta(provider?: string) {
|
||||
const normalized = provider?.trim();
|
||||
if (!normalized || normalized === 'local') return { label: '本地', color: 'grey' as const, title: '本地账号' };
|
||||
return { label: normalized, color: 'cyan' as const, title: `外部身份源:${normalized}` };
|
||||
}
|
||||
|
||||
export default function UsersPage() {
|
||||
const queryClient = useQueryClient();
|
||||
const mobileLayout = useMobileLayout();
|
||||
@@ -545,6 +551,7 @@ export default function UsersPage() {
|
||||
emptyContent={<Empty className="v2-user-filter-empty" title="没有匹配账号" description="请调整关键词或访问状态筛选。" />}
|
||||
renderItem={(user) => {
|
||||
const accessState = customerAccessState(user.status, user.menuKeys.length, user.vehicles.length);
|
||||
const authProvider = authProviderMeta(user.authProvider);
|
||||
return <List.Item key={user.id} className={`v2-user-list-row is-access-${accessState.key}${accessState.attention ? ' has-attention' : ''}${selectedID === user.id && !creating ? ' is-active' : ''}`}>
|
||||
<Button
|
||||
className="v2-user-list-item"
|
||||
@@ -556,7 +563,10 @@ export default function UsersPage() {
|
||||
onClick={() => selectCustomer(user)}
|
||||
>
|
||||
<Avatar className="v2-user-avatar" color={user.status === 'enabled' ? 'light-blue' : 'grey'} shape="square" size="small">{user.displayName.slice(0, 1)}</Avatar>
|
||||
<span className="v2-user-list-identity"><b>{user.displayName}</b><small>@{user.username}{user.customerRef ? ` · ${user.customerRef}` : ''}</small></span>
|
||||
<span className="v2-user-list-identity">
|
||||
<span className="v2-user-list-name"><b>{user.displayName}</b><span className="v2-user-provider-hint" title={authProvider.title}><Tag className="v2-user-provider-tag" color={authProvider.color} type="light" size="small">{authProvider.label}</Tag></span></span>
|
||||
<small>@{user.username}{user.customerRef ? ` · ${user.customerRef}` : ''}</small>
|
||||
</span>
|
||||
<span className="v2-user-list-facts is-menu"><small>菜单权限</small><b>{user.menuKeys.length}<i> / {customerMenus.length}</i></b></span>
|
||||
<span className="v2-user-list-facts is-vehicle"><small>车辆权限</small><b>{user.vehicles.length} 辆</b></span>
|
||||
<span className="v2-user-list-facts is-login"><small>最近登录</small><b>{formatTime(user.lastLoginAt)}</b></span>
|
||||
|
||||
@@ -24281,6 +24281,171 @@
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Semi UI account directory convergence.
|
||||
* Customer identities now read as one evidence table on desktop, while
|
||||
* permission counts and identity-provider provenance remain easy to scan.
|
||||
*/
|
||||
.v2-customer-list .semi-list-items {
|
||||
gap: 0;
|
||||
overflow: hidden;
|
||||
border: 1px solid #dfe7f0;
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.v2-customer-list .v2-user-list-row.semi-list-item {
|
||||
min-height: 68px;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #e8edf3;
|
||||
border-radius: 0;
|
||||
background: #fff;
|
||||
box-shadow: none;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.v2-customer-list .v2-user-list-row.semi-list-item:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.v2-customer-list .v2-user-list-row.semi-list-item:hover {
|
||||
border-color: #e8edf3;
|
||||
background: #f7faff;
|
||||
box-shadow: none;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.v2-customer-list .v2-user-list-row.is-active.semi-list-item {
|
||||
border-color: #dce8f8;
|
||||
background: linear-gradient(90deg, #edf5ff 0%, #f8fbff 62%, #fff 100%);
|
||||
box-shadow: inset 3px 0 var(--v2-blue);
|
||||
}
|
||||
|
||||
.v2-customer-list .v2-user-list-row.has-attention.semi-list-item {
|
||||
border-color: #eee6d7;
|
||||
background: linear-gradient(90deg, #fffaf1 0%, #fffdf9 30%, #fff 74%);
|
||||
}
|
||||
|
||||
.v2-customer-list .v2-user-list-row.is-access-disabled.semi-list-item {
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.v2-customer-list .v2-user-list-item.semi-button {
|
||||
min-height: 68px;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.v2-user-list-name {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
}
|
||||
|
||||
.v2-user-list-name > b {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.v2-user-provider-hint {
|
||||
display: inline-flex;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.v2-user-provider-tag.semi-tag {
|
||||
min-width: 36px;
|
||||
height: 20px;
|
||||
flex: 0 0 auto;
|
||||
justify-content: center;
|
||||
border-radius: 999px;
|
||||
padding-inline: 6px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.v2-user-list-facts {
|
||||
min-width: 0;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.v2-user-list-facts.is-menu,
|
||||
.v2-user-list-facts.is-vehicle {
|
||||
min-height: 40px;
|
||||
align-content: center;
|
||||
border: 1px solid #e8edf3;
|
||||
border-radius: 7px;
|
||||
background: #f7f9fc;
|
||||
padding: 5px 8px;
|
||||
}
|
||||
|
||||
.v2-user-list-facts.is-menu b,
|
||||
.v2-user-list-facts.is-vehicle b {
|
||||
color: #2d4966;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.v2-user-list-trailing {
|
||||
gap: 9px;
|
||||
}
|
||||
|
||||
.v2-user-list-trailing > svg {
|
||||
color: #9ba9b9;
|
||||
}
|
||||
|
||||
@media (max-width: 980px) and (min-width: 681px) {
|
||||
.v2-user-list-facts.is-login {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 680px) {
|
||||
.v2-customer-list .semi-list-items {
|
||||
gap: 6px;
|
||||
overflow: visible;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.v2-customer-list .v2-user-list-row.semi-list-item,
|
||||
.v2-customer-list .v2-user-list-row.semi-list-item:last-child {
|
||||
min-height: 76px;
|
||||
border: 1px solid #e2e9f2;
|
||||
border-radius: 9px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.v2-customer-list .v2-user-list-row.is-active.semi-list-item {
|
||||
border-color: #b8d1f3;
|
||||
background: #f2f7ff;
|
||||
}
|
||||
|
||||
.v2-customer-list .v2-user-list-row.has-attention.semi-list-item {
|
||||
border-color: #e7d8ba;
|
||||
background: #fffaf2;
|
||||
}
|
||||
|
||||
.v2-user-provider-tag.semi-tag {
|
||||
min-width: 32px;
|
||||
height: 18px;
|
||||
padding-inline: 5px;
|
||||
font-size: 10px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.v2-user-list-facts.is-menu,
|
||||
.v2-user-list-facts.is-vehicle {
|
||||
min-height: 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Guided Semi UI empty workspaces.
|
||||
* Empty result canvases should explain the task path instead of leaving a
|
||||
|
||||
Reference in New Issue
Block a user