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(null) const [openId, setOpenId] = useState('') // 已有 token 直接进首页(本期不做 checkLogin 校验,spec D5) useDidShow(() => { if (token && !loading) reLaunch(ROUTES.HOME) }) // 完整登录态校验与落地;未完成时推进 subState const applyResult = async (result: Awaited>) => { 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(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 ( 智 智慧养老 一起守护家人 {subState === 'noMobile' ? ( // 手机号授权必须用原生 Button open-type,微信弹官方授权框 ) : ( {submitText()} )} ) }