feat:路由的接入,部分类似 config.ts、interceptor.ts 的拦截,移植自 unibest3.x 代码

This commit is contained in:
YunaiV
2025-12-12 19:15:11 +08:00
parent 6a4c78f0a9
commit ae9451f415
4 changed files with 202 additions and 24 deletions

44
src/router/config.ts Normal file
View File

@@ -0,0 +1,44 @@
// import { getAllPages } from '@/utils'
export const LOGIN_STRATEGY_MAP = {
DEFAULT_NO_NEED_LOGIN: 0, // 黑名单策略默认可以进入APP
DEFAULT_NEED_LOGIN: 1, // 白名单策略默认不可以进入APP需要强制登录
}
// TODO: 1/3 登录策略,默认使用`无需登录策略`,即默认不需要登录就可以访问
export const LOGIN_STRATEGY = LOGIN_STRATEGY_MAP.DEFAULT_NEED_LOGIN // edit by 芋艿:管理后台,默认需要登录
export const isNeedLoginMode
= LOGIN_STRATEGY === LOGIN_STRATEGY_MAP.DEFAULT_NEED_LOGIN
export const LOGIN_PAGE = '/pages/auth/login' // edit by 芋艿:自定义了登录页路径
export const REGISTER_PAGE = '/pages/auth/register' // edit by 芋艿:自定义了注册页路径
export const CODE_LOGIN_PAGE = '/pages/auth/code-login' // edit by 芋艿:自定义了短信登录页路径
export const FORGET_PASSWORD_PAGE = '/pages/auth/forget-password' // edit by 芋艿:自定义了忘记密码页路径
export const NOT_FOUND_PAGE = '/pages/error/404' // edit by 芋艿:调整 404 页面路径
// TODO @芋艿:【优化】貌似 unibest 这个变量没用?!
export const LOGIN_PAGE_LIST = [
LOGIN_PAGE,
REGISTER_PAGE,
CODE_LOGIN_PAGE,
FORGET_PASSWORD_PAGE,
]
// 注释 by 芋艿:在 mp 环境下getAllPages 函数还没初始化好,所以不能直接调用。统一优化到 judgeIsExcludePath 函数里面去获取
// 在 definePage 里面配置了 excludeLoginPath 的页面,功能与 EXCLUDE_LOGIN_PATH_LIST 相同
// export const excludeLoginPathList = getAllPages('excludeLoginPath').map(
// page => page.path,
// )
// 排除在外的列表,白名单策略指白名单列表,黑名单策略指黑名单列表
// TODO: 2/3 在 definePage 配置 excludeLoginPath或者在下面配置 EXCLUDE_LOGIN_PATH_LIST
export const EXCLUDE_LOGIN_PATH_LIST = [
'/pages/xxx/index', // 示例值
'/pages-sub/xxx/index', // 示例值
// 注释 by 芋艿:在 mp 环境下getAllPages 函数还没初始化好,所以不能直接调用。统一优化到 judgeIsExcludePath 函数里面去获取
// ...excludeLoginPathList, // 都是以 / 开头的 path
]
// 在小程序里面是否使用H5的登录页默认为 false
// 如果为 true 则复用 h5 的登录逻辑
// TODO: 3/3 确定自己的登录页是否需要在小程序里面使用
export const LOGIN_PAGE_ENABLE_IN_MP = true // edit by 芋艿:管理后台,小程序也使用自定义登录页

View File

