feat(login): 登录页改为微信一键登录并移除账密表单
This commit is contained in:
parent
44918644ef
commit
90e6262c89
|
|
@ -42,7 +42,7 @@
|
||||||
color: rgba(255, 255, 255, 0.85);
|
color: rgba(255, 255, 255, 0.85);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 表单卡上浮叠在渐变区上
|
// 登录卡上浮叠在渐变区上
|
||||||
.form {
|
.form {
|
||||||
margin: -120px 24px 0;
|
margin: -120px 24px 0;
|
||||||
padding: 48px 32px;
|
padding: 48px 32px;
|
||||||
|
|
@ -53,15 +53,6 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input {
|
|
||||||
height: 100px;
|
|
||||||
background: $color-bg-gray;
|
|
||||||
border-radius: $radius-md;
|
|
||||||
padding: 0 32px;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
font-size: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit {
|
.submit {
|
||||||
height: 96px;
|
height: 96px;
|
||||||
background: $color-primary-grad;
|
background: $color-primary-grad;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { View, Text, Input } from '@tarojs/components'
|
import { View, Text } from '@tarojs/components'
|
||||||
import { useAppDispatch } from '@/store/hooks'
|
import { useDidShow } from '@tarojs/taro'
|
||||||
|
import { useAppDispatch, useAppSelector } from '@/store/hooks'
|
||||||
import { setAuth } from '@/store/authSlice'
|
import { setAuth } from '@/store/authSlice'
|
||||||
import { login } from '@/api/auth'
|
import { performWxLogin } from './wxLoginFlow'
|
||||||
import { useToast } from '@/hooks/useToast'
|
import { useToast } from '@/hooks/useToast'
|
||||||
import { ROUTES } from '@/constants/routes'
|
import { ROUTES } from '@/constants/routes'
|
||||||
import { reLaunch } from '@/utils/navigation'
|
import { reLaunch } from '@/utils/navigation'
|
||||||
|
|
@ -11,26 +12,28 @@ import styles from './index.module.scss'
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const [username, setUsername] = useState('')
|
const token = useAppSelector((s) => s.auth.token)
|
||||||
const [password, setPassword] = useState('')
|
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
const handleLogin = async () => {
|
// 已有 token 直接进首页(本期不做 checkLogin 校验,spec D5)
|
||||||
if (!username || !password) {
|
useDidShow(() => {
|
||||||
toast.error('请填写完整')
|
if (token && !loading) reLaunch(ROUTES.HOME)
|
||||||
return
|
})
|
||||||
}
|
|
||||||
|
const handleWxLogin = async () => {
|
||||||
|
if (loading) return
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const res = await login({ username, password })
|
const { code, userInfo } = await performWxLogin()
|
||||||
dispatch(setAuth(res))
|
dispatch(setAuth({ token: code, userInfo }))
|
||||||
reLaunch(ROUTES.HOME)
|
reLaunch(ROUTES.HOME)
|
||||||
} catch {
|
} catch (e) {
|
||||||
toast.error('登录失败')
|
// code 失效/已用等场景:toast 后端 Message,用户重按按钮重新 Taro.login(code 一次性,重试安全)
|
||||||
} finally {
|
toast.error(e instanceof Error && e.message ? e.message : '登录失败')
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className={styles.page}>
|
<View className={styles.page}>
|
||||||
<View className={styles.hero}>
|
<View className={styles.hero}>
|
||||||
|
|
@ -41,21 +44,8 @@ export default function Login() {
|
||||||
<Text className={styles.slogan}>一起守护家人</Text>
|
<Text className={styles.slogan}>一起守护家人</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className={styles.form}>
|
<View className={styles.form}>
|
||||||
<Input
|
<View className={styles.submit} onClick={handleWxLogin}>
|
||||||
className={styles.input}
|
<Text className={styles.submitText}>{loading ? '登录中...' : '微信一键登录'}</Text>
|
||||||
placeholder="账号"
|
|
||||||
value={username}
|
|
||||||
onInput={(e) => setUsername(e.detail.value)}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
className={styles.input}
|
|
||||||
placeholder="密码"
|
|
||||||
password
|
|
||||||
value={password}
|
|
||||||
onInput={(e) => setPassword(e.detail.value)}
|
|
||||||
/>
|
|
||||||
<View className={styles.submit} onClick={handleLogin}>
|
|
||||||
<Text className={styles.submitText}>{loading ? '登录中...' : '登录'}</Text>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,14 @@ function resolveAppId(): string {
|
||||||
// 微信一键登录主流程(spec D3):
|
// 微信一键登录主流程(spec D3):
|
||||||
// Taro.login 换一次性 code → 组装 appId/openId → 调 miniapp/login → 缓存响应中的 openId
|
// Taro.login 换一次性 code → 组装 appId/openId → 调 miniapp/login → 缓存响应中的 openId
|
||||||
export async function performWxLogin(): Promise<WxLoginResult> {
|
export async function performWxLogin(): Promise<WxLoginResult> {
|
||||||
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('获取微信登录凭证失败')
|
if (!code) throw new Error('获取微信登录凭证失败')
|
||||||
const openId = storage.get<string>(WX_OPENID_STORAGE_KEY) ?? undefined
|
const openId = storage.get<string>(WX_OPENID_STORAGE_KEY) ?? undefined
|
||||||
const result = await wxMiniappLogin({ appId: resolveAppId(), code, openId })
|
const result = await wxMiniappLogin({ appId: resolveAppId(), code, openId })
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,12 @@ describe('performWxLogin', () => {
|
||||||
expect(mockWxMiniappLogin).not.toHaveBeenCalled()
|
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 () => {
|
it('getAccountInfoSync 抛错时回退常量 appId', async () => {
|
||||||
mockGetAccountInfo.mockImplementation(() => { throw new Error('not supported') })
|
mockGetAccountInfo.mockImplementation(() => { throw new Error('not supported') })
|
||||||
mockWxMiniappLogin.mockResolvedValue(result())
|
mockWxMiniappLogin.mockResolvedValue(result())
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue