diff --git a/src/pages/login/index.module.scss b/src/pages/login/index.module.scss index 5f39425..535d798 100644 --- a/src/pages/login/index.module.scss +++ b/src/pages/login/index.module.scss @@ -42,7 +42,7 @@ color: rgba(255, 255, 255, 0.85); } -// 表单卡上浮叠在渐变区上 +// 登录卡上浮叠在渐变区上 .form { margin: -120px 24px 0; padding: 48px 32px; @@ -53,15 +53,6 @@ flex-direction: column; } -.input { - height: 100px; - background: $color-bg-gray; - border-radius: $radius-md; - padding: 0 32px; - margin-bottom: 24px; - font-size: 30px; -} - .submit { height: 96px; background: $color-primary-grad; diff --git a/src/pages/login/index.tsx b/src/pages/login/index.tsx index 72b6d73..f2e0a72 100644 --- a/src/pages/login/index.tsx +++ b/src/pages/login/index.tsx @@ -1,8 +1,9 @@ import { useState } from 'react' -import { View, Text, Input } from '@tarojs/components' -import { useAppDispatch } from '@/store/hooks' +import { View, Text } from '@tarojs/components' +import { useDidShow } from '@tarojs/taro' +import { useAppDispatch, useAppSelector } from '@/store/hooks' import { setAuth } from '@/store/authSlice' -import { login } from '@/api/auth' +import { performWxLogin } from './wxLoginFlow' import { useToast } from '@/hooks/useToast' import { ROUTES } from '@/constants/routes' import { reLaunch } from '@/utils/navigation' @@ -11,26 +12,28 @@ import styles from './index.module.scss' export default function Login() { const dispatch = useAppDispatch() const toast = useToast() - const [username, setUsername] = useState('') - const [password, setPassword] = useState('') + const token = useAppSelector((s) => s.auth.token) const [loading, setLoading] = useState(false) - const handleLogin = async () => { - if (!username || !password) { - toast.error('请填写完整') - return - } + // 已有 token 直接进首页(本期不做 checkLogin 校验,spec D5) + useDidShow(() => { + if (token && !loading) reLaunch(ROUTES.HOME) + }) + + const handleWxLogin = async () => { + if (loading) return setLoading(true) try { - const res = await login({ username, password }) - dispatch(setAuth(res)) + const { code, userInfo } = await performWxLogin() + dispatch(setAuth({ token: code, userInfo })) reLaunch(ROUTES.HOME) - } catch { - toast.error('登录失败') - } finally { + } catch (e) { + // code 失效/已用等场景:toast 后端 Message,用户重按按钮重新 Taro.login(code 一次性,重试安全) + toast.error(e instanceof Error && e.message ? e.message : '登录失败') setLoading(false) } } + return ( @@ -41,21 +44,8 @@ export default function Login() { 一起守护家人 - setUsername(e.detail.value)} - /> - setPassword(e.detail.value)} - /> - - {loading ? '登录中...' : '登录'} + + {loading ? '登录中...' : '微信一键登录'} diff --git a/src/pages/login/wxLoginFlow.ts b/src/pages/login/wxLoginFlow.ts index 85a3e0f..8f9f413 100644 --- a/src/pages/login/wxLoginFlow.ts +++ b/src/pages/login/wxLoginFlow.ts @@ -15,7 +15,14 @@ function resolveAppId(): string { // 微信一键登录主流程(spec D3): // Taro.login 换一次性 code → 组装 appId/openId → 调 miniapp/login → 缓存响应中的 openId export async function performWxLogin(): Promise { - const { code } = await Taro.login() + // Taro.login reject 时返回 { errMsg } 形状对象而非 Error,此处归一化为带 message 的 Error + let code: string + try { + const r = await Taro.login() + code = r.code + } catch (e) { + throw new Error(e instanceof Error ? e.message : '微信登录失败') + } if (!code) throw new Error('获取微信登录凭证失败') const openId = storage.get(WX_OPENID_STORAGE_KEY) ?? undefined const result = await wxMiniappLogin({ appId: resolveAppId(), code, openId }) diff --git a/tests/unit/login/wxLoginFlow.spec.ts b/tests/unit/login/wxLoginFlow.spec.ts index 845e814..2455685 100644 --- a/tests/unit/login/wxLoginFlow.spec.ts +++ b/tests/unit/login/wxLoginFlow.spec.ts @@ -77,6 +77,12 @@ describe('performWxLogin', () => { expect(mockWxMiniappLogin).not.toHaveBeenCalled() }) + it('Taro.login reject(errMsg 形状)时抛带 message 的 Error', async () => { + mockLogin.mockRejectedValue({ errMsg: 'login:fail:cancel' }) + await expect(performWxLogin()).rejects.toThrow('微信登录失败') + expect(mockWxMiniappLogin).not.toHaveBeenCalled() + }) + it('getAccountInfoSync 抛错时回退常量 appId', async () => { mockGetAccountInfo.mockImplementation(() => { throw new Error('not supported') }) mockWxMiniappLogin.mockResolvedValue(result())