diff --git a/CHANGELOG.md b/CHANGELOG.md index b9431c43..a97a4958 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # CHANGE LOG +## 3.3.5 + +### Feats + +- Router Meta 属性支持自定义图标,不再局限于 RayIcon,支持自定义图标 +- 更改部分组件默认值,默认值统一为 `null` +- 调整 validRole 方法逻辑,将该方法以前逻辑拆分为 validRole 与 validMenuItemShow 两个方法 +- 新增使用手册 + +### 补充 + +> 由于文档已经拖更很久,所以补充一个使用手册。最近太忙了,一直忙着更新完善模板本身,文档的事情暂时没有时间去维护更新,所以与模板断层太久。。。后续有时间肯定会补上!!! + ## 3.3.4 ### Feats diff --git a/MANUAL.md b/MANUAL.md new file mode 100644 index 00000000..826d1666 --- /dev/null +++ b/MANUAL.md @@ -0,0 +1,19 @@ +## Ray Template 使用手册 + +## 前言 + +> `Ray Template` 默认使用 `yarn` 作为包管理器,并且默认启用严格模式的 `eslint`。 + +### 使用 + +#### 依赖安装 + +```sh +# yarn +yarn + +# npm +npm i +``` + +#### 未完待续。。。后续慢慢更新该文件 diff --git a/src/appConfig/routerConfig.ts b/src/appConfig/routerConfig.ts index da455618..8583e79e 100644 --- a/src/appConfig/routerConfig.ts +++ b/src/appConfig/routerConfig.ts @@ -51,4 +51,4 @@ export const WHITE_ROUTES = ['login', 'error-page', 'doc'] * 超级管理员 * 配置默认超级管理员, 默认拥有全部最高权限 */ -export const SUPER_ADMIN = ['admin'] +export const SUPER_ADMIN: (string | number)[] = ['admin'] diff --git a/src/components/RayGlobalProvider/index.tsx b/src/components/RayGlobalProvider/index.tsx index 4b5ef664..cdbc4602 100644 --- a/src/components/RayGlobalProvider/index.tsx +++ b/src/components/RayGlobalProvider/index.tsx @@ -1,3 +1,22 @@ +/** + * + * @author Ray + * + * @date 2023-06-14 + * + * @workspace ray-template + * + * @remark 今天也是元气满满撸代码的一天 + */ + +/** + * + * 全局注入 naive ui 提示性组件 + * 使用该组件注册后, 可以直接通过 window.$message、window.$notification、window.$dialog、window.$loadingBar 访问 + * 但是, 使用该组件注册后, 使用 window.$notification 组件时不能更改 placement 位置(只能默认右上角弹出) + * 如果需要更改弹出位置, 需要在需要地方重新定义组件注册 + */ + import { NDialogProvider, NLoadingBarProvider, diff --git a/src/components/RayIcon/index.tsx b/src/components/RayIcon/index.tsx index f1491ec2..12128c0b 100644 --- a/src/components/RayIcon/index.tsx +++ b/src/components/RayIcon/index.tsx @@ -41,7 +41,7 @@ const RayIcon = defineComponent({ customClassName: { /** 自定义 class name */ type: String, - default: '', + default: null, }, depth: { /** 图标深度 */ diff --git a/src/components/RayIframe/src/index.tsx b/src/components/RayIframe/src/index.tsx index 173665f9..1377cc73 100644 --- a/src/components/RayIframe/src/index.tsx +++ b/src/components/RayIframe/src/index.tsx @@ -29,7 +29,7 @@ const RayIframe = defineComponent({ iframeWrapperClass: { /** 自定义类名 */ type: String, - default: '', + default: null, }, frameborder: { /** 边框尺寸, 0 则不显示 */ @@ -94,7 +94,7 @@ const RayIframe = defineComponent({ default: () => ({}), }, }, - setup(props) { + setup(props, { expose }) { const cssVars = computed(() => { const cssVar = { '--ray-iframe-frameborder': completeSize(props.frameborder), @@ -125,6 +125,8 @@ const RayIframe = defineComponent({ return iframeEl } + expose() + onMounted(() => { on(getIframeRef(), 'load', iframeLoadSuccess.bind(this)) on(getIframeRef(), 'error', iframeLoadError) diff --git a/src/layout/components/SiderBar/Components/GlobalSeach/index.tsx b/src/layout/components/SiderBar/Components/GlobalSeach/index.tsx index 4441bf9c..cc096901 100644 --- a/src/layout/components/SiderBar/Components/GlobalSeach/index.tsx +++ b/src/layout/components/SiderBar/Components/GlobalSeach/index.tsx @@ -17,7 +17,7 @@ import RayIcon from '@/components/RayIcon/index' import { on, off } from '@/utils/element' import { debounce } from 'lodash-es' import { useMenu } from '@/store' -import { validRole } from '@/router/helper/routerCopilot' +import { validMenuItemShow } from '@/router/helper/routerCopilot' import type { MenuOption } from 'naive-ui' import type { AppRouteMeta } from '@/router/type' @@ -90,7 +90,7 @@ const GlobalSeach = defineComponent({ if ( _breadcrumbLabel?.includes(_value) && - validRole(curr) && + validMenuItemShow(curr) && !curr.children?.length ) { arr.push(curr) diff --git a/src/router/helper/routerCopilot.ts b/src/router/helper/routerCopilot.ts index f0456441..6199aebf 100644 --- a/src/router/helper/routerCopilot.ts +++ b/src/router/helper/routerCopilot.ts @@ -26,18 +26,44 @@ import type { Router } from 'vue-router' /** * - * @remark 校验当前路由 + * 校验当前菜单项是否与权限匹配 + * 仅做对于 Meta Role 配置是否匹配做校验, 不关心 Meta Hidden 属性 + * + * 如果为超级管理员, 则会默认获取所有权限 */ export const validRole = (option: IMenuOptions) => { const { signinCallback } = storeToRefs(useSignin()) const role = computed(() => signinCallback.value.role) + const { meta } = option + + if (SUPER_ADMIN?.length && SUPER_ADMIN.includes(role.value)) { + return true + } else { + if (meta?.role) { + return meta.role.includes(role.value) + } + + return true + } +} + +/** + * + * @remark 校验当前路由 + * + * 该方法进行校验时, 会将 hidden 与 role 一起进行校验 + * 如果有一条不满足校验, 则视为校验失败 + * + * 如果你仅仅是希望校验是否满足权限, 应该使用另一个方法 validRole + */ +export const validMenuItemShow = (option: IMenuOptions) => { const { meta, name } = option const hidden = meta?.hidden === undefined || meta?.hidden === false ? false : meta?.hidden // 如果是超级管理员(预设为 admin), 则根据其菜单栏(hidden)字段判断是否显示 - if (SUPER_ADMIN.length && SUPER_ADMIN.includes(role.value)) { + if (validRole(option)) { return true && !hidden } else { // 如果为基础路由, 不进行鉴权则根据其菜单栏(hidden)字段判断是否显示 @@ -47,7 +73,7 @@ export const validRole = (option: IMenuOptions) => { // 判断权限是否匹配和菜单栏(hidden)字段判断是否显示 if (meta?.role) { - return meta.role.includes(role.value) && !hidden + return validRole(option) && !hidden } return true && !hidden diff --git a/src/router/type.ts b/src/router/type.ts index ace690c9..76333ecd 100644 --- a/src/router/type.ts +++ b/src/router/type.ts @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import type { RouteRecordRaw } from 'vue-router' import type { Recordable } from '@/types/type-utils' -import type { DefineComponent } from 'vue' +import type { DefineComponent, VNode } from 'vue' export type Component = | DefineComponent<{}, {}, any> @@ -11,7 +11,7 @@ export type Component = export interface AppRouteMeta { i18nKey?: string - icon?: string + icon?: string | VNode windowOpen?: string role?: string[] hidden?: boolean diff --git a/src/store/modules/menu/helper.ts b/src/store/modules/menu/helper.ts index 9f5505fc..2bd7f018 100644 --- a/src/store/modules/menu/helper.ts +++ b/src/store/modules/menu/helper.ts @@ -11,6 +11,12 @@ /** 本方法感谢 的支持 */ +import { MENU_COLLAPSED_CONFIG, ROOT_ROUTE } from '@/appConfig/appConfig' +import RayIcon from '@/components/RayIcon/index' +import { validteValueType } from '@/utils/hook' + +import type { VNode } from 'vue' + /** * * @param node 当前节点 @@ -127,3 +133,26 @@ export const updateDocumentTitle = (option: IMenuOptions) => { document.title = breadcrumbLabel + ' - ' + spliceTitle } + +export const hasMenuIcon = (option: IMenuOptions) => { + const { meta } = option + + if (!meta.icon) { + return + } + + if (validteValueType(meta.icon, 'Object')) { + return () => meta.icon + } + + const icon = h( + RayIcon, + { + name: meta!.icon as string, + size: MENU_COLLAPSED_CONFIG.MENU_COLLAPSED_ICON_SIZE, + }, + {}, + ) + + return () => icon +} diff --git a/src/store/modules/menu/index.ts b/src/store/modules/menu/index.ts index b8edf100..31735257 100644 --- a/src/store/modules/menu/index.ts +++ b/src/store/modules/menu/index.ts @@ -26,8 +26,13 @@ import { NEllipsis } from 'naive-ui' import RayIcon from '@/components/RayIcon/index' import { getCache, setCache } from '@/utils/cache' -import { validRole } from '@/router/helper/routerCopilot' -import { parse, matchMenuOption, updateDocumentTitle } from './helper' +import { validMenuItemShow } from '@/router/helper/routerCopilot' +import { + parse, + matchMenuOption, + updateDocumentTitle, + hasMenuIcon, +} from './helper' import { useI18n } from '@/locales/useI18n' import { MENU_COLLAPSED_CONFIG, ROOT_ROUTE } from '@/appConfig/appConfig' import routeModules from '@/router/routeModules' @@ -186,21 +191,10 @@ export const useMenu = defineStore( }), breadcrumbLabel: label.value, } as IMenuOptions - /** 是否有 icon */ - const expandIcon = { - icon: () => - h( - RayIcon, - { - name: meta!.icon as string, - size: MENU_COLLAPSED_CONFIG.MENU_COLLAPSED_ICON_SIZE, - }, - {}, - ), - } - const attr: IMenuOptions = meta?.icon - ? Object.assign({}, route, expandIcon) - : route + /** 合并 icon */ + const attr: IMenuOptions = Object.assign({}, route, { + icon: hasMenuIcon(option), + }) if (option.path === cacheMenuKey) { /** 设置菜单标签 */ @@ -209,7 +203,8 @@ export const useMenu = defineStore( updateDocumentTitle(attr) } - attr.show = validRole(option) + /** 检查该菜单项是否展示 */ + attr.show = validMenuItemShow(option) return attr } @@ -218,9 +213,10 @@ export const useMenu = defineStore( const catchArr: IMenuOptions[] = [] for (const curr of routes) { - if (curr.children?.length && validRole(curr)) { + if (curr.children?.length && validMenuItemShow(curr)) { curr.children = resolveRoutes(curr.children, index++) - } else if (!validRole(curr)) { + } else if (!validMenuItemShow(curr)) { + /** 如果校验失败, 则不会添加进 menu options */ continue }