Commit 26b6109ca08a28c37355474bf8593f2e2b741ef6
1 parent
6e03e050
feat: add permissionCacheType setting
Showing
15 changed files
with
123 additions
and
87 deletions
src/enums/cacheEnum.ts
src/hooks/core/useTimeout.ts
... | ... | @@ -23,7 +23,7 @@ export function useTimeoutFn(handle: Fn<any>, wait: number) { |
23 | 23 | export function useTimeoutRef(wait: number) { |
24 | 24 | const readyRef = ref(false); |
25 | 25 | |
26 | - let timer: ReturnType<typeof setTimeout> | undefined; | |
26 | + let timer: TimeoutHandle; | |
27 | 27 | function stop(): void { |
28 | 28 | readyRef.value = false; |
29 | 29 | timer && window.clearTimeout(timer); | ... | ... |
src/hooks/setting/useTransitionSetting.ts
... | ... | @@ -7,7 +7,7 @@ import { appStore } from '/@/store/modules/app'; |
7 | 7 | export function useTransitionSetting() { |
8 | 8 | const getTransitionSetting = computed(() => appStore.getProjectConfig.transitionSetting); |
9 | 9 | |
10 | - const getEnableTransition = computed(() => unref(getTransitionSetting).enable); | |
10 | + const getEnableTransition = computed(() => unref(getTransitionSetting)?.enable); | |
11 | 11 | |
12 | 12 | const getOpenNProgress = computed(() => unref(getTransitionSetting)?.openNProgress); |
13 | 13 | ... | ... |
src/hooks/web/useLockPage.ts
... | ... | @@ -5,7 +5,7 @@ import { appStore } from '/@/store/modules/app'; |
5 | 5 | import { userStore } from '/@/store/modules/user'; |
6 | 6 | |
7 | 7 | export function useLockPage() { |
8 | - let timeId: ReturnType<typeof setTimeout>; | |
8 | + let timeId: TimeoutHandle; | |
9 | 9 | |
10 | 10 | function clear(): void { |
11 | 11 | window.clearTimeout(timeId); | ... | ... |
src/layouts/default/multitabs/index.less
... | ... | @@ -40,12 +40,9 @@ |
40 | 40 | height: 12px; |
41 | 41 | font-size: 12px; |
42 | 42 | color: inherit; |
43 | - visibility: hidden; | |
44 | 43 | transition: none; |
45 | 44 | |
46 | 45 | &:hover { |
47 | - visibility: visible; | |
48 | - | |
49 | 46 | svg { |
50 | 47 | width: 0.8em; |
51 | 48 | } |
... | ... | @@ -72,10 +69,6 @@ |
72 | 69 | display: none; |
73 | 70 | } |
74 | 71 | |
75 | - .ant-tabs-close-x { | |
76 | - visibility: visible; | |
77 | - } | |
78 | - | |
79 | 72 | svg { |
80 | 73 | width: 0.7em; |
81 | 74 | fill: @white; | ... | ... |
src/layouts/default/multitabs/index.tsx
1 | +import './index.less'; | |
2 | + | |
1 | 3 | import type { TabContentProps } from './tab.data'; |
2 | 4 | import type { TabItem } from '/@/store/modules/tab'; |
3 | 5 | import type { AppRouteRecordRaw } from '/@/router/types'; |
4 | 6 | |
5 | -import { defineComponent, watch, computed, unref, toRaw } from 'vue'; | |
7 | +import { defineComponent, watch, computed, unref } from 'vue'; | |
6 | 8 | import { useRouter } from 'vue-router'; |
7 | -import router from '/@/router'; | |
8 | 9 | |
9 | 10 | import { Tabs } from 'ant-design-vue'; |
10 | 11 | import TabContent from './TabContent'; |
... | ... | @@ -18,12 +19,12 @@ import { userStore } from '/@/store/modules/user'; |
18 | 19 | |
19 | 20 | import { closeTab } from './useTabDropdown'; |
20 | 21 | import { useTabs } from '/@/hooks/web/useTabs'; |
22 | +import { initAffixTabs } from './useAffixTabs'; | |
21 | 23 | |
22 | -import './index.less'; | |
23 | 24 | export default defineComponent({ |
24 | - name: 'MultiTabs', | |
25 | + name: 'MultipleTabs', | |
25 | 26 | setup() { |
26 | - let isAddAffix = false; | |
27 | + initAffixTabs(); | |
27 | 28 | |
28 | 29 | const go = useGo(); |
29 | 30 | |
... | ... | @@ -36,11 +37,6 @@ export default defineComponent({ |
36 | 37 | watch( |
37 | 38 | () => tabStore.getLastChangeRouteState, |
38 | 39 | () => { |
39 | - if (!isAddAffix) { | |
40 | - addAffixTabs(); | |
41 | - isAddAffix = true; | |
42 | - } | |
43 | - | |
44 | 40 | const lastChangeRoute = unref(tabStore.getLastChangeRouteState); |
45 | 41 | |
46 | 42 | if (!lastChangeRoute || !userStore.getTokenState) return; |
... | ... | @@ -56,39 +52,15 @@ export default defineComponent({ |
56 | 52 | } |
57 | 53 | ); |
58 | 54 | |
59 | - /** | |
60 | - * @description: 过滤所有固定路由 | |
61 | - */ | |
62 | - function filterAffixTabs(routes: AppRouteRecordRaw[]) { | |
63 | - const tabs: TabItem[] = []; | |
64 | - routes && | |
65 | - routes.forEach((route) => { | |
66 | - if (route.meta && route.meta.affix) { | |
67 | - tabs.push(toRaw(route) as TabItem); | |
68 | - } | |
69 | - }); | |
70 | - return tabs; | |
71 | - } | |
72 | - | |
73 | - /** | |
74 | - * @description: 设置固定tabs | |
75 | - */ | |
76 | - function addAffixTabs(): void { | |
77 | - const affixTabs = filterAffixTabs((router.getRoutes() as unknown) as AppRouteRecordRaw[]); | |
78 | - for (const tab of affixTabs) { | |
79 | - tabStore.commitAddTab(tab); | |
80 | - } | |
81 | - } | |
82 | - | |
83 | 55 | // tab切换 |
84 | 56 | function handleChange(activeKey: any) { |
85 | 57 | activeKeyRef.value = activeKey; |
86 | 58 | go(activeKey, false); |
87 | 59 | } |
88 | 60 | |
89 | - // 关闭当前ab | |
61 | + // 关闭当前tab | |
90 | 62 | function handleEdit(targetKey: string) { |
91 | - // 新增操作隐藏,目前只使用删除操作 | |
63 | + // Added operation to hide, currently only use delete operation | |
92 | 64 | const index = unref(getTabsState).findIndex( |
93 | 65 | (item) => (item.fullPath || item.path) === targetKey |
94 | 66 | ); |
... | ... | @@ -107,12 +79,13 @@ export default defineComponent({ |
107 | 79 | </span> |
108 | 80 | ); |
109 | 81 | } |
82 | + | |
110 | 83 | function renderTabs() { |
111 | 84 | return unref(getTabsState).map((item: TabItem) => { |
112 | 85 | const key = item.query ? item.fullPath : item.path; |
113 | - | |
86 | + const closable = !(item && item.meta && item.meta.affix); | |
114 | 87 | return ( |
115 | - <Tabs.TabPane key={key} closable={!(item && item.meta && item.meta.affix)}> | |
88 | + <Tabs.TabPane key={key} closable={closable}> | |
116 | 89 | {{ |
117 | 90 | tab: () => <TabContent tabItem={item} />, |
118 | 91 | }} | ... | ... |
src/layouts/default/multitabs/useAffixTabs.ts
0 → 100644
1 | +import { toRaw } from 'vue'; | |
2 | +import router from '/@/router'; | |
3 | +import { AppRouteRecordRaw } from '/@/router/types'; | |
4 | +import { TabItem, tabStore } from '/@/store/modules/tab'; | |
5 | + | |
6 | +export function initAffixTabs() { | |
7 | + /** | |
8 | + * @description: Filter all fixed routes | |
9 | + */ | |
10 | + function filterAffixTabs(routes: AppRouteRecordRaw[]) { | |
11 | + const tabs: TabItem[] = []; | |
12 | + routes && | |
13 | + routes.forEach((route) => { | |
14 | + if (route.meta && route.meta.affix) { | |
15 | + tabs.push(toRaw(route) as TabItem); | |
16 | + } | |
17 | + }); | |
18 | + return tabs; | |
19 | + } | |
20 | + | |
21 | + /** | |
22 | + * @description: Set fixed tabs | |
23 | + */ | |
24 | + function addAffixTabs(): void { | |
25 | + const affixTabs = filterAffixTabs((router.getRoutes() as unknown) as AppRouteRecordRaw[]); | |
26 | + for (const tab of affixTabs) { | |
27 | + tabStore.commitAddTab(tab); | |
28 | + } | |
29 | + } | |
30 | + let isAddAffix = false; | |
31 | + if (!isAddAffix) { | |
32 | + addAffixTabs(); | |
33 | + isAddAffix = true; | |
34 | + } | |
35 | +} | ... | ... |
src/layouts/default/multitabs/useTabDropdown.ts
... | ... | @@ -203,6 +203,7 @@ export function closeTab(closedTab: TabItem | AppRouteRecordRaw) { |
203 | 203 | const getTabsState = computed(() => { |
204 | 204 | return tabStore.getTabsState; |
205 | 205 | }); |
206 | + | |
206 | 207 | const { path } = unref(currentRoute); |
207 | 208 | if (path !== closedTab.path) { |
208 | 209 | // 关闭的不是激活tab | ... | ... |
src/router/guard/index.ts
... | ... | @@ -14,9 +14,9 @@ import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel'; |
14 | 14 | |
15 | 15 | import { tabStore } from '/@/store/modules/tab'; |
16 | 16 | |
17 | +const { closeMessageOnSwitch, removeAllHttpPending } = useProjectSetting(); | |
17 | 18 | const globSetting = useGlobSetting(); |
18 | 19 | export function createGuard(router: Router) { |
19 | - const { closeMessageOnSwitch, removeAllHttpPending } = useProjectSetting(); | |
20 | 20 | let axiosCanceler: AxiosCanceler | null; |
21 | 21 | if (removeAllHttpPending) { |
22 | 22 | axiosCanceler = new AxiosCanceler(); | ... | ... |
src/settings/projectSetting.ts
1 | 1 | import type { ProjectConfig } from '/@/types/config'; |
2 | 2 | |
3 | 3 | import { MenuTypeEnum, MenuModeEnum, TriggerEnum } from '/@/enums/menuEnum'; |
4 | +import { CacheTypeEnum } from '/@/enums/cacheEnum'; | |
4 | 5 | import { ContentEnum, PermissionModeEnum, ThemeEnum, RouterTransitionEnum } from '/@/enums/appEnum'; |
5 | 6 | import { primaryColor } from '../../build/config/lessModifyVars'; |
6 | 7 | import { isProdMode } from '/@/utils/env'; |
... | ... | @@ -13,6 +14,9 @@ const setting: ProjectConfig = { |
13 | 14 | // Permission mode |
14 | 15 | permissionMode: PermissionModeEnum.ROLE, |
15 | 16 | |
17 | + // Permission-related cache is stored in sessionStorage or localStorage | |
18 | + permissionCacheType: CacheTypeEnum.LOCAL, | |
19 | + | |
16 | 20 | // color |
17 | 21 | // TODO Theme color |
18 | 22 | themeColor: primaryColor, |
... | ... | @@ -130,7 +134,7 @@ const setting: ProjectConfig = { |
130 | 134 | openPageLoading: true, |
131 | 135 | |
132 | 136 | // Whether to open the top progress bar |
133 | - openNProgress: true, | |
137 | + openNProgress: false, | |
134 | 138 | }, |
135 | 139 | |
136 | 140 | // Whether to enable KeepAlive cache is best to close during development, otherwise the cache needs to be cleared every time | ... | ... |
src/setup/App.ts
... | ... | @@ -20,6 +20,7 @@ import { |
20 | 20 | } from '/@/setup/theme'; |
21 | 21 | |
22 | 22 | import { appStore } from '/@/store/modules/app'; |
23 | +import { deepMerge } from '../utils/index'; | |
23 | 24 | |
24 | 25 | // Used to share global app instances |
25 | 26 | let app: App; |
... | ... | @@ -50,16 +51,15 @@ export function useThemeMode(mode: ThemeModeEnum) { |
50 | 51 | // Initial project configuration |
51 | 52 | export function initAppConfigStore() { |
52 | 53 | let projCfg: ProjectConfig = getLocal(PROJ_CFG_KEY) as ProjectConfig; |
53 | - if (!projCfg) { | |
54 | - projCfg = projectSetting; | |
55 | - } | |
56 | - const { | |
57 | - colorWeak, | |
58 | - grayMode, | |
59 | - headerSetting: { bgColor: headerBgColor }, | |
60 | - menuSetting: { bgColor }, | |
61 | - } = projCfg; | |
54 | + projCfg = deepMerge(projectSetting, projCfg || {}); | |
55 | + | |
62 | 56 | try { |
57 | + const { | |
58 | + colorWeak, | |
59 | + grayMode, | |
60 | + headerSetting: { bgColor: headerBgColor } = {}, | |
61 | + menuSetting: { bgColor } = {}, | |
62 | + } = projCfg; | |
63 | 63 | // if ( |
64 | 64 | // themeColor !== primaryColor && |
65 | 65 | // themeColor && | ... | ... |
src/store/modules/app.ts
... | ... | @@ -26,7 +26,7 @@ export interface LockInfo { |
26 | 26 | isLock: boolean; |
27 | 27 | } |
28 | 28 | |
29 | -let timeId: ReturnType<typeof setTimeout>; | |
29 | +let timeId: TimeoutHandle; | |
30 | 30 | const NAME = 'app'; |
31 | 31 | hotModuleUnregisterModule(NAME); |
32 | 32 | @Module({ dynamic: true, namespaced: true, store, name: NAME }) | ... | ... |
src/store/modules/user.ts
... | ... | @@ -11,7 +11,7 @@ import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper'; |
11 | 11 | |
12 | 12 | import { PageEnum } from '/@/enums/pageEnum'; |
13 | 13 | import { RoleEnum } from '/@/enums/roleEnum'; |
14 | -import { ROLES_KEY, TOKEN_KEY, USER_INFO_KEY } from '/@/enums/cacheEnum'; | |
14 | +import { CacheTypeEnum, ROLES_KEY, TOKEN_KEY, USER_INFO_KEY } from '/@/enums/cacheEnum'; | |
15 | 15 | |
16 | 16 | import { useMessage } from '/@/hooks/web/useMessage'; |
17 | 17 | |
... | ... | @@ -19,13 +19,29 @@ import router from '/@/router'; |
19 | 19 | |
20 | 20 | import { loginApi, getUserInfoById } from '/@/api/sys/user'; |
21 | 21 | |
22 | -import { setLocal, getLocal } from '/@/utils/helper/persistent'; | |
23 | -// import { FULL_PAGE_NOT_FOUND_ROUTE } from '/@/router/constant'; | |
22 | +import { setLocal, getLocal, getSession, setSession } from '/@/utils/helper/persistent'; | |
23 | +import { useProjectSetting } from '/@/hooks/setting'; | |
24 | 24 | |
25 | 25 | export type UserInfo = Omit<GetUserInfoByUserIdModel, 'roles'>; |
26 | 26 | |
27 | 27 | const NAME = 'user'; |
28 | 28 | hotModuleUnregisterModule(NAME); |
29 | + | |
30 | +const { permissionCacheType } = useProjectSetting(); | |
31 | + | |
32 | +function getCache<T>(key: string) { | |
33 | + const fn = permissionCacheType === CacheTypeEnum.LOCAL ? getLocal : getSession; | |
34 | + return fn(key) as T; | |
35 | +} | |
36 | + | |
37 | +function setCache(USER_INFO_KEY: string, info: any) { | |
38 | + if (!info) return; | |
39 | + // const fn = permissionCacheType === CacheTypeEnum.LOCAL ? setLocal : setSession; | |
40 | + setLocal(USER_INFO_KEY, info, true); | |
41 | + // TODO | |
42 | + setSession(USER_INFO_KEY, info, true); | |
43 | +} | |
44 | + | |
29 | 45 | @Module({ namespaced: true, name: NAME, dynamic: true, store }) |
30 | 46 | class User extends VuexModule { |
31 | 47 | // user info |
... | ... | @@ -38,15 +54,15 @@ class User extends VuexModule { |
38 | 54 | private roleListState: RoleEnum[] = []; |
39 | 55 | |
40 | 56 | get getUserInfoState(): UserInfo { |
41 | - return this.userInfoState || (getLocal(USER_INFO_KEY) as UserInfo) || {}; | |
57 | + return this.userInfoState || getCache<UserInfo>(USER_INFO_KEY) || {}; | |
42 | 58 | } |
43 | 59 | |
44 | 60 | get getTokenState(): string { |
45 | - return this.tokenState || (getLocal(TOKEN_KEY) as string); | |
61 | + return this.tokenState || getCache<string>(TOKEN_KEY); | |
46 | 62 | } |
47 | 63 | |
48 | 64 | get getRoleListState(): RoleEnum[] { |
49 | - return this.roleListState.length > 0 ? this.roleListState : (getLocal(ROLES_KEY) as RoleEnum[]); | |
65 | + return this.roleListState.length > 0 ? this.roleListState : getCache<RoleEnum[]>(ROLES_KEY); | |
50 | 66 | } |
51 | 67 | |
52 | 68 | @Mutation |
... | ... | @@ -59,25 +75,19 @@ class User extends VuexModule { |
59 | 75 | @Mutation |
60 | 76 | commitUserInfoState(info: UserInfo): void { |
61 | 77 | this.userInfoState = info; |
62 | - if (info) { | |
63 | - setLocal(USER_INFO_KEY, info, true); | |
64 | - } | |
78 | + setCache(USER_INFO_KEY, info); | |
65 | 79 | } |
66 | 80 | |
67 | 81 | @Mutation |
68 | 82 | commitRoleListState(roleList: RoleEnum[]): void { |
69 | 83 | this.roleListState = roleList; |
70 | - if (roleList) { | |
71 | - setLocal(ROLES_KEY, roleList, true); | |
72 | - } | |
84 | + setCache(ROLES_KEY, roleList); | |
73 | 85 | } |
74 | 86 | |
75 | 87 | @Mutation |
76 | 88 | commitTokenState(info: string): void { |
77 | 89 | this.tokenState = info; |
78 | - if (info) { | |
79 | - setLocal(TOKEN_KEY, info, true); | |
80 | - } | |
90 | + setCache(TOKEN_KEY, info); | |
81 | 91 | } |
82 | 92 | |
83 | 93 | /** | ... | ... |
src/types/config.d.ts
1 | -// 左侧菜单, 顶部菜单 | |
2 | 1 | import { MenuTypeEnum, MenuModeEnum, TriggerEnum } from '/@/enums/menuEnum'; |
3 | 2 | import { ContentEnum, PermissionModeEnum, ThemeEnum, RouterTransitionEnum } from '/@/enums/appEnum'; |
3 | +import { CacheTypeEnum } from '/@/enums/cacheEnum'; | |
4 | 4 | import type { LocaleType } from '/@/locales/types'; |
5 | 5 | |
6 | 6 | export interface MenuSetting { |
... | ... | @@ -76,6 +76,8 @@ export interface TransitionSetting { |
76 | 76 | export interface ProjectConfig { |
77 | 77 | locale: LocaleSetting; |
78 | 78 | |
79 | + permissionCacheType: CacheTypeEnum; | |
80 | + | |
79 | 81 | // 是否显示配置按钮 |
80 | 82 | showSettingButton: boolean; |
81 | 83 | // 权限模式 | ... | ... |
src/utils/helper/persistent.ts
... | ... | @@ -29,8 +29,12 @@ function initCache() { |
29 | 29 | initCache(); |
30 | 30 | |
31 | 31 | export function setLocal(key: string, value: any, immediate = false) { |
32 | - cacheStore.local[BASE_LOCAL_CACHE_KEY] = cacheStore.local[BASE_LOCAL_CACHE_KEY] || {}; | |
32 | + const local = ls.get(BASE_LOCAL_CACHE_KEY)?.[BASE_LOCAL_CACHE_KEY] || {}; | |
33 | + | |
34 | + cacheStore.local[BASE_LOCAL_CACHE_KEY] = | |
35 | + { ...local, ...cacheStore.local[BASE_LOCAL_CACHE_KEY] } || {}; | |
33 | 36 | cacheStore.local[BASE_LOCAL_CACHE_KEY][key] = value; |
37 | + | |
34 | 38 | if (immediate) { |
35 | 39 | ls.set(BASE_LOCAL_CACHE_KEY, cacheStore.local); |
36 | 40 | } |
... | ... | @@ -50,16 +54,21 @@ export function removeLocal(key: string) { |
50 | 54 | } |
51 | 55 | } |
52 | 56 | |
53 | -export function clearLocal() { | |
57 | +export function clearLocal(immediate = false) { | |
54 | 58 | cacheStore.local = {}; |
59 | + immediate && ls.remove(BASE_LOCAL_CACHE_KEY); | |
55 | 60 | } |
56 | 61 | |
57 | 62 | export function setSession(key: string, value: any, immediate = false) { |
58 | - cacheStore.session[BASE_SESSION_CACHE_KEY] = cacheStore.session[BASE_SESSION_CACHE_KEY] || {}; | |
63 | + const session = ss.get(BASE_SESSION_CACHE_KEY)?.[BASE_SESSION_CACHE_KEY] || {}; | |
64 | + | |
65 | + cacheStore.session[BASE_SESSION_CACHE_KEY] = | |
66 | + { ...session, ...cacheStore.session[BASE_SESSION_CACHE_KEY] } || {}; | |
67 | + | |
59 | 68 | cacheStore.session[BASE_SESSION_CACHE_KEY][key] = value; |
69 | + | |
60 | 70 | if (immediate) { |
61 | - const cache = cacheStore.session; | |
62 | - ss.set(BASE_SESSION_CACHE_KEY, cache); | |
71 | + ss.set(BASE_SESSION_CACHE_KEY, cacheStore.session); | |
63 | 72 | } |
64 | 73 | } |
65 | 74 | |
... | ... | @@ -77,8 +86,9 @@ export function getSession<T>(key: string): T | null { |
77 | 86 | } |
78 | 87 | } |
79 | 88 | |
80 | -export function clearSession() { | |
89 | +export function clearSession(immediate = false) { | |
81 | 90 | cacheStore.session = {}; |
91 | + immediate && ss.remove(BASE_SESSION_CACHE_KEY); | |
82 | 92 | } |
83 | 93 | |
84 | 94 | export function clearAll() { |
... | ... | @@ -86,14 +96,17 @@ export function clearAll() { |
86 | 96 | clearSession(); |
87 | 97 | } |
88 | 98 | |
99 | +export function persistentCache() { | |
100 | + const localCache = cacheStore.local; | |
101 | + const sessionCache = cacheStore.session; | |
102 | + ls.set(BASE_LOCAL_CACHE_KEY, localCache); | |
103 | + ss.set(BASE_SESSION_CACHE_KEY, sessionCache); | |
104 | +} | |
105 | + | |
89 | 106 | (() => { |
90 | 107 | // /** Write to local before closing window */ |
91 | 108 | window.addEventListener('beforeunload', () => { |
92 | - const localCache = cacheStore.local; | |
93 | - const sessionCache = cacheStore.session; | |
94 | - | |
95 | - ls.set(BASE_LOCAL_CACHE_KEY, localCache); | |
96 | - ss.set(BASE_SESSION_CACHE_KEY, sessionCache); | |
109 | + persistentCache(); | |
97 | 110 | }); |
98 | 111 | |
99 | 112 | function storageChange(e: any) { | ... | ... |