feat(权限系统): 添加联系卖家权限并优化注册流程
添加 CONTACT_SELLER 权限类型及相关配置 优化注册页面逻辑,完善后智能返回上一页 统一 API 响应状态码检查方式 更新权限系统修复说明文档
Showing
7 changed files
with
162 additions
and
24 deletions
permission-fix-guide.md
0 → 100644
| 1 | +# 权限系统问题修复说明 | ||
| 2 | + | ||
| 3 | +## 问题描述 | ||
| 4 | + | ||
| 5 | +用户从商品详情页点击"联系卖家"按钮时,如果没有权限会跳转到注册页面。但是存在以下问题: | ||
| 6 | + | ||
| 7 | +1. 注册完成后没有正确返回上一页 | ||
| 8 | +2. 手动返回上一页后权限状态没有刷新 | ||
| 9 | +3. 点击联系卖家依然进入注册页面 | ||
| 10 | + | ||
| 11 | +## 修复内容 | ||
| 12 | + | ||
| 13 | +### 1. 注册页面逻辑优化 | ||
| 14 | + | ||
| 15 | +**文件**: `/src/pages/register/index.vue` | ||
| 16 | + | ||
| 17 | +- 添加了用户状态管理的导入和初始化 | ||
| 18 | +- 修改了注册成功后的处理逻辑: | ||
| 19 | + - 更新用户状态到 store 中 | ||
| 20 | + - 设置认证状态为 true | ||
| 21 | + - 智能判断页面跳转逻辑(返回上一页或跳转首页) | ||
| 22 | + | ||
| 23 | +### 2. 权限类型补充 | ||
| 24 | + | ||
| 25 | +**文件**: `/src/utils/permission.js` | ||
| 26 | + | ||
| 27 | +- 添加了 `CONTACT_SELLER` 权限类型 | ||
| 28 | +- 配置了对应的权限检查规则 | ||
| 29 | + | ||
| 30 | +## 修复后的流程 | ||
| 31 | + | ||
| 32 | +1. 用户在商品详情页点击"联系卖家" | ||
| 33 | +2. 系统检查用户权限(主要是手机号是否完善) | ||
| 34 | +3. 如果没有权限,显示提示并跳转到注册页面 | ||
| 35 | +4. 用户完善信息并保存 | ||
| 36 | +5. **新增**: 系统自动更新用户状态到 store | ||
| 37 | +6. **新增**: 智能返回上一页(商品详情页) | ||
| 38 | +7. 用户再次点击"联系卖家"时,权限验证通过,正常显示联系弹窗 | ||
| 39 | + | ||
| 40 | +## 测试步骤 | ||
| 41 | + | ||
| 42 | +### 测试场景1:从商品详情页触发权限检查 | ||
| 43 | + | ||
| 44 | +1. 确保用户信息不完整(特别是手机号为空) | ||
| 45 | +2. 进入任意商品详情页 | ||
| 46 | +3. 点击"联系卖家"按钮 | ||
| 47 | +4. 验证是否显示权限提示并跳转到注册页面 | ||
| 48 | +5. 完善用户信息并保存 | ||
| 49 | +6. 验证是否自动返回商品详情页 | ||
| 50 | +7. 再次点击"联系卖家"按钮 | ||
| 51 | +8. 验证是否正常显示联系卖家弹窗 | ||
| 52 | + | ||
| 53 | +### 测试场景2:从商品详情页触发买车权限检查 | ||
| 54 | + | ||
| 55 | +1. 确保用户信息不完整 | ||
| 56 | +2. 进入任意商品详情页 | ||
| 57 | +3. 点击"我想购买"按钮 | ||
| 58 | +4. 验证权限检查和页面跳转流程 | ||
| 59 | +5. 完善信息后验证功能是否正常 | ||
| 60 | + | ||
| 61 | +### 测试场景3:直接访问注册页面 | ||
| 62 | + | ||
| 63 | +1. 直接访问注册页面(不是从其他页面跳转) | ||
| 64 | +2. 完善信息并保存 | ||
| 65 | +3. 验证是否跳转到首页(而不是返回上一页) | ||
| 66 | + | ||
| 67 | +## 技术细节 | ||
| 68 | + | ||
| 69 | +### 用户状态更新 | ||
| 70 | + | ||
| 71 | +```javascript | ||
| 72 | +// 更新用户状态 | ||
| 73 | +userStore.updateUserInfo({ | ||
| 74 | + ...formData, | ||
| 75 | + avatar: formData.avatar_url | ||
| 76 | +}) | ||
| 77 | +userStore.isAuthenticated = true | ||
| 78 | +``` | ||
| 79 | + | ||
| 80 | +### 智能页面跳转 | ||
| 81 | + | ||
| 82 | +```javascript | ||
| 83 | +// 获取页面栈信息 | ||
| 84 | +const pages = Taro.getCurrentPages() | ||
| 85 | + | ||
| 86 | +// 如果页面栈中有多于1个页面,说明是从其他页面跳转过来的 | ||
| 87 | +if (pages.length > 1) { | ||
| 88 | + // 返回上一页,权限状态已更新,用户可以正常使用功能 | ||
| 89 | + Taro.navigateBack() | ||
| 90 | +} else { | ||
| 91 | + // 如果是直接访问注册页面,跳转到首页 | ||
| 92 | + Taro.switchTab({ | ||
| 93 | + url: '/pages/index/index' | ||
| 94 | + }) | ||
| 95 | +} | ||
| 96 | +``` | ||
| 97 | + | ||
| 98 | +## 注意事项 | ||
| 99 | + | ||
| 100 | +1. 修复后用户状态会实时更新,无需刷新页面 | ||
| 101 | +2. 权限检查基于用户的手机号字段 | ||
| 102 | +3. 支持多种权限类型的扩展 | ||
| 103 | +4. 保持了良好的用户体验,避免了重复的权限验证流程 | ||
| 104 | + | ||
| 105 | +## 相关文件 | ||
| 106 | + | ||
| 107 | +- `/src/pages/register/index.vue` - 注册页面 | ||
| 108 | +- `/src/pages/productDetail/index.vue` - 商品详情页 | ||
| 109 | +- `/src/utils/permission.js` - 权限管理工具 | ||
| 110 | +- `/src/stores/user.js` - 用户状态管理 | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2022-09-19 14:11:06 | 2 | * @Date: 2022-09-19 14:11:06 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-07-09 10:44:50 | 4 | + * @LastEditTime: 2025-07-09 11:36:52 |
| 5 | * @FilePath: /jgdl/src/pages/auth/index.vue | 5 | * @FilePath: /jgdl/src/pages/auth/index.vue |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | --> | 7 | --> |
| ... | @@ -36,7 +36,7 @@ export default { | ... | @@ -36,7 +36,7 @@ export default { |
| 36 | }) | 36 | }) |
| 37 | request.post('/srv/?a=openid', { | 37 | request.post('/srv/?a=openid', { |
| 38 | code: res.code, | 38 | code: res.code, |
| 39 | - openid: 'h-002' | 39 | + openid: 'h-008' |
| 40 | // openid: 'o5NFZ5cFQtLRy3aVHaZMLkjHFusI' | 40 | // openid: 'o5NFZ5cFQtLRy3aVHaZMLkjHFusI' |
| 41 | // openid: 'o5NFZ5TpgG4FwYursGCLjcUJH2ak' | 41 | // openid: 'o5NFZ5TpgG4FwYursGCLjcUJH2ak' |
| 42 | // openid: 'o5NFZ5cqroPYwawCp8FEOxewtgnw' | 42 | // openid: 'o5NFZ5cqroPYwawCp8FEOxewtgnw' |
| ... | @@ -66,7 +66,7 @@ export default { | ... | @@ -66,7 +66,7 @@ export default { |
| 66 | }) | 66 | }) |
| 67 | } else { // 其他页面分享跳首页 | 67 | } else { // 其他页面分享跳首页 |
| 68 | Taro.reLaunch({ | 68 | Taro.reLaunch({ |
| 69 | - url: `/pages/index/index?first_in=${wx.getStorageSync("first_in")}` | 69 | + url: `/pages/index/index` |
| 70 | }) | 70 | }) |
| 71 | } | 71 | } |
| 72 | Taro.hideLoading(); | 72 | Taro.hideLoading(); | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2022-09-19 14:11:06 | 2 | * @Date: 2022-09-19 14:11:06 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-07-04 14:53:07 | 4 | + * @LastEditTime: 2025-07-09 11:34:24 |
| 5 | * @FilePath: /jgdl/src/pages/myAuthCar/index.vue | 5 | * @FilePath: /jgdl/src/pages/myAuthCar/index.vue |
| 6 | * @Description: 我的认证车页面 | 6 | * @Description: 我的认证车页面 |
| 7 | --> | 7 | --> |
| ... | @@ -208,7 +208,7 @@ const initData = async () => { | ... | @@ -208,7 +208,7 @@ const initData = async () => { |
| 208 | loading.value = true | 208 | loading.value = true |
| 209 | try { | 209 | try { |
| 210 | const response = await apiService.getAuthCarsList(1, pageSize.value) | 210 | const response = await apiService.getAuthCarsList(1, pageSize.value) |
| 211 | - if (response.code === 200) { | 211 | + if (response.code) { |
| 212 | authCars.value = response.data.list | 212 | authCars.value = response.data.list |
| 213 | hasMore.value = response.data.hasMore | 213 | hasMore.value = response.data.hasMore |
| 214 | currentPage.value = 1 | 214 | currentPage.value = 1 |
| ... | @@ -234,7 +234,7 @@ const loadMore = async () => { | ... | @@ -234,7 +234,7 @@ const loadMore = async () => { |
| 234 | const nextPage = currentPage.value + 1 | 234 | const nextPage = currentPage.value + 1 |
| 235 | const response = await apiService.getAuthCarsList(nextPage, pageSize.value) | 235 | const response = await apiService.getAuthCarsList(nextPage, pageSize.value) |
| 236 | 236 | ||
| 237 | - if (response.code === 200) { | 237 | + if (response.code) { |
| 238 | authCars.value.push(...response.data.list) | 238 | authCars.value.push(...response.data.list) |
| 239 | hasMore.value = response.data.hasMore | 239 | hasMore.value = response.data.hasMore |
| 240 | currentPage.value = nextPage | 240 | currentPage.value = nextPage | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2022-09-19 14:11:06 | 2 | * @Date: 2022-09-19 14:11:06 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-07-08 14:47:52 | 4 | + * @LastEditTime: 2025-07-09 11:35:02 |
| 5 | * @FilePath: /jgdl/src/pages/myFavorites/index.vue | 5 | * @FilePath: /jgdl/src/pages/myFavorites/index.vue |
| 6 | * @Description: 我的关注页面 | 6 | * @Description: 我的关注页面 |
| 7 | --> | 7 | --> |
| ... | @@ -132,7 +132,7 @@ const apiService = { | ... | @@ -132,7 +132,7 @@ const apiService = { |
| 132 | 132 | ||
| 133 | // 模拟API响应数据 | 133 | // 模拟API响应数据 |
| 134 | return { | 134 | return { |
| 135 | - code: 200, | 135 | + code: 1, |
| 136 | data: { | 136 | data: { |
| 137 | list: generateMockData(page, pageSize), | 137 | list: generateMockData(page, pageSize), |
| 138 | total: 50, // 模拟总数 | 138 | total: 50, // 模拟总数 |
| ... | @@ -156,7 +156,7 @@ const apiService = { | ... | @@ -156,7 +156,7 @@ const apiService = { |
| 156 | 156 | ||
| 157 | // 模拟API响应 | 157 | // 模拟API响应 |
| 158 | return { | 158 | return { |
| 159 | - code: 200, | 159 | + code: 1, |
| 160 | data: null, | 160 | data: null, |
| 161 | message: '取消关注成功' | 161 | message: '取消关注成功' |
| 162 | } | 162 | } |
| ... | @@ -254,7 +254,7 @@ const initData = async () => { | ... | @@ -254,7 +254,7 @@ const initData = async () => { |
| 254 | loading.value = true | 254 | loading.value = true |
| 255 | try { | 255 | try { |
| 256 | const response = await apiService.getFavoritesList(1, pageSize.value) | 256 | const response = await apiService.getFavoritesList(1, pageSize.value) |
| 257 | - if (response.code === 200) { | 257 | + if (response.code) { |
| 258 | favorites.value = response.data.list | 258 | favorites.value = response.data.list |
| 259 | hasMore.value = response.data.hasMore | 259 | hasMore.value = response.data.hasMore |
| 260 | currentPage.value = 1 | 260 | currentPage.value = 1 |
| ... | @@ -280,7 +280,7 @@ const loadMore = async () => { | ... | @@ -280,7 +280,7 @@ const loadMore = async () => { |
| 280 | const nextPage = currentPage.value + 1 | 280 | const nextPage = currentPage.value + 1 |
| 281 | const response = await apiService.getFavoritesList(nextPage, pageSize.value) | 281 | const response = await apiService.getFavoritesList(nextPage, pageSize.value) |
| 282 | 282 | ||
| 283 | - if (response.code === 200) { | 283 | + if (response.code) { |
| 284 | favorites.value.push(...response.data.list) | 284 | favorites.value.push(...response.data.list) |
| 285 | hasMore.value = response.data.hasMore | 285 | hasMore.value = response.data.hasMore |
| 286 | currentPage.value = nextPage | 286 | currentPage.value = nextPage |
| ... | @@ -325,7 +325,7 @@ const confirmUnfollow = async () => { | ... | @@ -325,7 +325,7 @@ const confirmUnfollow = async () => { |
| 325 | try { | 325 | try { |
| 326 | const response = await apiService.unfollowCar(selectedId.value) | 326 | const response = await apiService.unfollowCar(selectedId.value) |
| 327 | 327 | ||
| 328 | - if (response.code === 200) { | 328 | + if (response.code) { |
| 329 | // 从列表中移除该项 | 329 | // 从列表中移除该项 |
| 330 | favorites.value = favorites.value.filter(item => item.id !== selectedId.value) | 330 | favorites.value = favorites.value.filter(item => item.id !== selectedId.value) |
| 331 | showToast('取消关注成功', 'success') | 331 | showToast('取消关注成功', 'success') | ... | ... |
| ... | @@ -154,6 +154,10 @@ import { Right } from '@nutui/icons-vue-taro' | ... | @@ -154,6 +154,10 @@ import { Right } from '@nutui/icons-vue-taro' |
| 154 | import './index.less' | 154 | import './index.less' |
| 155 | import BASE_URL from '@/utils/config'; | 155 | import BASE_URL from '@/utils/config'; |
| 156 | import { updateProfileAPI, sendSmsCodeAPI } from '@/api/index'; | 156 | import { updateProfileAPI, sendSmsCodeAPI } from '@/api/index'; |
| 157 | +import { useUserStore } from '@/stores/user'; | ||
| 158 | + | ||
| 159 | +// 用户状态管理 | ||
| 160 | +const userStore = useUserStore() | ||
| 157 | 161 | ||
| 158 | // 主题配置 | 162 | // 主题配置 |
| 159 | const themeVars = { | 163 | const themeVars = { |
| ... | @@ -419,7 +423,7 @@ const validateForm = () => { | ... | @@ -419,7 +423,7 @@ const validateForm = () => { |
| 419 | return false | 423 | return false |
| 420 | } | 424 | } |
| 421 | 425 | ||
| 422 | - if (!formData.gender) { | 426 | + if (formData.gender === '') { |
| 423 | showToast('请选择性别', 'error') | 427 | showToast('请选择性别', 'error') |
| 424 | return false | 428 | return false |
| 425 | } | 429 | } |
| ... | @@ -451,7 +455,7 @@ const handleRegister = async () => { | ... | @@ -451,7 +455,7 @@ const handleRegister = async () => { |
| 451 | 455 | ||
| 452 | const result = await updateProfileAPI(formData) | 456 | const result = await updateProfileAPI(formData) |
| 453 | 457 | ||
| 454 | - if (result.code !== 0) { | 458 | + if (!result.code) { |
| 455 | Taro.showToast({ | 459 | Taro.showToast({ |
| 456 | title: result.msg || '注册失败', | 460 | title: result.msg || '注册失败', |
| 457 | icon: 'error' | 461 | icon: 'error' |
| ... | @@ -459,19 +463,36 @@ const handleRegister = async () => { | ... | @@ -459,19 +463,36 @@ const handleRegister = async () => { |
| 459 | return | 463 | return |
| 460 | } | 464 | } |
| 461 | 465 | ||
| 462 | - Taro.showToast({ | 466 | + // 更新用户状态 |
| 463 | - title: '注册成功', | 467 | + userStore.updateUserInfo({ |
| 464 | - icon: 'success' | 468 | + ...formData, |
| 469 | + avatar: formData.avatar_url | ||
| 465 | }) | 470 | }) |
| 471 | + userStore.isAuthenticated = true | ||
| 466 | 472 | ||
| 467 | - setTimeout(() => { | 473 | + Taro.showToast({ |
| 468 | - // 注册成功后跳转到登录页面或首页 | 474 | + title: '保存成功', |
| 469 | - Taro.navigateBack() | 475 | + icon: 'success', |
| 470 | - }, 1500) | 476 | + complete: () => { |
| 477 | + // 获取页面栈信息 | ||
| 478 | + const pages = Taro.getCurrentPages() | ||
| 479 | + | ||
| 480 | + // 如果页面栈中有多于1个页面,说明是从其他页面跳转过来的 | ||
| 481 | + if (pages.length > 1) { | ||
| 482 | + // 返回上一页,权限状态已更新,用户可以正常使用功能 | ||
| 483 | + Taro.navigateBack() | ||
| 484 | + } else { | ||
| 485 | + // 如果是直接访问注册页面,跳转到首页 | ||
| 486 | + Taro.reLaunch({ | ||
| 487 | + url: '/pages/index/index' | ||
| 488 | + }) | ||
| 489 | + } | ||
| 490 | + } | ||
| 491 | + }) | ||
| 471 | 492 | ||
| 472 | } catch (error) { | 493 | } catch (error) { |
| 473 | Taro.showToast({ | 494 | Taro.showToast({ |
| 474 | - title: '注册失败,请重试', | 495 | + title: '保存失败,请重试', |
| 475 | icon: 'error' | 496 | icon: 'error' |
| 476 | }) | 497 | }) |
| 477 | } finally { | 498 | } finally { | ... | ... |
| 1 | /* | 1 | /* |
| 2 | * @Date: 2025-01-08 18:00:00 | 2 | * @Date: 2025-01-08 18:00:00 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-07-09 10:34:50 | 4 | + * @LastEditTime: 2025-07-09 11:22:03 |
| 5 | * @FilePath: /jgdl/src/stores/user.js | 5 | * @FilePath: /jgdl/src/stores/user.js |
| 6 | * @Description: 用户状态管理 | 6 | * @Description: 用户状态管理 |
| 7 | */ | 7 | */ | ... | ... |
| 1 | /* | 1 | /* |
| 2 | * @Date: 2025-01-08 18:00:00 | 2 | * @Date: 2025-01-08 18:00:00 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-07-09 10:49:50 | 4 | + * @LastEditTime: 2025-07-09 11:16:49 |
| 5 | * @FilePath: /jgdl/src/utils/permission.js | 5 | * @FilePath: /jgdl/src/utils/permission.js |
| 6 | * @Description: 权限控制工具函数 | 6 | * @Description: 权限控制工具函数 |
| 7 | */ | 7 | */ |
| ... | @@ -18,6 +18,8 @@ export const PERMISSION_TYPES = { | ... | @@ -18,6 +18,8 @@ export const PERMISSION_TYPES = { |
| 18 | SELL_CAR: 'sell_car', | 18 | SELL_CAR: 'sell_car', |
| 19 | // 买车权限:需要完善基本信息 | 19 | // 买车权限:需要完善基本信息 |
| 20 | BUY_CAR: 'buy_car', | 20 | BUY_CAR: 'buy_car', |
| 21 | + // 联系卖家权限:需要完善基本信息 | ||
| 22 | + CONTACT_SELLER: 'contact_seller', | ||
| 21 | // 个人中心权限:需要完善基本信息 | 23 | // 个人中心权限:需要完善基本信息 |
| 22 | PROFILE: 'profile', | 24 | PROFILE: 'profile', |
| 23 | // 订单管理权限:需要完善基本信息 | 25 | // 订单管理权限:需要完善基本信息 |
| ... | @@ -45,6 +47,11 @@ const PERMISSION_CONFIG = { | ... | @@ -45,6 +47,11 @@ const PERMISSION_CONFIG = { |
| 45 | redirectUrl: '/pages/register/index', | 47 | redirectUrl: '/pages/register/index', |
| 46 | checkFields: ['phone'] | 48 | checkFields: ['phone'] |
| 47 | }, | 49 | }, |
| 50 | + [PERMISSION_TYPES.CONTACT_SELLER]: { | ||
| 51 | + message: '联系卖家需要先完善个人信息', | ||
| 52 | + redirectUrl: '/pages/register/index', | ||
| 53 | + checkFields: ['phone'] | ||
| 54 | + }, | ||
| 48 | [PERMISSION_TYPES.PROFILE]: { | 55 | [PERMISSION_TYPES.PROFILE]: { |
| 49 | message: '访问个人中心需要先完善个人信息', | 56 | message: '访问个人中心需要先完善个人信息', |
| 50 | redirectUrl: '/pages/register/index', | 57 | redirectUrl: '/pages/register/index', | ... | ... |
-
Please register or login to post a comment