添加登录和后端校验

完成后端设计(未在本仓库体现),通过安全的手段完成了登录鉴权
This commit is contained in:
2026-08-06 16:27:36 +08:00
commit b19a56003f
286 changed files with 51650 additions and 0 deletions
+119
View File
@@ -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>
)
}