hookehuyr

feat(权限系统): 添加联系卖家权限并优化注册流程

添加 CONTACT_SELLER 权限类型及相关配置
优化注册页面逻辑,完善后智能返回上一页
统一 API 响应状态码检查方式
更新权限系统修复说明文档
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',
......