xiaolongmr commited on
Commit
00a6ef3
·
1 Parent(s): e6c4018

导航加载失败时把原因显示出来,并支持点击重试

Browse files

之前 /api/navigation 失败是静默的:整条导航直接空掉,只能翻控制台才知道
是 401、跨域还是网络层错误。现在失败时在导航位置显示一枚琥珀色徽标
「导航加载失败(HTTP 401 / Network Error …)· 点此重试」,点一下重新拉取。
成功时清空错误态;仍保留上次成功结果的缓存兜底。

Files changed (1) hide show
  1. web/src/components/top-nav.tsx +23 -2
web/src/components/top-nav.tsx CHANGED
@@ -224,6 +224,10 @@ export function TopNav({ branding = DEFAULT_BRANDING }: { branding?: Branding })
224
  const [isLoadingApiKey, setIsLoadingApiKey] = useState(false);
225
  const [profile, setProfile] = useState<MyProfile | null>(null);
226
  const [customNavigation, setCustomNavigation] = useState<NavigationItem[]>([]);
 
 
 
 
227
  const [openCustomNavigationId, setOpenCustomNavigationId] = useState("");
228
  const [isProfileOpen, setIsProfileOpen] = useState(false);
229
  const [nicknameValue, setNicknameValue] = useState("");
@@ -352,7 +356,10 @@ export function TopNav({ branding = DEFAULT_BRANDING }: { branding?: Branding })
352
  try {
353
  const data = await fetchNavigation();
354
  const items = data.items || [];
355
- if (active) setCustomNavigation(items);
 
 
 
356
  writeCachedNavigation(items);
357
  return;
358
  } catch (error) {
@@ -360,6 +367,7 @@ export function TopNav({ branding = DEFAULT_BRANDING }: { branding?: Branding })
360
  const status = (error as { status?: number }).status;
361
  if (status || attempt === 2) {
362
  setCustomNavigation(readCachedNavigation());
 
363
  return;
364
  }
365
  await new Promise((resolve) => window.setTimeout(resolve, 400 * (attempt + 1)));
@@ -370,7 +378,7 @@ export function TopNav({ branding = DEFAULT_BRANDING }: { branding?: Branding })
370
  return () => {
371
  active = false;
372
  };
373
- }, [session?.role, sessionKey]);
374
 
375
  useEffect(() => {
376
  if (!sessionKey) {
@@ -624,6 +632,19 @@ export function TopNav({ branding = DEFAULT_BRANDING }: { branding?: Branding })
624
  opacity: activeIndicator.ready ? 1 : 0,
625
  }}
626
  />
 
 
 
 
 
 
 
 
 
 
 
 
 
627
  {customNavigation.map((item) => {
628
  const itemKey = `custom:${item.id}`;
629
  const active = activeCustomNavigationKey === itemKey;
 
224
  const [isLoadingApiKey, setIsLoadingApiKey] = useState(false);
225
  const [profile, setProfile] = useState<MyProfile | null>(null);
226
  const [customNavigation, setCustomNavigation] = useState<NavigationItem[]>([]);
227
+ // 导航加载失败时把真实原因摆到界面上:之前失败是静默的,整条导航直接空掉,
228
+ // 只能靠翻控制台才知道是 401、跨域还是网络层错误。
229
+ const [navError, setNavError] = useState<string | null>(null);
230
+ const [navReloadToken, setNavReloadToken] = useState(0);
231
  const [openCustomNavigationId, setOpenCustomNavigationId] = useState("");
232
  const [isProfileOpen, setIsProfileOpen] = useState(false);
233
  const [nicknameValue, setNicknameValue] = useState("");
 
356
  try {
357
  const data = await fetchNavigation();
358
  const items = data.items || [];
359
+ if (active) {
360
+ setCustomNavigation(items);
361
+ setNavError(null);
362
+ }
363
  writeCachedNavigation(items);
364
  return;
365
  } catch (error) {
 
367
  const status = (error as { status?: number }).status;
368
  if (status || attempt === 2) {
369
  setCustomNavigation(readCachedNavigation());
370
+ setNavError(status ? `HTTP ${status}` : (error as Error).message || "网络错误");
371
  return;
372
  }
373
  await new Promise((resolve) => window.setTimeout(resolve, 400 * (attempt + 1)));
 
378
  return () => {
379
  active = false;
380
  };
381
+ }, [session?.role, sessionKey, navReloadToken]);
382
 
383
  useEffect(() => {
384
  if (!sessionKey) {
 
632
  opacity: activeIndicator.ready ? 1 : 0,
633
  }}
634
  />
635
+ {customNavigation.length === 0 && navError ? (
636
+ <button
637
+ type="button"
638
+ onClick={() => {
639
+ setNavError(null);
640
+ setNavReloadToken((value) => value + 1);
641
+ }}
642
+ title={navError}
643
+ className="relative z-10 inline-flex h-9 shrink-0 items-center gap-1.5 whitespace-nowrap rounded-xl bg-amber-50 px-3 text-[13px] font-semibold text-amber-700 outline-none ring-1 ring-amber-200 transition-colors hover:bg-amber-100 focus-visible:ring-2 focus-visible:ring-amber-400"
644
+ >
645
+ 导航加载失败({navError})· 点此重试
646
+ </button>
647
+ ) : null}
648
  {customNavigation.map((item) => {
649
  const itemKey = `custom:${item.id}`;
650
  const active = activeCustomNavigationKey === itemKey;