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:
Blizzard
2026-07-08 11:02:36 +08:00
parent b388915fff
commit 248267984c
3 changed files with 71 additions and 5 deletions
+26
View File
@@ -139,6 +139,32 @@ export async function tenantCurrent(): Promise<TenantCtx | null> {
};
}
// ---- 我所属的租户 + 切换活跃租户(多租户)----
export interface MyTenant {
id: string;
name: string;
slug: string;
plan: string;
credit_balance_micro: number;
shared_billing: boolean;
members: number;
}
export async function myTenants(): Promise<{ tenants: MyTenant[]; active: string }> {
const res = guard401(await fetch(`${GATEWAY}/api/v1/me/tenants`, { headers: bearer() }));
if (!res.ok) return { tenants: [], active: "" };
const d = (await res.json()) as { tenants?: MyTenant[]; active?: string };
return { tenants: d.tenants ?? [], active: d.active ?? "" };
}
export async function switchTenant(tenantId: string): Promise<void> {
const res = guard401(await fetch(`${GATEWAY}/api/v1/me/tenant`, { method: "POST", headers: { "Content-Type": "application/json", ...bearer() }, body: JSON.stringify({ tenant_id: tenantId }) }));
if (!res.ok) {
const d = (await res.json().catch(() => ({}))) as { error?: string };
throw new Error(d.error ?? `switch failed: ${res.status}`);
}
}
// ---- 我的用量明细(当前用户自己租户:余额 + 按天趋势 + 最近消耗)----
export interface UsageDay {
day: string; // YYYYMMDD