feat(登录): 实现登录策略和路由拦截功能

添加登录相关配置文件和页面,包括登录策略配置、注册页面和路由拦截逻辑
移除旧的登录相关代码和配置,使用新的登录策略实现
在用户store中添加hasLogin计算属性用于检查登录状态
优化路由拦截器逻辑,支持白名单和黑名单策略
This commit is contained in:
feige996
2025-08-18 21:24:05 +08:00
parent 94248ab6b9
commit 168a19fbca
9 changed files with 91 additions and 54 deletions

3
env/.env vendored
View File

@@ -8,9 +8,6 @@ VITE_WX_APPID = 'wxa2abb91f64032a2b'
# https://uniapp.dcloud.net.cn/collocation/manifest.html#h5-router
VITE_APP_PUBLIC_BASE=/
# 登录页面
VITE_LOGIN_URL = '/pages/login/index'
# 后台请求地址
VITE_SERVER_BASEURL = 'https://ukw0y1.laf.run'
# 后台上传地址

View File

@@ -1,21 +1,20 @@
<script setup lang="ts">
import { onHide, onLaunch, onShow } from '@dcloudio/uni-app'
import { navigateToInterceptor } from '@/router/interceptor'
import { tabbarStore } from './tabbar/store'
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'
onLaunch((options) => {
// 处理直接进入页面路由的情况如h5直接输入路由、微信小程序分享后进入等
// https://github.com/unibest-tech/unibest/issues/192
console.log('App Launch', options)
if (options?.path) {
navigateToInterceptor.invoke({ url: `/${options.path}` })
}
else {
navigateToInterceptor.invoke({ url: '/' })
}
console.log('App Launch options: ', options)
const gotoPath = options?.path || ''
navigateToInterceptor.invoke({ url: gotoPath })
// 处理直接进入路由非首页时tabbarIndex 不正确的问题
tabbarStore.setAutoCurIdx(options.path)
tabbarStore.setAutoCurIdx(gotoPath)
})
onShow((options) => {
console.log('App Show', options)

15
src/login/README.md Normal file
View File

@@ -0,0 +1,15 @@
# 登录 说明
## 登录 2种策略
- 白名单策略
- 黑名单策略
### 白名单策略
进入任何页面都需要登录,只有进入到白名单中的页面,才不需要登录。默认进入应用需要先去登录页。
比如大部分2C的应用美团、今日头条、抖音等都可以直接浏览只有去个人中心或者点赞、评论、分享等操作才需要登录。
### 黑名单策略
进入任何页面都不需要登录,只有进入到黑名单中的页面/或者页面中某些动作需要登录,才需要登录。
比如大部分2B和后台管理类的应用比如企业微信、钉钉、飞书、内部报表系统、CMS系统等都需要登录只有登录后才能使用。

13
src/login/config.ts Normal file
View File

@@ -0,0 +1,13 @@
export const LOGIN_STRATEGY_MAP = {
BLACKLIST: 'BLACKLIST', // 黑名单策略默认可以进入APP
WHITELIST: 'WHITELIST', // 白名单策略默认不可以进入APP需要强制登录
}
// 登录策略,默认使用黑名单策略,即默认不需要登录就可以访问
export const LOGIN_STRATEGY = LOGIN_STRATEGY_MAP.WHITELIST
export const LOGIN_PAGE_LIST = ['/login/login', '/login/register']
// 排除在外的列表,白名单策略指白名单列表,黑名单策略指黑名单列表
export const EXCLUDE_LIST = [
'/xxx/index',
]

View File

@@ -72,6 +72,22 @@
"style": {
"navigationBarTitleText": "Vue Query 请求演示"
}
},
{
"path": "pages/login/login",
"type": "page",
"layout": "default",
"style": {
"navigationBarTitleText": "登录"
}
},
{
"path": "pages/login/register",
"type": "page",
"layout": "default",
"style": {
"navigationBarTitleText": "注册"
}
}
],
"subPackages": [

View File

@@ -0,0 +1,19 @@
<route lang="jsonc" type="page">
{
"layout": "default",
"style": {
"navigationBarTitleText": "注册"
}
}
</route>
<script lang="ts" setup>
</script>
<template>
<view>注册</view>
</template>
<style lang="scss" scoped>
//
</style>

View File

@@ -6,54 +6,43 @@
*/
import { useUserStore } from '@/store'
import { tabbarStore } from '@/tabbar/store'
import { needLoginPages as _needLoginPages, getLastPage, getNeedLoginPages } from '@/utils'
// TODO Check
const loginRoute = import.meta.env.VITE_LOGIN_URL
function isLogined() {
const userStore = useUserStore()
return !!userStore.userInfo.username
}
const isDev = import.meta.env.DEV
import { getLastPage } from '@/utils'
import { EXCLUDE_LIST, LOGIN_PAGE_LIST } from '../login/config'
// 黑名单登录拦截器 - (适用于大部分页面不需要登录,少部分页面需要登录)
export const navigateToInterceptor = {
// 注意这里的url是 '/' 开头的,如 '/pages/index/index',跟 'pages.json' 里面的 path 不同
// 增加对相对路径的处理BY 网友 @ideal
invoke({ url }: { url: string }) {
// console.log(url) // /pages/route-interceptor/index?name=feige&age=30
console.log(url) // /pages/route-interceptor/index?name=feige&age=30
let path = url.split('?')[0]
// 处理相对路径
if (!path.startsWith('/')) {
const currentPath = getLastPage().route
const currentPath = getLastPage()?.route || ''
const normalizedCurrentPath = currentPath.startsWith('/') ? currentPath : `/${currentPath}`
const baseDir = normalizedCurrentPath.substring(0, normalizedCurrentPath.lastIndexOf('/'))
path = `${baseDir}/${path}`
}
let needLoginPages: string[] = []
// 为了防止开发时出现BUG这里每次都获取一下。生产环境可以移到函数外性能更好
if (isDev) {
needLoginPages = getNeedLoginPages()
}
else {
needLoginPages = _needLoginPages
}
const isNeedLogin = needLoginPages.includes(path)
if (!isNeedLogin) {
return true
}
const hasLogin = isLogined()
if (hasLogin) {
return true
if (LOGIN_PAGE_LIST.includes(path)) {
console.log('000')
return
}
tabbarStore.restorePrevIdx()
const redirectRoute = `${loginRoute}?redirect=${encodeURIComponent(url)}`
uni.navigateTo({ url: redirectRoute })
return false
console.log('拦截器中得到的 path:', path)
const userStore = useUserStore()
if (userStore.hasLogin || [...EXCLUDE_LIST, ...LOGIN_PAGE_LIST].includes(path)) {
console.log('111')
uni.navigateTo({ url: path })
return
}
console.log('222')
const redirectUrl = `/login/login?redirect=${encodeURIComponent(path)}`
uni.navigateTo({ url: redirectUrl })
},
}

View File

@@ -103,6 +103,7 @@ export const useUserStore = defineStore(
getUserInfo,
setUserAvatar,
logout,
hasLogin: computed(() => !!userInfo.value.token),
}
},
{

View File

@@ -110,18 +110,6 @@ export function getCurrentPageI18nKey() {
return currPage.style.navigationBarTitleText
}
/**
* 得到所有的需要登录的 pages包括主包和分包的
* 只得到 path 数组
*/
export const getNeedLoginPages = (): string[] => getAllPages('needLogin').map(page => page.path)
/**
* 得到所有的需要登录的 pages包括主包和分包的
* 只得到 path 数组
*/
export const needLoginPages: string[] = getAllPages('needLogin').map(page => page.path)
/**
* 根据微信小程序当前环境,判断应该获取的 baseUrl
*/