+
-
-
-
-
-
-
-
+
+
|
+
+
+
+
+
+
diff --git a/web/src/layout/header/components/BreadCrumb.vue b/web/src/layout/header/components/BreadCrumb.vue
deleted file mode 100644
index 35de774f..00000000
--- a/web/src/layout/header/components/BreadCrumb.vue
+++ /dev/null
@@ -1,63 +0,0 @@
-
-
-
-
-
-
-
-
-
- {{ $t(routeItem.meta.title) }}
-
-
-
-
- {{ $t(routeItem.meta.title) }}
-
-
-
-
-
diff --git a/web/src/layout/header/components/UserAvatar.vue b/web/src/layout/header/components/UserAvatar.vue
index 308ec219..6ca99893 100644
--- a/web/src/layout/header/components/UserAvatar.vue
+++ b/web/src/layout/header/components/UserAvatar.vue
@@ -1,4 +1,5 @@
- {{ userStore.username }}
+ {{ username }}
diff --git a/web/src/layout/tab/IndexView.vue b/web/src/layout/tab/IndexView.vue
index 27411c66..54fcf8e2 100644
--- a/web/src/layout/tab/IndexView.vue
+++ b/web/src/layout/tab/IndexView.vue
@@ -3,7 +3,6 @@ import type { TabItem } from '@/store'
import { useTabStore } from '@/store'
import ContextMenu from './components/ContextMenu.vue'
-const route = useRoute()
const router = useRouter()
const tabStore = useTabStore()
@@ -21,16 +20,6 @@ const contextMenuOption = reactive
({
currentPath: ''
})
-watch(
- () => route.path,
- () => {
- const { name, fullPath: path } = route
- const title = (route.meta?.title as string) || ''
- tabStore.addTab({ name: name as string, path, title })
- },
- { immediate: true }
-)
-
function handleTagClick(path: string) {
tabStore.setActiveTab(path)
router.push(path)
@@ -61,12 +50,10 @@ async function handleContextMenu(e: MouseEvent, tabItem: TabItem) {
tabStore.removeTab(path)"
- bg-white
- dark:bg-dark
>
[
{
label: '重新加载',
key: 'reload',
- disabled: props.currentPath !== tabStore.activeTab,
+ disabled: props.currentPath !== tabStore.active,
icon: renderIcon('mdi:refresh', { size: 14 })
},
{
diff --git a/web/src/router/guard/index.ts b/web/src/router/guard/index.ts
index 0a23b2d2..ad590b75 100644
--- a/web/src/router/guard/index.ts
+++ b/web/src/router/guard/index.ts
@@ -1,3 +1,4 @@
+import { createTabGuard } from '@/router/guard/tab-guard'
import type { Router } from 'vue-router'
import { createAppInstallGuard } from './app-install-guard'
import { createPageLoadingGuard } from './page-loading-guard'
@@ -6,5 +7,6 @@ import { createPageTitleGuard } from './page-title-guard'
export function setupRouterGuard(router: Router) {
createPageLoadingGuard(router)
createPageTitleGuard(router)
+ createTabGuard(router)
createAppInstallGuard(router)
}
diff --git a/web/src/router/guard/tab-guard.ts b/web/src/router/guard/tab-guard.ts
new file mode 100644
index 00000000..15190237
--- /dev/null
+++ b/web/src/router/guard/tab-guard.ts
@@ -0,0 +1,18 @@
+import { useTabStore } from '@/store'
+import type { Router } from 'vue-router'
+
+export const EXCLUDE_TAB = ['/404', '/403', '/login']
+
+export function createTabGuard(router: Router) {
+ router.afterEach((to) => {
+ if (EXCLUDE_TAB.includes(to.path)) return
+ const tabStore = useTabStore()
+ const { name, fullPath: path } = to
+ const title = String(to.meta?.title)
+ tabStore.addTab({
+ name: String(name),
+ path,
+ title
+ })
+ })
+}
diff --git a/web/src/router/index.ts b/web/src/router/index.ts
index 28389d50..1aaa3b1b 100644
--- a/web/src/router/index.ts
+++ b/web/src/router/index.ts
@@ -1,4 +1,4 @@
-import { usePermissionStore, useUserStore } from '@/store'
+import { usePermissionStore } from '@/store'
import type { App } from 'vue'
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
import type { RoutesType, RouteType } from '~/types/router'
@@ -22,8 +22,6 @@ export async function setupRouter(app: App) {
export async function addDynamicRoutes() {
try {
- const userStore = useUserStore()
- await userStore.getUserInfo()
const permissionStore = usePermissionStore()
const accessRoutes = permissionStore.generateRoutes(['admin'])
accessRoutes.forEach((route: RouteType) => {
diff --git a/web/src/store/index.ts b/web/src/store/index.ts
index 467ee86c..148dea61 100644
--- a/web/src/store/index.ts
+++ b/web/src/store/index.ts
@@ -1,8 +1,11 @@
import { createPinia } from 'pinia'
+import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import type { App } from 'vue'
export async function setupStore(app: App) {
- app.use(createPinia())
+ const pinia = createPinia()
+ pinia.use(piniaPluginPersistedstate)
+ app.use(pinia)
}
export * from './modules'
diff --git a/web/src/store/modules/tab/helpers.ts b/web/src/store/modules/tab/helpers.ts
deleted file mode 100644
index e0a0c55b..00000000
--- a/web/src/store/modules/tab/helpers.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { getSession } from '@/utils'
-
-export const activeTab = getSession('activeTab')
-export const tabs = getSession('tabs')
-
-export const WITHOUT_TAB_PATHS = ['/404', '/login']
diff --git a/web/src/store/modules/tab/index.ts b/web/src/store/modules/tab/index.ts
index 1dcf95a8..048268d9 100644
--- a/web/src/store/modules/tab/index.ts
+++ b/web/src/store/modules/tab/index.ts
@@ -1,29 +1,32 @@
import { router } from '@/router'
-import { setSession } from '@/utils'
import { defineStore } from 'pinia'
-import { activeTab, tabs, WITHOUT_TAB_PATHS } from './helpers'
+
+export const WITHOUT_TAB_PATHS = ['/404', '/login']
+
+export interface Tab {
+ active: string
+ tabs: Array
+}
export interface TabItem {
name: string
path: string
- title?: string
+ title: string
}
export const useTabStore = defineStore('tab', {
- state() {
+ state: (): Tab => {
return {
- tabs: >tabs || [],
- activeTab: activeTab || ''
+ active: '',
+ tabs: []
}
},
actions: {
setActiveTab(path: string) {
- this.activeTab = path
- setSession('activeTab', path)
+ this.active = path
},
setTabs(tabs: Array) {
this.tabs = tabs
- setSession('tabs', tabs)
},
addTab(tab: TabItem) {
this.setActiveTab(tab.path)
@@ -32,7 +35,7 @@ export const useTabStore = defineStore('tab', {
this.setTabs([...this.tabs, tab])
},
removeTab(path: string) {
- if (path === this.activeTab) {
+ if (path === this.active) {
const activeIndex = this.tabs.findIndex((item) => item.path === path)
if (activeIndex > 0) router.push(this.tabs[activeIndex - 1].path)
else router.push(this.tabs[activeIndex + 1].path)
@@ -41,25 +44,26 @@ export const useTabStore = defineStore('tab', {
},
removeOther(curPath: string) {
this.setTabs(this.tabs.filter((tab) => tab.path === curPath))
- if (curPath !== this.activeTab) router.push(this.tabs[this.tabs.length - 1].path)
+ if (curPath !== this.active) router.push(this.tabs[this.tabs.length - 1].path)
},
removeLeft(curPath: string) {
const curIndex = this.tabs.findIndex((item) => item.path === curPath)
const filterTabs = this.tabs.filter((item, index) => index >= curIndex)
this.setTabs(filterTabs)
- if (!filterTabs.find((item) => item.path === this.activeTab))
+ if (!filterTabs.find((item) => item.path === this.active))
router.push(filterTabs[filterTabs.length - 1].path)
},
removeRight(curPath: string) {
const curIndex = this.tabs.findIndex((item) => item.path === curPath)
const filterTabs = this.tabs.filter((item, index) => index <= curIndex)
this.setTabs(filterTabs)
- if (!filterTabs.find((item) => item.path === this.activeTab))
+ if (!filterTabs.find((item) => item.path === this.active))
router.push(filterTabs[filterTabs.length - 1].path)
},
resetTabs() {
this.setTabs([])
this.setActiveTab('')
}
- }
+ },
+ persist: true
})
diff --git a/web/src/store/modules/theme/helpers.ts b/web/src/store/modules/theme/helpers.ts
index 8ec432e3..73b79d68 100644
--- a/web/src/store/modules/theme/helpers.ts
+++ b/web/src/store/modules/theme/helpers.ts
@@ -13,7 +13,7 @@ interface ColorAction {
}
/** 初始化主题配置 */
-export function initThemeSettings(): Theme.Setting {
+export function defaultSettings(): Theme.Setting {
const isMobile = themeSetting.isMobile || false
const darkMode = themeSetting.darkMode || false
const sider = themeSetting.sider || {
diff --git a/web/src/store/modules/theme/index.ts b/web/src/store/modules/theme/index.ts
index 61fc2248..d555afa1 100644
--- a/web/src/store/modules/theme/index.ts
+++ b/web/src/store/modules/theme/index.ts
@@ -10,7 +10,7 @@ import {
} from 'naive-ui'
import type { BuiltInGlobalTheme } from 'naive-ui/es/themes/interface'
import { defineStore } from 'pinia'
-import { getNaiveThemeOverrides, initThemeSettings } from './helpers'
+import { defaultSettings, getNaiveThemeOverrides } from './helpers'
type ThemeState = Theme.Setting
@@ -19,8 +19,8 @@ const locales: Record = {
en: { locale: enUS, dateLocale: dateEnUS }
}
-export const useThemeStore = defineStore('theme-store', {
- state: (): ThemeState => initThemeSettings(),
+export const useThemeStore = defineStore('theme', {
+ state: (): ThemeState => defaultSettings(),
getters: {
naiveThemeOverrides(): GlobalThemeOverrides {
return getNaiveThemeOverrides({
@@ -66,5 +66,6 @@ export const useThemeStore = defineStore('theme-store', {
setLocale(locale: string) {
this.locale = locale
}
- }
+ },
+ persist: true
})
diff --git a/web/src/store/modules/user/index.ts b/web/src/store/modules/user/index.ts
index ad3d9209..1b610cf9 100644
--- a/web/src/store/modules/user/index.ts
+++ b/web/src/store/modules/user/index.ts
@@ -1,56 +1,37 @@
-import user from '@/api/panel/user'
import { resetRouter } from '@/router'
import { usePermissionStore, useTabStore } from '@/store'
import { toLogin } from '@/utils'
import { defineStore } from 'pinia'
-interface UserInfo {
+export interface UserInfo {
id?: string
username?: string
role?: Array
}
export const useUserStore = defineStore('user', {
- state() {
+ state: (): UserInfo => {
return {
- userInfo: {}
- }
- },
- getters: {
- userId(): string {
- return this.userInfo.id || ''
- },
- username(): string {
- return this.userInfo.username || ''
- },
- role(): Array {
- return this.userInfo.role || []
+ id: '',
+ username: '',
+ role: []
}
},
actions: {
- async getUserInfo() {
- try {
- const res: any = await user.info()
- const { id, username, role } = res.data
- this.userInfo = { id, username, role }
- return Promise.resolve(res.data)
- } catch (error) {
- return Promise.reject(error)
- }
+ set(info: UserInfo) {
+ this.id = info.id
+ this.username = info.username
+ this.role = info.role
},
- async logout() {
- user.logout().then(() => {
- const { resetTabs } = useTabStore()
- const { resetPermission } = usePermissionStore()
- resetPermission()
- resetTabs()
- resetRouter()
- this.$reset()
- toLogin()
- })
- },
- setUserInfo(userInfo = {}) {
- this.userInfo = { ...this.userInfo, ...userInfo }
+ logout() {
+ const { resetTabs } = useTabStore()
+ const { resetPermission } = usePermissionStore()
+ resetPermission()
+ resetTabs()
+ resetRouter()
+ this.$reset()
+ toLogin()
}
- }
+ },
+ persist: true
})
diff --git a/web/src/utils/storage/index.ts b/web/src/utils/storage/index.ts
index 6992c87d..87c2c0ad 100644
--- a/web/src/utils/storage/index.ts
+++ b/web/src/utils/storage/index.ts
@@ -1,2 +1 @@
export * from './local'
-export * from './session'
diff --git a/web/src/utils/storage/local.ts b/web/src/utils/storage/local.ts
index 82ecae5d..1437862c 100644
--- a/web/src/utils/storage/local.ts
+++ b/web/src/utils/storage/local.ts
@@ -1,31 +1,28 @@
-import { decrypto, encrypto } from '@/utils'
-
interface StorageData {
- value: unknown
+ value: any
expire: number | null
}
-/** 默认缓存期限为7天 */
-const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7
+/** 默认保存期限为永久 */
+const DEFAULT_CACHE_TIME = 0
-export function setLocal(key: string, value: unknown, expire: number | null = DEFAULT_CACHE_TIME) {
+export function setLocal(key: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {
const storageData: StorageData = {
value,
- expire: expire !== null ? new Date().getTime() + expire * 1000 : null
+ expire: expire !== 0 && expire !== null ? new Date().getTime() + expire * 1000 : 0
}
- const json = encrypto(storageData)
+ const json = JSON.stringify(storageData)
window.localStorage.setItem(key, json)
}
export function getLocal(key: string) {
const json = window.localStorage.getItem(key)
if (json) {
- let storageData: StorageData | null = null
- storageData = decrypto(json)
+ const storageData = JSON.parse(json)
if (storageData) {
const { value, expire } = storageData
// 没有过期时间或者在有效期内则直接返回
- if (expire === null || expire >= Date.now()) return value as T
+ if (expire === 0 || expire >= Date.now()) return value as T
}
removeLocal(key)
return null
@@ -36,8 +33,7 @@ export function getLocal(key: string) {
export function getLocalExpire(key: string): number | null {
const json = window.localStorage.getItem(key)
if (json) {
- let storageData: StorageData | null = null
- storageData = decrypto(json)
+ const storageData = JSON.parse(json)
if (storageData) {
return storageData.expire
}
diff --git a/web/src/utils/storage/session.ts b/web/src/utils/storage/session.ts
deleted file mode 100644
index 2fc0b94a..00000000
--- a/web/src/utils/storage/session.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { decrypto, encrypto } from '@/utils'
-
-export function setSession(key: string, value: unknown) {
- const json = encrypto(value)
- sessionStorage.setItem(key, json)
-}
-
-export function getSession(key: string) {
- const json = sessionStorage.getItem(key)
- let data: T | null = null
- if (json) {
- data = decrypto(json)
- }
- return data
-}
-
-export function removeSession(key: string) {
- window.sessionStorage.removeItem(key)
-}
-
-export function clearSession() {
- window.sessionStorage.clear()
-}
diff --git a/web/src/views/login/IndexView.vue b/web/src/views/login/IndexView.vue
index 187559c1..da110ccd 100644
--- a/web/src/views/login/IndexView.vue
+++ b/web/src/views/login/IndexView.vue
@@ -28,9 +28,9 @@ if (localLoginInfo) {
loginInfo.value.password = localLoginInfo.password || ''
}
+const userStore = useUserStore()
const loging = ref(false)
const isRemember = useStorage('isRemember', false)
-const userStore = useUserStore()
async function handleLogin() {
const { username, password } = loginInfo.value
@@ -49,7 +49,8 @@ async function handleLogin() {
}
await addDynamicRoutes()
- await userStore.getUserInfo()
+ const { data } = await user.info()
+ userStore.set(data)
if (query.redirect) {
const path = query.redirect as string
Reflect.deleteProperty(query, 'redirect')
@@ -69,7 +70,8 @@ onMounted(async () => {
await user.isLogin().then(async (res) => {
if (res.data) {
await addDynamicRoutes()
- await userStore.getUserInfo()
+ const { data } = await user.info()
+ userStore.set(data)
if (query.redirect) {
const path = query.redirect as string
Reflect.deleteProperty(query, 'redirect')