mirror of
https://github.com/XiaoDaiGua-Ray/ray-template.git
synced 2025-04-06 03:57:49 +08:00
add: 补充部分文档,修复鉴权bug
This commit is contained in:
parent
d1cb4ddb41
commit
54d370c7a0
@ -44,7 +44,7 @@ export const SETUP_ROUTER_GUARD = true
|
||||
* 可以根据权限与白名单进行过滤, 但是 meta hidden 属性拥有最高的控制权限
|
||||
* 如果 mete hidden 设置为 false 则永远不会显示菜单选项
|
||||
*/
|
||||
export const WHITE_ROUTES = ['login', 'error-page', 'doc']
|
||||
export const WHITE_ROUTES = ['RLogin', 'ErrorPage', 'RayTemplateDoc']
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -18,19 +18,43 @@
|
||||
|
||||
import { useAxiosInterceptor, axiosCanceler } from '@/axios/helper/interceptor'
|
||||
import { appendRequestHeaders } from '@/axios/helper/axiosCopilot'
|
||||
import { APP_CATCH_KEY } from '@/appConfig/appConfig'
|
||||
import { getCache } from '@/utils/cache'
|
||||
|
||||
import type { RequestInterceptorConfig, ImplementFunction } from '@/axios/type'
|
||||
const { setImplementQueue } = useAxiosInterceptor()
|
||||
|
||||
/**
|
||||
*
|
||||
* 这里只是示例如何获取到系统缓存的 token 并且返回请求头 token 的 key 和 value
|
||||
* 尽可能的拆分每个拦截器的功能函数
|
||||
* 这是这个包存在的意义
|
||||
*
|
||||
* 当然你也可以根据 request instance 来特殊处理, 这里暂时不做演示
|
||||
*/
|
||||
const requestHeaderToken = (ins: RequestInterceptorConfig, mode: string) => {
|
||||
const token = getCache(APP_CATCH_KEY.token)
|
||||
|
||||
if (ins.url) {
|
||||
// TODO: 根据 url 不同是否设置 token
|
||||
}
|
||||
|
||||
return {
|
||||
key: 'X-TOKEN',
|
||||
value: token,
|
||||
}
|
||||
}
|
||||
|
||||
/** 注入请求头信息 */
|
||||
const injectRequestHeaders: ImplementFunction<RequestInterceptorConfig> = (
|
||||
ins,
|
||||
mode,
|
||||
) => {
|
||||
appendRequestHeaders(ins, [
|
||||
requestHeaderToken(ins, mode),
|
||||
{
|
||||
key: 'X-TOKEN',
|
||||
value: 'token',
|
||||
key: 'Demo-Header-Key',
|
||||
value: 'Demo Header Value',
|
||||
},
|
||||
])
|
||||
}
|
||||
|
9
src/components/AppComponents/README.md
Normal file
9
src/components/AppComponents/README.md
Normal file
@ -0,0 +1,9 @@
|
||||
## 描述
|
||||
|
||||
> 该组件包存放依赖系统数据的公共组件。
|
||||
|
||||
## 约束
|
||||
|
||||
- 该组件包仅存放与系统数据有绑定、关联的组件,纯组件或纯 UI 组件应放置于外层包中
|
||||
- 以 `App` 开头标记组件是系统组件
|
||||
- 组件应该尽量避免与其他系统组件有关联性
|
@ -24,42 +24,23 @@ import { getCache, setCache } from '@/utils/cache'
|
||||
import { useSignin } from '@/store'
|
||||
import { APP_CATCH_KEY, ROOT_ROUTE } from '@/appConfig/appConfig'
|
||||
import { redirectRouterToDashboard } from '@/router/helper/routerCopilot'
|
||||
import { validRole, validMenuItemShow } from '@/router/helper/routerCopilot'
|
||||
|
||||
import type { Router, NavigationGuardNext } from 'vue-router'
|
||||
import type {
|
||||
Router,
|
||||
NavigationGuardNext,
|
||||
RouteLocationNormalized,
|
||||
} from 'vue-router'
|
||||
|
||||
export const permissionRouter = (router: Router) => {
|
||||
const { beforeEach } = router
|
||||
|
||||
const { path } = ROOT_ROUTE
|
||||
|
||||
beforeEach((to, from, next) => {
|
||||
const token = getCache(APP_CATCH_KEY.token)
|
||||
const route = getCache('menuKey')
|
||||
const { signinCallback } = storeToRefs(useSignin())
|
||||
const role = computed(() => signinCallback.value.role)
|
||||
const { meta } = to
|
||||
|
||||
/**
|
||||
*
|
||||
* 检查是否有权限, 如果权限不匹配则重定向至首页(默认为 dashboard)
|
||||
* 权限匹配使用严格比对, 对大小写、空格等敏感
|
||||
*/
|
||||
const hasRole = () => {
|
||||
/** 如果未设置权限则默认无需鉴权 */
|
||||
if (meta.role) {
|
||||
/** 空权限列表默认无需鉴权 */
|
||||
if (meta.role.length === 0) {
|
||||
return true
|
||||
} else {
|
||||
return meta.role.includes(role.value)
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (token !== 'no') {
|
||||
if (hasRole()) {
|
||||
if (validMenuItemShow(to as unknown as IMenuOptions)) {
|
||||
if (to.path === '/' || from.path === '/login') {
|
||||
if (route !== 'no') {
|
||||
next(route)
|
||||
|
@ -23,6 +23,7 @@ import { ROOT_ROUTE } from '@/appConfig/appConfig'
|
||||
import { setCache } from '@/utils/cache'
|
||||
|
||||
import type { Router } from 'vue-router'
|
||||
import type { AppRouteMeta } from '@/router/type'
|
||||
|
||||
/**
|
||||
*
|
||||
@ -31,17 +32,23 @@ import type { Router } from 'vue-router'
|
||||
*
|
||||
* 如果为超级管理员, 则会默认获取所有权限
|
||||
*/
|
||||
export const validRole = (option: IMenuOptions) => {
|
||||
export const validRole = (meta: AppRouteMeta) => {
|
||||
const { signinCallback } = storeToRefs(useSignin())
|
||||
const role = computed(() => signinCallback.value.role)
|
||||
|
||||
const { meta } = option
|
||||
const { role: metaRole } = meta
|
||||
|
||||
if (SUPER_ADMIN?.length && SUPER_ADMIN.includes(role.value)) {
|
||||
return true
|
||||
} else {
|
||||
if (meta?.role) {
|
||||
return meta.role.includes(role.value)
|
||||
// 如果 role 为 undefind 或者空数组, 则认为该路由不做权限过滤
|
||||
if (!metaRole || !metaRole?.length) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 判断是否含有该权限
|
||||
if (metaRole) {
|
||||
return metaRole.includes(role.value)
|
||||
}
|
||||
|
||||
return true
|
||||
@ -63,7 +70,7 @@ export const validMenuItemShow = (option: IMenuOptions) => {
|
||||
meta?.hidden === undefined || meta?.hidden === false ? false : meta?.hidden
|
||||
|
||||
// 如果是超级管理员(预设为 admin), 则根据其菜单栏(hidden)字段判断是否显示
|
||||
if (validRole(option)) {
|
||||
if (validRole(meta)) {
|
||||
return true && !hidden
|
||||
} else {
|
||||
// 如果为基础路由, 不进行鉴权则根据其菜单栏(hidden)字段判断是否显示
|
||||
@ -71,9 +78,14 @@ export const validMenuItemShow = (option: IMenuOptions) => {
|
||||
return true && !hidden
|
||||
}
|
||||
|
||||
// 如果 role 为 undefind 或者空数组, 则认为该路由不做权限过滤
|
||||
if (!meta?.role || !meta.role?.length) {
|
||||
return true && !hidden
|
||||
}
|
||||
|
||||
// 判断权限是否匹配和菜单栏(hidden)字段判断是否显示
|
||||
if (meta?.role) {
|
||||
return validRole(option) && !hidden
|
||||
if (meta?.role && meta.role.length) {
|
||||
return validRole(meta) && !hidden
|
||||
}
|
||||
|
||||
return true && !hidden
|
||||
@ -127,7 +139,8 @@ export const redirectRouterToDashboard = (isReplace = true) => {
|
||||
const { push, replace } = router
|
||||
const { path } = ROOT_ROUTE
|
||||
|
||||
isReplace ? push(path) : replace(path)
|
||||
|
||||
setCache('menuKey', path)
|
||||
console.log('path', path)
|
||||
|
||||
isReplace ? push(path) : replace(path)
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ const axios: AppRouteRecordRaw = {
|
||||
icon: 'axios',
|
||||
order: 3,
|
||||
keepAlive: true,
|
||||
hidden: false,
|
||||
},
|
||||
}
|
||||
|
||||
|
29
src/store/README.md
Normal file
29
src/store/README.md
Normal file
@ -0,0 +1,29 @@
|
||||
## 描述
|
||||
|
||||
> pinia store 仓库包。存放全局公共状态。
|
||||
|
||||
## 约束
|
||||
|
||||
- 状态管理器应该按照其用途进行分包(见名知意)
|
||||
- 包名以用途名命名
|
||||
- 默认以 index.ts 作为入口,其余的辅助函数、类型,分别在该文件夹下进行补充(type.ts、helper.ts。。。)
|
||||
- 仓库使用 `piniaPluginPersistedstate` 作为中间件,用于缓存仓库数据避免刷新丢失(但是该方法有缺陷,不能缓存函数)
|
||||
- 默认不全部缓存参数,如果需要缓存参数,需要在 `defineStore` 第三个参数配置 `persist` 属性
|
||||
- `defineStore` 第一个参数必须全局唯一
|
||||
- 缓存插件 key 应该按照 `piniaXXXStore` 格式命名(XXX 表示该包名称)
|
||||
|
||||
```ts
|
||||
export const useDemoStore = defineStore('demo', () => {}, {
|
||||
persist: {
|
||||
key: 'piniaDemoStore',
|
||||
paths: ['demoState'],
|
||||
storage: sessionStorage | localStorage,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
- 最后在 index.ts 中暴露使用
|
||||
|
||||
```ts
|
||||
export { useDemo } from './modules/demo/index'
|
||||
```
|
@ -14,6 +14,7 @@
|
||||
import { MENU_COLLAPSED_CONFIG, ROOT_ROUTE } from '@/appConfig/appConfig'
|
||||
import RayIcon from '@/components/RayIcon/index'
|
||||
import { validteValueType } from '@/utils/hook'
|
||||
import { getCache, setCache } from '@/utils/cache'
|
||||
|
||||
import type { VNode } from 'vue'
|
||||
|
||||
@ -156,3 +157,12 @@ export const hasMenuIcon = (option: IMenuOptions) => {
|
||||
|
||||
return () => icon
|
||||
}
|
||||
|
||||
/** 获取缓存的 menu key, 如果未获取到则使用 ROOTROUTE path 当作默认激活路由菜单 */
|
||||
export const getCatchMenuKey = () => {
|
||||
const { path: rootPath } = ROOT_ROUTE
|
||||
const cacheMenuKey: MenuKey =
|
||||
getCache('menuKey') === 'no' ? rootPath : getCache('menuKey')
|
||||
|
||||
return cacheMenuKey
|
||||
}
|
||||
|
@ -23,7 +23,6 @@
|
||||
*/
|
||||
|
||||
import { NEllipsis } from 'naive-ui'
|
||||
import RayIcon from '@/components/RayIcon/index'
|
||||
|
||||
import { getCache, setCache } from '@/utils/cache'
|
||||
import { validMenuItemShow } from '@/router/helper/routerCopilot'
|
||||
@ -32,9 +31,9 @@ import {
|
||||
matchMenuOption,
|
||||
updateDocumentTitle,
|
||||
hasMenuIcon,
|
||||
getCatchMenuKey,
|
||||
} from './helper'
|
||||
import { useI18n } from '@/locales/useI18n'
|
||||
import { MENU_COLLAPSED_CONFIG, ROOT_ROUTE } from '@/appConfig/appConfig'
|
||||
import routeModules from '@/router/routeModules'
|
||||
import { useKeepAlive } from '@/store'
|
||||
import { useVueRouter } from '@/router/helper/useVueRouter'
|
||||
@ -50,13 +49,8 @@ export const useMenu = defineStore(
|
||||
const { t } = useI18n()
|
||||
const { setKeepAliveInclude } = useKeepAlive()
|
||||
|
||||
const { path: rootPath } = ROOT_ROUTE
|
||||
|
||||
const cacheMenuKey =
|
||||
getCache('menuKey') === 'no' ? rootPath : getCache('menuKey')
|
||||
|
||||
const menuState = reactive({
|
||||
menuKey: cacheMenuKey as MenuKey, // 当前菜单 `key`
|
||||
menuKey: getCatchMenuKey(), // 当前菜单 `key`
|
||||
options: [] as IMenuOptions[], // 菜单列表
|
||||
collapsed: false, // 是否折叠菜单
|
||||
menuTagOptions: [] as MenuTagOptions[], // tag 标签菜单
|
||||
@ -196,7 +190,7 @@ export const useMenu = defineStore(
|
||||
icon: hasMenuIcon(option),
|
||||
})
|
||||
|
||||
if (option.path === cacheMenuKey) {
|
||||
if (option.path === getCatchMenuKey()) {
|
||||
/** 设置菜单标签 */
|
||||
setMenuTagOptions(attr)
|
||||
/** 设置浏览器标题 */
|
||||
|
@ -21,7 +21,6 @@
|
||||
|
||||
import { isEmpty } from 'lodash-es'
|
||||
import { removeCache } from '@/utils/cache'
|
||||
import { useMenu } from '@/store'
|
||||
|
||||
import type {
|
||||
SigninForm,
|
||||
|
Loading…
x
Reference in New Issue
Block a user