添加登录和后端校验
完成后端设计(未在本仓库体现),通过安全的手段完成了登录鉴权
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user