Compare commits
6 Commits
304e6e94fc
...
c191002cc6
| Author | SHA1 | Date |
|---|---|---|
|
|
c191002cc6 | |
|
|
2b4a3b6f1b | |
|
|
dba965344a | |
|
|
913c2ba036 | |
|
|
089c9f8494 | |
|
|
53155db139 |
102
App.vue
102
App.vue
|
|
@ -1,13 +1,115 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { wxLogin } from '@/http/api/auth.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
onLaunch: function() {
|
onLaunch: function() {
|
||||||
console.log('App Launch')
|
console.log('App Launch')
|
||||||
|
this.initApp()
|
||||||
},
|
},
|
||||||
onShow: function() {
|
onShow: function() {
|
||||||
console.log('App Show')
|
console.log('App Show')
|
||||||
},
|
},
|
||||||
onHide: function() {
|
onHide: function() {
|
||||||
console.log('App Hide')
|
console.log('App Hide')
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 应用初始化
|
||||||
|
async initApp() {
|
||||||
|
console.log('应用初始化开始')
|
||||||
|
await this.checkAutoLogin()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 等待HTTP配置初始化
|
||||||
|
async waitForHttpInit() {
|
||||||
|
// 等待uni.$u.http可用
|
||||||
|
let retryCount = 0
|
||||||
|
const maxRetries = 10
|
||||||
|
|
||||||
|
while (!uni.$u || !uni.$u.http) {
|
||||||
|
if (retryCount >= maxRetries) {
|
||||||
|
throw new Error('HTTP配置初始化超时')
|
||||||
|
}
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100))
|
||||||
|
retryCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('HTTP配置初始化完成')
|
||||||
|
},
|
||||||
|
|
||||||
|
// 检查自动登录
|
||||||
|
async checkAutoLogin() {
|
||||||
|
try {
|
||||||
|
// 检查本地是否有有效token
|
||||||
|
const token = uni.getStorageSync('token')
|
||||||
|
if (token) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待HTTP配置初始化完成
|
||||||
|
await this.waitForHttpInit()
|
||||||
|
|
||||||
|
// 获取微信登录code
|
||||||
|
const loginRes = await new Promise((resolve, reject) => {
|
||||||
|
uni.login({
|
||||||
|
success: resolve,
|
||||||
|
fail: (error) => {
|
||||||
|
console.warn('微信登录失败:', error)
|
||||||
|
// 不reject,而是resolve一个空对象,避免阻止应用启动
|
||||||
|
resolve({ code: null, error })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (loginRes.code) {
|
||||||
|
// 调用后端登录接口
|
||||||
|
const result = await uni.$u.http.post('/wechat/user/mini/login', {
|
||||||
|
code: loginRes.code
|
||||||
|
}, {
|
||||||
|
custom: {
|
||||||
|
loading: false,
|
||||||
|
toast: false,
|
||||||
|
auth: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result && result.token) {
|
||||||
|
// 用户已授权过手机号,保存登录信息
|
||||||
|
this.saveLoginData(result)
|
||||||
|
} else {
|
||||||
|
// 用户未授权手机号,保存微信登录code供后续使用
|
||||||
|
this.saveWxCode(loginRes.code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 静默处理错误,不影响应用正常启动
|
||||||
|
console.error('自动登录失败:', error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 保存登录数据
|
||||||
|
saveLoginData(result) {
|
||||||
|
uni.setStorageSync('token', result.token)
|
||||||
|
if (result.refreshToken) {
|
||||||
|
uni.setStorageSync('refreshToken', result.refreshToken)
|
||||||
|
}
|
||||||
|
if (result.userInfo) {
|
||||||
|
uni.setStorageSync('wxUserInfo', result.userInfo)
|
||||||
|
uni.setStorageSync('userInfo', result.userInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置登录状态
|
||||||
|
const loginStep = result.userInfo && result.userInfo.profileCompleted
|
||||||
|
? 'profile_completed'
|
||||||
|
: 'phone_authed'
|
||||||
|
uni.setStorageSync('loginStep', loginStep)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 保存微信code
|
||||||
|
saveWxCode(code) {
|
||||||
|
uni.setStorageSync('wxLoginCode', code)
|
||||||
|
uni.setStorageSync('loginDate', new Date().toISOString())
|
||||||
|
uni.setStorageSync('loginStep', 'wx_logged')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,249 +0,0 @@
|
||||||
# Profile.js 深层次优化报告
|
|
||||||
|
|
||||||
## 优化概览
|
|
||||||
|
|
||||||
对 `http/api/profile.js` 文件进行了深层次的细致优化,显著提升了代码质量、可维护性和开发体验。
|
|
||||||
|
|
||||||
## 1. 方法复用优化 ✅
|
|
||||||
|
|
||||||
### 重复模式识别与提取
|
|
||||||
**优化前的问题:**
|
|
||||||
- 每个API方法都有相似的配置结构
|
|
||||||
- 重复的 `custom` 配置合并逻辑
|
|
||||||
- 不一致的默认参数处理
|
|
||||||
|
|
||||||
**优化后的解决方案:**
|
|
||||||
```javascript
|
|
||||||
// 提取了5个通用请求执行器
|
|
||||||
const executeGetRequest = (url, params, template, config) => { ... }
|
|
||||||
const executePostRequest = (url, data, template, loadingText, config) => { ... }
|
|
||||||
const executePutRequest = (url, data, template, loadingText, config) => { ... }
|
|
||||||
const executeDeleteRequest = (url, data, template, loadingText, config) => { ... }
|
|
||||||
|
|
||||||
// 统一的配置生成器
|
|
||||||
const createRequestConfig = (template, customConfig, loadingText) => { ... }
|
|
||||||
```
|
|
||||||
|
|
||||||
### 配置模板化
|
|
||||||
创建了4种标准配置模板:
|
|
||||||
- `AUTHENTICATED_QUERY`: 需要认证的查询(无loading)
|
|
||||||
- `AUTHENTICATED_QUERY_WITH_LOADING`: 需要认证的查询(有loading)
|
|
||||||
- `AUTHENTICATED_UPDATE`: 需要认证的更新操作
|
|
||||||
- `AUTHENTICATED_DELETE`: 需要认证的删除操作
|
|
||||||
|
|
||||||
## 2. 样式和配置复用 ✅
|
|
||||||
|
|
||||||
### 统一的配置常量
|
|
||||||
```javascript
|
|
||||||
const DEFAULT_CONFIG_TEMPLATES = {
|
|
||||||
AUTHENTICATED_QUERY: {
|
|
||||||
auth: true,
|
|
||||||
loading: false,
|
|
||||||
toast: true
|
|
||||||
},
|
|
||||||
// ... 其他模板
|
|
||||||
}
|
|
||||||
|
|
||||||
const LOADING_TEXTS = {
|
|
||||||
UPDATING_USER_INFO: '正在更新用户信息...',
|
|
||||||
SAVING_PROFILE: '正在保存...',
|
|
||||||
DELETING_ACCOUNT: '正在注销账户...',
|
|
||||||
UPLOADING_AVATAR: '正在上传头像...',
|
|
||||||
LOADING_DATA: '正在加载...'
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 配置复用效果对比
|
|
||||||
**优化前:**
|
|
||||||
```javascript
|
|
||||||
export const updateUserInfo = (userInfo, config = {}) => {
|
|
||||||
return uni.$u.http.put('/user/info', userInfo, {
|
|
||||||
custom: {
|
|
||||||
auth: true,
|
|
||||||
loading: true,
|
|
||||||
loadingText: '正在更新用户信息...',
|
|
||||||
...config.custom
|
|
||||||
},
|
|
||||||
...config
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**优化后:**
|
|
||||||
```javascript
|
|
||||||
export const updateUserInfo = (userInfo, config = {}) => {
|
|
||||||
return executePutRequest('/user/info', userInfo, 'AUTHENTICATED_UPDATE', LOADING_TEXTS.UPDATING_USER_INFO, config)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 3. 代码结构优化 ✅
|
|
||||||
|
|
||||||
### 功能分组重构
|
|
||||||
将API方法按功能进行了清晰的分组:
|
|
||||||
|
|
||||||
1. **用户信息相关API**
|
|
||||||
- `getUserInfo()` - 获取用户基本信息
|
|
||||||
- `updateUserInfo()` - 更新用户基本信息
|
|
||||||
- `getUserPets()` - 获取用户宠物列表
|
|
||||||
|
|
||||||
2. **用户统计相关API**
|
|
||||||
- `getUserStats()` - 获取用户统计数据
|
|
||||||
|
|
||||||
3. **账户管理相关API**
|
|
||||||
- `deleteAccount()` - 注销用户账户
|
|
||||||
|
|
||||||
4. **用户资料完善相关API**
|
|
||||||
- `completeUserProfile()` - 完善用户资料信息
|
|
||||||
|
|
||||||
5. **头像上传相关API**
|
|
||||||
- `uploadAvatar()` - 上传用户头像
|
|
||||||
|
|
||||||
6. **用户偏好设置相关API** (新增)
|
|
||||||
- `getUserPreferences()` - 获取用户偏好设置
|
|
||||||
- `updateUserPreferences()` - 更新用户偏好设置
|
|
||||||
|
|
||||||
7. **用户活动记录相关API** (新增)
|
|
||||||
- `getUserActivities()` - 获取用户活动记录
|
|
||||||
|
|
||||||
### JSDoc注释标准化
|
|
||||||
**优化前:**
|
|
||||||
```javascript
|
|
||||||
/**
|
|
||||||
* 获取用户信息
|
|
||||||
* @param {Object} config 自定义配置
|
|
||||||
* @returns {Promise}
|
|
||||||
*/
|
|
||||||
```
|
|
||||||
|
|
||||||
**优化后:**
|
|
||||||
```javascript
|
|
||||||
/**
|
|
||||||
* 获取用户基本信息
|
|
||||||
* @description 获取当前登录用户的基本信息,包括昵称、头像、个人资料等
|
|
||||||
* @param {Object} [config={}] 自定义请求配置
|
|
||||||
* @param {Object} [config.custom] 自定义请求选项
|
|
||||||
* @param {boolean} [config.custom.loading] 是否显示loading,默认true
|
|
||||||
* @param {boolean} [config.custom.toast] 是否显示错误提示,默认true
|
|
||||||
* @returns {Promise<Object>} 返回用户信息对象
|
|
||||||
* @example
|
|
||||||
* // 基本用法
|
|
||||||
* const userInfo = await getUserInfo()
|
|
||||||
*
|
|
||||||
* // 自定义配置
|
|
||||||
* const userInfo = await getUserInfo({
|
|
||||||
* custom: { loading: false }
|
|
||||||
* })
|
|
||||||
*/
|
|
||||||
```
|
|
||||||
|
|
||||||
## 4. 类型定义和文档完善 ✅
|
|
||||||
|
|
||||||
### 添加了完整的类型定义
|
|
||||||
```javascript
|
|
||||||
/**
|
|
||||||
* @typedef {Object} UserInfo 用户信息对象
|
|
||||||
* @property {string} id 用户ID
|
|
||||||
* @property {string} nickName 用户昵称
|
|
||||||
* @property {string} avatarUrl 头像URL
|
|
||||||
* @property {string} gender 性别:'男' | '女' | '保密'
|
|
||||||
* @property {string} birthday 生日,格式:YYYY-MM-DD
|
|
||||||
* @property {string} region 所在地区
|
|
||||||
* @property {string} bio 个人简介
|
|
||||||
* @property {string} createTime 创建时间
|
|
||||||
* @property {string} updateTime 更新时间
|
|
||||||
*/
|
|
||||||
```
|
|
||||||
|
|
||||||
### 文件头部说明完善
|
|
||||||
- 添加了详细的模块说明
|
|
||||||
- 包含了版本信息和作者信息
|
|
||||||
- 提供了完整的使用示例
|
|
||||||
- 列出了所有功能分组
|
|
||||||
|
|
||||||
## 5. 新增功能和工具 ✅
|
|
||||||
|
|
||||||
### 导出配置常量和工具函数
|
|
||||||
```javascript
|
|
||||||
export const PROFILE_CONFIG = {
|
|
||||||
DEFAULT_CONFIG_TEMPLATES,
|
|
||||||
LOADING_TEXTS
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PROFILE_UTILS = {
|
|
||||||
createRequestConfig,
|
|
||||||
executeGetRequest,
|
|
||||||
executePostRequest,
|
|
||||||
executePutRequest,
|
|
||||||
executeDeleteRequest
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 新增实用API方法
|
|
||||||
- `getUserPreferences()` - 用户偏好设置管理
|
|
||||||
- `updateUserPreferences()` - 偏好设置更新
|
|
||||||
- `getUserActivities()` - 用户活动记录查询
|
|
||||||
|
|
||||||
## 6. 优化成果统计
|
|
||||||
|
|
||||||
### 代码质量指标
|
|
||||||
- **代码复用率**: 提升 60%
|
|
||||||
- **配置一致性**: 提升 80%
|
|
||||||
- **文档完整性**: 提升 90%
|
|
||||||
- **类型安全性**: 提升 70%
|
|
||||||
|
|
||||||
### 文件结构对比
|
|
||||||
**优化前:**
|
|
||||||
- 文件行数: ~270行
|
|
||||||
- API方法: 6个
|
|
||||||
- 配置模板: 0个
|
|
||||||
- 工具函数: 0个
|
|
||||||
- 类型定义: 0个
|
|
||||||
|
|
||||||
**优化后:**
|
|
||||||
- 文件行数: ~450行
|
|
||||||
- API方法: 9个 (+3个新增)
|
|
||||||
- 配置模板: 4个
|
|
||||||
- 工具函数: 5个
|
|
||||||
- 类型定义: 3个
|
|
||||||
|
|
||||||
### 开发体验提升
|
|
||||||
- ✅ **智能提示**: 完整的JSDoc注释支持IDE智能提示
|
|
||||||
- ✅ **类型安全**: TypeScript风格的类型定义
|
|
||||||
- ✅ **示例丰富**: 每个方法都有详细的使用示例
|
|
||||||
- ✅ **配置灵活**: 支持多种配置模板和自定义选项
|
|
||||||
|
|
||||||
## 7. 向后兼容性保证
|
|
||||||
|
|
||||||
### API接口兼容性
|
|
||||||
- ✅ 所有现有API方法的调用方式保持不变
|
|
||||||
- ✅ 参数结构和返回值格式完全兼容
|
|
||||||
- ✅ 登录流程重构中新添加的API接口完整保留
|
|
||||||
|
|
||||||
### 配置兼容性
|
|
||||||
- ✅ 现有的自定义配置方式继续有效
|
|
||||||
- ✅ 新的配置模板作为增强功能,不影响现有代码
|
|
||||||
|
|
||||||
## 8. 使用建议
|
|
||||||
|
|
||||||
### 推荐的使用方式
|
|
||||||
```javascript
|
|
||||||
// 基本API调用
|
|
||||||
import { getUserInfo, updateUserInfo } from '@/http/api/profile.js'
|
|
||||||
|
|
||||||
// 使用配置常量
|
|
||||||
import { PROFILE_CONFIG } from '@/http/api/profile.js'
|
|
||||||
|
|
||||||
// 使用工具函数创建自定义API
|
|
||||||
import { PROFILE_UTILS } from '@/http/api/profile.js'
|
|
||||||
const customAPI = PROFILE_UTILS.executeGetRequest('/custom/endpoint')
|
|
||||||
```
|
|
||||||
|
|
||||||
### 最佳实践
|
|
||||||
1. 优先使用标准配置模板
|
|
||||||
2. 合理利用工具函数创建自定义API
|
|
||||||
3. 充分利用JSDoc注释获得IDE支持
|
|
||||||
4. 使用类型定义提升代码安全性
|
|
||||||
|
|
||||||
## 总结
|
|
||||||
|
|
||||||
本次深层次优化显著提升了 `profile.js` 文件的代码质量和开发体验,建立了可复用的配置体系和工具函数,为后续开发提供了强大的基础设施。所有优化都保持了向后兼容性,确保现有功能正常运行。
|
|
||||||
108
http/api/auth.js
108
http/api/auth.js
|
|
@ -150,6 +150,33 @@ const executeAuthRequest = (url, data = {}, template = 'PUBLIC_AUTH', loadingTex
|
||||||
return uni.$u.http.post(url, data, requestConfig)
|
return uni.$u.http.post(url, data, requestConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行认证请求(支持不同HTTP方法)
|
||||||
|
* @param {string} method HTTP方法
|
||||||
|
* @param {string} url 请求URL
|
||||||
|
* @param {Object} data 请求数据
|
||||||
|
* @param {string} template 配置模板
|
||||||
|
* @param {string} loadingText loading文本
|
||||||
|
* @param {Object} config 自定义配置
|
||||||
|
* @returns {Promise} 请求Promise
|
||||||
|
*/
|
||||||
|
const executeAuthRequestWithMethod = (method, url, data = {}, template = 'PUBLIC_AUTH', loadingText = null, config = {}) => {
|
||||||
|
const requestConfig = createAuthConfig(template, config, loadingText)
|
||||||
|
|
||||||
|
switch (method.toUpperCase()) {
|
||||||
|
case 'GET':
|
||||||
|
return uni.$u.http.get(url, requestConfig)
|
||||||
|
case 'POST':
|
||||||
|
return uni.$u.http.post(url, data, requestConfig)
|
||||||
|
case 'PUT':
|
||||||
|
return uni.$u.http.put(url, data, requestConfig)
|
||||||
|
case 'DELETE':
|
||||||
|
return uni.$u.http.delete(url, requestConfig)
|
||||||
|
default:
|
||||||
|
return uni.$u.http.post(url, data, requestConfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== API方法 ====================
|
// ==================== API方法 ====================
|
||||||
|
|
||||||
// ==================== 用户登录相关API ====================
|
// ==================== 用户登录相关API ====================
|
||||||
|
|
@ -202,30 +229,88 @@ export const userLogin = (loginData, config = {}) => {
|
||||||
* })
|
* })
|
||||||
*/
|
*/
|
||||||
export const wxLogin = (wxData, config = {}) => {
|
export const wxLogin = (wxData, config = {}) => {
|
||||||
return executeAuthRequest('/auth/wx-login', wxData, 'PUBLIC_AUTH', AUTH_LOADING_TEXTS.WX_LOGIN, config)
|
return executeAuthRequest('/wechat/user/mini/login', wxData, 'PUBLIC_AUTH', AUTH_LOADING_TEXTS.WX_LOGIN, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信手机号授权登录
|
* 微信手机号登录
|
||||||
* @description 使用微信手机号授权进行登录认证,适用于小程序环境
|
* @description 通过微信手机号授权进行登录,获取用户token和信息
|
||||||
* @param {PhoneAuthData} phoneData 手机号授权数据对象
|
* @param {PhoneLoginData} phoneData 手机号登录数据对象
|
||||||
* @param {string} phoneData.code 微信登录凭证code
|
* @param {string} phoneData.code 微信登录code
|
||||||
* @param {string} phoneData.encryptedData 加密的手机号数据
|
* @param {string} phoneData.encryptedData 加密的手机号数据
|
||||||
* @param {string} phoneData.iv 初始向量
|
* @param {string} phoneData.iv 初始向量
|
||||||
* @param {Object} [config={}] 自定义请求配置
|
* @param {Object} [config={}] 自定义请求配置
|
||||||
* @returns {Promise<AuthResult>} 返回认证结果,包含token和用户信息
|
* @returns {Promise<Object>} 返回登录结果,包含token和用户信息
|
||||||
* @example
|
* @example
|
||||||
* // 微信手机号登录
|
* // 微信手机号登录
|
||||||
* const result = await wxPhoneLogin({
|
* const result = await wxPhoneLogin({
|
||||||
* code: 'wx_code_123',
|
* code: 'wx_login_code',
|
||||||
|
* encryptedData: 'encrypted_phone_data',
|
||||||
|
* iv: 'initial_vector'
|
||||||
|
* })
|
||||||
|
*/
|
||||||
|
export const wxPhoneLogin = (phoneData, config = {}) => {
|
||||||
|
return executeAuthRequest('/wechat/user/mini/phone-login', phoneData, 'PUBLIC_AUTH', AUTH_LOADING_TEXTS.PHONE_LOGIN, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新用户手机号
|
||||||
|
* @description 通过微信手机号授权更新已登录用户的手机号,需要用户已登录
|
||||||
|
* @param {PhoneAuthData} phoneData 手机号授权数据对象
|
||||||
|
* @param {string} phoneData.encryptedData 加密的手机号数据
|
||||||
|
* @param {string} phoneData.iv 初始向量
|
||||||
|
* @param {Object} [config={}] 自定义请求配置
|
||||||
|
* @returns {Promise<string>} 返回解密后的手机号
|
||||||
|
* @example
|
||||||
|
* // 更新用户手机号
|
||||||
|
* const phoneNumber = await updatePhoneNumber({
|
||||||
* encryptedData: 'encrypted_phone_data',
|
* encryptedData: 'encrypted_phone_data',
|
||||||
* iv: 'initial_vector'
|
* iv: 'initial_vector'
|
||||||
* })
|
* })
|
||||||
*
|
*
|
||||||
* @since 2.0.0 新增的登录流程重构功能
|
* @since 2.0.0 重构为手机号更新功能
|
||||||
*/
|
*/
|
||||||
export const wxPhoneLogin = (phoneData, config = {}) => {
|
export const updatePhoneNumber = (phoneData, config = {}) => {
|
||||||
return executeAuthRequest('/auth/wx-phone-login', phoneData, 'PUBLIC_AUTH', AUTH_LOADING_TEXTS.PHONE_VERIFY, config)
|
return executeAuthRequest('/user/wechat/mini/phone-update', phoneData, 'AUTHENTICATED_SESSION', AUTH_LOADING_TEXTS.PHONE_VERIFY, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户信息
|
||||||
|
* @description 获取当前登录用户的详细信息
|
||||||
|
* @param {Object} [config={}] 自定义请求配置
|
||||||
|
* @returns {Promise<Object>} 返回用户信息
|
||||||
|
* @example
|
||||||
|
* // 获取用户信息
|
||||||
|
* const userInfo = await getUserInfo()
|
||||||
|
*
|
||||||
|
* @since 2.0.0 微信小程序用户信息接口
|
||||||
|
*/
|
||||||
|
export const getUserInfo = (config = {}) => {
|
||||||
|
return executeAuthRequestWithMethod('GET', '/user/wechat/mini/userinfo', {}, 'AUTHENTICATED_SESSION', '获取用户信息中...', config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新用户信息
|
||||||
|
* @description 更新当前登录用户的信息
|
||||||
|
* @param {Object} userData 用户数据对象
|
||||||
|
* @param {string} [userData.nickname] 用户昵称
|
||||||
|
* @param {string} [userData.avatar] 用户头像URL
|
||||||
|
* @param {number} [userData.gender] 性别:0-未知,1-男,2-女
|
||||||
|
* @param {string} [userData.city] 城市
|
||||||
|
* @param {string} [userData.province] 省份
|
||||||
|
* @param {string} [userData.country] 国家
|
||||||
|
* @param {Object} [config={}] 自定义请求配置
|
||||||
|
* @returns {Promise<Object>} 返回更新结果
|
||||||
|
* @example
|
||||||
|
* // 更新用户昵称
|
||||||
|
* const result = await updateUserInfo({
|
||||||
|
* nickname: '新昵称'
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* @since 2.0.0 微信小程序用户信息更新接口
|
||||||
|
*/
|
||||||
|
export const updateUserInfo = (userData, config = {}) => {
|
||||||
|
return executeAuthRequestWithMethod('PUT', '/user/wechat/mini/userinfo', userData, 'AUTHENTICATED_SESSION', '更新用户信息中...', config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 用户注册相关API ====================
|
// ==================== 用户注册相关API ====================
|
||||||
|
|
@ -393,5 +478,6 @@ export const AUTH_CONFIG = {
|
||||||
*/
|
*/
|
||||||
export const AUTH_UTILS = {
|
export const AUTH_UTILS = {
|
||||||
createAuthConfig,
|
createAuthConfig,
|
||||||
executeAuthRequest
|
executeAuthRequest,
|
||||||
|
executeAuthRequestWithMethod
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -230,6 +230,27 @@ export const getUserInfo = (config = {}) => {
|
||||||
return executeGetRequest('/user/info', {}, 'AUTHENTICATED_QUERY_WITH_LOADING', config)
|
return executeGetRequest('/user/info', {}, 'AUTHENTICATED_QUERY_WITH_LOADING', config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取微信用户信息
|
||||||
|
* @description 获取当前登录用户的微信相关信息,包括微信昵称、头像等
|
||||||
|
* @param {Object} [config={}] 自定义请求配置
|
||||||
|
* @param {Object} [config.custom] 自定义请求选项
|
||||||
|
* @param {boolean} [config.custom.loading] 是否显示loading,默认false
|
||||||
|
* @param {boolean} [config.custom.toast] 是否显示错误提示,默认true
|
||||||
|
* @returns {Promise<Object>} 返回微信用户信息对象
|
||||||
|
* @example
|
||||||
|
* // 基本用法
|
||||||
|
* const wxUserInfo = await getWxUserInfo()
|
||||||
|
*
|
||||||
|
* // 自定义配置
|
||||||
|
* const wxUserInfo = await getWxUserInfo({
|
||||||
|
* custom: { loading: true }
|
||||||
|
* })
|
||||||
|
*/
|
||||||
|
export const getWxUserInfo = (config = {}) => {
|
||||||
|
return executeGetRequest('/user/wechat/mini/userinfo', {}, 'AUTHENTICATED_QUERY', config)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新用户基本信息
|
* 更新用户基本信息
|
||||||
* @description 更新用户的基本信息,如昵称、头像、个人简介等
|
* @description 更新用户的基本信息,如昵称、头像、个人简介等
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
const ENV_CONFIG = {
|
const ENV_CONFIG = {
|
||||||
// 开发环境
|
// 开发环境
|
||||||
development: {
|
development: {
|
||||||
baseURL: 'https://dev-api.pet-ai.com',
|
baseURL: 'http://localhost:8888',
|
||||||
timeout: 30000
|
timeout: 30000
|
||||||
},
|
},
|
||||||
// 测试环境
|
// 测试环境
|
||||||
|
|
@ -57,32 +57,18 @@ export const HTTP_CONFIG = {
|
||||||
*/
|
*/
|
||||||
export const NO_AUTH_APIS = [
|
export const NO_AUTH_APIS = [
|
||||||
// 用户认证相关
|
// 用户认证相关
|
||||||
'/auth/login',
|
'/wechat/user/mini/login', // 微信小程序登录
|
||||||
'/auth/register',
|
]
|
||||||
'/auth/wx-login',
|
|
||||||
'/auth/wx-phone-login', // 微信手机号登录
|
|
||||||
'/auth/refresh',
|
|
||||||
'/auth/reset-password', // 重置密码
|
|
||||||
|
|
||||||
// 短信验证相关
|
/**
|
||||||
'/sms/send', // 发送短信验证码
|
* 需要鉴权的接口列表(明确需要登录的接口)
|
||||||
'/sms/verify', // 验证短信验证码
|
* 这些接口在调用时如果未登录会自动跳转到登录页面
|
||||||
|
*/
|
||||||
// 公开浏览的接口
|
export const AUTH_REQUIRED_APIS = [
|
||||||
'/ai/knowledge', // AI知识库
|
// 微信小程序用户相关接口
|
||||||
'/adoption/pets', // 浏览待领养宠物
|
'/user/wechat/mini/userinfo', // 获取用户信息
|
||||||
'/adoption/pets/search', // 搜索宠物
|
'/user/wechat/mini/phone-update', // 更新手机号
|
||||||
'/adoption/pets/filter', // 筛选宠物
|
'/user/wechat/mini/check-unionid', // 检查UnionID
|
||||||
'/adoption/organizations', // 领养机构列表
|
|
||||||
'/reviews', // 查看评价
|
|
||||||
'/reviews/*', // 查看特定对象的评价
|
|
||||||
|
|
||||||
// 系统相关
|
|
||||||
'/system/config', // 系统配置
|
|
||||||
'/system/version', // 版本信息
|
|
||||||
'/system/check-update', // 检查更新
|
|
||||||
'/common/regions', // 地区数据
|
|
||||||
'/common/upload-config' // 上传配置
|
|
||||||
]
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -111,6 +97,31 @@ export function checkApiAuth(url) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查接口是否需要强制登录
|
||||||
|
* @param {String} url 接口URL
|
||||||
|
* @returns {Boolean} 是否需要强制登录
|
||||||
|
*/
|
||||||
|
export function checkAuthRequiredApi(url) {
|
||||||
|
// 移除查询参数,只保留路径
|
||||||
|
const path = url.split('?')[0]
|
||||||
|
|
||||||
|
// 检查是否在需要强制登录的列表中
|
||||||
|
for (const authApi of AUTH_REQUIRED_APIS) {
|
||||||
|
// 支持通配符匹配
|
||||||
|
if (authApi.includes('*')) {
|
||||||
|
const regex = new RegExp('^' + authApi.replace(/\*/g, '.*') + '$')
|
||||||
|
if (regex.test(path)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else if (path === authApi || path.startsWith(authApi + '/')) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加不需要鉴权的接口
|
* 添加不需要鉴权的接口
|
||||||
* @param {String|Array} apis 接口路径或接口路径数组
|
* @param {String|Array} apis 接口路径或接口路径数组
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,111 @@
|
||||||
// HTTP请求配置和拦截器
|
// HTTP请求配置和拦截器
|
||||||
// 基于uView-next的luch-request库实现
|
// 基于uView-next的luch-request库实现
|
||||||
|
|
||||||
import { HTTP_CONFIG, checkApiAuth } from './config.js'
|
import { HTTP_CONFIG, checkApiAuth, AUTH_REQUIRED_APIS } from './config.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理认证数据
|
||||||
|
*/
|
||||||
|
function clearAuthData() {
|
||||||
|
console.log('HTTP拦截器: 清理认证数据被调用')
|
||||||
|
console.trace('调用堆栈:')
|
||||||
|
uni.removeStorageSync(HTTP_CONFIG.storageKeys.token)
|
||||||
|
uni.removeStorageSync(HTTP_CONFIG.storageKeys.refreshToken)
|
||||||
|
uni.removeStorageSync(HTTP_CONFIG.storageKeys.userInfo)
|
||||||
|
uni.removeStorageSync('loginStep')
|
||||||
|
uni.removeStorageSync('wxLoginCode')
|
||||||
|
uni.removeStorageSync('wxUserInfo')
|
||||||
|
uni.removeStorageSync('loginDate')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查接口是否需要强制登录
|
||||||
|
* @param {String} url 接口URL
|
||||||
|
* @returns {Boolean} 是否需要强制登录
|
||||||
|
*/
|
||||||
|
function checkAuthRequiredApi(url) {
|
||||||
|
// 移除查询参数,只保留路径
|
||||||
|
const path = url.split('?')[0]
|
||||||
|
|
||||||
|
// 检查是否在需要强制登录的列表中
|
||||||
|
for (const authApi of AUTH_REQUIRED_APIS) {
|
||||||
|
// 支持通配符匹配
|
||||||
|
if (authApi.includes('*')) {
|
||||||
|
const regex = new RegExp('^' + authApi.replace(/\*/g, '.*') + '$')
|
||||||
|
if (regex.test(path)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else if (path === authApi || path.startsWith(authApi + '/')) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查用户是否已完成登录
|
||||||
|
* @returns {Boolean} 是否已完成登录
|
||||||
|
*/
|
||||||
|
function isUserLoggedIn() {
|
||||||
|
const token = uni.getStorageSync(HTTP_CONFIG.storageKeys.token)
|
||||||
|
const loginStep = uni.getStorageSync('loginStep')
|
||||||
|
|
||||||
|
// 检查是否有有效的token和完成的登录流程
|
||||||
|
return !!(token && (loginStep === 'profile_completed' || loginStep === 'phone_authed'))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试自动登录
|
||||||
|
* @returns {Promise<Boolean>} 是否登录成功
|
||||||
|
*/
|
||||||
|
async function tryAutoLogin() {
|
||||||
|
try {
|
||||||
|
return await autoLogin()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('自动登录失败:', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理需要鉴权的情况
|
||||||
|
*/
|
||||||
|
function handleAuthRequired() {
|
||||||
|
const loginStep = uni.getStorageSync('loginStep')
|
||||||
|
|
||||||
|
// 根据当前登录状态决定跳转页面
|
||||||
|
let targetPage = '/pages/login/login'
|
||||||
|
let toastMessage = '请先登录'
|
||||||
|
|
||||||
|
switch (loginStep) {
|
||||||
|
case 'wx_logged':
|
||||||
|
targetPage = '/pages/auth/phone-auth'
|
||||||
|
toastMessage = '请完成手机号授权'
|
||||||
|
break
|
||||||
|
case 'phone_authed':
|
||||||
|
case 'phone_skipped':
|
||||||
|
targetPage = '/pages/profile/user-info?mode=setup'
|
||||||
|
toastMessage = '请完善个人信息'
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
targetPage = '/pages/login/login'
|
||||||
|
toastMessage = '请先登录'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示提示并跳转
|
||||||
|
uni.showToast({
|
||||||
|
title: toastMessage,
|
||||||
|
icon: 'none',
|
||||||
|
duration: 1500
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateTo({ url: targetPage }).catch(() => {
|
||||||
|
uni.reLaunch({ url: targetPage })
|
||||||
|
})
|
||||||
|
}, 1500)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化HTTP配置
|
* 初始化HTTP配置
|
||||||
|
|
@ -24,20 +128,34 @@ export default (vm) => {
|
||||||
config.data = config.data || {}
|
config.data = config.data || {}
|
||||||
config.custom = config.custom || {}
|
config.custom = config.custom || {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 自动检查接口是否需要鉴权(如果没有明确指定)
|
// 自动检查接口是否需要鉴权(如果没有明确指定)
|
||||||
if (config.custom.auth === undefined) {
|
if (config.custom.auth === undefined) {
|
||||||
config.custom.auth = checkApiAuth(config.url)
|
config.custom.auth = checkApiAuth(config.url)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据custom参数中配置的是否需要token,添加对应的请求头
|
// 检查是否为需要强制登录的接口
|
||||||
if (config.custom.auth) {
|
const isAuthRequiredApi = checkAuthRequiredApi(config.url)
|
||||||
// 从本地存储获取token
|
|
||||||
|
// 如果是需要强制登录的接口,检查登录状态
|
||||||
|
if (isAuthRequiredApi) {
|
||||||
|
if (!isUserLoggedIn()) {
|
||||||
|
console.log('访问需要鉴权的接口但未完成登录,跳转到登录流程:', config.url)
|
||||||
|
handleAuthRequired()
|
||||||
|
return Promise.reject({
|
||||||
|
message: '需要登录',
|
||||||
|
code: 'AUTH_REQUIRED',
|
||||||
|
url: config.url
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加Authorization请求头
|
||||||
|
if (config.custom.auth !== false) {
|
||||||
const token = uni.getStorageSync(HTTP_CONFIG.storageKeys.token)
|
const token = uni.getStorageSync(HTTP_CONFIG.storageKeys.token)
|
||||||
if (token) {
|
if (token) {
|
||||||
config.header.Authorization = `Bearer ${token}`
|
config.header.Authorization = `Bearer ${token}`
|
||||||
} else {
|
|
||||||
// 如果需要token但没有token,可以跳转到登录页
|
|
||||||
console.warn('需要token但未找到,请先登录')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,6 +170,11 @@ export default (vm) => {
|
||||||
return config
|
return config
|
||||||
}, config => {
|
}, config => {
|
||||||
// 请求错误处理
|
// 请求错误处理
|
||||||
|
// 如果是AUTH_REQUIRED错误,不显示loading
|
||||||
|
if (config.code === 'AUTH_REQUIRED') {
|
||||||
|
// 不显示loading,因为会跳转页面
|
||||||
|
return Promise.reject(config)
|
||||||
|
}
|
||||||
return Promise.reject(config)
|
return Promise.reject(config)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -67,8 +190,8 @@ export default (vm) => {
|
||||||
|
|
||||||
// 统一的响应数据处理
|
// 统一的响应数据处理
|
||||||
if (data.code !== undefined) {
|
if (data.code !== undefined) {
|
||||||
// 如果服务端返回的状态码不等于200,则reject()
|
// 如果服务端返回的状态码不等于0(成功),则reject()
|
||||||
if (data.code !== 200) {
|
if (data.code !== 0) {
|
||||||
// 如果没有显式定义custom的toast参数为false的话,默认对报错进行toast弹出提示
|
// 如果没有显式定义custom的toast参数为false的话,默认对报错进行toast弹出提示
|
||||||
if (custom?.toast !== false) {
|
if (custom?.toast !== false) {
|
||||||
uni.$u.toast(data.message || '请求失败')
|
uni.$u.toast(data.message || '请求失败')
|
||||||
|
|
@ -76,13 +199,9 @@ export default (vm) => {
|
||||||
|
|
||||||
// 特殊状态码处理
|
// 特殊状态码处理
|
||||||
if (data.code === 401) {
|
if (data.code === 401) {
|
||||||
// token过期,清除本地token并跳转到登录页
|
// token过期,清除本地认证信息并跳转到登录流程
|
||||||
uni.removeStorageSync(HTTP_CONFIG.storageKeys.token)
|
clearAuthData()
|
||||||
uni.removeStorageSync(HTTP_CONFIG.storageKeys.refreshToken)
|
handleAuthRequired()
|
||||||
uni.removeStorageSync(HTTP_CONFIG.storageKeys.userInfo)
|
|
||||||
uni.reLaunch({
|
|
||||||
url: HTTP_CONFIG.loginPage
|
|
||||||
})
|
|
||||||
return Promise.reject(data)
|
return Promise.reject(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,14 +235,8 @@ export default (vm) => {
|
||||||
break
|
break
|
||||||
case 401:
|
case 401:
|
||||||
errorMessage = '未授权,请重新登录'
|
errorMessage = '未授权,请重新登录'
|
||||||
// 清除本地认证信息
|
clearAuthData()
|
||||||
uni.removeStorageSync(HTTP_CONFIG.storageKeys.token)
|
handleAuthRequired()
|
||||||
uni.removeStorageSync(HTTP_CONFIG.storageKeys.refreshToken)
|
|
||||||
uni.removeStorageSync(HTTP_CONFIG.storageKeys.userInfo)
|
|
||||||
// 跳转到登录页
|
|
||||||
uni.reLaunch({
|
|
||||||
url: HTTP_CONFIG.loginPage
|
|
||||||
})
|
|
||||||
break
|
break
|
||||||
case 403:
|
case 403:
|
||||||
errorMessage = '拒绝访问'
|
errorMessage = '拒绝访问'
|
||||||
|
|
|
||||||
|
|
@ -1,163 +0,0 @@
|
||||||
# Pages 目录优化报告
|
|
||||||
|
|
||||||
## 优化概览
|
|
||||||
|
|
||||||
本次优化对 `pages` 目录下的页面组件进行了全面的代码质量、性能和可维护性优化。
|
|
||||||
|
|
||||||
## 1. Auth 模块优化 ✅
|
|
||||||
|
|
||||||
### pages/auth/phone-auth.vue
|
|
||||||
**优化内容:**
|
|
||||||
- ✅ **错误处理优化**:提取了 `handleError` 方法,统一处理各种错误类型
|
|
||||||
- ✅ **代码结构优化**:简化了错误处理逻辑,减少重复代码
|
|
||||||
- ✅ **用户体验提升**:改进了错误提示信息的准确性和友好性
|
|
||||||
|
|
||||||
**优化前后对比:**
|
|
||||||
```javascript
|
|
||||||
// 优化前:重复的错误处理逻辑
|
|
||||||
} catch (error) {
|
|
||||||
// 30+ 行重复的错误处理代码
|
|
||||||
}
|
|
||||||
|
|
||||||
// 优化后:统一的错误处理方法
|
|
||||||
} catch (error) {
|
|
||||||
handleError(error)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 2. Profile 模块优化 ✅
|
|
||||||
|
|
||||||
### pages/profile/profile.vue
|
|
||||||
**优化内容:**
|
|
||||||
- ✅ **数据结构优化**:清理了多余的空行,优化了响应式数据的组织
|
|
||||||
- ✅ **方法重构**:提取了 `resetStatsToDefault` 方法,避免重复的数据重置逻辑
|
|
||||||
- ✅ **导航优化**:改进了 `navigateTo` 方法,添加了更好的错误处理和自定义选项
|
|
||||||
- ✅ **代码清理**:移除了多余的空行和注释
|
|
||||||
|
|
||||||
**优化前后对比:**
|
|
||||||
```javascript
|
|
||||||
// 优化前:重复的数据重置
|
|
||||||
petStats.petCount = 0
|
|
||||||
petStats.recordCount = 0
|
|
||||||
// ... 更多重复代码
|
|
||||||
|
|
||||||
// 优化后:统一的重置方法
|
|
||||||
resetStatsToDefault()
|
|
||||||
```
|
|
||||||
|
|
||||||
### pages/profile/user-info.vue
|
|
||||||
**优化内容:**
|
|
||||||
- ✅ **选择器配置优化**:统一了选择器数据的管理
|
|
||||||
- ✅ **事件处理简化**:移除了不必要的 `@change` 事件处理器
|
|
||||||
- ✅ **代码清理**:删除了未使用的方法(`onNickNameChange`, `onBioChange`)
|
|
||||||
|
|
||||||
**优化前后对比:**
|
|
||||||
```javascript
|
|
||||||
// 优化前:不必要的事件处理
|
|
||||||
<u-input v-model="userInfo.nickName" @change="onNickNameChange" />
|
|
||||||
|
|
||||||
// 优化后:直接使用 v-model
|
|
||||||
<u-input v-model="userInfo.nickName" />
|
|
||||||
```
|
|
||||||
|
|
||||||
## 3. Pets 模块优化 ✅
|
|
||||||
|
|
||||||
### pages/pets/pets.vue
|
|
||||||
**优化内容:**
|
|
||||||
- ✅ **配置数据提取**:将硬编码的配置数据提取到文件顶部
|
|
||||||
- ✅ **方法重构**:创建了 `generateDefaultPets` 方法,分离了数据生成逻辑
|
|
||||||
- ✅ **代码组织**:优化了 `getPetEmoji` 方法,使用配置映射
|
|
||||||
|
|
||||||
**优化前后对比:**
|
|
||||||
```javascript
|
|
||||||
// 优化前:硬编码的表情映射
|
|
||||||
getPetEmoji(breed) {
|
|
||||||
const emojiMap = {
|
|
||||||
'橘猫': '🐱',
|
|
||||||
// ... 更多硬编码
|
|
||||||
}
|
|
||||||
return emojiMap[breed] || '🐾'
|
|
||||||
}
|
|
||||||
|
|
||||||
// 优化后:使用配置常量
|
|
||||||
getPetEmoji(breed) {
|
|
||||||
return PET_EMOJI_MAP[breed] || '🐾'
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 4. Adoption 模块优化 ✅
|
|
||||||
|
|
||||||
### pages/adoption/adoption.vue
|
|
||||||
**优化内容:**
|
|
||||||
- ✅ **数据结构优化**:重新组织了 data 中的属性,按功能分组
|
|
||||||
- ✅ **代码清理**:清理了多余的空行和注释
|
|
||||||
- ✅ **可读性提升**:改进了数据属性的分组和命名
|
|
||||||
|
|
||||||
## 5. 全局优化成果
|
|
||||||
|
|
||||||
### 代码质量提升
|
|
||||||
- ✅ **减少重复代码**:提取了公共方法和配置
|
|
||||||
- ✅ **统一错误处理**:建立了一致的错误处理模式
|
|
||||||
- ✅ **改进代码结构**:优化了方法组织和数据结构
|
|
||||||
|
|
||||||
### 性能优化
|
|
||||||
- ✅ **减少不必要的事件监听**:移除了冗余的 change 事件
|
|
||||||
- ✅ **优化数据初始化**:改进了默认数据的生成方式
|
|
||||||
- ✅ **提升渲染效率**:减少了不必要的响应式更新
|
|
||||||
|
|
||||||
### 可维护性提升
|
|
||||||
- ✅ **配置化管理**:将硬编码数据提取为配置常量
|
|
||||||
- ✅ **方法职责单一**:每个方法都有明确的职责
|
|
||||||
- ✅ **代码组织清晰**:按功能模块组织代码结构
|
|
||||||
|
|
||||||
## 6. 保留的功能特性
|
|
||||||
|
|
||||||
### 登录流程功能 ✅
|
|
||||||
- ✅ 保留了所有登录流程重构中新添加的功能
|
|
||||||
- ✅ 保持了页面间的数据传递和状态管理
|
|
||||||
- ✅ 确保了所有页面的路由配置正确
|
|
||||||
|
|
||||||
### 用户体验 ✅
|
|
||||||
- ✅ 维护了现有的用户体验和交互逻辑
|
|
||||||
- ✅ 保持了页面的响应式设计
|
|
||||||
- ✅ 确保了样式的一致性
|
|
||||||
|
|
||||||
## 7. 优化统计
|
|
||||||
|
|
||||||
### 文件修改统计
|
|
||||||
- **优化文件数量**: 5个主要文件
|
|
||||||
- **代码行数减少**: ~150行
|
|
||||||
- **方法提取**: 6个新的工具方法
|
|
||||||
- **配置常量**: 3个新的配置对象
|
|
||||||
|
|
||||||
### 质量指标改进
|
|
||||||
- 📊 **代码复用率**: 提升 25%
|
|
||||||
- 🚀 **错误处理一致性**: 提升 40%
|
|
||||||
- 🔧 **可维护性**: 提升 30%
|
|
||||||
- 📚 **代码可读性**: 提升 35%
|
|
||||||
|
|
||||||
## 8. 后续建议
|
|
||||||
|
|
||||||
### 进一步优化方向
|
|
||||||
1. **组件化重构**: 将重复的UI组件提取为公共组件
|
|
||||||
2. **状态管理**: 考虑引入 Pinia 进行全局状态管理
|
|
||||||
3. **类型安全**: 考虑引入 TypeScript 提升代码安全性
|
|
||||||
4. **性能监控**: 添加性能监控和错误上报机制
|
|
||||||
|
|
||||||
### 维护建议
|
|
||||||
1. **代码规范**: 建立统一的代码规范和 ESLint 配置
|
|
||||||
2. **文档完善**: 为复杂的业务逻辑添加详细注释
|
|
||||||
3. **测试覆盖**: 为关键功能添加单元测试
|
|
||||||
4. **持续优化**: 定期进行代码审查和重构
|
|
||||||
|
|
||||||
## 总结
|
|
||||||
|
|
||||||
本次优化成功提升了 pages 目录下代码的质量、性能和可维护性,同时保持了所有现有功能的完整性。新的代码结构更加清晰、易于维护,为后续开发提供了良好的基础。
|
|
||||||
|
|
||||||
优化重点关注了:
|
|
||||||
- 代码复用和模块化
|
|
||||||
- 错误处理的统一性
|
|
||||||
- 配置数据的管理
|
|
||||||
- 用户体验的保持
|
|
||||||
|
|
||||||
所有优化都经过了仔细的测试和验证,确保不会影响现有功能的正常运行。
|
|
||||||
|
|
@ -81,22 +81,27 @@ export default {
|
||||||
// 错误处理函数
|
// 错误处理函数
|
||||||
const handleError = (error) => {
|
const handleError = (error) => {
|
||||||
let errorMessage = '授权失败,请重试'
|
let errorMessage = '授权失败,请重试'
|
||||||
|
let shouldReturnToLogin = false
|
||||||
|
|
||||||
|
console.error('手机号授权错误详情:', error)
|
||||||
|
|
||||||
if (error.message) {
|
if (error.message) {
|
||||||
if (error.message.includes('网络')) {
|
if (error.message.includes('网络')) {
|
||||||
errorMessage = '网络连接异常,请检查网络后重试'
|
errorMessage = '网络连接异常,请检查网络后重试'
|
||||||
} else if (error.message.includes('登录状态已过期')) {
|
} else if (error.message.includes('登录状态已过期') || error.message.includes('重新获取微信登录code失败')) {
|
||||||
errorMessage = '登录状态已过期,请重新登录'
|
errorMessage = '登录状态已过期,请重新登录'
|
||||||
// 3秒后自动返回profile页面
|
shouldReturnToLogin = true
|
||||||
setTimeout(() => {
|
|
||||||
uni.reLaunch({
|
|
||||||
url: '/pages/profile/profile'
|
|
||||||
})
|
|
||||||
}, 3000)
|
|
||||||
} else if (error.message.includes('API返回数据格式错误')) {
|
} else if (error.message.includes('API返回数据格式错误')) {
|
||||||
errorMessage = '服务器响应异常,请稍后重试'
|
errorMessage = '服务器响应异常,请稍后重试'
|
||||||
|
} else if (error.message.includes('用户拒绝')) {
|
||||||
|
errorMessage = '需要授权手机号才能继续使用'
|
||||||
|
} else if (error.code === 401) {
|
||||||
|
errorMessage = '授权已过期,请重新登录'
|
||||||
|
shouldReturnToLogin = true
|
||||||
|
} else if (error.code === 500) {
|
||||||
|
errorMessage = '服务器错误,请稍后重试'
|
||||||
} else {
|
} else {
|
||||||
errorMessage = error.message
|
errorMessage = error.message || '授权失败,请重试'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,6 +110,15 @@ export default {
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
duration: 3000
|
duration: 3000
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 如果需要返回登录页面
|
||||||
|
if (shouldReturnToLogin) {
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/profile/profile'
|
||||||
|
})
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 方法定义
|
// 方法定义
|
||||||
|
|
@ -120,46 +134,75 @@ export default {
|
||||||
if (e.detail.errMsg === 'getPhoneNumber:ok') {
|
if (e.detail.errMsg === 'getPhoneNumber:ok') {
|
||||||
isProcessing = true
|
isProcessing = true
|
||||||
authorizing.value = true
|
authorizing.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 获取微信登录code
|
// 获取微信登录code
|
||||||
const loginCode = uni.getStorageSync(STORAGE_KEYS.WX_LOGIN_CODE)
|
let loginCode = uni.getStorageSync(STORAGE_KEYS.WX_LOGIN_CODE)
|
||||||
|
|
||||||
|
// 检查登录code是否存在和有效
|
||||||
if (!loginCode) {
|
if (!loginCode) {
|
||||||
throw new Error('登录状态已过期,请重新登录')
|
console.warn('微信登录code不存在,尝试重新获取')
|
||||||
|
// 尝试重新获取微信登录code
|
||||||
|
try {
|
||||||
|
const loginRes = await new Promise((resolve, reject) => {
|
||||||
|
uni.login({
|
||||||
|
success: resolve,
|
||||||
|
fail: reject
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (loginRes.code) {
|
||||||
|
loginCode = loginRes.code
|
||||||
|
uni.setStorageSync(STORAGE_KEYS.WX_LOGIN_CODE, loginCode)
|
||||||
|
console.log('重新获取微信登录code成功')
|
||||||
|
} else {
|
||||||
|
throw new Error('重新获取微信登录code失败')
|
||||||
|
}
|
||||||
|
} catch (loginError) {
|
||||||
|
console.error('重新获取微信登录code失败:', loginError)
|
||||||
|
throw new Error('登录状态已过期,请重新登录')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调用后端API验证手机号
|
// 调用后端API进行手机号登录
|
||||||
const phoneData = {
|
const phoneData = {
|
||||||
code: loginCode,
|
code: loginCode,
|
||||||
encryptedData: e.detail.encryptedData,
|
encryptedData: e.detail.encryptedData,
|
||||||
iv: e.detail.iv
|
iv: e.detail.iv
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调用真实API接口
|
// 调用手机号登录API
|
||||||
const result = await wxPhoneLogin(phoneData, {
|
const result = await wxPhoneLogin(phoneData, {
|
||||||
custom: {
|
custom: {
|
||||||
loading: false // 使用页面自己的loading状态
|
loading: false, // 使用页面自己的loading状态
|
||||||
|
catch: true // 确保错误能被catch到
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 使用状态管理工具保存手机号授权数据
|
// 处理登录结果
|
||||||
if (result && result.token) {
|
if (result && result.token) {
|
||||||
savePhoneAuthData(result)
|
// 保存手机号登录数据
|
||||||
} else {
|
savePhoneAuthData({
|
||||||
throw new Error('API返回数据格式错误')
|
token: result.token,
|
||||||
}
|
refreshToken: result.refreshToken,
|
||||||
|
userInfo: result.userInfo || result
|
||||||
uni.showToast({
|
|
||||||
title: '授权成功',
|
|
||||||
icon: 'success'
|
|
||||||
})
|
|
||||||
|
|
||||||
// 跳转到个人信息设置页面
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pages/profile/user-info?mode=setup'
|
|
||||||
})
|
})
|
||||||
}, 1500)
|
|
||||||
|
uni.showToast({
|
||||||
|
title: '手机号授权成功',
|
||||||
|
icon: 'success',
|
||||||
|
duration: 1500
|
||||||
|
})
|
||||||
|
|
||||||
|
// 跳转到用户信息设置页面
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/profile/user-info?mode=setup'
|
||||||
|
})
|
||||||
|
}, 1500)
|
||||||
|
} else {
|
||||||
|
throw new Error('手机号登录失败')
|
||||||
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('手机号授权失败:', error)
|
console.error('手机号授权失败:', error)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<view class="profile-container page-container-unified">
|
<view class="profile-container page-container-unified">
|
||||||
<!-- 用户信息卡片 -->
|
<!-- 用户信息卡片 -->
|
||||||
<view class="user-info-card">
|
<view class="user-info-card">
|
||||||
<view class="user-avatar-section" :class="{ 'logged-in': userInfo.nickName }" @click="handleUserAction">
|
<view class="user-avatar-section" :class="{ 'logged-in': isLoggedIn }" @click="handleUserAction">
|
||||||
<view class="avatar-wrapper">
|
<view class="avatar-wrapper">
|
||||||
<u-avatar
|
<u-avatar
|
||||||
:src="userInfo.avatarUrl || ''"
|
:src="userInfo.avatarUrl || ''"
|
||||||
|
|
@ -13,19 +13,19 @@
|
||||||
color="white"
|
color="white"
|
||||||
font-size="40"
|
font-size="40"
|
||||||
></u-avatar>
|
></u-avatar>
|
||||||
<view class="online-status" v-if="userInfo.nickName">
|
<view class="online-status" v-if="isLoggedIn">
|
||||||
<view class="status-dot"></view>
|
<view class="status-dot"></view>
|
||||||
</view>
|
</view>
|
||||||
<!-- 登录提示图标,仅在未登录时显示 -->
|
<!-- 登录提示图标,仅在未登录时显示 -->
|
||||||
<view class="login-hint" v-if="!userInfo.nickName">
|
<view class="login-hint" v-if="!isLoggedIn">
|
||||||
<text class="hint-icon">👋</text>
|
<text class="hint-icon">👋</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="user-info-section">
|
<view class="user-info-section">
|
||||||
<view class="user-name">{{ userInfo.nickName || '点击头像登录' }}</view>
|
<view class="user-name">{{ isLoggedIn ? (userInfo.nickName || '用户') : '点击头像登录' }}</view>
|
||||||
<view class="user-status">{{ userInfo.nickName ? '已登录' : '未登录' }}</view>
|
<view class="user-status">{{ isLoggedIn ? '已登录' : '未登录' }}</view>
|
||||||
<view class="login-days" v-if="userInfo.nickName">已使用 {{ loginDays }} 天</view>
|
<view class="login-days" v-if="isLoggedIn">已使用 {{ loginDays }} 天</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
@ -143,14 +143,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { reactive, ref, onMounted, computed } from 'vue'
|
import { reactive, ref, onMounted, computed } from 'vue'
|
||||||
import { uploadImage } from '@/http/api/common.js'
|
import { uploadImage } from '@/http/api/common.js'
|
||||||
import { updateUserInfo } from '@/http/api/profile.js'
|
import { getUserInfo, getWxUserInfo, updateUserInfo } from '@/http/api/profile.js'
|
||||||
import {
|
|
||||||
getCurrentLoginStep,
|
|
||||||
isLoginCompleted,
|
|
||||||
checkAndResumeLogin,
|
|
||||||
saveWxLoginData,
|
|
||||||
clearTempLoginData
|
|
||||||
} from '@/utils/loginState.js'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProfilePage',
|
name: 'ProfilePage',
|
||||||
|
|
@ -163,6 +156,11 @@ export default {
|
||||||
|
|
||||||
const loginDays = ref(0)
|
const loginDays = ref(0)
|
||||||
|
|
||||||
|
// 计算属性
|
||||||
|
const isLoggedIn = computed(() => {
|
||||||
|
return !!uni.getStorageSync('token')
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -189,47 +187,76 @@ export default {
|
||||||
|
|
||||||
// 方法定义
|
// 方法定义
|
||||||
const initPage = () => {
|
const initPage = () => {
|
||||||
checkLogin()
|
const token = uni.getStorageSync('token')
|
||||||
loadUserStats()
|
if (token) {
|
||||||
}
|
// 有token时加载用户信息和统计数据
|
||||||
|
loadUserInfo()
|
||||||
const checkLogin = () => {
|
loadUserStats()
|
||||||
// 检查登录状态
|
} else {
|
||||||
console.log('Profile页面登录状态检查')
|
// 没有token时只加载本地缓存的用户信息(如果有)
|
||||||
|
|
||||||
// 检查是否需要继续未完成的登录流程
|
|
||||||
const nextPage = checkAndResumeLogin()
|
|
||||||
if (nextPage) {
|
|
||||||
console.log('检测到未完成的登录流程,跳转到:', nextPage)
|
|
||||||
|
|
||||||
// 显示提示信息
|
|
||||||
uni.showToast({
|
|
||||||
title: '继续完成登录流程',
|
|
||||||
icon: 'none',
|
|
||||||
duration: 1500
|
|
||||||
})
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: nextPage
|
|
||||||
})
|
|
||||||
}, 1500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否已完成登录
|
|
||||||
if (isLoginCompleted()) {
|
|
||||||
const savedUserInfo = uni.getStorageSync('userInfo')
|
const savedUserInfo = uni.getStorageSync('userInfo')
|
||||||
if (savedUserInfo) {
|
if (savedUserInfo) {
|
||||||
userInfo.value = savedUserInfo
|
userInfo.value = savedUserInfo
|
||||||
calculateLoginDays()
|
|
||||||
|
|
||||||
// 清理临时登录数据
|
|
||||||
clearTempLoginData()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const loadUserInfo = async () => {
|
||||||
|
const token = uni.getStorageSync('token')
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 优先获取微信用户信息
|
||||||
|
const wxUserInfo = await getWxUserInfo({
|
||||||
|
custom: {
|
||||||
|
loading: false,
|
||||||
|
toast: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (wxUserInfo) {
|
||||||
|
userInfo.value = wxUserInfo
|
||||||
|
// 同步到本地存储
|
||||||
|
uni.setStorageSync('userInfo', wxUserInfo)
|
||||||
|
uni.setStorageSync('wxUserInfo', wxUserInfo)
|
||||||
|
calculateLoginDays()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('获取微信用户信息失败,尝试获取基本用户信息:', error)
|
||||||
|
|
||||||
|
// 微信用户信息获取失败,尝试获取基本用户信息
|
||||||
|
try {
|
||||||
|
const apiUserInfo = await getUserInfo({
|
||||||
|
custom: {
|
||||||
|
loading: false,
|
||||||
|
toast: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (apiUserInfo) {
|
||||||
|
userInfo.value = apiUserInfo
|
||||||
|
// 同步到本地存储
|
||||||
|
uni.setStorageSync('userInfo', apiUserInfo)
|
||||||
|
calculateLoginDays()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch (basicError) {
|
||||||
|
console.log('获取基本用户信息也失败,使用本地缓存:', basicError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// API获取失败时,尝试从本地存储加载用户信息
|
||||||
|
const savedUserInfo = uni.getStorageSync('userInfo')
|
||||||
|
if (savedUserInfo) {
|
||||||
|
userInfo.value = savedUserInfo
|
||||||
|
calculateLoginDays()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const calculateLoginDays = () => {
|
const calculateLoginDays = () => {
|
||||||
const loginDate = uni.getStorageSync('loginDate')
|
const loginDate = uni.getStorageSync('loginDate')
|
||||||
if (loginDate) {
|
if (loginDate) {
|
||||||
|
|
@ -302,49 +329,18 @@ export default {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleUserAction = () => {
|
const handleUserAction = async () => {
|
||||||
if (userInfo.value.nickName) {
|
const token = uni.getStorageSync('token')
|
||||||
// 已登录,跳转到个人信息设置
|
|
||||||
|
if (token) {
|
||||||
|
// 有token,直接跳转到个人信息设置
|
||||||
navigateTo('/pages/profile/user-info')
|
navigateTo('/pages/profile/user-info')
|
||||||
} else {
|
} else {
|
||||||
// 未登录,执行登录
|
// 没有token,跳转到登录页面
|
||||||
wxLogin()
|
navigateTo('/pages/login/login')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const wxLogin = () => {
|
|
||||||
uni.login({
|
|
||||||
provider: 'weixin',
|
|
||||||
success: (res) => {
|
|
||||||
console.log('登录成功', res)
|
|
||||||
|
|
||||||
// 使用状态管理工具保存微信登录数据
|
|
||||||
saveWxLoginData(res)
|
|
||||||
calculateLoginDays()
|
|
||||||
|
|
||||||
uni.showToast({
|
|
||||||
title: '登录成功',
|
|
||||||
icon: 'success',
|
|
||||||
duration: 1500
|
|
||||||
})
|
|
||||||
|
|
||||||
// 跳转到手机号授权页面
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pages/auth/phone-auth'
|
|
||||||
})
|
|
||||||
}, 1500)
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
console.error('登录失败', err)
|
|
||||||
uni.showToast({
|
|
||||||
title: '登录失败,请重试',
|
|
||||||
icon: 'none'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 统一的页面导航方法
|
// 统一的页面导航方法
|
||||||
const navigateTo = (url, options = {}) => {
|
const navigateTo = (url, options = {}) => {
|
||||||
const { fallbackMessage = '页面开发中' } = options
|
const { fallbackMessage = '页面开发中' } = options
|
||||||
|
|
@ -396,11 +392,21 @@ export default {
|
||||||
|
|
||||||
// 刷新页面数据
|
// 刷新页面数据
|
||||||
const refreshData = () => {
|
const refreshData = () => {
|
||||||
|
// 只刷新统计数据,用户信息在initPage时已加载
|
||||||
loadUserStats()
|
loadUserStats()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 强制刷新用户信息(用于特殊情况)
|
||||||
|
const forceRefreshUserInfo = () => {
|
||||||
|
const token = uni.getStorageSync('token')
|
||||||
|
if (token) {
|
||||||
|
loadUserInfo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 页面显示时刷新数据
|
// 页面显示时刷新数据
|
||||||
const onShow = () => {
|
const onShow = () => {
|
||||||
|
// 只刷新数据,不重复调用loadUserInfo
|
||||||
refreshData()
|
refreshData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -438,9 +444,12 @@ export default {
|
||||||
return {
|
return {
|
||||||
userInfo,
|
userInfo,
|
||||||
loginDays,
|
loginDays,
|
||||||
|
isLoggedIn,
|
||||||
petStats,
|
petStats,
|
||||||
familyStats,
|
familyStats,
|
||||||
adoptionStats,
|
adoptionStats,
|
||||||
|
loadUserInfo,
|
||||||
|
forceRefreshUserInfo,
|
||||||
handleUserAction,
|
handleUserAction,
|
||||||
navigateTo,
|
navigateTo,
|
||||||
navigateToFamily,
|
navigateToFamily,
|
||||||
|
|
@ -456,11 +465,19 @@ export default {
|
||||||
|
|
||||||
// 页面生命周期
|
// 页面生命周期
|
||||||
onShow() {
|
onShow() {
|
||||||
this.onShow()
|
// 重新检查登录状态并刷新数据
|
||||||
|
const token = uni.getStorageSync('token')
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
// 有token时总是重新获取最新用户信息
|
||||||
|
this.loadUserInfo()
|
||||||
|
}
|
||||||
|
this.refreshData()
|
||||||
},
|
},
|
||||||
|
|
||||||
onPullDownRefresh() {
|
onPullDownRefresh() {
|
||||||
this.onPullDownRefresh()
|
this.refreshData()
|
||||||
|
uni.stopPullDownRefresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -231,28 +231,9 @@ export default {
|
||||||
// 生命周期
|
// 生命周期
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initPageMode()
|
initPageMode()
|
||||||
validateLoginState()
|
|
||||||
loadUserInfo()
|
loadUserInfo()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 验证登录状态
|
|
||||||
const validateLoginState = () => {
|
|
||||||
if (isSetupMode.value) {
|
|
||||||
// 首次设置模式需要验证登录状态
|
|
||||||
console.log('个人信息设置页面状态验证')
|
|
||||||
|
|
||||||
// 检查是否有有效的登录状态
|
|
||||||
const currentStep = uni.getStorageSync(STORAGE_KEYS.LOGIN_STEP)
|
|
||||||
if (!currentStep || currentStep === 'not_logged') {
|
|
||||||
console.warn('无效的登录状态,返回profile页面')
|
|
||||||
uni.reLaunch({
|
|
||||||
url: '/pages/profile/profile'
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 方法定义
|
// 方法定义
|
||||||
const initPageMode = () => {
|
const initPageMode = () => {
|
||||||
// 获取页面参数
|
// 获取页面参数
|
||||||
|
|
@ -548,8 +529,7 @@ export default {
|
||||||
// 新增的模式相关变量和方法
|
// 新增的模式相关变量和方法
|
||||||
isSetupMode,
|
isSetupMode,
|
||||||
phoneSkipped,
|
phoneSkipped,
|
||||||
skipSetup,
|
skipSetup
|
||||||
validateLoginState
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ export const STORAGE_KEYS = {
|
||||||
LOGIN_STEP: 'loginStep',
|
LOGIN_STEP: 'loginStep',
|
||||||
WX_LOGIN_CODE: 'wxLoginCode',
|
WX_LOGIN_CODE: 'wxLoginCode',
|
||||||
TOKEN: 'token',
|
TOKEN: 'token',
|
||||||
|
REFRESH_TOKEN: 'refreshToken',
|
||||||
PHONE_NUMBER: 'phoneNumber',
|
PHONE_NUMBER: 'phoneNumber',
|
||||||
WX_USER_INFO: 'wxUserInfo',
|
WX_USER_INFO: 'wxUserInfo',
|
||||||
USER_INFO: 'userInfo',
|
USER_INFO: 'userInfo',
|
||||||
|
|
@ -97,17 +98,21 @@ export const saveWxLoginData = (loginData) => {
|
||||||
* @param {Object} phoneData 手机号数据
|
* @param {Object} phoneData 手机号数据
|
||||||
*/
|
*/
|
||||||
export const savePhoneAuthData = (phoneData) => {
|
export const savePhoneAuthData = (phoneData) => {
|
||||||
const { token, userInfo } = phoneData
|
const { token, refreshToken, userInfo } = phoneData
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
uni.setStorageSync(STORAGE_KEYS.TOKEN, token)
|
uni.setStorageSync(STORAGE_KEYS.TOKEN, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (refreshToken) {
|
||||||
|
uni.setStorageSync(STORAGE_KEYS.REFRESH_TOKEN, refreshToken)
|
||||||
|
}
|
||||||
|
|
||||||
if (userInfo) {
|
if (userInfo) {
|
||||||
if (userInfo.phoneNumber) {
|
if (userInfo.phoneNumber) {
|
||||||
uni.setStorageSync(STORAGE_KEYS.PHONE_NUMBER, userInfo.phoneNumber)
|
uni.setStorageSync(STORAGE_KEYS.PHONE_NUMBER, userInfo.phoneNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存微信用户信息供后续使用
|
// 保存微信用户信息供后续使用
|
||||||
const wxUserInfo = {
|
const wxUserInfo = {
|
||||||
phoneNumber: userInfo.phoneNumber || '',
|
phoneNumber: userInfo.phoneNumber || '',
|
||||||
|
|
@ -116,10 +121,17 @@ export const savePhoneAuthData = (phoneData) => {
|
||||||
avatarUrl: userInfo.avatarUrl || ''
|
avatarUrl: userInfo.avatarUrl || ''
|
||||||
}
|
}
|
||||||
uni.setStorageSync(STORAGE_KEYS.WX_USER_INFO, wxUserInfo)
|
uni.setStorageSync(STORAGE_KEYS.WX_USER_INFO, wxUserInfo)
|
||||||
|
uni.setStorageSync(STORAGE_KEYS.USER_INFO, userInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoginStep(LOGIN_STEPS.PHONE_AUTHED)
|
// 根据用户信息完善状态设置登录步骤
|
||||||
console.log('保存手机号授权数据:', { token: !!token, userInfo: !!userInfo })
|
if (userInfo && userInfo.profileCompleted) {
|
||||||
|
setLoginStep(LOGIN_STEPS.PROFILE_COMPLETED)
|
||||||
|
} else {
|
||||||
|
setLoginStep(LOGIN_STEPS.PHONE_AUTHED)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('保存手机号授权数据:', { token: !!token, refreshToken: !!refreshToken, userInfo: !!userInfo })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -151,6 +163,133 @@ export const clearTempLoginData = () => {
|
||||||
console.log('清理临时登录数据')
|
console.log('清理临时登录数据')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查并验证token有效性
|
||||||
|
* @returns {Promise<boolean>} token是否有效
|
||||||
|
*/
|
||||||
|
export const validateToken = async () => {
|
||||||
|
const token = uni.getStorageSync(STORAGE_KEYS.TOKEN)
|
||||||
|
if (!token) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 调用一个简单的API来验证token
|
||||||
|
const { getUserInfo } = await import('@/http/api/profile.js')
|
||||||
|
await getUserInfo({
|
||||||
|
custom: {
|
||||||
|
loading: false,
|
||||||
|
toast: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Token验证失败:', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试刷新token
|
||||||
|
* @returns {Promise<boolean>} 是否刷新成功
|
||||||
|
*/
|
||||||
|
export const tryRefreshToken = async () => {
|
||||||
|
const refreshToken = uni.getStorageSync(STORAGE_KEYS.REFRESH_TOKEN)
|
||||||
|
if (!refreshToken) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { refreshToken: refreshTokenApi } = await import('@/http/api/auth.js')
|
||||||
|
const result = await refreshTokenApi({
|
||||||
|
custom: {
|
||||||
|
loading: false,
|
||||||
|
toast: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result && result.token) {
|
||||||
|
uni.setStorageSync(STORAGE_KEYS.TOKEN, result.token)
|
||||||
|
if (result.refreshToken) {
|
||||||
|
uni.setStorageSync(STORAGE_KEYS.REFRESH_TOKEN, result.refreshToken)
|
||||||
|
}
|
||||||
|
console.log('Token刷新成功')
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Token刷新失败:', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动登录(用于用户再次进入小程序)
|
||||||
|
* @returns {Promise<boolean>} 是否登录成功
|
||||||
|
*/
|
||||||
|
export const autoLogin = async () => {
|
||||||
|
console.log('开始自动登录检查')
|
||||||
|
|
||||||
|
// 1. 检查token有效性
|
||||||
|
if (await validateToken()) {
|
||||||
|
console.log('Token有效,自动登录成功')
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 尝试刷新token
|
||||||
|
if (await tryRefreshToken()) {
|
||||||
|
console.log('Token刷新成功,自动登录成功')
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 尝试使用微信code重新登录(不需要手机号授权)
|
||||||
|
try {
|
||||||
|
const loginRes = await new Promise((resolve, reject) => {
|
||||||
|
uni.login({
|
||||||
|
success: resolve,
|
||||||
|
fail: reject
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (loginRes.code) {
|
||||||
|
const { wxLogin } = await import('@/http/api/auth.js')
|
||||||
|
const result = await wxLogin({
|
||||||
|
code: loginRes.code
|
||||||
|
}, {
|
||||||
|
custom: {
|
||||||
|
loading: false,
|
||||||
|
toast: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result && result.token) {
|
||||||
|
// 保存新的登录信息
|
||||||
|
uni.setStorageSync(STORAGE_KEYS.TOKEN, result.token)
|
||||||
|
if (result.refreshToken) {
|
||||||
|
uni.setStorageSync(STORAGE_KEYS.REFRESH_TOKEN, result.refreshToken)
|
||||||
|
}
|
||||||
|
if (result.userInfo) {
|
||||||
|
uni.setStorageSync(STORAGE_KEYS.WX_USER_INFO, result.userInfo)
|
||||||
|
uni.setStorageSync(STORAGE_KEYS.USER_INFO, result.userInfo)
|
||||||
|
// 如果用户已完善过信息,设置为已完成状态
|
||||||
|
if (result.userInfo.profileCompleted) {
|
||||||
|
setLoginStep(LOGIN_STEPS.PROFILE_COMPLETED)
|
||||||
|
} else {
|
||||||
|
setLoginStep(LOGIN_STEPS.PHONE_AUTHED)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('微信code登录成功,自动登录成功')
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('微信code登录失败:', error)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('自动登录失败,需要用户手动登录')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 完全重置登录状态(用于登出)
|
* 完全重置登录状态(用于登出)
|
||||||
*/
|
*/
|
||||||
|
|
@ -158,11 +297,12 @@ export const resetLoginState = () => {
|
||||||
uni.removeStorageSync(STORAGE_KEYS.LOGIN_STEP)
|
uni.removeStorageSync(STORAGE_KEYS.LOGIN_STEP)
|
||||||
uni.removeStorageSync(STORAGE_KEYS.WX_LOGIN_CODE)
|
uni.removeStorageSync(STORAGE_KEYS.WX_LOGIN_CODE)
|
||||||
uni.removeStorageSync(STORAGE_KEYS.TOKEN)
|
uni.removeStorageSync(STORAGE_KEYS.TOKEN)
|
||||||
|
uni.removeStorageSync(STORAGE_KEYS.REFRESH_TOKEN)
|
||||||
uni.removeStorageSync(STORAGE_KEYS.PHONE_NUMBER)
|
uni.removeStorageSync(STORAGE_KEYS.PHONE_NUMBER)
|
||||||
uni.removeStorageSync(STORAGE_KEYS.WX_USER_INFO)
|
uni.removeStorageSync(STORAGE_KEYS.WX_USER_INFO)
|
||||||
uni.removeStorageSync(STORAGE_KEYS.USER_INFO)
|
uni.removeStorageSync(STORAGE_KEYS.USER_INFO)
|
||||||
uni.removeStorageSync(STORAGE_KEYS.LOGIN_DATE)
|
uni.removeStorageSync(STORAGE_KEYS.LOGIN_DATE)
|
||||||
|
|
||||||
console.log('重置登录状态')
|
console.log('重置登录状态')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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