120 lines
3.7 KiB
TypeScript
120 lines
3.7 KiB
TypeScript
import { View, Text, Image, Button, Input } from '@tarojs/components'
|
|
import Taro from '@tarojs/taro'
|
|
import { useState } from 'react'
|
|
import './index.scss'
|
|
import { setUserInfo } from '../../utils/store'
|
|
import { login, getMe } from '../../utils/api'
|
|
import { safeHideLoading } from '../../utils/request'
|
|
|
|
/**
|
|
* 登录弹窗(一步流程,无需手机号)
|
|
*
|
|
* 个人主体小程序无 getPhoneNumber 权限,故以 openid 作为用户唯一标识:
|
|
* 用户填头像+昵称 -> 点「登录」-> wx.login 拿 code -> 后端换 openid 自动注册/续登
|
|
* -> getMe 取用户信息 -> 完成
|
|
*/
|
|
export default function LoginModal({ visible, onClose, onLogin }: { visible: boolean; onClose: () => void; onLogin: () => void }) {
|
|
const [nickName, setNickName] = useState('')
|
|
const [avatarUrl, setAvatarUrl] = useState('')
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
if (!visible) return null
|
|
|
|
const fail = (msg: string) => {
|
|
setLoading(false)
|
|
safeHideLoading()
|
|
Taro.showToast({ title: msg, icon: 'none' })
|
|
}
|
|
|
|
const finish = (accessToken: string, openid: string) => {
|
|
const info = {
|
|
openid,
|
|
nickName: nickName || '微信用户',
|
|
avatarUrl: avatarUrl || '',
|
|
accessToken,
|
|
loginAt: Date.now()
|
|
}
|
|
setUserInfo(info)
|
|
setLoading(false)
|
|
safeHideLoading()
|
|
Taro.showToast({ title: '登录成功', icon: 'success' })
|
|
onLogin()
|
|
}
|
|
|
|
const handleLogin = async () => {
|
|
if (loading) return
|
|
console.log('[LoginModal] 登录按钮被点击,开始登录流程')
|
|
setLoading(true)
|
|
Taro.showLoading({ title: '登录中', mask: true })
|
|
try {
|
|
const wxLogin = await Taro.login()
|
|
console.log('[LoginModal] wx.login 成功,code=', wxLogin.code)
|
|
const result = await login(wxLogin.code)
|
|
console.log('[LoginModal] 后端 /auth/login 返回:', JSON.stringify(result))
|
|
if (!result?.accessToken) {
|
|
fail('登录失败,未拿到 token')
|
|
return
|
|
}
|
|
let openid = ''
|
|
try {
|
|
const me = await getMe()
|
|
openid = me?.openid || ''
|
|
console.log('[LoginModal] getMe 返回:', JSON.stringify(me))
|
|
} catch {
|
|
openid = ''
|
|
}
|
|
if (!openid) {
|
|
fail('登录异常,请重试')
|
|
return
|
|
}
|
|
finish(result.accessToken, openid)
|
|
} catch (e) {
|
|
console.error('[LoginModal] 登录流程出错:', e)
|
|
fail((e as Error)?.message || '后端连接失败,请检查网络')
|
|
}
|
|
}
|
|
|
|
const onChooseAvatar = (e: any) => {
|
|
setAvatarUrl(e.detail.avatarUrl || '')
|
|
}
|
|
|
|
return (
|
|
<View className='modal-overlay'>
|
|
<View className='login-card dashed-card'>
|
|
<View className='star-badge' />
|
|
<Text className='login-title'>智绘微刻</Text>
|
|
<Text className='login-desc'>登录后同步您的设计清单和订单</Text>
|
|
|
|
<Button
|
|
className='avatar-btn'
|
|
openType='chooseAvatar'
|
|
onChooseAvatar={onChooseAvatar}
|
|
>
|
|
{avatarUrl ? (
|
|
<Image className='avatar-img' src={avatarUrl} mode='aspectFill' />
|
|
) : (
|
|
<Text className='avatar-placeholder'>点击选择头像</Text>
|
|
)}
|
|
</Button>
|
|
|
|
<Input
|
|
type='nickname'
|
|
className='nickname-input'
|
|
placeholder='请输入昵称'
|
|
value={nickName}
|
|
onInput={(e: any) => setNickName(e.detail.value)}
|
|
/>
|
|
|
|
<View className='login-actions'>
|
|
<View className='btn-outline' onTap={onClose}>
|
|
<Text>取消</Text>
|
|
</View>
|
|
<View className='btn-gradient' onTap={handleLogin}>
|
|
<Text>登录</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
)
|
|
}
|