mirror of
https://github.com/XiaoDaiGua-Ray/ray-template.git
synced 2025-05-21 03:55:11 +08:00
bug fixed: 修复浏览器标题渲染错误问题,以及v4.0.2的一些小细节更新
This commit is contained in:
parent
f21eeddd7f
commit
dbb27902aa
@ -74,12 +74,22 @@ module.exports = {
|
||||
'no-ex-assign': 2, // 禁止给 `catch` 语句中的异常参数赋值
|
||||
'no-extend-native': 2, // 禁止扩展 `native` 对象
|
||||
'no-extra-bind': 2, // 禁止不必要的函数绑定
|
||||
'no-extra-boolean-cast': 2, // 禁止不必要的 `bool` 转换
|
||||
'no-extra-boolean-cast': [
|
||||
'error',
|
||||
{
|
||||
enforceForLogicalOperands: true,
|
||||
},
|
||||
], // 禁止不必要的 `bool` 转换
|
||||
'no-extra-parens': 0, // 禁止非必要的括号
|
||||
semi: ['error', 'never', { beforeStatementContinuationChars: 'always' }],
|
||||
'no-fallthrough': 1, // 禁止 `switch` 穿透
|
||||
'no-func-assign': 2, // 禁止重复的函数声明
|
||||
'no-implicit-coercion': 1, // 禁止隐式转换
|
||||
'no-implicit-coercion': [
|
||||
'error',
|
||||
{
|
||||
allow: ['!!', '~'],
|
||||
},
|
||||
], // 禁止隐式转换
|
||||
'no-implied-eval': 2, // 禁止使用隐式 `eval`
|
||||
'no-invalid-regexp': 2, // 禁止无效的正则表达式
|
||||
'no-invalid-this': 2, // 禁止无效的 `this`
|
||||
|
@ -6,6 +6,13 @@
|
||||
|
||||
- 新增平级路由配置(router meta)配置项,sameLevel 允许你将子路由标记为平级模式,跳转时不会出发菜单、标签页更新,仅会更新面包屑
|
||||
- 修改路由菜单显示、隐藏逻辑,现在仅会针对权限的验证匹配选择是否加入菜单列表中
|
||||
- 更新 setupAppMenu 方法触发时机(Layout => menu store),现在将在 pinia menu store 初始化时触发 App Menu 更新
|
||||
- 更新了 utils 包中的一些方法,进行了一些重写和重命名
|
||||
|
||||
### Fixes
|
||||
|
||||
- 修复不能正确渲染浏览器标题问题
|
||||
- 修复初始化模板菜单函数与菜单更新函数重复执行一些方法的问题
|
||||
|
||||
## 4.0.1
|
||||
|
||||
|
12
src/App.tsx
12
src/App.tsx
@ -3,7 +3,7 @@ import { RouterView } from 'vue-router'
|
||||
import GlobalSpin from '@/spin/index'
|
||||
import LockScreen from '@/components/AppComponents/AppLockScreen/index'
|
||||
|
||||
import { getCache } from '@/utils/cache'
|
||||
import { getStorage } from '@/utils/cache'
|
||||
import { get } from 'lodash-es'
|
||||
import { useSetting } from '@/store'
|
||||
import { addClass, removeClass, addStyle, colorToRgba } from '@/utils/element'
|
||||
@ -24,18 +24,20 @@ const App = defineComponent({
|
||||
} = __APP_CFG__ // 默认主题色
|
||||
const body = document.body
|
||||
|
||||
const primaryColorOverride = getCache<SettingState>(
|
||||
const primaryColorOverride = getStorage<SettingState>(
|
||||
'piniaSettingStore',
|
||||
'localStorage',
|
||||
primaryColor,
|
||||
)
|
||||
const _p = get(
|
||||
primaryColorOverride,
|
||||
primaryColorOverride as SettingState,
|
||||
'primaryColorOverride.common.primaryColor',
|
||||
primaryColor,
|
||||
)
|
||||
const _fp = colorToRgba(_p || primaryColor, 0.3)
|
||||
const _fp = colorToRgba(_p, 0.3)
|
||||
|
||||
/** 设置全局主题色 css 变量 */
|
||||
body.style.setProperty('--ray-theme-primary-color', _p || primaryColor)
|
||||
body.style.setProperty('--ray-theme-primary-color', _p)
|
||||
body.style.setProperty(
|
||||
'--ray-theme-primary-fade-color',
|
||||
_fp || primaryFadeColor,
|
||||
|
@ -21,7 +21,7 @@
|
||||
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 { getStorage } from '@/utils/cache'
|
||||
|
||||
import type {
|
||||
RequestInterceptorConfig,
|
||||
@ -40,7 +40,7 @@ const { setImplement } = useAxiosInterceptor()
|
||||
* 当然你也可以根据 request instance 来特殊处理, 这里暂时不做演示
|
||||
*/
|
||||
const requestHeaderToken = (ins: RequestInterceptorConfig, mode: string) => {
|
||||
const token = getCache<string>(APP_CATCH_KEY.token)
|
||||
const token = getStorage<string>(APP_CATCH_KEY.token)
|
||||
|
||||
if (ins.url) {
|
||||
// TODO: 根据 url 不同是否设置 token
|
||||
|
@ -23,7 +23,7 @@ import { NAvatar, NSpace } from 'naive-ui'
|
||||
|
||||
import { avatarProps, spaceProps } from 'naive-ui'
|
||||
import { APP_CATCH_KEY } from '@/appConfig/appConfig'
|
||||
import { getCache } from '@/utils/cache'
|
||||
import { getStorage } from '@/utils/cache'
|
||||
|
||||
import type { PropType } from 'vue'
|
||||
import type { AvatarProps, SpaceProps } from 'naive-ui'
|
||||
@ -48,7 +48,7 @@ const AppAvatar = defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const signin = getCache<SigninCallback>(APP_CATCH_KEY.signin)
|
||||
const signin = getStorage<SigninCallback>(APP_CATCH_KEY.signin)
|
||||
const cssVars = computed(() => {
|
||||
const vars = {
|
||||
'--app-avatar-cursor': props.cursor,
|
||||
|
@ -9,6 +9,7 @@ import { useVueRouter } from '@/router/helper/useVueRouter'
|
||||
|
||||
import type { MenuInst } from 'naive-ui'
|
||||
import type { NaiveMenuOptions } from '@/types/modules/component'
|
||||
import type { AppMenuOption } from '@/types/modules/app'
|
||||
|
||||
const LayoutMenu = defineComponent({
|
||||
name: 'LayoutMenu',
|
||||
@ -106,7 +107,9 @@ const LayoutMenu = defineComponent({
|
||||
collapsed={this.modelCollapsed}
|
||||
collapsedIconSize={APP_MENU_CONFIG.MENU_COLLAPSED_ICON_SIZE}
|
||||
collapsedWidth={APP_MENU_CONFIG.MENU_COLLAPSED_WIDTH}
|
||||
onUpdateValue={this.changeMenuModelValue.bind(this)}
|
||||
onUpdateValue={(key, op) => {
|
||||
this.changeMenuModelValue(key, op as unknown as AppMenuOption)
|
||||
}}
|
||||
accordion={APP_MENU_CONFIG.MENU_ACCORDION}
|
||||
/>
|
||||
</NLayoutSider>
|
||||
|
@ -33,10 +33,10 @@ import { uuid } from '@/utils/hook'
|
||||
import { hasClass } from '@/utils/element'
|
||||
import { redirectRouterToDashboard } from '@/router/helper/routerCopilot'
|
||||
import { ROOT_ROUTE } from '@/appConfig/appConfig'
|
||||
import { getElement } from '@use-utils/element'
|
||||
import { getElements } from '@use-utils/element'
|
||||
|
||||
import type { MenuOption, ScrollbarInst } from 'naive-ui'
|
||||
import type { MenuTagOptions } from '@/types/modules/app'
|
||||
import type { MenuTagOptions, AppMenuOption } from '@/types/modules/app'
|
||||
|
||||
const MenuTag = defineComponent({
|
||||
name: 'MenuTag',
|
||||
@ -247,7 +247,7 @@ const MenuTag = defineComponent({
|
||||
*
|
||||
* @param item 当前菜单值
|
||||
*/
|
||||
const handleTagClick = (item: MenuOption) => {
|
||||
const handleTagClick = (item: AppMenuOption) => {
|
||||
changeMenuModelValue(item.key as string, item)
|
||||
}
|
||||
|
||||
@ -381,7 +381,7 @@ const MenuTag = defineComponent({
|
||||
/** 动态更新 menu tag 所在位置 */
|
||||
const positionMenuTag = () => {
|
||||
nextTick().then(() => {
|
||||
const tags = getElement<HTMLElement>(
|
||||
const tags = getElements<HTMLElement>(
|
||||
`attr:${MENU_TAG_DATA}="${menuKey.value}"`,
|
||||
)
|
||||
|
||||
|
@ -42,12 +42,16 @@ const Breadcrumb = defineComponent({
|
||||
key: string | number,
|
||||
option: DropdownOption,
|
||||
) => {
|
||||
changeMenuModelValue(key, option)
|
||||
changeMenuModelValue(key, option as unknown as AppMenuOption)
|
||||
}
|
||||
|
||||
const handleBreadcrumbItemClick = (option: AppMenuOption) => {
|
||||
if (!option.children?.length) {
|
||||
changeMenuModelValue(option.key, option as unknown as MenuOption)
|
||||
const { meta = {} } = option
|
||||
|
||||
if (!meta.sameLevel) {
|
||||
changeMenuModelValue(option.key, option)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,7 @@ const GlobalSeach = defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
const handleSearchItemClick = (option: MenuOption) => {
|
||||
const handleSearchItemClick = (option: AppMenuOption) => {
|
||||
const meta = option.meta as AppRouteMeta
|
||||
|
||||
/** 如果配置站外跳转则不会关闭搜索框 */
|
||||
@ -118,7 +118,7 @@ const GlobalSeach = defineComponent({
|
||||
} else {
|
||||
modelShow.value = false
|
||||
|
||||
changeMenuModelValue(option.key as string, option)
|
||||
changeMenuModelValue(option.key, option)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
import { set } from 'lodash-es'
|
||||
import { zhCN, dateZhCN } from 'naive-ui' // 导入 `naive ui` 中文包
|
||||
import { getCache } from '@use-utils/cache'
|
||||
import { getStorage } from '@use-utils/cache'
|
||||
import { SYSTEM_DEFAULT_LOCAL } from '@/appConfig/localConfig'
|
||||
import { APP_CATCH_KEY } from '@/appConfig/appConfig'
|
||||
|
||||
@ -126,10 +126,11 @@ export const naiveLocales = (key: string) => {
|
||||
* @remak 未避免出现加载语言错误问题, 故而在 `main.ts` 注册时, 应优先加载 `i18n` 避免出现该问题
|
||||
*/
|
||||
export const getAppDefaultLanguage = () => {
|
||||
const language = getCache<string>(
|
||||
const language = getStorage<string>(
|
||||
APP_CATCH_KEY.localeLanguage,
|
||||
'localStorage',
|
||||
SYSTEM_DEFAULT_LOCAL,
|
||||
)
|
||||
|
||||
return language ? language : SYSTEM_DEFAULT_LOCAL
|
||||
return language || SYSTEM_DEFAULT_LOCAL
|
||||
}
|
||||
|
@ -115,5 +115,5 @@ hidden: 是否显示
|
||||
noLocalTitle: 不使用国际化渲染 Menu Titile
|
||||
ignoreAutoResetScroll: 该页面内容区域自动初始化滚动条位置
|
||||
keepAlive: 是否缓存该页面(需要配置 APP_KEEP_ALIVE setupKeepAlive 属性为 true 启用才有效)
|
||||
sameLevel: 是否标记该路由为平级模式
|
||||
sameLevel: 是否标记该路由为平级模式,如果标记为平级模式,会使路由菜单项隐藏。如果在含有子节点处,设置了该属性,会导致子节点全部被隐藏
|
||||
```
|
||||
|
@ -20,11 +20,10 @@
|
||||
* 当然, 你可以指定一个超级管理员角色, 默认获取全部路由
|
||||
*/
|
||||
|
||||
import { getCache, setCache } from '@/utils/cache'
|
||||
import { useSignin } from '@/store'
|
||||
import { getStorage } from '@/utils/cache'
|
||||
import { APP_CATCH_KEY, ROOT_ROUTE } from '@/appConfig/appConfig'
|
||||
import { redirectRouterToDashboard } from '@/router/helper/routerCopilot'
|
||||
import { validRole, validMenuItemShow } from '@/router/helper/routerCopilot'
|
||||
import { validRole } from '@/router/helper/routerCopilot'
|
||||
|
||||
import type {
|
||||
Router,
|
||||
@ -38,8 +37,12 @@ export const permissionRouter = (router: Router) => {
|
||||
const { beforeEach } = router
|
||||
|
||||
beforeEach((to, from, next) => {
|
||||
const token = getCache<string>(APP_CATCH_KEY.token)
|
||||
const route = getCache<string>('menuKey') || ROOT_ROUTE.path
|
||||
const token = getStorage<string>(APP_CATCH_KEY.token)
|
||||
const route = getStorage<string>(
|
||||
'menuKey',
|
||||
'sessionStorage',
|
||||
ROOT_ROUTE.path,
|
||||
) as string
|
||||
const { meta } = to
|
||||
|
||||
if (token !== null) {
|
||||
|
@ -20,7 +20,7 @@ import {
|
||||
import { useSignin } from '@/store'
|
||||
import { useVueRouter } from '@/router/helper/useVueRouter'
|
||||
import { ROOT_ROUTE } from '@/appConfig/appConfig'
|
||||
import { setCache } from '@/utils/cache'
|
||||
import { setStorage } from '@/utils/cache'
|
||||
|
||||
import type { Router } from 'vue-router'
|
||||
import type { AppRouteMeta } from '@/router/type'
|
||||
@ -58,18 +58,27 @@ export const validRole = (meta: AppRouteMeta) => {
|
||||
|
||||
/**
|
||||
*
|
||||
* @remark 校验当前路由
|
||||
* @remark 校验当前路由是否显示
|
||||
*
|
||||
* 该方法进行校验时, 会将 hidden 与 role 一起进行校验
|
||||
* 如果有一条不满足校验, 则视为校验失败
|
||||
* 该方法进行校验时, 会将 hidden 与 sameLevel 一起进行校验
|
||||
* sameLevel 的优先级最高
|
||||
*
|
||||
* 如果你仅仅是希望校验是否满足权限, 应该使用另一个方法 validRole
|
||||
*/
|
||||
export const validMenuItemShow = (option: AppMenuOption) => {
|
||||
const { meta = {} } = option
|
||||
const { hidden } = meta
|
||||
const { hidden, sameLevel } = meta
|
||||
|
||||
return hidden === undefined || hidden === false ? true : false
|
||||
// 如果该路由被标记为平级模式, 则会强制不显示在菜单中
|
||||
if (sameLevel) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!sameLevel && !hidden) {
|
||||
return true
|
||||
}
|
||||
|
||||
return !hidden ? true : false
|
||||
}
|
||||
|
||||
/**
|
||||
@ -119,7 +128,7 @@ export const redirectRouterToDashboard = (isReplace = true) => {
|
||||
const { push, replace } = router
|
||||
const { path } = ROOT_ROUTE
|
||||
|
||||
setCache('menuKey', path)
|
||||
setStorage('menuKey', path)
|
||||
|
||||
isReplace ? push(path) : replace(path)
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { t } from '@/locales/useI18n'
|
||||
import { LAYOUT } from '@/router/constant/index'
|
||||
|
||||
import type { AppRouteRecordRaw } from '@/router/type'
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { t } from '@/locales/useI18n'
|
||||
import { LAYOUT } from '@/router/constant/index'
|
||||
|
||||
import type { AppRouteRecordRaw } from '@/router/type'
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { t } from '@/locales/useI18n'
|
||||
import { LAYOUT } from '@/router/constant/index'
|
||||
|
||||
import type { AppRouteRecordRaw } from '@/router/type'
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { t } from '@/locales/useI18n'
|
||||
import { LAYOUT } from '@/router/constant/index'
|
||||
|
||||
import type { AppRouteRecordRaw } from '@/router/type'
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { t } from '@/locales/useI18n'
|
||||
import { LAYOUT } from '@/router/constant/index'
|
||||
|
||||
import type { AppRouteRecordRaw } from '@/router/type'
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { t } from '@/locales/useI18n'
|
||||
import { LAYOUT } from '@/router/constant/index'
|
||||
|
||||
import type { AppRouteRecordRaw } from '@/router/type'
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { t } from '@/locales/useI18n'
|
||||
import { LAYOUT } from '@/router/constant/index'
|
||||
|
||||
import type { AppRouteRecordRaw } from '@/router/type'
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
import { t } from '@/locales/useI18n'
|
||||
import { LAYOUT } from '@/router/constant/index'
|
||||
|
||||
import type { AppRouteRecordRaw } from '@/router/type'
|
||||
|
||||
import { LAYOUT } from '@/router/constant/index'
|
||||
|
||||
const multiMenu: AppRouteRecordRaw = {
|
||||
path: '/multi',
|
||||
name: 'MultiMenu',
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { t } from '@/locales/useI18n'
|
||||
import { LAYOUT } from '@/router/constant/index'
|
||||
|
||||
import type { AppRouteRecordRaw } from '@/router/type'
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { t } from '@/locales/useI18n'
|
||||
import { LAYOUT } from '@/router/constant/index'
|
||||
|
||||
import type { AppRouteRecordRaw } from '@/router/type'
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
import { t } from '@/locales/useI18n'
|
||||
import { LAYOUT } from '@/router/constant/index'
|
||||
|
||||
import type { AppRouteRecordRaw } from '@/router/type'
|
||||
|
||||
import { LAYOUT } from '@/router/constant/index'
|
||||
|
||||
const rely: AppRouteRecordRaw = {
|
||||
path: '/rely',
|
||||
name: 'RelyAbout',
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { t } from '@/locales/useI18n'
|
||||
import { LAYOUT } from '@/router/constant/index'
|
||||
|
||||
import type { AppRouteRecordRaw } from '@/router/type'
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { t } from '@/locales/useI18n'
|
||||
import { LAYOUT } from '@/router/constant/index'
|
||||
|
||||
import type { AppRouteRecordRaw } from '@/router/type'
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
import { APP_MENU_CONFIG, ROOT_ROUTE } from '@/appConfig/appConfig'
|
||||
import RayIcon from '@/components/RayIcon/index'
|
||||
import { isValueType } from '@/utils/hook'
|
||||
import { getCache, setCache } from '@/utils/cache'
|
||||
import { getStorage, setStorage } from '@/utils/cache'
|
||||
|
||||
import type { VNode } from 'vue'
|
||||
import type {
|
||||
@ -174,10 +174,11 @@ export const hasMenuIcon = (option: AppMenuOption) => {
|
||||
/** 获取缓存的 menu key, 如果未获取到则使用 ROOTROUTE path 当作默认激活路由菜单 */
|
||||
export const getCatchMenuKey = () => {
|
||||
const { path: rootPath } = ROOT_ROUTE
|
||||
const cacheMenuKey =
|
||||
getCache<AppMenuKey>('menuKey') === null
|
||||
? rootPath
|
||||
: getCache<AppMenuKey>('menuKey')
|
||||
const cacheMenuKey = getStorage<AppMenuKey>(
|
||||
'menuKey',
|
||||
'sessionStorage',
|
||||
rootPath,
|
||||
)
|
||||
|
||||
return cacheMenuKey
|
||||
}
|
||||
|
@ -24,8 +24,8 @@
|
||||
|
||||
import { NEllipsis } from 'naive-ui'
|
||||
|
||||
import { getCache, setCache } from '@/utils/cache'
|
||||
import { validMenuItemShow, validRole } from '@/router/helper/routerCopilot'
|
||||
import { setStorage } from '@/utils/cache'
|
||||
import { validRole, validMenuItemShow } from '@/router/helper/routerCopilot'
|
||||
import {
|
||||
parseAndFindMatchingNodes,
|
||||
updateDocumentTitle,
|
||||
@ -86,7 +86,10 @@ export const useMenu = defineStore(
|
||||
*
|
||||
* 如果识别到为平级模式, 则会自动追加一层面包屑
|
||||
*/
|
||||
const setBreadcrumbOptions = (key: string | number, option: MenuOption) => {
|
||||
const setBreadcrumbOptions = (
|
||||
key: string | number,
|
||||
option: AppMenuOption,
|
||||
) => {
|
||||
const { meta } = option as unknown as AppRouteRecordRaw
|
||||
|
||||
menuState.breadcrumbOptions = getCompleteRoutePath(menuState.options, key)
|
||||
@ -124,12 +127,12 @@ export const useMenu = defineStore(
|
||||
/** 当 url 地址发生变化触发 menuTagOptions 更新 */
|
||||
const setMenuTagOptionsWhenMenuValueChange = (
|
||||
key: string | number,
|
||||
option: MenuOption,
|
||||
option: AppMenuOption,
|
||||
) => {
|
||||
const tag = menuState.menuTagOptions.find((curr) => curr.path === key)
|
||||
|
||||
if (!tag) {
|
||||
menuState.menuTagOptions.push(option as unknown as MenuTagOptions)
|
||||
menuState.menuTagOptions.push(option as MenuTagOptions)
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,8 +144,11 @@ export const useMenu = defineStore(
|
||||
* @remark 修改 `menu key` 后的回调函数
|
||||
* @remark 修改后, 缓存当前选择 key 并且存储标签页与跳转页面(router push 操作)
|
||||
*/
|
||||
const changeMenuModelValue = (key: string | number, option: MenuOption) => {
|
||||
const { meta, path } = option as unknown as AppRouteRecordRaw
|
||||
const changeMenuModelValue = (
|
||||
key: string | number,
|
||||
option: AppMenuOption,
|
||||
) => {
|
||||
const { meta, path } = option
|
||||
|
||||
if (meta.windowOpen) {
|
||||
window.open(meta.windowOpen)
|
||||
@ -169,10 +175,10 @@ export const useMenu = defineStore(
|
||||
/** 检查是否为根路由 */
|
||||
const count = (path.match(new RegExp('/', 'g')) || []).length
|
||||
|
||||
/** 更新浏览器标题 */
|
||||
updateDocumentTitle(option as unknown as AppMenuOption)
|
||||
/** 更新缓存队列 */
|
||||
setKeepAliveInclude(option as unknown as AppMenuOption)
|
||||
/** 更新浏览器标题 */
|
||||
updateDocumentTitle(option as unknown as AppMenuOption)
|
||||
|
||||
if (!meta.sameLevel || (meta.sameLevel && count === 1)) {
|
||||
/** 更新标签菜单 */
|
||||
@ -182,7 +188,7 @@ export const useMenu = defineStore(
|
||||
|
||||
menuState.menuKey = key
|
||||
/** 缓存菜单 key(sessionStorage) */
|
||||
setCache('menuKey', key)
|
||||
setStorage('menuKey', key)
|
||||
} else {
|
||||
setBreadcrumbOptions(menuState.menuKey || '', option)
|
||||
}
|
||||
@ -196,21 +202,33 @@ export const useMenu = defineStore(
|
||||
* @remark 监听路由地址变化更新菜单状态
|
||||
* @remark 递归查找匹配项
|
||||
*/
|
||||
const updateMenuKeyWhenRouteUpdate = (path: string) => {
|
||||
const appRawRoutes = expandRoutes(getAppRawRoutes())
|
||||
const updateMenuKeyWhenRouteUpdate = async (path: string) => {
|
||||
// 获取 `/` 出现次数(如果为 1 则表示该路径为根路由路径)
|
||||
const count = (path.match(new RegExp('/', 'g')) || []).length
|
||||
const fd = appRawRoutes.find((curr) => curr.path === path)
|
||||
let combinePath = path
|
||||
|
||||
if (count > 1) {
|
||||
// 如果不是跟路径则取出最后一项字符
|
||||
const splitPath = path.split('/').filter((curr) => curr)
|
||||
|
||||
combinePath = splitPath[splitPath.length - 1]
|
||||
}
|
||||
|
||||
if (fd) {
|
||||
changeMenuModelValue(combinePath, fd as unknown as MenuOption)
|
||||
const findMenuOption = (pathKey: string, options: AppMenuOption[]) => {
|
||||
for (const curr of options) {
|
||||
if (curr.children?.length) {
|
||||
findMenuOption(pathKey, curr.children)
|
||||
}
|
||||
|
||||
if (pathKey === curr.key) {
|
||||
changeMenuModelValue(pathKey, curr)
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
findMenuOption(combinePath, menuState.options)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -237,8 +255,6 @@ export const useMenu = defineStore(
|
||||
}),
|
||||
breadcrumbLabel: label.value,
|
||||
/** 检查该菜单项是否展示 */
|
||||
show:
|
||||
meta.hidden === false || meta.hidden === undefined ? true : false,
|
||||
} as AppMenuOption
|
||||
/** 合并 icon */
|
||||
const attr: AppMenuOption = Object.assign({}, route, {
|
||||
@ -246,14 +262,12 @@ export const useMenu = defineStore(
|
||||
})
|
||||
|
||||
if (option.path === getCatchMenuKey()) {
|
||||
/** 设置浏览器标题 */
|
||||
updateDocumentTitle(attr)
|
||||
setMenuTagOptionsWhenMenuValueChange(
|
||||
option.path,
|
||||
attr as unknown as MenuOption,
|
||||
)
|
||||
/** 设置标签页(初始化时执行设置一次, 避免含有平级路由模式情况时出现不能正确设置标签页的情况) */
|
||||
setMenuTagOptionsWhenMenuValueChange(option.path, attr)
|
||||
}
|
||||
|
||||
attr.show = validMenuItemShow(attr)
|
||||
|
||||
return attr
|
||||
}
|
||||
|
||||
@ -320,9 +334,9 @@ export const useMenu = defineStore(
|
||||
const setupPiniaMenuStore = async () => {
|
||||
if (isSetupAppMenuLock.value) {
|
||||
await setupAppMenu()
|
||||
|
||||
isSetupAppMenuLock.value = false
|
||||
}
|
||||
|
||||
isSetupAppMenuLock.value = false
|
||||
}
|
||||
|
||||
/** 监听路由变化并且更新路由菜单与菜单标签 */
|
||||
@ -333,7 +347,7 @@ export const useMenu = defineStore(
|
||||
const match = newData.match(reg)?.[1]
|
||||
|
||||
await setupPiniaMenuStore()
|
||||
updateMenuKeyWhenRouteUpdate(match || '')
|
||||
await updateMenuKeyWhenRouteUpdate(match || '')
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { getAppDefaultLanguage } from '@/locales/helper'
|
||||
import { setCache } from '@use-utils/cache'
|
||||
import { setStorage } from '@use-utils/cache'
|
||||
import { set } from 'lodash-es'
|
||||
import { addClass, removeClass, colorToRgba } from '@/utils/element'
|
||||
import { useI18n } from '@/locales/useI18n'
|
||||
@ -46,7 +46,7 @@ export const useSetting = defineStore(
|
||||
|
||||
settingState.localeLanguage = key
|
||||
|
||||
setCache('localeLanguage', key, 'localStorage')
|
||||
setStorage('localeLanguage', key, 'localStorage')
|
||||
}
|
||||
|
||||
/** 切换主题色 */
|
||||
|
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
import { isEmpty } from 'lodash-es'
|
||||
import { removeCache } from '@/utils/cache'
|
||||
import { removeStorage } from '@/utils/cache'
|
||||
|
||||
import type {
|
||||
SigninForm,
|
||||
@ -79,7 +79,7 @@ export const useSignin = defineStore(
|
||||
*/
|
||||
const logout = () => {
|
||||
window.$message.info('账号退出中...')
|
||||
removeCache('all-sessionStorage')
|
||||
removeStorage('all-sessionStorage')
|
||||
|
||||
setTimeout(() => window.location.reload())
|
||||
}
|
||||
|
@ -42,3 +42,9 @@ export type CipherParams = CryptoJS.lib.CipherParams
|
||||
export type AnyFunc = (...args: any[]) => any
|
||||
|
||||
export type AnyVoidFunc = (...args: any[]) => void
|
||||
|
||||
export type PartialCSSStyleDeclaration = Partial<
|
||||
Record<keyof CSSStyleDeclaration, string>
|
||||
>
|
||||
|
||||
export type ElementSelector = string | `attr:${string}`
|
||||
|
@ -18,16 +18,26 @@ import type { CacheType } from '@/types/modules/utils'
|
||||
* @param key 需要设置的key
|
||||
* @param value 需要缓存的值
|
||||
*/
|
||||
export const setCache = <T = unknown>(
|
||||
export const setStorage = <T = unknown>(
|
||||
key: string,
|
||||
value: T,
|
||||
type: CacheType = 'sessionStorage',
|
||||
) => {
|
||||
const waitCacheValue = JSON.stringify(value)
|
||||
if (!key) {
|
||||
console.error('Failed to set stored data: key is empty or undefined')
|
||||
|
||||
type === 'localStorage'
|
||||
? window.localStorage.setItem(key, waitCacheValue)
|
||||
: window.sessionStorage.setItem(key, waitCacheValue)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const waitCacheValue = JSON.stringify(value)
|
||||
|
||||
type === 'localStorage'
|
||||
? window.localStorage.setItem(key, waitCacheValue)
|
||||
: window.sessionStorage.setItem(key, waitCacheValue)
|
||||
} catch (error) {
|
||||
console.error(`Failed to set stored data for key '${key}'`, error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,16 +45,27 @@ export const setCache = <T = unknown>(
|
||||
* @param key 需要获取目标缓存的key
|
||||
* @returns 获取缓存值
|
||||
*/
|
||||
export const getCache = <T>(
|
||||
export const getStorage = <T>(
|
||||
key: string,
|
||||
type: CacheType = 'sessionStorage',
|
||||
storageType: CacheType = 'sessionStorage',
|
||||
defaultValue?: T,
|
||||
): T | null => {
|
||||
const data =
|
||||
type === 'localStorage'
|
||||
? window.localStorage.getItem(key)
|
||||
: window.sessionStorage.getItem(key)
|
||||
try {
|
||||
const data =
|
||||
storageType === 'localStorage'
|
||||
? window.localStorage.getItem(key)
|
||||
: window.sessionStorage.getItem(key)
|
||||
|
||||
return Object.is(data, null) ? null : JSON.parse(data as string)
|
||||
if (data === null) {
|
||||
return defaultValue ?? null
|
||||
}
|
||||
|
||||
return JSON.parse(data) as T
|
||||
} catch (error) {
|
||||
console.error(`Failed to get stored data for key '${key}'`, error)
|
||||
|
||||
return defaultValue ?? null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,7 +77,7 @@ export const getCache = <T>(
|
||||
* - all-sessionStorage: 删除所有 sessionStorage 缓存值
|
||||
* - all-localStorage: 删除所有 localStorage 缓存值
|
||||
*/
|
||||
export const removeCache = (
|
||||
export const removeStorage = (
|
||||
key: string | 'all' | 'all-sessionStorage' | 'all-localStorage',
|
||||
type: CacheType = 'sessionStorage',
|
||||
) => {
|
||||
@ -78,6 +99,12 @@ export const removeCache = (
|
||||
break
|
||||
|
||||
default:
|
||||
if (!key) {
|
||||
console.error('Failed to remove stored data: key is empty or undefined')
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type === 'localStorage'
|
||||
? window.localStorage.removeItem(key)
|
||||
: window.sessionStorage.removeItem(key)
|
||||
|
@ -1,7 +1,11 @@
|
||||
import { isValueType } from '@use-utils/hook'
|
||||
import { APP_REGEX } from '@/appConfig/regConfig'
|
||||
|
||||
import type { EventListenerOrEventListenerObject } from '@/types/modules/utils'
|
||||
import type {
|
||||
EventListenerOrEventListenerObject,
|
||||
PartialCSSStyleDeclaration,
|
||||
ElementSelector,
|
||||
} from '@/types/modules/utils'
|
||||
|
||||
/**
|
||||
*
|
||||
@ -116,6 +120,8 @@ export const hasClass = (element: HTMLElement, className: string) => {
|
||||
* @param el Target element dom
|
||||
* @param styles 所需绑定样式(如果为字符串, 则必须以分号结尾每个行内样式描述)
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* style of string
|
||||
* ```
|
||||
* const styles = 'width: 100px; height: 100px; background: red;'
|
||||
@ -132,27 +138,37 @@ export const hasClass = (element: HTMLElement, className: string) => {
|
||||
* addStyle(styles)
|
||||
* ```
|
||||
*/
|
||||
export const addStyle = <K extends keyof CSSStyleDeclaration & string>(
|
||||
export const addStyle = (
|
||||
el: HTMLElement,
|
||||
styles: K | Partial<CSSStyleDeclaration>,
|
||||
styles: PartialCSSStyleDeclaration | string,
|
||||
) => {
|
||||
if (el) {
|
||||
if (isValueType<object>(styles, 'Object')) {
|
||||
Object.keys(styles).forEach((item) => {
|
||||
el.style[item] = styles[item]
|
||||
})
|
||||
} else if (isValueType<string>(styles, 'String')) {
|
||||
const _styles = styles
|
||||
|
||||
_styles.split(';').forEach((item) => {
|
||||
const [_k, _v] = item.split(':')
|
||||
|
||||
if (_k && _v) {
|
||||
el.style[_k.trim()] = _v.trim()
|
||||
}
|
||||
})
|
||||
}
|
||||
if (!el) {
|
||||
return
|
||||
}
|
||||
|
||||
let styleObj: PartialCSSStyleDeclaration
|
||||
|
||||
if (isValueType<string>(styles, 'String')) {
|
||||
styleObj = styles.split(';').reduce((pre, curr) => {
|
||||
const [key, value] = curr.split(':').map((s) => s.trim())
|
||||
|
||||
if (key && value) {
|
||||
pre[key] = value
|
||||
}
|
||||
|
||||
return pre
|
||||
}, {} as PartialCSSStyleDeclaration)
|
||||
} else {
|
||||
styleObj = styles
|
||||
}
|
||||
|
||||
Object.keys(styleObj).forEach((key) => {
|
||||
const value = styleObj[key]
|
||||
|
||||
if (key in el.style) {
|
||||
el.style[key] = value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -160,15 +176,17 @@ export const addStyle = <K extends keyof CSSStyleDeclaration & string>(
|
||||
* @param el Target element dom
|
||||
* @param styles 所需卸载样式
|
||||
*/
|
||||
export const removeStyle = <K extends keyof CSSStyleDeclaration & string>(
|
||||
export const removeStyle = (
|
||||
el: HTMLElement,
|
||||
styles: K[],
|
||||
styles: (keyof CSSStyleDeclaration & string)[],
|
||||
) => {
|
||||
if (el) {
|
||||
styles.forEach((curr) => {
|
||||
el.style.removeProperty(curr)
|
||||
})
|
||||
if (!el) {
|
||||
return
|
||||
}
|
||||
|
||||
styles.forEach((curr) => {
|
||||
el.style.removeProperty(curr)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@ -222,33 +240,33 @@ export const colorToRgba = (color: string, alpha = 1) => {
|
||||
* 示例:
|
||||
*
|
||||
* class:
|
||||
* const el = getElement('.demo')
|
||||
* const el = getElements('.demo')
|
||||
* id:
|
||||
* const el = getElement('#demo')
|
||||
* const el = getElements('#demo')
|
||||
* attribute:
|
||||
* const el = getElement('attr:type=button')
|
||||
* const el = getElements('attr:type=button')
|
||||
* 或者可以这样写
|
||||
* const el = getElement('attr:type')
|
||||
* const el = getElements('attr:type')
|
||||
*/
|
||||
export const getElement = <T extends Element>(element: string) => {
|
||||
if (!element) {
|
||||
export const getElements = <T extends Element = Element>(
|
||||
selector: ElementSelector,
|
||||
) => {
|
||||
if (!selector) {
|
||||
return null
|
||||
}
|
||||
|
||||
let queryParam: string
|
||||
|
||||
if (element.startsWith('attr:')) {
|
||||
queryParam = '[' + element.replace('attr:', '') + ']'
|
||||
} else {
|
||||
queryParam = element
|
||||
}
|
||||
const queryParam = selector.startsWith('attr:')
|
||||
? `[${selector.replace('attr:', '')}]`
|
||||
: selector
|
||||
|
||||
try {
|
||||
const el = Array.from(document.querySelectorAll<T>(queryParam))
|
||||
const elements = Array.from(document.querySelectorAll<T>(queryParam))
|
||||
|
||||
return el
|
||||
} catch (e) {
|
||||
return []
|
||||
return elements
|
||||
} catch (error) {
|
||||
console.error(`Failed to get elements for selector '${selector}'`, error)
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { NForm, NFormItem, NInput, NButton, NSpace, NDivider } from 'naive-ui'
|
||||
|
||||
import { setCache } from '@/utils/cache'
|
||||
import { setStorage } from '@/utils/cache'
|
||||
import { setSpin } from '@/spin'
|
||||
import { useSignin } from '@/store'
|
||||
import { useI18n } from '@/locales/useI18n'
|
||||
@ -55,8 +55,8 @@ const Signin = defineComponent({
|
||||
|
||||
window.$message.success(`欢迎${signinForm.value.name}登陆~`)
|
||||
|
||||
setCache(APP_CATCH_KEY.token, 'tokenValue')
|
||||
setCache(APP_CATCH_KEY.signin, res.data)
|
||||
setStorage(APP_CATCH_KEY.token, 'tokenValue')
|
||||
setStorage(APP_CATCH_KEY.signin, res.data)
|
||||
|
||||
router.push(path)
|
||||
}, 2 * 1000)
|
||||
|
Loading…
x
Reference in New Issue
Block a user