248 lines
6.6 KiB
JavaScript
248 lines
6.6 KiB
JavaScript
/**
|
|
* 登录拦截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<Boolean>} 是否已登录(用户登录成功返回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<any>} 函数返回值或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<any>}
|
|
*/
|
|
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))
|
|
}
|