hookehuyr

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
/**
* 计划书权限检查 Composable
*
* @description 统一处理制作计划书的登录权限检查
* @module composables/usePlanPermission
* @author Claude Code
* @created 2026-02-12
*/
import { useUserStore } from '@/stores/user'
import Taro from '@tarojs/taro'
/**
* 计划书权限检查 Hook
*
* @description 提供统一的权限检查逻辑,用于制作计划书前的登录验证
* @returns {Object} 权限检查方法
*
* @example
* const { checkPlanPermission } = usePlanPermission()
*
* // 在点击计划书按钮时使用
* checkPlanPermission(() => {
* // 已登录时的回调逻辑
* openPlanPopup(productId)
* })
*/
export function usePlanPermission() {
const userStore = useUserStore()
/**
* 检查计划书权限
*
* @description 判断用户是否登录,未登录时提示并引导登录,已登录时执行回调
* @param {Function} callback - 已登录时执行的回调函数
* @returns {boolean} 是否有权限(true=已登录,false=未登录)
*
* @example
* const hasPermission = checkPlanPermission(() => {
* console.log('用户已登录,可以制作计划书')
* })
*/
const checkPlanPermission = (callback) => {
console.log('[usePlanPermission] 检查权限,当前登录状态:', userStore.isLoggedIn)
// 检查登录状态
if (!userStore.isLoggedIn) {
console.log('[usePlanPermission] 用户未登录,显示登录提示')
// 未登录,显示提示框
Taro.showModal({
title: '提示',
content: '请先登录后再制作计划书',
confirmText: '去登录',
cancelText: '取消',
success: (res) => {
if (res.confirm) {
// 用户点击"去登录",跳转到登录页
console.log('[usePlanPermission] 用户点击去登录')
Taro.navigateTo({
url: '/pages/login/index'
})
}
}
})
return false
}
console.log('[usePlanPermission] 用户已登录,执行回调')
// 已登录,执行回调
callback?.()
return true
}
return {
checkPlanPermission
}
}
......@@ -95,7 +95,7 @@
:tags="product.tags"
:class="{ 'mb-[24rpx]': index < hotProducts.length - 1 }"
@detail="goToProductDetail"
@plan="openPlanPopup"
@plan="(productId) => checkPlanPermission(() => openPlanPopup(productId))"
/>
</view>
</view>
......@@ -173,11 +173,15 @@ import { listAPI } from '@/api/get_product';
import { weekHotAPI } from '@/api/file';
import { homeIconAPI } from '@/api/home';
import { usePlanSubmit } from '@/composables/usePlanSubmit';
import { usePlanPermission } from '@/composables/usePlanPermission';
// User Store
const userStore = useUserStore();
// 获取权限检查方法
const { checkPlanPermission } = usePlanPermission();
// Header Image Error State
/**
* 头部图片加载失败状态
......
......@@ -142,6 +142,7 @@
import { ref, computed } from 'vue'
import Taro, { useLoad } from '@tarojs/taro'
import { useGo } from '@/hooks/useGo'
import { useUserStore } from '@/stores/user'
import { useListItemClick, ListType } from '@/composables/useListItemClick'
import LoadMoreList from '@/components/list/LoadMoreList'
import NavHeader from '@/components/navigation/NavHeader.vue'
......@@ -150,6 +151,7 @@ import PlanFormContainer from '@/components/plan/PlanFormContainer.vue'
import { listAPI } from '@/api/get_product'
import { mockProductListAPI } from '@/utils/mockData'
import { usePlanSubmit } from '@/composables/usePlanSubmit'
import { usePlanPermission } from '@/composables/usePlanPermission'
// ⚠️ MOCK 数据开关 - 开发环境使用 mock 数据,生产环境使用真实 API
// const USE_MOCK_DATA = process.env.NODE_ENV === 'development'
......@@ -157,6 +159,12 @@ const USE_MOCK_DATA = false
const go = useGo()
// User Store
const userStore = useUserStore()
// 获取权限检查方法
const { checkPlanPermission } = usePlanPermission()
/**
* 当前列表数据
* @type {Ref<Array<any>>}
......@@ -495,8 +503,12 @@ const { handleClick: handleProductClick } = useListItemClick({
* @param {Object} product - 产品对象
*/
const openPlanPopup = (product) => {
selectedProduct.value = product
showPlanPopup.value = true
console.log('[Product Center] 点击制作计划书,当前登录状态:', userStore.isLoggedIn)
checkPlanPermission(() => {
console.log('[Product Center] 权限检查通过,打开计划书弹窗')
selectedProduct.value = product
showPlanPopup.value = true
})
}
// 使用 composable 统一处理计划书提交后逻辑
......
......@@ -104,7 +104,7 @@
<nut-button
color="#2563EB"
class="!w-full !h-[88rpx] !rounded-[16rpx] !text-[28rpx] !font-bold"
@tap="openPlanPopup"
@tap="() => checkPlanPermission(() => openPlanPopup())"
>
制作计划书
</nut-button>
......@@ -134,6 +134,7 @@ import Taro, { useLoad } from '@tarojs/taro'
import { getDocumentIcon, getDocumentLabel } from '@/utils/documentIcons'
import { detailAPI } from '@/api/get_product'
import { usePlanSubmit } from '@/composables/usePlanSubmit'
import { usePlanPermission } from '@/composables/usePlanPermission'
const { viewFile } = useFileOperation()
......@@ -211,10 +212,12 @@ const viewDocument = (doc) => {
/**
* 打开计划书弹窗
*
* @description 打开当前产品的计划书表单
* @description 检查登录权限后,打开当前产品的计划书表单
*/
const { checkPlanPermission } = usePlanPermission()
const openPlanPopup = () => {
showPlanPopup.value = true
showPlanPopup.value = true
}
// 使用 composable 统一处理计划书提交后逻辑
......
......@@ -74,7 +74,7 @@
:tags="item.tags || []"
class="search-result-item"
@detail="goToProductDetail"
@plan="openPlanPopup"
@plan="(productId) => checkPlanPermission(() => openPlanPopup(productId))"
/>
<!-- File Results -->
......@@ -138,12 +138,16 @@ import PlanFormContainer from '@/components/plan/PlanFormContainer.vue'
import { searchAPI } from '@/api/search'
import { mockSearchAPI } from '@/utils/mockData'
import { usePlanSubmit } from '@/composables/usePlanSubmit'
import { usePlanPermission } from '@/composables/usePlanPermission'
// ⚠️ MOCK 数据开关 - 开发环境使用 mock 数据,生产环境使用真实 API
const USE_MOCK_DATA = process.env.NODE_ENV === 'development'
const go = useGo()
// 获取权限检查方法
const { checkPlanPermission } = usePlanPermission()
/**
* 搜索页面状态管理
* @description 支持双类型(产品/资料)搜索,自动切换分类
......