feat(desktop): 顶栏租户切换器(多租户)—— 切换活跃租户即换工作区+计费
用户属多个租户时,顶栏显示租户下拉(Building2 图标):切换即调 POST /me/tenant 换活跃租户,随后刷新余额芯片(显示新计费租户的可花余额)。仅 >1 租户时出现。 api 加 myTenants/switchTenant。 live 验证(preview):demoB(属公司A+公司B) 切换器显两租户;从公司A(共享,余额15.84) 切到公司B(个人,余额6.38)→ 后端 active 持久化、芯片随之更新为 6.4(橙,偏低)。tsc+48 测试全过。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import type { CSSProperties } from "react";
|
||||
import { User, ChevronDown, Search as SearchIcon, LogOut, Sun, Moon, Coins } from "lucide-react";
|
||||
import type { AuthUser, TenantCtx } from "../lib/api";
|
||||
import { User, ChevronDown, Search as SearchIcon, LogOut, Sun, Moon, Coins, Building2 } from "lucide-react";
|
||||
import type { AuthUser, TenantCtx, MyTenant } from "../lib/api";
|
||||
import { useHealth } from "../lib/health";
|
||||
import { isMacDesktop } from "../lib/desktop";
|
||||
import { useTheme } from "../lib/theme";
|
||||
@@ -42,8 +42,31 @@ function CreditChip({ tenant, onClick }: { tenant: TenantCtx; onClick?: () => vo
|
||||
);
|
||||
}
|
||||
|
||||
// 租户切换器:仅当用户属多个租户时显示;切换即改活跃租户(工作区 + 计费上下文)。
|
||||
function TenantSwitcher({ tenants, activeId, onSwitch }: { tenants: MyTenant[]; activeId?: string; onSwitch?: (id: string) => void }) {
|
||||
if (tenants.length < 2) return null;
|
||||
return (
|
||||
<div className="relative" style={NODRAG}>
|
||||
<Building2 className="pointer-events-none absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-slate-500" />
|
||||
<select
|
||||
value={activeId ?? ""}
|
||||
onChange={(e) => onSwitch?.(e.target.value)}
|
||||
title="切换当前租户(工作区 + 计费)"
|
||||
className="appearance-none rounded-md border border-line bg-ink-800 py-1 pl-7 pr-7 text-xs text-slate-300 focus:border-brand focus:outline-none"
|
||||
>
|
||||
{tenants.map((t) => (
|
||||
<option key={t.id} value={t.id}>
|
||||
{t.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<ChevronDown className="pointer-events-none absolute right-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-slate-500" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 顶栏:品牌 · 垂直切换 · 健康灯 · 积分余额 · 登录用户 + 登出(深色 + 毛玻璃)。
|
||||
export function TopBar({ user, tenant, onLogout, onCommand, onOpenUsage }: { user: AuthUser; tenant?: TenantCtx | null; onLogout: () => void; onCommand?: () => void; onOpenUsage?: () => void }) {
|
||||
export function TopBar({ user, tenant, tenants = [], onSwitchTenant, onLogout, onCommand, onOpenUsage }: { user: AuthUser; tenant?: TenantCtx | null; tenants?: MyTenant[]; onSwitchTenant?: (id: string) => void; onLogout: () => void; onCommand?: () => void; onOpenUsage?: () => void }) {
|
||||
const h = useHealth();
|
||||
const { theme, toggle } = useTheme();
|
||||
return (
|
||||
@@ -86,6 +109,7 @@ export function TopBar({ user, tenant, onLogout, onCommand, onOpenUsage }: { use
|
||||
<Light on={h.neo4j} label="Neo4j" />
|
||||
</div>
|
||||
<div className="ml-auto flex items-center gap-2" style={NODRAG}>
|
||||
<TenantSwitcher tenants={tenants} activeId={tenant?.tenant?.id} onSwitch={onSwitchTenant} />
|
||||
{tenant?.tenant && <CreditChip tenant={tenant} onClick={onOpenUsage} />}
|
||||
<button
|
||||
onClick={toggle}
|
||||
|
||||
Reference in New Issue
Block a user