feat: 优化web
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { introspectAccessToken } from '@/lib/api/auth';
|
||||
import { iamUser } from '@/lib/api/iam';
|
||||
import type { IamUser } from '@/lib/api/types/user';
|
||||
import { useAuthStore } from '@/stores/auth-store';
|
||||
import { useTenantStore } from '@/stores/tenant-store';
|
||||
|
||||
type UserProfileState = {
|
||||
profile: IamUser | null;
|
||||
userSub: string | null;
|
||||
loading: boolean;
|
||||
label: string;
|
||||
};
|
||||
|
||||
function displayLabel(profile: IamUser | null, userSub: string | null, loading: boolean): string {
|
||||
if (loading) return '加载中…';
|
||||
if (profile) {
|
||||
const n = profile.real_name?.trim() || profile.user_name?.trim();
|
||||
if (n) return n;
|
||||
}
|
||||
if (userSub) return userSub.length > 12 ? `${userSub.slice(0, 10)}…` : userSub;
|
||||
return '用户';
|
||||
}
|
||||
|
||||
/** 从 UserMenu 提取的用户资料获取逻辑 */
|
||||
export function useUserProfile(): UserProfileState {
|
||||
const accessToken = useAuthStore((s) => s.accessToken);
|
||||
const [profile, setProfile] = useState<IamUser | null>(null);
|
||||
const [userSub, setUserSub] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!accessToken) {
|
||||
setProfile(null);
|
||||
setUserSub(null);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
setLoading(true);
|
||||
setProfile(null);
|
||||
setUserSub(null);
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const intro = await introspectAccessToken(accessToken);
|
||||
if (cancelled) return;
|
||||
if (!intro.active || !intro.sub) {
|
||||
setUserSub(null);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
setUserSub(intro.sub);
|
||||
try {
|
||||
const u = await iamUser.get(intro.sub);
|
||||
if (!cancelled) {
|
||||
setProfile(u);
|
||||
useTenantStore.getState().hydrateFromUserTenant(u.tenant_id);
|
||||
}
|
||||
} catch {
|
||||
if (!cancelled) setProfile(null);
|
||||
}
|
||||
} catch {
|
||||
if (!cancelled) {
|
||||
setUserSub(null);
|
||||
setProfile(null);
|
||||
}
|
||||
} finally {
|
||||
if (!cancelled) setLoading(false);
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [accessToken]);
|
||||
|
||||
return {
|
||||
profile,
|
||||
userSub,
|
||||
loading,
|
||||
label: displayLabel(profile, userSub, loading),
|
||||
};
|
||||
}
|
||||
|
||||
export function avatarInitials(profile: IamUser | null, userSub: string | null): string {
|
||||
const name = profile?.real_name?.trim() || profile?.user_name?.trim();
|
||||
if (name) {
|
||||
const arr = [...name];
|
||||
if (arr.length >= 2) return (arr[0] + arr[1]).toUpperCase();
|
||||
return name.slice(0, 2).toUpperCase();
|
||||
}
|
||||
if (userSub) return userSub.replace(/-/g, '').slice(0, 2).toUpperCase() || '?';
|
||||
return '?';
|
||||
}
|
||||
Reference in New Issue
Block a user