feat: 为所有制作计划书按钮添加登录权限检查
- 创建 usePlanPermission composable 统一权限检查逻辑 - 首页:在模板层添加权限检查 - 搜索页:在模板层添加权限检查 - 产品中心页:保持 openPlanPopup 函数内的权限检查 - 产品详情页:在模板层添加权限检查 - 修复权限重复调用问题(只在模板层检查一次) 影响文件: - src/composables/usePlanPermission.js - src/pages/index/index.vue - src/pages/search/index.vue - src/pages/product-center/index.vue - src/pages/product-detail/index.vue
Showing
5 changed files
with
103 additions
and
4 deletions
src/composables/usePlanPermission.js
0 → 100644
| 1 | +/** | ||
| 2 | + * 计划书权限检查 Composable | ||
| 3 | + * | ||
| 4 | + * @description 统一处理制作计划书的登录权限检查 | ||
| 5 | + * @module composables/usePlanPermission | ||
| 6 | + * @author Claude Code | ||
| 7 | + * @created 2026-02-12 | ||
| 8 | + */ | ||
| 9 | + | ||
| 10 | +import { useUserStore } from '@/stores/user' | ||
| 11 | +import Taro from '@tarojs/taro' | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * 计划书权限检查 Hook | ||
| 15 | + * | ||
| 16 | + * @description 提供统一的权限检查逻辑,用于制作计划书前的登录验证 | ||
| 17 | + * @returns {Object} 权限检查方法 | ||
| 18 | + * | ||
| 19 | + * @example | ||
| 20 | + * const { checkPlanPermission } = usePlanPermission() | ||
| 21 | + * | ||
| 22 | + * // 在点击计划书按钮时使用 | ||
| 23 | + * checkPlanPermission(() => { | ||
| 24 | + * // 已登录时的回调逻辑 | ||
| 25 | + * openPlanPopup(productId) | ||
| 26 | + * }) | ||
| 27 | + */ | ||
| 28 | +export function usePlanPermission() { | ||
| 29 | + const userStore = useUserStore() | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * 检查计划书权限 | ||
| 33 | + * | ||
| 34 | + * @description 判断用户是否登录,未登录时提示并引导登录,已登录时执行回调 | ||
| 35 | + * @param {Function} callback - 已登录时执行的回调函数 | ||
| 36 | + * @returns {boolean} 是否有权限(true=已登录,false=未登录) | ||
| 37 | + * | ||
| 38 | + * @example | ||
| 39 | + * const hasPermission = checkPlanPermission(() => { | ||
| 40 | + * console.log('用户已登录,可以制作计划书') | ||
| 41 | + * }) | ||
| 42 | + */ | ||
| 43 | + const checkPlanPermission = (callback) => { | ||
| 44 | + console.log('[usePlanPermission] 检查权限,当前登录状态:', userStore.isLoggedIn) | ||
| 45 | + // 检查登录状态 | ||
| 46 | + if (!userStore.isLoggedIn) { | ||
| 47 | + console.log('[usePlanPermission] 用户未登录,显示登录提示') | ||
| 48 | + // 未登录,显示提示框 | ||
| 49 | + Taro.showModal({ | ||
| 50 | + title: '提示', | ||
| 51 | + content: '请先登录后再制作计划书', | ||
| 52 | + confirmText: '去登录', | ||
| 53 | + cancelText: '取消', | ||
| 54 | + success: (res) => { | ||
| 55 | + if (res.confirm) { | ||
| 56 | + // 用户点击"去登录",跳转到登录页 | ||
| 57 | + console.log('[usePlanPermission] 用户点击去登录') | ||
| 58 | + Taro.navigateTo({ | ||
| 59 | + url: '/pages/login/index' | ||
| 60 | + }) | ||
| 61 | + } | ||
| 62 | + } | ||
| 63 | + }) | ||
| 64 | + return false | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + console.log('[usePlanPermission] 用户已登录,执行回调') | ||
| 68 | + // 已登录,执行回调 | ||
| 69 | + callback?.() | ||
| 70 | + return true | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + return { | ||
| 74 | + checkPlanPermission | ||
| 75 | + } | ||
| 76 | +} |
| ... | @@ -95,7 +95,7 @@ | ... | @@ -95,7 +95,7 @@ |
| 95 | :tags="product.tags" | 95 | :tags="product.tags" |
| 96 | :class="{ 'mb-[24rpx]': index < hotProducts.length - 1 }" | 96 | :class="{ 'mb-[24rpx]': index < hotProducts.length - 1 }" |
| 97 | @detail="goToProductDetail" | 97 | @detail="goToProductDetail" |
| 98 | - @plan="openPlanPopup" | 98 | + @plan="(productId) => checkPlanPermission(() => openPlanPopup(productId))" |
| 99 | /> | 99 | /> |
| 100 | </view> | 100 | </view> |
| 101 | </view> | 101 | </view> |
| ... | @@ -173,11 +173,15 @@ import { listAPI } from '@/api/get_product'; | ... | @@ -173,11 +173,15 @@ import { listAPI } from '@/api/get_product'; |
| 173 | import { weekHotAPI } from '@/api/file'; | 173 | import { weekHotAPI } from '@/api/file'; |
| 174 | import { homeIconAPI } from '@/api/home'; | 174 | import { homeIconAPI } from '@/api/home'; |
| 175 | import { usePlanSubmit } from '@/composables/usePlanSubmit'; | 175 | import { usePlanSubmit } from '@/composables/usePlanSubmit'; |
| 176 | +import { usePlanPermission } from '@/composables/usePlanPermission'; | ||
| 176 | 177 | ||
| 177 | 178 | ||
| 178 | // User Store | 179 | // User Store |
| 179 | const userStore = useUserStore(); | 180 | const userStore = useUserStore(); |
| 180 | 181 | ||
| 182 | +// 获取权限检查方法 | ||
| 183 | +const { checkPlanPermission } = usePlanPermission(); | ||
| 184 | + | ||
| 181 | // Header Image Error State | 185 | // Header Image Error State |
| 182 | /** | 186 | /** |
| 183 | * 头部图片加载失败状态 | 187 | * 头部图片加载失败状态 | ... | ... |
| ... | @@ -142,6 +142,7 @@ | ... | @@ -142,6 +142,7 @@ |
| 142 | import { ref, computed } from 'vue' | 142 | import { ref, computed } from 'vue' |
| 143 | import Taro, { useLoad } from '@tarojs/taro' | 143 | import Taro, { useLoad } from '@tarojs/taro' |
| 144 | import { useGo } from '@/hooks/useGo' | 144 | import { useGo } from '@/hooks/useGo' |
| 145 | +import { useUserStore } from '@/stores/user' | ||
| 145 | import { useListItemClick, ListType } from '@/composables/useListItemClick' | 146 | import { useListItemClick, ListType } from '@/composables/useListItemClick' |
| 146 | import LoadMoreList from '@/components/list/LoadMoreList' | 147 | import LoadMoreList from '@/components/list/LoadMoreList' |
| 147 | import NavHeader from '@/components/navigation/NavHeader.vue' | 148 | import NavHeader from '@/components/navigation/NavHeader.vue' |
| ... | @@ -150,6 +151,7 @@ import PlanFormContainer from '@/components/plan/PlanFormContainer.vue' | ... | @@ -150,6 +151,7 @@ import PlanFormContainer from '@/components/plan/PlanFormContainer.vue' |
| 150 | import { listAPI } from '@/api/get_product' | 151 | import { listAPI } from '@/api/get_product' |
| 151 | import { mockProductListAPI } from '@/utils/mockData' | 152 | import { mockProductListAPI } from '@/utils/mockData' |
| 152 | import { usePlanSubmit } from '@/composables/usePlanSubmit' | 153 | import { usePlanSubmit } from '@/composables/usePlanSubmit' |
| 154 | +import { usePlanPermission } from '@/composables/usePlanPermission' | ||
| 153 | 155 | ||
| 154 | // ⚠️ MOCK 数据开关 - 开发环境使用 mock 数据,生产环境使用真实 API | 156 | // ⚠️ MOCK 数据开关 - 开发环境使用 mock 数据,生产环境使用真实 API |
| 155 | // const USE_MOCK_DATA = process.env.NODE_ENV === 'development' | 157 | // const USE_MOCK_DATA = process.env.NODE_ENV === 'development' |
| ... | @@ -157,6 +159,12 @@ const USE_MOCK_DATA = false | ... | @@ -157,6 +159,12 @@ const USE_MOCK_DATA = false |
| 157 | 159 | ||
| 158 | const go = useGo() | 160 | const go = useGo() |
| 159 | 161 | ||
| 162 | +// User Store | ||
| 163 | +const userStore = useUserStore() | ||
| 164 | + | ||
| 165 | +// 获取权限检查方法 | ||
| 166 | +const { checkPlanPermission } = usePlanPermission() | ||
| 167 | + | ||
| 160 | /** | 168 | /** |
| 161 | * 当前列表数据 | 169 | * 当前列表数据 |
| 162 | * @type {Ref<Array<any>>} | 170 | * @type {Ref<Array<any>>} |
| ... | @@ -495,8 +503,12 @@ const { handleClick: handleProductClick } = useListItemClick({ | ... | @@ -495,8 +503,12 @@ const { handleClick: handleProductClick } = useListItemClick({ |
| 495 | * @param {Object} product - 产品对象 | 503 | * @param {Object} product - 产品对象 |
| 496 | */ | 504 | */ |
| 497 | const openPlanPopup = (product) => { | 505 | const openPlanPopup = (product) => { |
| 506 | + console.log('[Product Center] 点击制作计划书,当前登录状态:', userStore.isLoggedIn) | ||
| 507 | + checkPlanPermission(() => { | ||
| 508 | + console.log('[Product Center] 权限检查通过,打开计划书弹窗') | ||
| 498 | selectedProduct.value = product | 509 | selectedProduct.value = product |
| 499 | showPlanPopup.value = true | 510 | showPlanPopup.value = true |
| 511 | + }) | ||
| 500 | } | 512 | } |
| 501 | 513 | ||
| 502 | // 使用 composable 统一处理计划书提交后逻辑 | 514 | // 使用 composable 统一处理计划书提交后逻辑 | ... | ... |
| ... | @@ -104,7 +104,7 @@ | ... | @@ -104,7 +104,7 @@ |
| 104 | <nut-button | 104 | <nut-button |
| 105 | color="#2563EB" | 105 | color="#2563EB" |
| 106 | class="!w-full !h-[88rpx] !rounded-[16rpx] !text-[28rpx] !font-bold" | 106 | class="!w-full !h-[88rpx] !rounded-[16rpx] !text-[28rpx] !font-bold" |
| 107 | - @tap="openPlanPopup" | 107 | + @tap="() => checkPlanPermission(() => openPlanPopup())" |
| 108 | > | 108 | > |
| 109 | 制作计划书 | 109 | 制作计划书 |
| 110 | </nut-button> | 110 | </nut-button> |
| ... | @@ -134,6 +134,7 @@ import Taro, { useLoad } from '@tarojs/taro' | ... | @@ -134,6 +134,7 @@ import Taro, { useLoad } from '@tarojs/taro' |
| 134 | import { getDocumentIcon, getDocumentLabel } from '@/utils/documentIcons' | 134 | import { getDocumentIcon, getDocumentLabel } from '@/utils/documentIcons' |
| 135 | import { detailAPI } from '@/api/get_product' | 135 | import { detailAPI } from '@/api/get_product' |
| 136 | import { usePlanSubmit } from '@/composables/usePlanSubmit' | 136 | import { usePlanSubmit } from '@/composables/usePlanSubmit' |
| 137 | +import { usePlanPermission } from '@/composables/usePlanPermission' | ||
| 137 | 138 | ||
| 138 | const { viewFile } = useFileOperation() | 139 | const { viewFile } = useFileOperation() |
| 139 | 140 | ||
| ... | @@ -211,8 +212,10 @@ const viewDocument = (doc) => { | ... | @@ -211,8 +212,10 @@ const viewDocument = (doc) => { |
| 211 | /** | 212 | /** |
| 212 | * 打开计划书弹窗 | 213 | * 打开计划书弹窗 |
| 213 | * | 214 | * |
| 214 | - * @description 打开当前产品的计划书表单 | 215 | + * @description 检查登录权限后,打开当前产品的计划书表单 |
| 215 | */ | 216 | */ |
| 217 | +const { checkPlanPermission } = usePlanPermission() | ||
| 218 | + | ||
| 216 | const openPlanPopup = () => { | 219 | const openPlanPopup = () => { |
| 217 | showPlanPopup.value = true | 220 | showPlanPopup.value = true |
| 218 | } | 221 | } | ... | ... |
| ... | @@ -74,7 +74,7 @@ | ... | @@ -74,7 +74,7 @@ |
| 74 | :tags="item.tags || []" | 74 | :tags="item.tags || []" |
| 75 | class="search-result-item" | 75 | class="search-result-item" |
| 76 | @detail="goToProductDetail" | 76 | @detail="goToProductDetail" |
| 77 | - @plan="openPlanPopup" | 77 | + @plan="(productId) => checkPlanPermission(() => openPlanPopup(productId))" |
| 78 | /> | 78 | /> |
| 79 | 79 | ||
| 80 | <!-- File Results --> | 80 | <!-- File Results --> |
| ... | @@ -138,12 +138,16 @@ import PlanFormContainer from '@/components/plan/PlanFormContainer.vue' | ... | @@ -138,12 +138,16 @@ import PlanFormContainer from '@/components/plan/PlanFormContainer.vue' |
| 138 | import { searchAPI } from '@/api/search' | 138 | import { searchAPI } from '@/api/search' |
| 139 | import { mockSearchAPI } from '@/utils/mockData' | 139 | import { mockSearchAPI } from '@/utils/mockData' |
| 140 | import { usePlanSubmit } from '@/composables/usePlanSubmit' | 140 | import { usePlanSubmit } from '@/composables/usePlanSubmit' |
| 141 | +import { usePlanPermission } from '@/composables/usePlanPermission' | ||
| 141 | 142 | ||
| 142 | // ⚠️ MOCK 数据开关 - 开发环境使用 mock 数据,生产环境使用真实 API | 143 | // ⚠️ MOCK 数据开关 - 开发环境使用 mock 数据,生产环境使用真实 API |
| 143 | const USE_MOCK_DATA = process.env.NODE_ENV === 'development' | 144 | const USE_MOCK_DATA = process.env.NODE_ENV === 'development' |
| 144 | 145 | ||
| 145 | const go = useGo() | 146 | const go = useGo() |
| 146 | 147 | ||
| 148 | +// 获取权限检查方法 | ||
| 149 | +const { checkPlanPermission } = usePlanPermission() | ||
| 150 | + | ||
| 147 | /** | 151 | /** |
| 148 | * 搜索页面状态管理 | 152 | * 搜索页面状态管理 |
| 149 | * @description 支持双类型(产品/资料)搜索,自动切换分类 | 153 | * @description 支持双类型(产品/资料)搜索,自动切换分类 | ... | ... |
-
Please register or login to post a comment