From 6a931c0cbb97ad0f054f24d66ca66b79b4de014c Mon Sep 17 00:00:00 2001
From: ray_wuhao <443547225@qq.com>
Date: Tue, 21 Mar 2023 13:48:12 +0800
Subject: [PATCH] =?UTF-8?q?v3.1.4=E5=8F=91=E5=B8=83?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.eslintrc.cjs | 10 +-
CHANGELOG.md | 13 +
README.md | 2 +-
cfg.ts | 11 +
package.json | 2 +-
src/icons/close.svg | 6 +
src/icons/more.svg | 12 +
src/icons/other.svg | 9 +
src/layout/components/MenuTag/index.scss | 29 ++
src/layout/components/MenuTag/index.tsx | 399 ++++++++++++++++--
.../Components/SettingDrawer/index.tsx | 2 +-
src/layout/components/SiderBar/index.tsx | 2 +-
src/router/basic.ts | 8 +-
src/router/configuration.ts | 37 ++
src/router/permission.ts | 10 +-
src/router/routes.ts | 6 +-
src/store/modules/menu/helper.ts | 4 +-
src/store/modules/menu/index.ts | 79 +++-
src/store/modules/setting.ts | 2 +
src/types/cfg.ts | 7 +
src/types/store.d.ts | 2 +-
src/views/error/index.tsx | 6 +-
src/views/login/components/Signin/index.tsx | 5 +-
tsconfig.json | 1 +
vite.config.ts | 13 +-
25 files changed, 622 insertions(+), 55 deletions(-)
create mode 100644 src/icons/close.svg
create mode 100644 src/icons/more.svg
create mode 100644 src/icons/other.svg
create mode 100644 src/router/configuration.ts
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index f634fc45..422cc9b4 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -100,7 +100,6 @@ module.exports = {
allowTaggedTemplates: true,
},
], // 禁止无用的表达式
- 'no-use-before-define': 2, // 禁止定义前使用
'no-useless-call': 2, // 禁止不必要的 `call` 和 `apply`
'no-var': 'error', // 禁用 `var`
'no-with': 2, // 禁用 `with`
@@ -122,5 +121,14 @@ module.exports = {
],
'vue/require-v-for-key': ['error'],
'vue/require-valid-default-prop': ['error'],
+ 'no-use-before-define': [
+ 'error',
+ {
+ functions: true,
+ classes: true,
+ variables: false,
+ allowNamedExports: false,
+ },
+ ],
},
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c7c9f45f..d10e193e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,18 @@
# CHANGE LOG
+## 3.1.4
+
+### Fixes
+
+- 修复主题色切换后,点击、鼠标滑入主题未被修改问题
+- 修复 menu store 菜单切换可能会重复执行问题
+
+### Feats
+
+- 补充 MenuTag 标签页功能,现在支持丰富的关闭操作与右键菜单激活操作菜单功能
+- 新增配置全局重定向地址配置(详情见:[cfg](https://github.com/XiaoDaiGua-Ray/xiaodaigua-ray.github.io/blob/main/cfg.ts))
+- 补充了一些不值一提的小东西
+
## 3.1.3
### Fixes
diff --git a/README.md b/README.md
index 362f67f5..7ed96b85 100644
--- a/README.md
+++ b/README.md
@@ -27,6 +27,7 @@
## 功能
- 主题切换
+- 带有拓展功能的表格
- 封装 `axios` 自动取消重复请求
- 动态菜单(多级菜单)
- 主题色切换
@@ -36,7 +37,6 @@
- 国际化(允许按模块管理语言包)
- 权限路由
- 动态切换主题、贴花的 `EChart` 图
-- 带有拓展功能的表格
- 最佳构建体验
- 体积分析
- 还有一些不值一提的小东西...
diff --git a/cfg.ts b/cfg.ts
index e9776aaa..e8908008 100644
--- a/cfg.ts
+++ b/cfg.ts
@@ -9,6 +9,17 @@ import {
import type { AppConfigExport } from './src/types/cfg'
const config: AppConfigExport = {
+ /**
+ *
+ * 配置根页面
+ * 该项目所有重定向至首页, 都依赖该配置项
+ *
+ * 如果修改了该项目的首页路由配置, 需要更改该配置项, 以免重定向首页操作出现错误
+ */
+ rootRoute: {
+ name: 'dashboard',
+ path: '/dashboard',
+ },
/**
*
* icon: LOGO 图标, 依赖 `RayIcon` 实现
diff --git a/package.json b/package.json
index 4d394197..5d3affc0 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "ray-template",
"private": true,
- "version": "3.1.3",
+ "version": "3.1.4",
"type": "module",
"scripts": {
"dev": "vite",
diff --git a/src/icons/close.svg b/src/icons/close.svg
new file mode 100644
index 00000000..bd6c6877
--- /dev/null
+++ b/src/icons/close.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/src/icons/more.svg b/src/icons/more.svg
new file mode 100644
index 00000000..c18fd376
--- /dev/null
+++ b/src/icons/more.svg
@@ -0,0 +1,12 @@
+
\ No newline at end of file
diff --git a/src/icons/other.svg b/src/icons/other.svg
new file mode 100644
index 00000000..517ad080
--- /dev/null
+++ b/src/icons/other.svg
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/src/layout/components/MenuTag/index.scss b/src/layout/components/MenuTag/index.scss
index 39988bce..a1f6c543 100644
--- a/src/layout/components/MenuTag/index.scss
+++ b/src/layout/components/MenuTag/index.scss
@@ -1,4 +1,5 @@
$space: calc($layoutRouterViewContainer / 2);
+$menuTagWrapperWidth: 76px;
.menu-tag {
height: $layoutMenuHeight;
@@ -7,6 +8,34 @@ $space: calc($layoutRouterViewContainer / 2);
& .menu-tag-sapce {
width: calc(100% - $space * 2);
padding: $space;
+
+ & .menu-tag-wrapper {
+ width: calc(100% - $space * 2 - $menuTagWrapperWidth);
+ }
+
+ & .ray-icon {
+ cursor: pointer;
+ }
+
+ & .menu-tag__left-arrow {
+ transform: rotate(90deg);
+ }
+
+ & .menu-tag__right-wrapper {
+ display: inline-flex;
+ align-items: center;
+
+ & .menu-tag__right-arrow {
+ transform: rotate(270deg);
+ }
+
+ & .menu-tag__right-setting {
+ width: 28px;
+ height: 20px;
+ // display: inline-flex;
+ // align-items: center;
+ }
+ }
}
& .n-tag {
diff --git a/src/layout/components/MenuTag/index.tsx b/src/layout/components/MenuTag/index.tsx
index ff3af757..748ff299 100644
--- a/src/layout/components/MenuTag/index.tsx
+++ b/src/layout/components/MenuTag/index.tsx
@@ -10,19 +10,174 @@
*/
import './index.scss'
-import { NScrollbar, NTag, NSpace, NLayoutHeader } from 'naive-ui'
-import { useMenu } from '@/store'
-import type { MenuOption } from 'naive-ui'
+import { NScrollbar, NTag, NSpace, NLayoutHeader, NDropdown } from 'naive-ui'
+import RayIcon from '@/components/RayIcon/index'
+
+import { useMenu, useSetting } from '@/store'
+import { uuid } from '@/utils/hook'
+import { hasClass } from '@/utils/element'
+
+import type { MenuOption, ScrollbarInst } from 'naive-ui'
const MenuTag = defineComponent({
name: 'MenuTag',
setup() {
- const menuStore = useMenu()
- const { menuKey } = storeToRefs(menuStore)
- const { menuModelValueChange, spliceMenTagOptions } = menuStore
+ const scrollRef = ref(null)
- const modelMenuTagOptions = computed(() => menuStore.menuTagOptions)
+ const menuStore = useMenu()
+ const settingStore = useSetting()
+ const router = useRouter()
+
+ const { menuKey, menuTagOptions } = storeToRefs(menuStore)
+ const {
+ menuModelValueChange,
+ spliceMenTagOptions,
+ emptyMenuTagOptions,
+ setMenuTagOptions,
+ } = menuStore
+ const { changeSwitcher } = settingStore
+ const {
+ rootRoute: { path },
+ } = __APP_CFG__
+
+ const exclude = ['closeAll', 'closeRight', 'closeLeft', 'closeOther']
+ let currentContentmenuIndex = -1 // 当前右键标签页索引位置
+ const modelMenuTagOptions = computed(() => menuTagOptions.value)
+ const moreOptions = ref([
+ {
+ label: '重新加载',
+ key: 'reloadCurrentPage',
+ icon: () =>
+ h(
+ RayIcon,
+ {
+ size: 16,
+ name: 'reload',
+ },
+ {},
+ ),
+ },
+ {
+ label: '关闭其他',
+ key: 'closeOther',
+ icon: () =>
+ h(
+ RayIcon,
+ {
+ size: 16,
+ name: 'other',
+ },
+ {},
+ ),
+ },
+ {
+ label: '关闭右侧',
+ key: 'closeRight',
+ icon: () =>
+ h(
+ RayIcon,
+ {
+ size: 16,
+ name: 'right_arrow',
+ },
+ {},
+ ),
+ },
+ {
+ label: '关闭左侧',
+ key: 'closeLeft',
+ icon: () =>
+ h(
+ RayIcon,
+ {
+ size: 16,
+ name: 'left_arrow',
+ },
+ {},
+ ),
+ },
+ {
+ type: 'divider',
+ key: 'd1',
+ },
+ {
+ label: '全部关闭',
+ key: 'closeAll',
+ icon: () =>
+ h(
+ RayIcon,
+ {
+ size: 16,
+ name: 'close',
+ },
+ {},
+ ),
+ disabled: false,
+ },
+ ])
+ const scrollBarUUID = uuid()
+ const actionMap = {
+ reloadCurrentPage: () => {
+ changeSwitcher(false, 'reloadRouteSwitch')
+
+ setTimeout(() => changeSwitcher(true, 'reloadRouteSwitch'))
+ },
+ closeAll: () => {
+ /**
+ *
+ * 关闭全部标签页, 然后重定向至首页(dashboard)
+ * 如果做了相关更改, 则需要手动更新
+ */
+ if (moreOptions.value.length > 1) {
+ emptyMenuTagOptions()
+ router.replace({
+ path: path,
+ })
+ }
+ },
+ closeRight: () => {
+ /**
+ *
+ * 关闭右侧标签
+ *
+ * 如果当前选择标签与 menuKey 不匹配, 则会关闭当前标签右侧所有变迁并且跳转至该页面
+ */
+ const length = moreOptions.value.length
+ const routeItem = modelMenuTagOptions.value[currentContentmenuIndex]
+
+ spliceMenTagOptions(currentContentmenuIndex + 1, length - 1)
+
+ if (menuKey.value !== routeItem.key) {
+ menuModelValueChange(routeItem.key, routeItem)
+ }
+ },
+ closeLeft: () => {
+ spliceMenTagOptions(0, currentContentmenuIndex)
+ },
+ closeOther: () => {
+ /**
+ *
+ * 关闭其他标签
+ *
+ * 如果关闭标签与当前 menuKey 不匹配, 则会关闭当前选择标签页以外的所有标签页并且跳转至该页面
+ */
+ const routeItem = modelMenuTagOptions.value[currentContentmenuIndex]
+
+ if (menuKey.value !== routeItem.key) {
+ emptyMenuTagOptions()
+ menuModelValueChange(routeItem.key, routeItem)
+ } else {
+ setMenuTagOptions(routeItem, false)
+ }
+ },
+ }
+ /** 右键菜单 */
+ const actionState = reactive({
+ x: 0,
+ y: 0,
+ actionDropdownShow: false,
+ })
/**
*
@@ -30,10 +185,10 @@ const MenuTag = defineComponent({
*
* @remark 关闭 `tag` 菜单, 如果仅有一个则不能关闭
*/
- const handleCloseTag = (idx: number) => {
+ const closeCurrentMenuTag = (idx: number) => {
spliceMenTagOptions(idx)
- if (menuKey.value !== '/dashboard') {
+ if (menuKey.value !== path) {
const options = modelMenuTagOptions.value
const length = options.length
@@ -43,6 +198,19 @@ const MenuTag = defineComponent({
}
}
+ const setMoreOptionsDisabled = (
+ key: string | number,
+ disabled: boolean,
+ ) => {
+ moreOptions.value.forEach((curr) => {
+ if (curr.key === key) {
+ curr.disabled = disabled
+
+ return
+ }
+ })
+ }
+
/**
*
* @param item 当前菜单值
@@ -51,35 +219,214 @@ const MenuTag = defineComponent({
menuModelValueChange(item.key as string, item)
}
+ const handleScrollX = (type: 'left' | 'right') => {
+ const scroll = document.getElementById(scrollBarUUID) // 获取滚动条容器
+
+ if (scroll) {
+ /**
+ *
+ * 找到实际横向滚动元素(class: n-scrollbar-container)
+ * 获取 scrollLeft 属性后, 用于左右滚动边界值进行处理
+ */
+ const scrollContentElement = Array.from(
+ scroll.childNodes,
+ ) as HTMLElement[]
+ const findElement = scrollContentElement.find((el) =>
+ hasClass(el, 'n-scrollbar-container'),
+ )
+ const scrollX = findElement!.scrollLeft || 0
+ const rolling =
+ type === 'left' ? Math.max(0, scrollX - 200) : scrollX + 200
+
+ scrollRef.value?.scrollTo({
+ left: rolling,
+ behavior: 'smooth',
+ })
+ }
+ }
+
+ /** 更多操作操作栏 */
+ const actionDropdownSelect = (key: string | number) => {
+ actionState.actionDropdownShow = false
+
+ actionMap[key]?.()
+ }
+
+ /**
+ *
+ * 右键点击标签页
+ *
+ * 缓存当前点击标签页索引值(用于关闭左或者右侧标签页操作)
+ */
+ const handleContextMenu = (idx: number, e: MouseEvent) => {
+ e.preventDefault()
+
+ actionState.actionDropdownShow = false
+ currentContentmenuIndex = idx
+
+ nextTick().then(() => {
+ actionState.actionDropdownShow = true
+ actionState.x = e.clientX
+ actionState.y = e.clientY
+ })
+ }
+
+ const setDisabledAccordionToIndex = () => {
+ const length = modelMenuTagOptions.value.length - 1
+
+ if (currentContentmenuIndex === length) {
+ setMoreOptionsDisabled('closeRight', true)
+ } else if (currentContentmenuIndex < length) {
+ setMoreOptionsDisabled('closeRight', false)
+ }
+
+ if (currentContentmenuIndex === 0) {
+ setMoreOptionsDisabled('closeLeft', true)
+ } else if (currentContentmenuIndex > 0) {
+ setMoreOptionsDisabled('closeLeft', false)
+ }
+ }
+
+ /**
+ *
+ * 如果通过更多按钮触发关闭事件, 则根据当前标签所在索引值为 currentContentmenuIndex
+ *
+ * 并且动态设置是否可操作状态
+ */
+ const setCurrentContentmenuIndex = () => {
+ const index = modelMenuTagOptions.value.findIndex(
+ (curr) => curr.key === menuKey.value,
+ )
+
+ currentContentmenuIndex = index
+
+ setDisabledAccordionToIndex()
+ }
+
+ /** 如果有且只有一个标签页时, 禁止全部关闭操作 */
+ watch(
+ () => modelMenuTagOptions.value,
+ (newData) => {
+ moreOptions.value.forEach((curr) => {
+ if (exclude.includes(curr.key)) {
+ newData.length > 1
+ ? (curr.disabled = false)
+ : (curr.disabled = true)
+ }
+ })
+ },
+ {
+ immediate: true,
+ deep: true,
+ },
+ )
+
+ /** 动态设置关闭按钮是否可操作 */
+ watch(
+ () => actionState.actionDropdownShow,
+ () => {
+ setDisabledAccordionToIndex()
+ },
+ )
+
return {
modelMenuTagOptions,
menuModelValueChange,
- handleCloseTag,
+ closeCurrentMenuTag,
menuKey,
handleTagClick,
+ moreOptions,
+ handleScrollX,
+ scrollRef,
+ scrollBarUUID,
+ actionDropdownSelect,
+ rootPath: path,
+ actionState,
+ handleContextMenu,
+ setCurrentContentmenuIndex,
}
},
render() {
return (
-
)
},
diff --git a/src/layout/components/SiderBar/Components/SettingDrawer/index.tsx b/src/layout/components/SiderBar/Components/SettingDrawer/index.tsx
index 74e7ae37..94824dcb 100644
--- a/src/layout/components/SiderBar/Components/SettingDrawer/index.tsx
+++ b/src/layout/components/SiderBar/Components/SettingDrawer/index.tsx
@@ -127,7 +127,7 @@ const SettingDrawer = defineComponent({
界面显示
diff --git a/src/layout/components/SiderBar/index.tsx b/src/layout/components/SiderBar/index.tsx
index fbe552fc..b7e15546 100644
--- a/src/layout/components/SiderBar/index.tsx
+++ b/src/layout/components/SiderBar/index.tsx
@@ -86,7 +86,7 @@ const SiderBar = defineComponent({
reload: () => {
changeSwitcher(false, 'reloadRouteSwitch')
- setTimeout(() => changeSwitcher(true, 'reloadRouteSwitch'), 1.5 * 1000)
+ setTimeout(() => changeSwitcher(true, 'reloadRouteSwitch'))
},
setting: () => {
showSettings.value = true
diff --git a/src/router/basic.ts b/src/router/basic.ts
index bf81abe5..e4676ac6 100644
--- a/src/router/basic.ts
+++ b/src/router/basic.ts
@@ -23,9 +23,7 @@
*/
import { useSignin } from '@/store'
-
-const BASIC_ROUTER = ['login', 'error-page', 'doc']
-const BASE_ROLES = ['admin']
+import { whiteRoutes, superAdmin } from './configuration'
export const validRole = (options: IMenuOptions) => {
const { role } = storeToRefs(useSignin())
@@ -35,11 +33,11 @@ export const validRole = (options: IMenuOptions) => {
meta?.hidden === undefined || meta?.hidden === false ? false : meta?.hidden
// 如果是超级管理员(预设为 admin), 则根据其菜单栏(hidden)字段判断是否显示
- if (BASE_ROLES.includes(role.value)) {
+ if (superAdmin.length && superAdmin.includes(role.value)) {
return true && !hidden
} else {
// 如果为基础路由, 不进行鉴权则根据其菜单栏(hidden)字段判断是否显示
- if (BASIC_ROUTER.includes(name)) {
+ if (whiteRoutes.includes(name)) {
return true && !hidden
}
diff --git a/src/router/configuration.ts b/src/router/configuration.ts
new file mode 100644
index 00000000..f8b7478d
--- /dev/null
+++ b/src/router/configuration.ts
@@ -0,0 +1,37 @@
+/**
+ *
+ * @author Ray
+ *
+ * @date 2023-03-19
+ *
+ * @workspace ray-template
+ *
+ * @remark 今天也是元气满满撸代码的一天
+ */
+
+/**
+ *
+ * 配置动态路由菜单
+ *
+ * 可以根据权限与白名单进行过滤, 但是 meta hidden 属性拥有最高的控制权限
+ * 如果 mete hidden 设置为 false 则永远不会显示菜单选项
+ */
+
+/**
+ *
+ * 路由表单白名单
+ *
+ * 如果需要启用该功能, 则需要配置路由 name 属性, 并且需要一一对应(对大小写敏感)
+ * 如果未设置, 则不会生效
+ *
+ * 配置该路由白名单列表后, 则不会对配置中的路由列表进行鉴权处理(配合 basic.ts 中的方法进行使用)
+ */
+export const whiteRoutes = ['login', 'error-page', 'doc']
+
+/**
+ *
+ * 超级管理员
+ *
+ * 配置默认超级管理员, 默认拥有全部最高权限
+ */
+export const superAdmin = ['admin']
diff --git a/src/router/permission.ts b/src/router/permission.ts
index 6078ae2f..0fe72192 100644
--- a/src/router/permission.ts
+++ b/src/router/permission.ts
@@ -30,10 +30,14 @@ import type { Router, NavigationGuardNext } from 'vue-router'
export const permissionRouter = (router: Router) => {
const { beforeEach } = router
- const redirectToDashboard = (next: NavigationGuardNext) => {
- next('/dashboard')
+ const {
+ rootRoute: { path },
+ } = __APP_CFG__
- setCache('menuKey', '/dashboard')
+ const redirectToDashboard = (next: NavigationGuardNext) => {
+ next(path)
+
+ setCache('menuKey', path)
}
beforeEach((to, from, next) => {
diff --git a/src/router/routes.ts b/src/router/routes.ts
index 44d5bb3d..e72f999b 100644
--- a/src/router/routes.ts
+++ b/src/router/routes.ts
@@ -1,6 +1,10 @@
import Layout from '@/layout/index'
import childrenRoutes from './modules/index'
+const {
+ rootRoute: { path },
+} = __APP_CFG__
+
export const constantRoutes = [
{
path: '/',
@@ -10,7 +14,7 @@ export const constantRoutes = [
{
path: '/',
name: 'layout',
- redirect: '/dashboard',
+ redirect: path,
component: Layout,
children: childrenRoutes,
},
diff --git a/src/store/modules/menu/helper.ts b/src/store/modules/menu/helper.ts
index 542bff84..3b1500bf 100644
--- a/src/store/modules/menu/helper.ts
+++ b/src/store/modules/menu/helper.ts
@@ -92,13 +92,15 @@ export const parse = (
/**
*
* @param item menu options
+ * @param key current menu key
+ * @param menuTagOptions menu tag options
*
* @remark 查找当前菜单项
*/
export const matchMenuOption = (
item: IMenuOptions,
key: MenuKey,
- menuTagOptions: TagMenuOptions[],
+ menuTagOptions: MenuTagOptions[],
) => {
if (item.path !== key) {
const tag = menuTagOptions.find((curr) => curr.path === item.path)
diff --git a/src/store/modules/menu/index.ts b/src/store/modules/menu/index.ts
index 3aa5c62f..37e01864 100644
--- a/src/store/modules/menu/index.ts
+++ b/src/store/modules/menu/index.ts
@@ -35,23 +35,44 @@ export const useMenu = defineStore(
const route = useRoute()
const { t } = useI18n()
+ const {
+ rootRoute: { path },
+ } = __APP_CFG__
+
const cacheMenuKey =
- getCache('menuKey') === 'no' ? '/dashboard' : getCache('menuKey')
+ getCache('menuKey') === 'no' ? path : getCache('menuKey')
const menuState = reactive({
menuKey: cacheMenuKey as MenuKey, // 当前菜单 `key`
options: [] as IMenuOptions[], // 菜单列表
collapsed: false, // 是否折叠菜单
- menuTagOptions: [] as TagMenuOptions[], // tag 标签菜单
+ menuTagOptions: [] as MenuTagOptions[], // tag 标签菜单
breadcrumbOptions: [] as IMenuOptions[], // 面包屑菜单
})
+ /**
+ *
+ * @param options menu options
+ * @param key target key
+ *
+ * @remark 获取完整菜单项
+ */
+ const getCompleteRoutePath = (
+ options: IMenuOptions[],
+ key: string | number,
+ ) => {
+ const ops = parse(options, 'key', key)
+
+ return ops
+ }
+
/**
*
* @param key 菜单更新后的 `key`
* @param item 菜单当前 `item`
*
- * 修改 `menu key` 后的回调函数
+ * @remark 修改 `menu key` 后的回调函数
+ * @remark 修改后, 缓存当前选择 key 并且存储标签页与跳转页面(router push 操作)
*/
const menuModelValueChange = (key: string | number, item: MenuOption) => {
const meta = item.meta as RouteMeta
@@ -62,7 +83,7 @@ export const useMenu = defineStore(
// 防止重复点击做重复操作处理
if (menuState.menuKey !== key) {
matchMenuOption(
- item as unknown as TagMenuOptions,
+ item as unknown as MenuTagOptions,
menuState.menuKey,
menuState.menuTagOptions,
)
@@ -70,16 +91,17 @@ export const useMenu = defineStore(
menuState.breadcrumbOptions = parse(menuState.options, 'key', key) // 获取面包屑
if (key[0] !== '/') {
- const p = menuState.breadcrumbOptions
+ const path = getCompleteRoutePath(menuState.options, key)
.map((curr) => curr.key)
.join('/')
- router.push(p)
+ router.push(path)
} else {
router.push(item.path as string)
}
menuState.menuKey = key
+
setCache('menuKey', key)
}
}
@@ -109,6 +131,28 @@ export const useMenu = defineStore(
matchMenuItem(menuState.options as MenuOption[])
}
+ /**
+ *
+ * @param optins menu tag option(s)
+ * @param isAppend true: 追加操作(push), false: 覆盖操作
+ */
+ const setMenuTagOptions = (
+ optins: MenuTagOptions | MenuTagOptions[],
+ isAppend = true,
+ ) => {
+ const isArray = Array.isArray(optins)
+
+ if (isAppend) {
+ isArray
+ ? menuState.menuTagOptions.push(...optins)
+ : menuState.menuTagOptions.push(optins)
+ } else {
+ isArray
+ ? (menuState.menuTagOptions = optins)
+ : (menuState.menuTagOptions = [optins])
+ }
+ }
+
/**
*
* @remark 初始化菜单列表, 并且按照权限过滤
@@ -159,7 +203,7 @@ export const useMenu = defineStore(
: route
if (curr.path === cacheMenuKey) {
- menuState.menuTagOptions.push(attr)
+ setMenuTagOptions(attr)
}
attr.show = validRole(curr)
@@ -187,8 +231,23 @@ export const useMenu = defineStore(
const collapsedMenu = (collapsed: boolean) =>
(menuState.collapsed = collapsed)
- const spliceMenTagOptions = (idx: number) =>
- menuState.menuTagOptions.splice(idx, 1)
+ /**
+ *
+ * @param idx 当前关闭标签索引
+ * @param length 裁剪标签页长度
+ *
+ * @returns 被关闭标签项
+ */
+ const spliceMenTagOptions = (idx: number, length = 1) =>
+ menuState.menuTagOptions.splice(idx, length)
+
+ /**
+ *
+ * @remark 置空 menuTagOptions
+ */
+ const emptyMenuTagOptions = () => {
+ menuState.menuTagOptions = []
+ }
watch(
() => route.fullPath,
@@ -206,6 +265,8 @@ export const useMenu = defineStore(
setupAppRoutes,
collapsedMenu,
spliceMenTagOptions,
+ emptyMenuTagOptions,
+ setMenuTagOptions,
}
},
{
diff --git a/src/store/modules/setting.ts b/src/store/modules/setting.ts
index 5058cb7e..6b8479d7 100644
--- a/src/store/modules/setting.ts
+++ b/src/store/modules/setting.ts
@@ -23,6 +23,7 @@ export const useSetting = defineStore(
primaryColorOverride: {
common: {
primaryColor: '#2d8cf0', // 主题色
+ primaryColorHover: '#2d8cf0',
},
},
themeValue: false, // `true` 为黑夜主题, `false` 为白色主题
@@ -44,6 +45,7 @@ export const useSetting = defineStore(
const changePrimaryColor = (value: string) => {
settingState.primaryColorOverride.common!.primaryColor = value
+ settingState.primaryColorOverride.common!.primaryColorHover = value
}
/**
diff --git a/src/types/cfg.ts b/src/types/cfg.ts
index 480cde4c..51237db7 100644
--- a/src/types/cfg.ts
+++ b/src/types/cfg.ts
@@ -15,6 +15,11 @@ export interface LayoutSideBarLogo {
export type LayoutCopyright = string | number | VNodeChild
+export interface RootRoute {
+ name: string
+ path: string
+}
+
export interface HTMLTitle {
name: string
transformIndexHtml: (title: string) => string
@@ -28,6 +33,7 @@ export interface Config {
copyright?: LayoutCopyright
sideBarLogo?: LayoutSideBarLogo
mixinCSS?: string
+ rootRoute?: RootRoute
}
export type Recordable = Record
@@ -43,6 +49,7 @@ export interface AppConfig {
copyright?: LayoutCopyright
sideBarLogo?: LayoutSideBarLogo
}
+ rootRoute: RootRoute
}
export type AppConfigExport = Config & UserConfigExport
diff --git a/src/types/store.d.ts b/src/types/store.d.ts
index 94eece83..52aeea47 100644
--- a/src/types/store.d.ts
+++ b/src/types/store.d.ts
@@ -17,7 +17,7 @@ declare global {
noLocalTitle?: string | number
}
- declare interface TagMenuOptions extends IMenuOptions {}
+ declare interface MenuTagOptions extends IMenuOptions {}
declare type MenuKey = null | string | number
}
diff --git a/src/views/error/index.tsx b/src/views/error/index.tsx
index ce60eef3..b0a35dd8 100644
--- a/src/views/error/index.tsx
+++ b/src/views/error/index.tsx
@@ -6,8 +6,12 @@ const ErrorPage = defineComponent({
setup() {
const router = useRouter()
+ const {
+ rootRoute: { path },
+ } = __APP_CFG__
+
const handleBack = () => {
- router.push('/dashboard')
+ router.push(path)
}
return {
diff --git a/src/views/login/components/Signin/index.tsx b/src/views/login/components/Signin/index.tsx
index 4cb7dbe5..382980ad 100644
--- a/src/views/login/components/Signin/index.tsx
+++ b/src/views/login/components/Signin/index.tsx
@@ -12,6 +12,9 @@ const Signin = defineComponent({
const signinStore = useSignin()
const { signin } = signinStore
+ const {
+ rootRoute: { path },
+ } = __APP_CFG__
const useSigninForm = () => ({
name: 'ray',
@@ -48,7 +51,7 @@ const Signin = defineComponent({
setCache('token', 'tokenValue')
setCache('person', signinForm.value)
- router.push('/dashboard')
+ router.push(path)
}, 2 * 1000)
}
} else {
diff --git a/tsconfig.json b/tsconfig.json
index fdc7fcf6..d6324113 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -35,6 +35,7 @@
"src/**/*.vue",
"src/*.ts",
"src/*.vue",
+ "src/*",
"components.d.ts",
"auto-imports.d.ts"
],
diff --git a/vite.config.ts b/vite.config.ts
index b6be6536..6501ecf0 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -23,8 +23,16 @@ import config from './cfg'
import pkg from './package.json'
const { dependencies, devDependencies, name, version } = pkg
-const { server, buildOptions, alias, title, copyright, sideBarLogo, mixinCSS } =
- config
+const {
+ server,
+ buildOptions,
+ alias,
+ title,
+ copyright,
+ sideBarLogo,
+ mixinCSS,
+ rootRoute,
+} = config
/**
*
@@ -42,6 +50,7 @@ const __APP_CFG__ = {
copyright,
sideBarLogo,
},
+ rootRoute,
}
// https://vitejs.dev/config/