diff --git a/video-gen-app/src/api/client.ts b/video-gen-app/src/api/client.ts index 7136298e..6586ed11 100644 --- a/video-gen-app/src/api/client.ts +++ b/video-gen-app/src/api/client.ts @@ -14,6 +14,7 @@ interface RequestOptions { auth?: boolean; encryptBody?: boolean; signal?: AbortSignal; + skipAuthRedirect?: boolean; } /** Convert snake_case string to camelCase */ @@ -110,7 +111,7 @@ export async function apiRequest(path: string, options: RequestOptions = {}): // Handle error responses if (!res.ok) { const msg = parsed?.detail || `请求失败 (${res.status})`; - if (res.status === 401) { + if (res.status === 401 && !options.skipAuthRedirect) { clearToken(); window.location.href = '/login'; } @@ -122,11 +123,12 @@ if (!res.ok) { // Convenience methods export const api = { - get: (path: string, options: boolean | { auth?: boolean; signal?: AbortSignal } = true) => { + get: (path: string, options: boolean | { auth?: boolean; signal?: AbortSignal; skipAuthRedirect?: boolean } = true) => { const opts = typeof options === 'boolean' ? { auth: options } : options; return apiRequest(path, opts); }, - post: (path: string, body?: unknown, auth = true) => apiRequest(path, { method: 'POST', body, auth }), + post: (path: string, body?: unknown, auth = true, skipAuthRedirect = false) => + apiRequest(path, { method: 'POST', body, auth, skipAuthRedirect }), put: (path: string, body?: unknown, auth = true) => apiRequest(path, { method: 'PUT', body, auth }), delete: (path: string, auth = true) => apiRequest(path, { method: 'DELETE', auth }), }; diff --git a/video-gen-app/src/api/index.ts b/video-gen-app/src/api/index.ts index 1daeffe8..96f28bcc 100644 --- a/video-gen-app/src/api/index.ts +++ b/video-gen-app/src/api/index.ts @@ -12,13 +12,13 @@ const USE_MOCK = import.meta.env.VITE_USE_MOCK === 'true'; // ── Auth ────────────────────────────────────────────────── export async function login(username: string, password: string, captchaToken?: string, rememberMe?: boolean): Promise { if (USE_MOCK) return mock.mockLogin({ username, password }); - const res = await api.post<{ accessToken: string; user: User }>('/auth/login', { username, password, captcha_token: captchaToken, remember_me: rememberMe || false }, false); + const res = await api.post<{ accessToken: string; user: User }>('/auth/login', { username, password, captcha_token: captchaToken, remember_me: rememberMe || false }, false, true); setToken(res.accessToken); return res.user; } export async function phonelogin(phone: string, code: string): Promise { // if (USE_MOCK) return mock.mockLogin({ username, password }); - const res = await api.post<{ accessToken: string; user: User }>('/auth/sms-login', { phone, code }, false); + const res = await api.post<{ accessToken: string; user: User }>('/auth/sms-login', { phone, code }, false, true); setToken(res.accessToken); return res.user; } diff --git a/video-gen-app/src/pages/InitialInfo.tsx b/video-gen-app/src/pages/InitialInfo.tsx index ffa432ea..51002bda 100644 --- a/video-gen-app/src/pages/InitialInfo.tsx +++ b/video-gen-app/src/pages/InitialInfo.tsx @@ -256,6 +256,8 @@ function InitialInfo() { }); } + + // 下一步按钮点击处理 const handleNextStep = (stepId: number) => { // 获取引擎 ID 和视频参数 diff --git a/video-gen-app/src/pages/InitialReplication.tsx b/video-gen-app/src/pages/InitialReplication.tsx index c3fffee9..b7e1be31 100644 --- a/video-gen-app/src/pages/InitialReplication.tsx +++ b/video-gen-app/src/pages/InitialReplication.tsx @@ -468,6 +468,8 @@ const GenerateConver: React.FC = () => { >
{item.finalVideoUrl ? ( + + { const [showSliderVerify, setShowSliderVerify] = useState(false); const [sliderVerified, setSliderVerified] = useState(false); const [loginSliderVerified, setLoginSliderVerified] = useState(false); + const [sliderKey, setSliderKey] = useState(0); + const [showResend, setShowResend] = useState(false); + const [loginShowResend, setLoginShowResend] = useState(false); const [siteName, setSiteName] = useState('VideoGen.AI'); const [siteLogo, setSiteLogo] = useState(''); const [agreementUrl, setAgreementUrl] = useState(''); @@ -53,15 +56,16 @@ const LoginPage: React.FC = () => { if (!checkAgreed()) return; try { const values = await pwdForm.validateFields(); - setLoading(true); + // setLoading(true); await login(values.phone, values.password, undefined, values.rememberMe); message.success('登录成功,欢迎回来'); await checkAuth(); navigate('/projects'); - } catch { - message.error('登录失败'); + } catch (error: any) { + const errorMsg = error?.response?.data?.detail || error?.response?.data?.message || error?.message || '登录失败'; + message.error(errorMsg); } finally { - setLoading(false); + // setLoading(false); } }; @@ -78,8 +82,9 @@ const LoginPage: React.FC = () => { message.success('登录成功,欢迎回来'); await checkAuth(); navigate('/projects'); - } catch { - message.error('登录失败'); + } catch (error: any) { + const errorMsg = error?.response?.data?.detail || error?.response?.data?.message || error?.message || '登录失败'; + message.error(errorMsg); } finally { setLoading(false); } @@ -98,17 +103,23 @@ const LoginPage: React.FC = () => { const errorMsg = error?.response?.data?.detail || error?.response?.data?.message || error?.message || '注册失败'; message.error(errorMsg); } finally { - // setLoading(false); + setLoading(false); } }; - const startCountdown = (setter: React.Dispatch>) => { + const startCountdown = (setter: React.Dispatch>, isReg?: boolean) => { setter(60); const timer = setInterval(() => { setter((c) => { if (c <= 1) { clearInterval(timer); setShowSliderVerify(false); + // 倒计时结束后,设置重新发送状态 + if (isReg) { + setShowResend(true); + } else { + setLoginShowResend(true); + } return 0; } return c - 1; @@ -154,8 +165,19 @@ const LoginPage: React.FC = () => { await sendSms(phone,mode2); startCountdown(isReg ? setRegCountdown : setCountdown); message.success('验证码已发送'); - } catch { - message.error('发送失败'); + } catch (error: any) { + const errorMsg = error?.response?.data?.detail || error?.response?.data?.message || error?.message || '登录失败'; + message.error(errorMsg); + // 发送失败,重置验证状态、倒计时和滑块组件 + if (mode === 'register') { + setSliderVerified(false); + setRegCountdown(0); + } else { + setLoginSliderVerified(false); + setCountdown(0); + } + // 通过更新 key 强制刷新滑块组件 + setSliderKey(prev => prev + 1); } }; @@ -177,20 +199,27 @@ const LoginPage: React.FC = () => { // 设置对应的验证状态 if (mode === 'register') { setSliderVerified(true); - startCountdown(setRegCountdown); + startCountdown(setRegCountdown, true); } else { setLoginSliderVerified(true); - startCountdown(setCountdown); + startCountdown(setCountdown, false); } message.success('验证码已发送'); - } catch { - message.error('发送失败'); - // 发送失败,重置滑动验证组件 + } catch (error: any) { + const errorMsg = error?.response?.data?.detail || error?.response?.data?.message || error?.message || '登录失败'; + message.error(errorMsg); + // 发送失败,重置验证状态、倒计时和滑块组件 if (mode === 'register') { setSliderVerified(false); + setRegCountdown(0); + setShowResend(true); } else { setLoginSliderVerified(false); + setCountdown(0); + setLoginShowResend(true); } + // 通过更新 key 强制刷新滑块组件 + setSliderKey(prev => prev + 1); } }; @@ -198,8 +227,20 @@ const LoginPage: React.FC = () => { setTab(t); setMode(t); setCountdown(0); + setRegCountdown(0); setLoginSliderVerified(false); + setSliderVerified(false); setShowSliderVerify(false); + setShowResend(false); + setLoginShowResend(false); + // 刷新滑动验证组件 + setSliderKey(prev => prev + 1); + // 清空当前模式相关的表单 + if (t === 'password') { + pwdForm.resetFields(); + } else { + phoneForm.resetFields(); + } }; const features = [ @@ -338,7 +379,7 @@ const LoginPage: React.FC = () => { {/* Password Login */} {mode === 'password' && ( -
+ } placeholder="请输入手机号" style={inputStyle} /> @@ -349,7 +390,7 @@ const LoginPage: React.FC = () => { 记住我的登录状态 - {/* 滑动验证 - 获取验证码后显示 */} {showSliderVerify && ( - + { - } placeholder="请输入验证码" maxLength={6} + } + placeholder="请输入验证码" + maxLength={6} + disabled={!sliderVerified} style={{ ...inputStyle, borderRadius: '10px 0 0 10px', flex: 1 }} /> - - } placeholder="请设置密码(至少6位)" style={inputStyle} /> - - {/* 滑动验证 - 获取验证码后显示 */} {showSliderVerify && ( - + )} + + } placeholder="请设置密码(至少6位)" style={inputStyle} /> + + +