/** * 登录拦截Hook * 用于在需要登录的操作前检查用户登录状态,未登录则弹出登录框 * * 使用方式: * import { useLoginGuard } from '@/common/hooks/useLoginGuard.js' * * const { checkLogin, requireLogin } = useLoginGuard() * * // 方式1:检查登录状态(返回布尔值) * if (!checkLogin()) return * * // 方式2:需要登录才执行(推荐) * await requireLogin() * // 之后的代码只有在登录后才执行 * * // 方式3:在点击事件中使用 * async function handleClick() { * await requireLogin() * // 执行需要登录的操作 * } */ import { getToken } from '@/utils/request.js' import { loadMemberStore } from '@/common/memberInfo/store.js' // 保存当前页面的登录弹窗引用 let loginModalRef = null /** * 注册登录弹窗组件 * 在App.vue或页面onMounted中调用 * @param {Object} modalRef - loginModal组件的ref */ export function registerLoginModal(modalRef) { loginModalRef = modalRef } /** * 获取当前登录状态 * @returns {Boolean} */ export function isLoggedIn() { const token = getToken() const isLoginStorage = uni.getStorageSync('isLogin') return !!(token || isLoginStorage) } /** * 获取登录用户信息 * @returns {Object|null} */ export function getLoginUser() { const store = loadMemberStore() return store.profile || null } /** * 使用登录拦截Hook * @returns {Object} */ export function useLoginGuard() { /** * 检查登录状态(同步) * @returns {Boolean} 是否已登录 */ function checkLogin() { return isLoggedIn() } /** * 需要登录才继续执行(异步) * 如果未登录,弹出登录框,等待用户登录或取消 * @param {Object} options - 配置选项 * @param {String} options.title - 弹窗标题 * @param {String} options.subtitle - 弹窗副标题 * @param {Function} options.onSuccess - 登录成功回调 * @param {Function} options.onCancel - 取消登录回调 * @returns {Promise} 是否已登录(用户登录成功返回true,取消返回false) */ async function requireLogin(options = {}) { // 如果已经登录,直接返回true if (isLoggedIn()) { return true } // 如果传入了onSuccess回调,先检查 if (options.onSuccess) { const store = loadMemberStore() if (store.profile?.id) { options.onSuccess(store.profile) return true } } // 显示登录弹窗 return new Promise((resolve) => { if (!loginModalRef) { console.warn('[useLoginGuard] 登录弹窗未注册,请先调用 registerLoginModal') // 尝试跳转到登录页 uni.navigateTo({ url: '/pages/memberInfo/login', fail: () => { uni.showToast({ title: '请先登录', icon: 'none' }) } }) resolve(false) return } // 保存resolve函数 let resolvePromise = resolve // 显示登录弹窗 loginModalRef.show() // 监听登录成功 const loginSuccessHandler = (result) => { if (options.onSuccess) { options.onSuccess(result) } resolvePromise(true) } // 监听登录失败/取消 const closeHandler = () => { if (options.onCancel) { options.onCancel() } resolvePromise(false) } // 注册事件监听 uni.$once('loginModal:success', loginSuccessHandler) uni.$once('loginModal:close', closeHandler) // 设置超时 setTimeout(() => { uni.$off('loginModal:success', loginSuccessHandler) uni.$off('loginModal:close', closeHandler) }, 60000) // 60秒超时 }) } /** * 安全执行函数(仅登录后执行) * @param {Function} fn - 要执行的函数 * @param {Object} options - 配置选项 * @returns {Promise} 函数返回值或null */ async function safeExecute(fn, options = {}) { const loggedIn = await requireLogin(options) if (loggedIn && typeof fn === 'function') { return await fn() } return null } /** * 包装需要登录的API调用 * @param {Function} apiFn - API函数 * @param {Array} args - API参数 * @param {Object} options - 配置选项 * @returns {Promise} */ async function callLoggedInApi(apiFn, args = [], options = {}) { const loggedIn = await requireLogin(options) if (loggedIn && typeof apiFn === 'function') { return await apiFn(...args) } throw new Error('用户未登录') } return { checkLogin, requireLogin, safeExecute, callLoggedInApi, isLoggedIn, getLoginUser } } /** * 创建登录拦截Mixin(用于选项式API) * @returns {Object} */ export function loginGuardMixin() { return { data() { return { isLogin: false } }, onLoad() { this.checkLoginStatus() }, onShow() { this.checkLoginStatus() }, methods: { checkLoginStatus() { this.isLogin = isLoggedIn() }, async ensureLogin() { if (!this.isLogin) { if (!loginModalRef) { uni.navigateTo({ url: '/pages/memberInfo/login' }) return false } loginModalRef.show() return false } return true } } } } /** * 路由守卫配置 * 配置需要登录的页面路径 */ export const protectedRoutes = [ '/pages/memberInfo/memberInfo', '/pages/memberInfo/checkIn', '/pages/memberInfo/booking', '/pages/memberInfo/coupons', '/pages/memberInfo/points', '/pages/memberInfo/userInfo' ] /** * 检查当前页面是否需要登录 * @param {String} currentPage - 当前页面路径 * @returns {Boolean} */ export function isProtectedRoute(currentPage) { return protectedRoutes.some(route => currentPage.includes(route)) }