fix(auth): 登录页不停刷新、无法输入

未登录时五个 Tab 视图会同时挂载各自的 Screen,每个都判定「没登录」,
于是每个都对**当前**视图发一次 navigate('/login/', {reloadAll: true})。
每次 reloadAll 又让其他几个重新挂载,再各发一次——登录表单被持续拆掉重建,
根本打不出字。关掉页面过渡动画后这个循环变紧,症状才明显起来。

判断登录与否是外壳的职责,不是每个页面各自抢着跳转:
- App 持有会话状态:没有会话就只渲染一个 /login/ 视图,连 Tab 栏都不出;
  有会话才渲染五个 Tab。
- Screen 不再做任何跳转。
- setSession / clearSession 派发 ghl:auth 事件。storage 事件只在**其他**
  标签页触发,同标签页的登录登出需要自己的信号。
- 登录成功后不再手动 navigate:外壳会换掉整个视图,从一个正要被卸载的
  视图里发起路由是在和它抢。

实测:登出后登录页只有 1 个视图、无 Tab 栏,输入的内容 2.5 秒后仍在,
且输入框还是同一个 DOM 节点(没有重挂);恢复会话后自动切回 5 个 Tab。

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
ericwyuan
2026-08-24 09:08:02 +08:00
parent 6ad87115ab
commit 564aaef6c1
4 changed files with 52 additions and 24 deletions

View File

@@ -2,6 +2,8 @@ import axios, { AxiosInstance } from 'axios';
const API_BASE_URL = process.env.REACT_APP_API_URL || 'http://localhost:5000/api';
const TOKEN_KEY = 'ghl_token';
/** Fired on this tab whenever the session is created or cleared. */
export const AUTH_EVENT = 'ghl:auth';
// The Flask backend returns bare JSON (an array, or the object itself) and
// signals failure with `{ error: "..." }` plus a non-2xx status. There is no
@@ -385,12 +387,20 @@ class ApiClient {
}
// --- session ---
/* `storage` only fires in *other* tabs, so a same-tab login or logout needs
its own signal for the shell to notice. */
private announce() {
window.dispatchEvent(new Event(AUTH_EVENT));
}
setSession(token: string) {
localStorage.setItem(TOKEN_KEY, token);
this.announce();
}
clearSession() {
localStorage.removeItem(TOKEN_KEY);
this.announce();
}
isAuthenticated(): boolean {