This commit is contained in:
parent
4ae72d107c
commit
53155db139
44
App.vue
44
App.vue
|
|
@ -1,13 +1,57 @@
|
|||
<script>
|
||||
import WechatAuth from '@/utils/wechat-auth.js'
|
||||
import { getUserInfo } from '@/http/api/profile.js'
|
||||
|
||||
export default {
|
||||
onLaunch: function() {
|
||||
console.log('App Launch')
|
||||
this.initApp()
|
||||
},
|
||||
onShow: function() {
|
||||
console.log('App Show')
|
||||
},
|
||||
onHide: function() {
|
||||
console.log('App Hide')
|
||||
},
|
||||
methods: {
|
||||
// 应用初始化
|
||||
async initApp() {
|
||||
try {
|
||||
await this.checkLoginStatus()
|
||||
} catch (error) {
|
||||
// 静默处理初始化错误
|
||||
}
|
||||
},
|
||||
|
||||
// 检查登录状态
|
||||
async checkLoginStatus() {
|
||||
const token = WechatAuth.getToken()
|
||||
|
||||
if (!token) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 验证token有效性
|
||||
const userInfo = await getUserInfo({
|
||||
custom: {
|
||||
loading: false,
|
||||
toast: false
|
||||
}
|
||||
})
|
||||
|
||||
// 更新本地用户信息
|
||||
if (userInfo) {
|
||||
WechatAuth.saveLoginInfo(token, WechatAuth.getRefreshToken(), userInfo)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// token无效,清除登录信息
|
||||
if (error.code === 401 || error.statusCode === 401) {
|
||||
WechatAuth.clearLoginInfo()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -71,13 +71,23 @@ export const userLogin = (loginData, config = {}) => {
|
|||
}
|
||||
|
||||
/**
|
||||
* 微信登录
|
||||
* 微信小程序登录
|
||||
* @param {Object} wxData 微信登录数据
|
||||
* @param {string} wxData.code 微信授权码(必需)
|
||||
* @param {string} wxData.encryptedData 加密数据(可选,用户授权后获取)
|
||||
* @param {string} wxData.iv 初始向量(可选,用户授权后获取)
|
||||
* @param {string} wxData.signature 签名(可选,用户授权后获取)
|
||||
* @param {Object} wxData.userInfo 用户信息(可选,用户授权后获取)
|
||||
* @param {Object} config 自定义配置
|
||||
* @returns {Promise}
|
||||
* @returns {Promise} 返回包含token和用户信息的Promise
|
||||
*/
|
||||
export const wxLogin = (wxData, config = {}) => {
|
||||
return uni.$u.http.post('/auth/wx-login', wxData, {
|
||||
// 验证必需参数
|
||||
if (!wxData || !wxData.code) {
|
||||
return Promise.reject(new Error('微信授权码(code)是必需的'))
|
||||
}
|
||||
|
||||
return uni.$u.http.post('/wechat/user/mini/login', wxData, {
|
||||
custom: {
|
||||
auth: false,
|
||||
loading: true,
|
||||
|
|
@ -210,12 +220,16 @@ export const updateUserSettings = (settings, config = {}) => {
|
|||
/**
|
||||
* 绑定手机号
|
||||
* @param {Object} phoneData 手机号数据
|
||||
* @param {string} phoneData.encryptedData 加密数据
|
||||
* @param {string} phoneData.iv 初始向量
|
||||
* @param {string} phoneData.cloudID 云函数ID(可选)
|
||||
* @param {Object} config 自定义配置
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const bindPhone = (phoneData, config = {}) => {
|
||||
return uni.$u.http.post('/user/bind-phone', phoneData, {
|
||||
custom: {
|
||||
auth: true,
|
||||
loading: true,
|
||||
loadingText: '正在绑定手机号...',
|
||||
...config.custom
|
||||
|
|
@ -224,6 +238,32 @@ export const bindPhone = (phoneData, config = {}) => {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信小程序登录并绑定手机号
|
||||
* @param {Object} loginData 登录和手机号数据
|
||||
* @param {string} loginData.code 微信授权码
|
||||
* @param {string} loginData.phoneEncryptedData 手机号加密数据(可选)
|
||||
* @param {string} loginData.phoneIv 手机号初始向量(可选)
|
||||
* @param {string} loginData.phoneCloudID 手机号云函数ID(可选)
|
||||
* @param {Object} config 自定义配置
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const wxLoginWithPhone = (loginData, config = {}) => {
|
||||
if (!loginData || !loginData.code) {
|
||||
return Promise.reject(new Error('微信授权码(code)是必需的'))
|
||||
}
|
||||
|
||||
return uni.$u.http.post('/auth/wx-login-phone', loginData, {
|
||||
custom: {
|
||||
auth: false,
|
||||
loading: true,
|
||||
loadingText: '正在登录...',
|
||||
...config.custom
|
||||
},
|
||||
...config
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户权限
|
||||
* @param {Object} config 自定义配置
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
const ENV_CONFIG = {
|
||||
// 开发环境
|
||||
development: {
|
||||
baseURL: 'https://dev-api.pet-ai.com',
|
||||
baseURL: 'http://127.0.0.1:8080',
|
||||
timeout: 30000
|
||||
},
|
||||
// 测试环境
|
||||
|
|
@ -33,8 +33,8 @@ export const HTTP_CONFIG = {
|
|||
// 当前环境配置
|
||||
...ENV_CONFIG[CURRENT_ENV],
|
||||
|
||||
// 登录页面路径
|
||||
loginPage: '/pages/login/login',
|
||||
// 登录页面路径(个人中心页面,点击头像登录)
|
||||
loginPage: '/pages/profile/profile',
|
||||
|
||||
// 存储键名配置
|
||||
storageKeys: {
|
||||
|
|
@ -59,7 +59,7 @@ export const NO_AUTH_APIS = [
|
|||
// 用户认证相关
|
||||
'/auth/login',
|
||||
'/auth/register',
|
||||
'/auth/wx-login',
|
||||
'/wechat/user/mini/login',
|
||||
'/auth/refresh',
|
||||
|
||||
// 公开浏览的接口
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,271 @@
|
|||
// 微信小程序认证工具类
|
||||
// 封装微信小程序登录的核心逻辑
|
||||
|
||||
import { HTTP_CONFIG } from '@/http/config/config.js'
|
||||
|
||||
/**
|
||||
* 微信小程序认证工具类
|
||||
*/
|
||||
export class WechatAuth {
|
||||
/**
|
||||
* 微信登录获取code
|
||||
* @returns {Promise<string>} 微信授权码
|
||||
*/
|
||||
static async getWxCode() {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.login({
|
||||
success: (res) => {
|
||||
console.log(res)
|
||||
if (res.code) {
|
||||
resolve(res.code)
|
||||
} else {
|
||||
reject(new Error('获取微信授权码失败'))
|
||||
}
|
||||
},
|
||||
fail: (error) => {
|
||||
reject(new Error('微信登录失败: ' + (error.errMsg || '未知错误')))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息(需要用户授权)
|
||||
* @param {string} desc 申请理由
|
||||
* @returns {Promise<Object>} 用户信息
|
||||
*/
|
||||
static async getUserProfile(desc = '获取用户头像昵称') {
|
||||
return new Promise((resolve, reject) => {
|
||||
// #ifdef MP-WEIXIN
|
||||
wx.getUserProfile({
|
||||
desc: desc,
|
||||
success: (res) => {
|
||||
resolve(res)
|
||||
},
|
||||
fail: (error) => {
|
||||
if (error.errMsg && error.errMsg.includes('auth deny')) {
|
||||
reject(new Error('用户拒绝授权'))
|
||||
} else {
|
||||
reject(new Error('获取用户信息失败: ' + (error.errMsg || '未知错误')))
|
||||
}
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
reject(new Error('当前环境不支持微信授权'))
|
||||
// #endif
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户基本信息(无需授权,但信息有限)
|
||||
* @returns {Promise<Object>} 用户基本信息
|
||||
*/
|
||||
static async getUserInfo() {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.getUserInfo({
|
||||
success: (res) => {
|
||||
resolve(res)
|
||||
},
|
||||
fail: (error) => {
|
||||
reject(new Error('获取用户基本信息失败: ' + (error.errMsg || '未知错误')))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户手机号(需要用户授权)
|
||||
* 注意:这个方法需要在button组件的open-type="getPhoneNumber"的回调中使用
|
||||
* @param {Object} e 手机号授权回调事件对象
|
||||
* @returns {Object} 手机号相关数据
|
||||
*/
|
||||
static getPhoneNumber(e) {
|
||||
if (e.detail.errMsg === 'getPhoneNumber:ok') {
|
||||
return {
|
||||
success: true,
|
||||
encryptedData: e.detail.encryptedData,
|
||||
iv: e.detail.iv,
|
||||
cloudID: e.detail.cloudID
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
success: false,
|
||||
error: e.detail.errMsg || '获取手机号失败'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机头像
|
||||
* @returns {string} 随机头像URL
|
||||
*/
|
||||
static generateRandomAvatar() {
|
||||
const avatars = [
|
||||
'https://img.yzcdn.cn/vant/cat.jpeg',
|
||||
'https://img.yzcdn.cn/vant/dog.jpeg',
|
||||
'https://img.yzcdn.cn/vant/rabbit.jpeg',
|
||||
'https://img.yzcdn.cn/vant/bird.jpeg'
|
||||
]
|
||||
return avatars[Math.floor(Math.random() * avatars.length)]
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存登录信息到本地存储
|
||||
* @param {string} token 访问令牌
|
||||
* @param {string} refreshToken 刷新令牌(可选)
|
||||
* @param {Object} userInfo 用户信息
|
||||
*/
|
||||
static saveLoginInfo(token, refreshToken = null, userInfo = null) {
|
||||
try {
|
||||
// 使用配置中定义的存储键名
|
||||
uni.setStorageSync(HTTP_CONFIG.storageKeys.token, token)
|
||||
|
||||
if (refreshToken) {
|
||||
uni.setStorageSync(HTTP_CONFIG.storageKeys.refreshToken, refreshToken)
|
||||
}
|
||||
|
||||
if (userInfo) {
|
||||
uni.setStorageSync(HTTP_CONFIG.storageKeys.userInfo, userInfo)
|
||||
}
|
||||
|
||||
console.log('登录信息保存成功')
|
||||
} catch (error) {
|
||||
console.error('保存登录信息失败:', error)
|
||||
throw new Error('保存登录信息失败')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本地存储的token
|
||||
* @returns {string|null} 访问令牌
|
||||
*/
|
||||
static getToken() {
|
||||
try {
|
||||
return uni.getStorageSync(HTTP_CONFIG.storageKeys.token) || null
|
||||
} catch (error) {
|
||||
console.error('获取token失败:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本地存储的刷新令牌
|
||||
* @returns {string|null} 刷新令牌
|
||||
*/
|
||||
static getRefreshToken() {
|
||||
try {
|
||||
return uni.getStorageSync(HTTP_CONFIG.storageKeys.refreshToken) || null
|
||||
} catch (error) {
|
||||
console.error('获取refreshToken失败:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本地存储的用户信息
|
||||
* @returns {Object|null} 用户信息
|
||||
*/
|
||||
static getUserInfoFromStorage() {
|
||||
try {
|
||||
const userInfo = uni.getStorageSync(HTTP_CONFIG.storageKeys.userInfo)
|
||||
return userInfo ? (typeof userInfo === 'string' ? JSON.parse(userInfo) : userInfo) : null
|
||||
} catch (error) {
|
||||
console.error('获取用户信息失败:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否已登录
|
||||
* @returns {boolean} 是否已登录
|
||||
*/
|
||||
static isLoggedIn() {
|
||||
const token = this.getToken()
|
||||
return !!token
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有登录信息
|
||||
*/
|
||||
static clearLoginInfo() {
|
||||
try {
|
||||
uni.removeStorageSync(HTTP_CONFIG.storageKeys.token)
|
||||
uni.removeStorageSync(HTTP_CONFIG.storageKeys.refreshToken)
|
||||
uni.removeStorageSync(HTTP_CONFIG.storageKeys.userInfo)
|
||||
console.log('登录信息清除成功')
|
||||
} catch (error) {
|
||||
console.error('清除登录信息失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到登录页面
|
||||
*/
|
||||
static navigateToLogin() {
|
||||
uni.reLaunch({
|
||||
url: HTTP_CONFIG.loginPage
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查登录状态并处理
|
||||
* @returns {boolean} 是否已登录
|
||||
*/
|
||||
static checkLoginStatus() {
|
||||
if (!this.isLoggedIn()) {
|
||||
console.log('用户未登录,跳转到登录页')
|
||||
this.navigateToLogin()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 完整的微信小程序登录流程
|
||||
* @param {boolean} needUserInfo 是否需要获取用户详细信息
|
||||
* @returns {Promise<Object>} 登录结果
|
||||
*/
|
||||
static async performWxLogin(needUserInfo = true) {
|
||||
try {
|
||||
// 1. 获取微信授权码
|
||||
const code = await this.getWxCode()
|
||||
|
||||
// 2. 准备登录数据
|
||||
const loginData = { code }
|
||||
|
||||
// 3. 如果需要用户信息,尝试获取
|
||||
if (needUserInfo) {
|
||||
try {
|
||||
const userProfile = await this.getUserProfile()
|
||||
loginData.encryptedData = userProfile.encryptedData
|
||||
loginData.iv = userProfile.iv
|
||||
loginData.signature = userProfile.signature
|
||||
loginData.userInfo = userProfile.userInfo
|
||||
} catch (userInfoError) {
|
||||
console.warn('获取用户详细信息失败,使用基本登录:', userInfoError.message)
|
||||
// 继续使用基本登录,不中断流程
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: loginData,
|
||||
message: '微信授权成功'
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('微信登录流程失败:', error)
|
||||
return {
|
||||
success: false,
|
||||
error: error.message,
|
||||
message: '微信登录失败'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认导出,便于直接导入使用
|
||||
*/
|
||||
export default WechatAuth
|
||||
Loading…
Reference in New Issue