@@ -1,13 +1,37 @@
import { isMp } from '@uni-helper/uni-env'
/**
* by 菲鸽 on 2025-08-19
* 路由拦截,通常也是登录拦截
* 黑、白名单的配置,请看 config.ts 文件, EXCLUDE_LOGIN_PATH_LIST
*/
import { tabbarStore } from '@/tabbar/store'
import { getAllPages, getLastPage, parseUrlToObj } from '@/utils/index'
import { useTokenStore } from '@/store/token'
import { isPageTabbar, tabbarStore } from '@/tabbar/store'
import { getAllPages, getLastPage, HOME_PAGE, parseUrlToObj } from '@/utils/index'
import { toLoginPage } from '@/utils/toLoginPage'
import { EXCLUDE_LOGIN_PATH_LIST, isNeedLoginMode, LOGIN_PAGE, LOGIN_PAGE_ENABLE_IN_MP, NOT_FOUND_PAGE } from './config'
export const FG_LOG_ENABLE = false
let excludeListInited = false // 标记 EXCLUDE_LOGIN_PATH_LIST 是否已经根据 getAllPages('excludeLoginPath') 做过一次性补充(仅非开发环境)
export function judgeIsExcludePath(path: string) {
const isDev = import.meta.env.DEV
if (!isDev) {
// edit by 芋艿:非开发环境下,只初始化一次
if (!excludeListInited) {
const pages = getAllPages('excludeLoginPath')
pages.forEach((page) => {
if (!EXCLUDE_LOGIN_PATH_LIST.includes(page.path)) {
EXCLUDE_LOGIN_PATH_LIST.push(page.path)
}
})
excludeListInited = true
}
return EXCLUDE_LOGIN_PATH_LIST.includes(path)
}
const allExcludeLoginPages = getAllPages('excludeLoginPath') // dev 环境下,需要每次都重新获取,否则新配置就不会生效
return EXCLUDE_LOGIN_PATH_LIST.includes(path) || (isDev && allExcludeLoginPages.some(page => page.path === path))
}
export const navigateToInterceptor = {
// 注意这里的url是 '/' 开头的,如 '/pages/index/index',跟 'pages.json' 里面的 path 不同
// 增加对相对路径的处理BY 网友 @ideal
@@ -35,6 +59,7 @@ export const navigateToInterceptor = {
// 处理路由不存在的情况
if (path !== '/' && !getAllPages().some(page => page.path !== path)) {
console.warn('路由不存在:', path)
uni.navigateTo({ url: NOT_FOUND_PAGE })
return false // 明确表示阻止原路由继续执行
}
@@ -46,6 +71,66 @@ export const navigateToInterceptor = {
// 处理直接进入路由非首页时tabbarIndex 不正确的问题
tabbarStore.setAutoCurIdx(path)
// 小程序里面使用平台自带的登录,则不走下面的逻辑
if (isMp && !LOGIN_PAGE_ENABLE_IN_MP) {
return true // 明确表示允许路由继续执行
}
const tokenStore = useTokenStore()
FG_LOG_ENABLE && console.log('tokenStore.hasLogin:', tokenStore.hasLogin)
// 不管黑白名单,登录了就直接去吧(但是当前不能是登录页)
if (tokenStore.hasLogin) {
if (path !== LOGIN_PAGE) {
return true // 明确表示允许路由继续执行
} else {
console.log('已经登录,但是还在登录页', myQuery.redirect)
const url = myQuery.redirect || HOME_PAGE
if (isPageTabbar(url)) {
uni.switchTab({ url })
} else {
uni.navigateTo({ url })
}
return false // 明确表示阻止原路由继续执行
}
}
let fullPath = path
if (Object.keys(myQuery).length) {
fullPath += `?${Object.keys(myQuery).map(key => `${key}=${myQuery[key]}`).join('&')}`
}
const redirectQuery = `?redirect=${encodeURIComponent(fullPath)}`
// #region 1/2 默认需要登录的情况(白名单策略) ---------------------------
if (isNeedLoginMode) {
// 需要登录里面的 EXCLUDE_LOGIN_PATH_LIST 表示白名单,可以直接通过
if (judgeIsExcludePath(path)) {
return true // 明确表示允许路由继续执行
}
// 否则需要重定向到登录页
else {
if (path === LOGIN_PAGE) {
return true // 明确表示允许路由继续执行
}
FG_LOG_ENABLE && console.log('1 isNeedLogin(白名单策略) url:', fullPath)
toLoginPage({ queryString: redirectQuery })
return false // 明确表示阻止原路由继续执行
}
}
// #endregion 1/2 默认需要登录的情况(白名单策略) ---------------------------
// #region 2/2 默认不需要登录的情况(黑名单策略) ---------------------------
else {
// 不需要登录里面的 EXCLUDE_LOGIN_PATH_LIST 表示黑名单,需要重定向到登录页
if (judgeIsExcludePath(path)) {
FG_LOG_ENABLE && console.log('2 isNeedLogin(黑名单策略) url:', fullPath)
toLoginPage({ queryString: redirectQuery })
return false // 修改为false阻止原路由继续执行
}
return true // 明确表示允许路由继续执行
}
// #endregion 2/2 默认不需要登录的情况(黑名单策略) ---------------------------
},
}

View File

@@ -34,7 +34,7 @@ export const nativeTabbarList: NativeTabBarItem[] = [
{
iconPath: 'static/tabbar/personal.png',
selectedIconPath: 'static/tabbar/personalHL.png',
pagePath: 'pages/me/me',
pagePath: 'pages/user/index', // edit by 芋艿:原 me 被删除,改为 user 避免 IDE linter 报错
text: '个人',
},
]
@@ -42,25 +42,25 @@ export const nativeTabbarList: NativeTabBarItem[] = [
// TODO: 3/3. 使用 CUSTOM_TABBAR(2,3) 时,更新下面的 tabbar 配置
// 如果需要配置鼓包,需要在 'tabbar/store.ts' 里面设置,最后在 `tabbar/index.vue` 里面更改鼓包的图片
export const customTabbarList: CustomTabBarItem[] = [
{
text: '首页',
pagePath: 'pages/index/index',
// 注意 unocss 图标需要如下处理:(二选一)
// 1在fg-tabbar.vue页面上引入一下并注释掉见tabbar/index.vue代码第2行
// 2配置到 unocss.config.ts 的 safelist 中
iconType: 'unocss',
icon: 'i-carbon-home',
// badge: 'dot',
},
{
pagePath: 'pages/me/me',
text: '我的',
// 1在fg-tabbar.vue页面上引入一下并注释掉见tabbar/index.vue代码第2行
// 2配置到 unocss.config.ts 的 safelist 中
iconType: 'unocss',
icon: 'i-carbon-user',
// badge: 10,
},
// {
// text: '首页',
// pagePath: 'pages/index/index',
// // 注意 unocss 图标需要如下处理:(二选一)
// // 1在fg-tabbar.vue页面上引入一下并注释掉见tabbar/index.vue代码第2行
// // 2配置到 unocss.config.ts 的 safelist 中
// iconType: 'unocss',
// icon: 'i-carbon-home',
// // badge: 'dot',
// },
// {
// pagePath: 'pages/me/me',
// text: '我的',
// // 1在fg-tabbar.vue页面上引入一下并注释掉见tabbar/index.vue代码第2行
// // 2配置到 unocss.config.ts 的 safelist 中
// iconType: 'unocss',
// icon: 'i-carbon-user',
// // badge: 10,
// },
// 其他类型演示
// 1、uiLib
// {
@@ -86,6 +86,37 @@ export const customTabbarList: CustomTabBarItem[] = [
// icon: '/static/tabbar/home.png',
// iconActive: '/static/tabbar/homeHL.png',
// },
// add by 芋艿:图标可到 https://icon-sets.iconify.design/carbon/ 选择。另外,需要在 uno.config.ts 的 safelist 中添加图标类名
{
text: '工作台',
pagePath: 'pages/index/index',
iconType: 'unocss',
icon: 'i-carbon-home',
},
{
text: '审批',
pagePath: 'pages/bpm/index',
iconType: 'unocss',
icon: 'i-carbon-document',
},
{
text: '通讯录',
pagePath: 'pages/contact/index',
iconType: 'unocss',
icon: 'i-carbon-user-avatar',
},
{
text: '消息',
pagePath: 'pages/message/index',
iconType: 'unocss',
icon: 'i-carbon-chat',
},
{
text: '我的',
pagePath: 'pages/user/index',
iconType: 'unocss',
icon: 'i-carbon-user',
},
]
/**
@@ -111,6 +142,16 @@ export const needHideNativeTabbar = selectedTabbarStrategy === TABBAR_STRATEGY_M
const _tabbarList = customTabbarEnable ? customTabbarList.map(item => ({ text: item.text, pagePath: item.pagePath })) : nativeTabbarList
export const tabbarList = customTabbarEnable ? customTabbarList : nativeTabbarList
/**
* 判断路径是否是 tabBar 页面
* @param path 页面路径(支持带或不带 / 前缀)
*/
export function isTabBarPage(path: string): boolean {
// 统一处理路径格式:去掉开头的 /
const normalizedPath = path.startsWith('/') ? path.slice(1) : path
return tabbarList.some(item => item.pagePath === normalizedPath)
}
const _tabbar: TabBar = {
// 只有微信小程序支持 custom。App 和 H5 不生效
custom: selectedTabbarStrategy === TABBAR_STRATEGY_MAP.CUSTOM_TABBAR_WITH_CACHE,

View File

@@ -1,6 +1,9 @@
import type { CustomTabBarItem, CustomTabBarItemBadge } from './types'
import { reactive } from 'vue'
import { isNeedLoginMode } from '@/router/config'
import { FG_LOG_ENABLE, judgeIsExcludePath } from '@/router/interceptor'
import { useTokenStore } from '@/store/token'
import { tabbarList as _tabbarList, customTabbarEnable, selectedTabbarStrategy, TABBAR_STRATEGY_MAP } from './config'
// TODO 1/2: 中间的鼓包tabbarItem的开关
@@ -38,8 +41,12 @@ const tabbarStore = reactive({
curIdx: uni.getStorageSync('app-tabbar-index') || 0,
prevIdx: uni.getStorageSync('app-tabbar-index') || 0,
setCurIdx(idx: number) {
this.curIdx = idx
uni.setStorageSync('app-tabbar-index', idx)
const tokenStore = useTokenStore()
// 已登录 或 (url 需要登录 && 在白名单 || 不需要登录 && 不在黑名单) (关于 白名单|黑名单 逻辑: src/router/interceptor.ts
if (tokenStore.hasLogin || (isNeedLoginMode && judgeIsExcludePath(tabbarList[idx].pagePath)) || (!isNeedLoginMode && !judgeIsExcludePath(tabbarList[idx].pagePath))) {
this.curIdx = idx
uni.setStorageSync('app-tabbar-index', idx)
}
},
setTabbarItemBadge(idx: number, badge: CustomTabBarItemBadge) {
if (tabbarList[idx]) {
@@ -53,6 +60,7 @@ const tabbarStore = reactive({
return
}
const index = tabbarList.findIndex(item => item.pagePath === path)
FG_LOG_ENABLE && console.log('index:', index, path)
// console.log('tabbarList:', tabbarList)
if (index === -1) {
const pagesPathList = getCurrentPages().map(item => item.route.startsWith('/') ? item.route : `/${item.route}`)