diff --git a/docs/design/PLATFORM_HOME_PUBLIC_BROWSE_AND_LOGIN_MODAL_GATING_DESIGN_2026-04-19.md b/docs/design/PLATFORM_HOME_PUBLIC_BROWSE_AND_LOGIN_MODAL_GATING_DESIGN_2026-04-19.md
index 8eac1260..2f6412bb 100644
--- a/docs/design/PLATFORM_HOME_PUBLIC_BROWSE_AND_LOGIN_MODAL_GATING_DESIGN_2026-04-19.md
+++ b/docs/design/PLATFORM_HOME_PUBLIC_BROWSE_AND_LOGIN_MODAL_GATING_DESIGN_2026-04-19.md
@@ -112,6 +112,7 @@
- 用户主动关闭弹窗时,只关闭弹窗,不改变当前平台页面
- 不清空首页浏览状态
- 不自动跳转到其他 tab
+- 登录弹窗下次重新打开时必须恢复初始表单状态:回到默认登录页签、关闭重置密码面板、清空密码 / 验证码 / 图形验证码 / 提示 / 倒计时等本次草稿状态;只允许保留“最近一次成功登录手机号”的本地回填能力。
---
diff --git a/src/components/auth/AuthGate.test.tsx b/src/components/auth/AuthGate.test.tsx
index cb879e3b..5b793f01 100644
--- a/src/components/auth/AuthGate.test.tsx
+++ b/src/components/auth/AuthGate.test.tsx
@@ -312,6 +312,68 @@ test('auth gate shows sms send feedback in the login modal', async () => {
expect(within(dialog).getByRole('button', { name: '60s' })).toBeTruthy();
});
+test('login modal resets draft state every time it is reopened', async () => {
+ const user = userEvent.setup();
+
+ authMocks.getAuthLoginOptions.mockResolvedValue({
+ availableLoginMethods: ['phone', 'password'],
+ });
+
+ render(
+
+
+ ,
+ );
+
+ await user.click(await screen.findByRole('button', { name: '进入作品' }));
+
+ const firstDialog = screen.getByRole('dialog', { name: '账号入口' });
+ await user.type(within(firstDialog).getByLabelText('手机号'), '13800000000');
+ await user.click(within(firstDialog).getByRole('button', { name: '获取验证码' }));
+
+ expect(
+ await within(firstDialog).findByText('短信请求已提交,验证码有效期约 5 分钟。'),
+ ).toBeTruthy();
+ await user.type(within(firstDialog).getByLabelText('验证码'), '123456');
+ await user.click(within(firstDialog).getByRole('tab', { name: '密码登录' }));
+ await user.type(within(firstDialog).getByLabelText('密码'), 'passw0rd');
+ await user.click(within(firstDialog).getByRole('button', { name: '忘记密码' }));
+
+ expect(
+ screen.getByRole('dialog', { name: '重置密码' }),
+ ).toBeTruthy();
+
+ await user.click(
+ screen.getByRole('button', { name: '关闭登录弹窗' }),
+ );
+
+ await waitFor(() => {
+ expect(screen.queryByRole('dialog', { name: '账号入口' })).toBeNull();
+ });
+
+ await user.click(screen.getByRole('button', { name: '进入作品' }));
+
+ const reopenedDialog = screen.getByRole('dialog', { name: '账号入口' });
+ expect(
+ within(reopenedDialog)
+ .getByRole('tab', { name: '短信登录' })
+ .getAttribute('aria-selected'),
+ ).toBe('true');
+ expect(
+ (within(reopenedDialog).getByLabelText('手机号') as HTMLInputElement).value,
+ ).toBe('');
+ expect(
+ (within(reopenedDialog).getByLabelText('验证码') as HTMLInputElement).value,
+ ).toBe('');
+ expect(within(reopenedDialog).queryByLabelText('密码')).toBeNull();
+ expect(
+ within(reopenedDialog).queryByText('短信请求已提交,验证码有效期约 5 分钟。'),
+ ).toBeNull();
+ expect(
+ within(reopenedDialog).getByRole('button', { name: '获取验证码' }),
+ ).toBeTruthy();
+});
+
test('auth gate separates sms and password login by tabs', async () => {
const user = userEvent.setup();
diff --git a/src/components/auth/LoginScreen.tsx b/src/components/auth/LoginScreen.tsx
index 02aa9bb0..11634992 100644
--- a/src/components/auth/LoginScreen.tsx
+++ b/src/components/auth/LoginScreen.tsx
@@ -75,6 +75,26 @@ export function LoginScreen({
const wechatLoginEnabled = availableLoginMethods.includes('wechat');
const [activeLoginTab, setActiveLoginTab] = useState('phone');
+ useEffect(() => {
+ if (!isOpen) {
+ return;
+ }
+
+ // 每次重新打开弹窗都丢弃上一次未完成的表单草稿,只保留最近成功登录手机号回填。
+ setIsResetPanelOpen(false);
+ setPhone(getStoredLastLoginPhone());
+ setPassword('');
+ setCode('');
+ setResetPhone('');
+ setResetCode('');
+ setResetPasswordValue('');
+ setCaptchaAnswer('');
+ setCooldownSeconds(0);
+ setResetCooldownSeconds(0);
+ setHint('');
+ setActiveLoginTab(phoneLoginEnabled ? 'phone' : 'password');
+ }, [isOpen, phoneLoginEnabled]);
+
useEffect(() => {
if (activeLoginTab === 'phone' && !phoneLoginEnabled && passwordLoginEnabled) {
setActiveLoginTab('password');