diff --git a/locales/en-US.json b/locales/en-US.json
index 21cbc8e3..119f8788 100644
--- a/locales/en-US.json
+++ b/locales/en-US.json
@@ -2,7 +2,8 @@
"GlobalMenuOptions": {
"Dashboard": "Home",
"Rely": "Rely",
- "RelyAbout": "Rely About"
+ "RelyAbout": "Rely About",
+ "Error": "Error Page"
},
"LayoutHeaderTooltipOptions": {
"Reload": "Reload Current Page",
diff --git a/locales/zh-CN.json b/locales/zh-CN.json
index aacbb22f..df32458c 100644
--- a/locales/zh-CN.json
+++ b/locales/zh-CN.json
@@ -2,7 +2,8 @@
"GlobalMenuOptions": {
"Dashboard": "首页",
"Rely": "依赖项",
- "RelyAbout": "关于"
+ "RelyAbout": "关于",
+ "Error": "错误页"
},
"LayoutHeaderTooltipOptions": {
"Reload": "刷新当前页面",
diff --git a/package.json b/package.json
index cbf19721..97421ecc 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"crypto-js": "^4.1.1",
"naive-ui": "^2.34.0",
"pinia": "^2.0.17",
+ "pinia-plugin-persistedstate": "^2.4.0",
"sass": "^1.54.3",
"scrollreveal": "^4.0.9",
"vue": "^3.2.37",
diff --git a/src/icons/error.svg b/src/icons/error.svg
new file mode 100644
index 00000000..ad9fb923
--- /dev/null
+++ b/src/icons/error.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/src/layout/SiderBar/Components/SettingDrawer/index.scss b/src/layout/SiderBar/Components/SettingDrawer/index.scss
deleted file mode 100644
index 56effcc9..00000000
--- a/src/layout/SiderBar/Components/SettingDrawer/index.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-.setting-drawer__space {
- width: 100%;
-}
diff --git a/src/layout/Menu/index.tsx b/src/layout/components/Menu/index.tsx
similarity index 86%
rename from src/layout/Menu/index.tsx
rename to src/layout/components/Menu/index.tsx
index 27231754..fba5acdc 100644
--- a/src/layout/Menu/index.tsx
+++ b/src/layout/components/Menu/index.tsx
@@ -6,7 +6,11 @@ const LayoutMenu = defineComponent({
setup() {
const menuStore = useMenu()
const { menuModelValueChange, setupAppRoutes, collapsedMenu } = menuStore
- const modelMenuKey = ref(menuStore.menuKey)
+ const modelMenuKey = computed({
+ get: () => menuStore.menuKey,
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
+ set: () => {},
+ })
const modelMenuOptions = computed(() => menuStore.options)
const modelCollapsed = computed(() => menuStore.collapsed)
diff --git a/src/layout/components/MenuTag/index.scss b/src/layout/components/MenuTag/index.scss
new file mode 100644
index 00000000..5d2977bf
--- /dev/null
+++ b/src/layout/components/MenuTag/index.scss
@@ -0,0 +1,14 @@
+$space: $layoutRouterViewContainer / 2;
+
+.menu-tag {
+ height: $layoutMenuHeight;
+
+ & .menu-tag-sapce {
+ width: calc(100% - $space * 2);
+ padding: $layoutRouterViewContainer / 2;
+ }
+
+ & .n-tag {
+ cursor: pointer;
+ }
+}
diff --git a/src/layout/components/MenuTag/index.tsx b/src/layout/components/MenuTag/index.tsx
new file mode 100644
index 00000000..344a5cbc
--- /dev/null
+++ b/src/layout/components/MenuTag/index.tsx
@@ -0,0 +1,61 @@
+import './index.scss'
+import { NScrollbar, NTag, NSpace } from 'naive-ui'
+import { useMenu } from '@/store'
+
+import type { MenuOption } from 'naive-ui'
+
+const MenuTag = defineComponent({
+ name: 'MenuTag',
+ setup() {
+ const menuStore = useMenu()
+ const { menuTagOptions, menuKey } = storeToRefs(menuStore)
+ const { menuModelValueChange, spliceMenTagOptions } = menuStore
+
+ const handleCloseTag = (idx: number) => {
+ spliceMenTagOptions(idx)
+
+ if (menuKey.value !== '/dashboard') {
+ const options = menuTagOptions.value as MenuOption[]
+ const length = options.length
+
+ const tag = options[length - 1]
+
+ menuModelValueChange(tag.key as string, tag)
+ }
+ }
+
+ const handleTagClick = (item: MenuOption) => {
+ menuModelValueChange(item.key as string, item)
+ }
+
+ return {
+ menuTagOptions,
+ menuModelValueChange,
+ handleCloseTag,
+ menuKey,
+ handleTagClick,
+ }
+ },
+ render() {
+ return (
+
+ )
+ },
+})
+
+export default MenuTag
diff --git a/src/layout/SiderBar/Components/SettingDrawer/hook.ts b/src/layout/components/SiderBar/Components/SettingDrawer/hook.ts
similarity index 100%
rename from src/layout/SiderBar/Components/SettingDrawer/hook.ts
rename to src/layout/components/SiderBar/Components/SettingDrawer/hook.ts
diff --git a/src/layout/components/SiderBar/Components/SettingDrawer/index.scss b/src/layout/components/SiderBar/Components/SettingDrawer/index.scss
new file mode 100644
index 00000000..760db0c1
--- /dev/null
+++ b/src/layout/components/SiderBar/Components/SettingDrawer/index.scss
@@ -0,0 +1,8 @@
+.setting-drawer__space {
+ width: 100%;
+
+ & .n-descriptions-table-content {
+ display: flex !important;
+ justify-content: space-between;
+ }
+}
diff --git a/src/layout/SiderBar/Components/SettingDrawer/index.tsx b/src/layout/components/SiderBar/Components/SettingDrawer/index.tsx
similarity index 83%
rename from src/layout/SiderBar/Components/SettingDrawer/index.tsx
rename to src/layout/components/SiderBar/Components/SettingDrawer/index.tsx
index a9bfcaf6..6ef7e098 100644
--- a/src/layout/SiderBar/Components/SettingDrawer/index.tsx
+++ b/src/layout/components/SiderBar/Components/SettingDrawer/index.tsx
@@ -7,6 +7,8 @@ import {
NSwitch,
NColorPicker,
NTooltip,
+ NDescriptions,
+ NDescriptionsItem,
} from 'naive-ui'
import RayIcon from '@/components/RayIcon/index'
import { useSwatchesColorOptions } from './hook'
@@ -35,8 +37,9 @@ const SettingDrawer = defineComponent({
const { t } = useI18n()
const settingStore = useSetting()
- const { changeTheme, changePrimaryColor } = settingStore
- const { themeValue, primaryColorOverride } = storeToRefs(settingStore)
+ const { changeTheme, changePrimaryColor, changeMenuTagLog } = settingStore
+ const { themeValue, primaryColorOverride, menuTagLog } =
+ storeToRefs(settingStore)
const modelShow = computed({
get: () => props.show,
@@ -57,6 +60,8 @@ const SettingDrawer = defineComponent({
changeTheme,
themeValue,
primaryColorOverride,
+ changeMenuTagLog,
+ menuTagLog,
}
},
render() {
@@ -119,6 +124,15 @@ const SettingDrawer = defineComponent({
v-model:value={this.primaryColorOverride.common.primaryColor}
onUpdateValue={this.changePrimaryColor.bind(this)}
/>
+ 界面显示
+
+
+
+
+
diff --git a/src/layout/SiderBar/hook.ts b/src/layout/components/SiderBar/hook.ts
similarity index 100%
rename from src/layout/SiderBar/hook.ts
rename to src/layout/components/SiderBar/hook.ts
diff --git a/src/layout/SiderBar/index.scss b/src/layout/components/SiderBar/index.scss
similarity index 100%
rename from src/layout/SiderBar/index.scss
rename to src/layout/components/SiderBar/index.scss
diff --git a/src/layout/SiderBar/index.tsx b/src/layout/components/SiderBar/index.tsx
similarity index 96%
rename from src/layout/SiderBar/index.tsx
rename to src/layout/components/SiderBar/index.tsx
index c7b0eb3b..93e0a016 100644
--- a/src/layout/SiderBar/index.tsx
+++ b/src/layout/components/SiderBar/index.tsx
@@ -1,7 +1,7 @@
import './index.scss'
import { NLayoutHeader, NSpace, NTooltip, NDropdown } from 'naive-ui'
import RayIcon from '@/components/RayIcon/index'
-import { useMenu, useSetting } from '@/store'
+import { useSetting } from '@/store'
import { useLanguageOptions } from '@/language/index'
import SettingDrawer from './Components/SettingDrawer/index'
import { useAvatarOptions } from './hook'
@@ -12,12 +12,10 @@ import type { IconEventMapOptions, IconEventMap } from './type'
const SiderBar = defineComponent({
name: 'SiderBar',
setup() {
- const menuStore = useMenu()
const settingStore = useSetting()
const { t } = useI18n()
- const { changeReloadLog } = menuStore
- const { updateLocale } = settingStore
+ const { updateLocale, changeReloadLog } = settingStore
const modelDrawerPlacement = ref(settingStore.drawerPlacement)
const showSettings = ref(false)
diff --git a/src/layout/SiderBar/type.ts b/src/layout/components/SiderBar/type.ts
similarity index 100%
rename from src/layout/SiderBar/type.ts
rename to src/layout/components/SiderBar/type.ts
diff --git a/src/layout/index.scss b/src/layout/index.scss
index c2dd7908..41c12ada 100644
--- a/src/layout/index.scss
+++ b/src/layout/index.scss
@@ -6,7 +6,8 @@
}
& .layout-content__router-view {
- height: calc(100% - $layoutHeaderHeight);
- padding: $layoutRouterViewContainer;
+ // height: calc(100% - $layoutHeaderHeight - $layoutMenuHeight);
+ height: var(--layout-content-height);
+ padding: $layoutRouterViewContainer / 2;
}
}
diff --git a/src/layout/index.tsx b/src/layout/index.tsx
index 903ebd41..8160bed6 100644
--- a/src/layout/index.tsx
+++ b/src/layout/index.tsx
@@ -1,31 +1,53 @@
import './index.scss'
-import { Transition } from 'vue'
import { NLayout, NLayoutContent } from 'naive-ui'
import RayTransitionComponent from '@/components/RayTransitionComponent/index.vue'
-import LayoutMenu from './Menu/index'
-import SiderBar from './SiderBar/index'
-import { useMenu } from '@/store'
+import LayoutMenu from './components/Menu/index'
+import SiderBar from './components/SiderBar/index'
+import MenuTag from './components/MenuTag/index'
+import { useSetting } from '@/store'
const Layout = defineComponent({
name: 'Layout',
props: {},
setup() {
- const menuStore = useMenu()
+ const menuStore = useSetting()
const { height: windowHeight } = useWindowSize()
const modelReloadRoute = computed(() => menuStore.reloadRouteLog)
+ const modelMenuTagLog = computed(() => menuStore.menuTagLog)
+ const cssVarsRef = computed(() => {
+ let cssVar = {}
+
+ if (menuStore.menuTagLog) {
+ cssVar = {
+ '--layout-content-height': 'calc(100% - 110px)',
+ }
+ } else {
+ cssVar = {
+ '--layout-content-height': 'calc(100% - 64px)',
+ }
+ }
+
+ return cssVar
+ })
return {
windowHeight,
modelReloadRoute,
+ modelMenuTagLog,
+ cssVarsRef,
}
},
render() {
return (
-
+
+ {this.modelMenuTagLog ? : ''}
import('@/views/error/index'),
+ meta: {
+ i18nKey: 'Error',
+ icon: 'error',
+ },
+}
diff --git a/src/router/modules/index.ts b/src/router/modules/index.ts
index 4ad7e60b..389eb8a9 100644
--- a/src/router/modules/index.ts
+++ b/src/router/modules/index.ts
@@ -1,7 +1,8 @@
import dashboard from './dashboard'
import reyl from './rely'
+import error from './error'
-const routes = [dashboard, reyl]
+const routes = [dashboard, error, reyl]
export default routes
diff --git a/src/store/index.ts b/src/store/index.ts
index 6f394921..64e51525 100644
--- a/src/store/index.ts
+++ b/src/store/index.ts
@@ -1,3 +1,5 @@
+import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
+
import type { App } from 'vue'
export { useSetting } from './modules/setting' // import { useSetting } from '@/store' 即可使用
@@ -7,4 +9,6 @@ const store = createPinia()
export const setupStore = (app: App) => {
app.use(store)
+
+ store.use(piniaPluginPersistedstate)
}
diff --git a/src/store/modules/menu.ts b/src/store/modules/menu.ts
index 9850bead..a1b10365 100644
--- a/src/store/modules/menu.ts
+++ b/src/store/modules/menu.ts
@@ -9,15 +9,28 @@ export const useMenu = defineStore('menu', () => {
const router = useRouter()
const { t } = useI18n()
- const cacheMenuKey = getCache('menuKey') === 'no' ? '' : getCache('menuKey')
+ const cacheMenuKey =
+ getCache('menuKey') === 'no' ? '/dashboard' : getCache('menuKey')
const menuState = reactive({
menuKey: cacheMenuKey as string | null, // 当前菜单 `key`
options: [] as RouteRecordRaw[], // 菜单列表
collapsed: false, // 是否折叠菜单
- reloadRouteLog: true, // 刷新路由开关
+ menuTagOptions: [] as RouteRecordRaw[],
})
+ const handleMenuTagOptions = (item: RouteRecordRaw) => {
+ if (item.path !== menuState.menuKey) {
+ const tag = menuState.menuTagOptions.find(
+ (curr) => curr.path === item.path,
+ )
+
+ if (!tag) {
+ menuState.menuTagOptions.push(item)
+ }
+ }
+ }
+
/**
*
* @param key 菜单更新后的 `key`
@@ -26,6 +39,8 @@ export const useMenu = defineStore('menu', () => {
* 修改 `menu key` 后的回调函数
*/
const menuModelValueChange = (key: string, item: MenuOption) => {
+ handleMenuTagOptions(item as unknown as RouteRecordRaw)
+
menuState.menuKey = key
router.push(`${item.path}`)
@@ -69,7 +84,14 @@ export const useMenu = defineStore('menu', () => {
),
}
- return curr.meta?.icon ? Object.assign(route, expandIcon) : route
+ const attr = curr.meta?.icon ? Object.assign(route, expandIcon) : route
+
+ // 初始化 `menu tag`
+ if (curr.path === cacheMenuKey) {
+ menuState.menuTagOptions.push(attr)
+ }
+
+ return attr
})
}
@@ -83,17 +105,14 @@ export const useMenu = defineStore('menu', () => {
const collapsedMenu = (collapsed: boolean) =>
(menuState.collapsed = collapsed)
- /**
- *
- * @param bool 刷新页面开关
- */
- const changeReloadLog = (bool: boolean) => (menuState.reloadRouteLog = bool)
+ const spliceMenTagOptions = (idx: number) =>
+ menuState.menuTagOptions.splice(idx, 1)
return {
...toRefs(menuState),
menuModelValueChange,
setupAppRoutes,
collapsedMenu,
- changeReloadLog,
+ spliceMenTagOptions,
}
})
diff --git a/src/store/modules/setting.ts b/src/store/modules/setting.ts
index f8b91f20..1e9baac7 100644
--- a/src/store/modules/setting.ts
+++ b/src/store/modules/setting.ts
@@ -1,49 +1,57 @@
-import { setCache, getCache } from '@/utils/cache'
-
-export const useSetting = defineStore('setting', () => {
- const cachePrimaryColor: string =
- getCache('primaryColor', 'localStorage') === 'no'
- ? '#18A058'
- : getCache('primaryColor', 'localStorage')
- const cacheTheme =
- getCache('theme', 'localStorage') === 'no'
- ? false
- : getCache('theme', 'localStorage')
-
- const settingState = reactive({
- drawerPlacement: 'right' as NaiveDrawerPlacement,
- primaryColorOverride: {
- common: {
- primaryColor: cachePrimaryColor, // 主题色
+export const useSetting = defineStore(
+ 'setting',
+ () => {
+ const settingState = reactive({
+ drawerPlacement: 'right' as NaiveDrawerPlacement,
+ primaryColorOverride: {
+ common: {
+ primaryColor: '#18A058', // 主题色
+ },
},
+ themeValue: false, // `true` 为黑夜主题, `false` 为白色主题
+ reloadRouteLog: true, // 刷新路由开关
+ menuTagLog: true, // 多标签页开关
+ })
+ const { locale } = useI18n()
+
+ const updateLocale = (key: string) => {
+ // TODO: 修改语言
+ locale.value = key
+ }
+
+ const changeTheme = (bool: boolean) => {
+ settingState.themeValue = bool
+ }
+
+ const changePrimaryColor = (value: string) => {
+ settingState.primaryColorOverride.common.primaryColor = value
+ }
+
+ /**
+ *
+ * @param bool 刷新页面开关
+ */
+ const changeReloadLog = (bool: boolean) =>
+ (settingState.reloadRouteLog = bool)
+
+ /**
+ *
+ * @param bool 刷新页面开关
+ */
+ const changeMenuTagLog = (bool: boolean) => (settingState.menuTagLog = bool)
+
+ return {
+ ...toRefs(settingState),
+ updateLocale,
+ changeTheme,
+ changePrimaryColor,
+ changeReloadLog,
+ changeMenuTagLog,
+ }
+ },
+ {
+ persist: {
+ key: 'piniaSettingStore',
},
- themeValue: cacheTheme, // `true` 为黑夜主题, `false` 为白色主题
- })
- const { locale } = useI18n()
-
- const updateLocale = (key: string) => {
- // TODO: 修改语言
- locale.value = key
-
- setCache('localeLanguage', key, 'localStorage')
- }
-
- const changeTheme = (bool: boolean) => {
- settingState.themeValue = bool
-
- setCache('theme', bool, 'localStorage')
- }
-
- const changePrimaryColor = (value: string) => {
- settingState.primaryColorOverride.common.primaryColor = value
-
- setCache('primaryColor', value, 'localStorage')
- }
-
- return {
- ...toRefs(settingState),
- updateLocale,
- changeTheme,
- changePrimaryColor,
- }
-})
+ },
+)
diff --git a/src/styles/setting.scss b/src/styles/setting.scss
index d7aeb144..e6146e3d 100644
--- a/src/styles/setting.scss
+++ b/src/styles/setting.scss
@@ -1,2 +1,3 @@
$layoutRouterViewContainer: 18px;
$layoutHeaderHeight: 64px;
+$layoutMenuHeight: 46px;
diff --git a/src/views/login/index.tsx b/src/views/login/index.tsx
index 98357ee8..9e112fb3 100644
--- a/src/views/login/index.tsx
+++ b/src/views/login/index.tsx
@@ -22,13 +22,11 @@ const Login = defineComponent({
const { t } = useI18n()
const { height: windowHeight } = useWindowSize()
const settingStore = useSetting()
- const { themeValue } = storeToRefs(settingStore)
const { updateLocale } = settingStore
return {
...toRefs(state),
windowHeight,
- themeValue,
updateLocale,
ray: t,
}
diff --git a/yarn.lock b/yarn.lock
index f3dbd889..60ea79df 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3489,6 +3489,11 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+pinia-plugin-persistedstate@^2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-2.4.0.tgz#fda569b3c397517a0cf8aba83a628283767da620"
+ integrity sha512-bQcpv47jk3ISl+InuJWsFaS/K7pRZ97kfoD2WCf/suhnlLy48k3BnFM2tI6YZ1xMsDaPv4yOsaPuPAUuSmEO2Q==
+
pinia@^2.0.17:
version "2.0.17"
resolved "https://registry.yarnpkg.com/pinia/-/pinia-2.0.17.tgz#f925e5e4f73c15e16dfb4838176a9ca50752f26b"