SEC_Warehouse/src/pages/login/index.tsx

145 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState } from 'react'
import { View, Text, Button } from '@tarojs/components'
import { useDidShow } from '@tarojs/taro'
import { useAppDispatch, useAppSelector } from '@/store/hooks'
import { setAuth } from '@/store/authSlice'
import {
performWxLogin,
completeUserInfoLogin,
completeMobileLogin,
getLoginSubState,
type LoginSubState
} from './wxLoginFlow'
import { useToast } from '@/hooks/useToast'
import { ROUTES } from '@/constants/routes'
import { reLaunch } from '@/utils/navigation'
import { storage } from '@/utils/storage'
import { WX_OPENID_STORAGE_KEY } from '@/constants/auth'
import styles from './index.module.scss'
export default function Login() {
const dispatch = useAppDispatch()
const toast = useToast()
const token = useAppSelector((s) => s.auth.token)
const [loading, setLoading] = useState(false)
// 注册中间态:needUserInfo(待头像昵称授权)→ noMobile(待手机号授权)→ null(初始/完成)
const [subState, setSubState] = useState<LoginSubState | null>(null)
const [openId, setOpenId] = useState('')
// 已有 token 直接进首页(本期不做 checkLogin 校验,spec D5)
useDidShow(() => {
if (token && !loading) reLaunch(ROUTES.HOME)
})
// 完整登录态校验与落地;未完成时推进 subState
const applyResult = async (result: Awaited<ReturnType<typeof performWxLogin>>) => {
const state = getLoginSubState(result)
if (state !== 'complete') {
console.log('[login-page] 注册未完成,推进到', state)
// openId 兜底链:noMobile 阶段后端把 openId 放在 bindKey 字段返回(联调实测);
// 其余阶段从本地缓存(needUserInfo 时已写入)兜底
const openIdNext =
result.openId ?? (state === 'noMobile' ? result.bindKey : undefined) ??
storage.get<string>(WX_OPENID_STORAGE_KEY) ?? openId
if (!openIdNext) throw new Error('响应缺少 openId,无法继续授权')
setOpenId(openIdNext)
setSubState(state)
setLoading(false)
return
}
if (!result.code || !result.userInfo) throw new Error('登录响应缺少凭证')
console.log('[login-page] 登录完成,token 已落地')
setSubState(null)
dispatch(setAuth({ token: result.code, userInfo: result.userInfo }))
await reLaunch(ROUTES.HOME)
}
const handleWxLogin = async () => {
if (loading) return
setLoading(true)
try {
applyResult(await performWxLogin())
} catch (e) {
// code 失效/已用等场景:toast 后端 Message,用户重按按钮重新 Taro.login(code 一次性,重试安全)
console.error('[login-page] performWxLogin 失败', e)
toast.error(e instanceof Error && e.message ? e.message : '登录失败')
setLoading(false)
}
}
// needUserInfo:getUserProfile 授权头像昵称
const handleAuthUserInfo = async () => {
if (loading || !openId) return
setLoading(true)
try {
applyResult(await completeUserInfoLogin(openId))
} catch (e) {
// 授权失败/取消:保留待授权态,用户可重试(getUserProfile 每次都需用户确认)
console.error('[login-page] completeUserInfoLogin 失败', e)
toast.error(e instanceof Error && e.message ? e.message : '授权失败')
setLoading(false)
}
}
// noMobile:手机号授权按钮回调(Button open-type=getPhoneNumber)
const handleGetPhoneNumber = async (e: {
detail: { iv: string; encryptedData: string; code?: string; errMsg?: string }
}) => {
console.log('[login-page] 手机号授权回调', {
errMsg: e.detail.errMsg,
// 两种手机号回传形态:新版 code(动态令牌)或旧版 iv/encryptedData(加密数据)
hasCode: Boolean(e.detail.code),
hasEncrypted: Boolean(e.detail.encryptedData)
})
if (loading || !openId) return
setLoading(true)
try {
applyResult(await completeMobileLogin(openId, e.detail))
} catch (err) {
console.error('[login-page] completeMobileLogin 失败', err)
toast.error(err instanceof Error && err.message ? err.message : '授权失败')
setLoading(false)
}
}
const submitText = () => {
if (loading) {
return subState === 'noMobile' ? '绑定中...' : subState === 'needUserInfo' ? '授权中...' : '登录中...'
}
if (subState === 'needUserInfo') return '授权头像昵称,继续'
if (subState === 'noMobile') return '授权手机号,完成注册'
return '微信一键登录'
}
return (
<View className={styles.page}>
<View className={styles.hero}>
<View className={styles.logo}>
<Text className={styles.logoText}>智</Text>
</View>
<Text className={styles.productName}>智慧养老</Text>
<Text className={styles.slogan}>一起守护家人</Text>
</View>
<View className={styles.form}>
{subState === 'noMobile' ? (
// 手机号授权必须用原生 Button open-type,微信弹官方授权框
<Button
className={styles.submitBtn}
openType="getPhoneNumber"
onGetPhoneNumber={handleGetPhoneNumber}
>
{submitText()}
</Button>
) : (
<View
className={styles.submit}
onClick={subState === 'needUserInfo' ? handleAuthUserInfo : handleWxLogin}
>
<Text className={styles.submitText}>{submitText()}</Text>
</View>
)}
</View>
</View>
)
}