Commit 0a3683a186ab55d34a12a5a3c6d794dfa1094ad4
1 parent
f5e31feb
feat: customized user home page
新增自定义的用户首页(可以每个用户都不相同)
Showing
9 changed files
with
62 additions
and
6 deletions
CHANGELOG.zh_CN.md
... | ... | @@ -2,6 +2,8 @@ |
2 | 2 | |
3 | 3 | - **NoticeList** 添加分页、超长自动省略、标题点击事件、标题删除线等功能 |
4 | 4 | - **MixSider** 优化 Mix 菜单布局时 底部折叠按钮 的样式,与其它菜单布局时的风格保持一致 |
5 | +- 可以为不同的用户指定不同的后台首页: | |
6 | + - 在`getUserInfo`接口返回的用户信息中增加`homePath`字段(可选)即可为当前用户定制首页路径 | |
5 | 7 | |
6 | 8 | ### 🐛 Bug Fixes |
7 | 9 | |
... | ... | @@ -18,6 +20,7 @@ |
18 | 20 | - 修复左侧混合菜单的悬停触发逻辑 |
19 | 21 | - 修复顶栏菜单在显示包含需要隐藏的菜单项目时出错的问题 |
20 | 22 | - 修复悬停触发模式下左侧混合菜单会在没有子菜单且被激活时直接跳转路由 |
23 | +- **Breadcrumb** 修复带有重定向的菜单点击无法跳转的问题 | |
21 | 24 | - **其它** |
22 | 25 | - 修复菜单默认折叠的配置不起作用的问题 |
23 | 26 | - 修复`safari`浏览器报错导致网站打不开 | ... | ... |
mock/sys/user.ts
... | ... | @@ -11,6 +11,7 @@ export function createFakeUserList() { |
11 | 11 | desc: 'manager', |
12 | 12 | password: '123456', |
13 | 13 | token: 'fakeToken1', |
14 | + homePath: '/dashboard/analysis', | |
14 | 15 | roles: [ |
15 | 16 | { |
16 | 17 | roleName: 'Super Admin', |
... | ... | @@ -26,6 +27,7 @@ export function createFakeUserList() { |
26 | 27 | avatar: 'https://q1.qlogo.cn/g?b=qq&nk=339449197&s=640', |
27 | 28 | desc: 'tester', |
28 | 29 | token: 'fakeToken2', |
30 | + homePath: '/dashboard/workbench', | |
29 | 31 | roles: [ |
30 | 32 | { |
31 | 33 | roleName: 'Tester', | ... | ... |
src/components/Application/src/AppLogo.vue
... | ... | @@ -17,6 +17,7 @@ |
17 | 17 | import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; |
18 | 18 | import { useDesign } from '/@/hooks/web/useDesign'; |
19 | 19 | import { PageEnum } from '/@/enums/pageEnum'; |
20 | + import { useUserStore } from '/@/store/modules/user'; | |
20 | 21 | |
21 | 22 | const props = { |
22 | 23 | /** |
... | ... | @@ -39,6 +40,7 @@ |
39 | 40 | setup(props) { |
40 | 41 | const { prefixCls } = useDesign('app-logo'); |
41 | 42 | const { getCollapsedShowTitle } = useMenuSetting(); |
43 | + const userStore = useUserStore(); | |
42 | 44 | const { title } = useGlobSetting(); |
43 | 45 | const go = useGo(); |
44 | 46 | |
... | ... | @@ -56,7 +58,7 @@ |
56 | 58 | ]); |
57 | 59 | |
58 | 60 | function goHome() { |
59 | - go(PageEnum.BASE_HOME); | |
61 | + go(userStore.getUserInfo.homePath || PageEnum.BASE_HOME); | |
60 | 62 | } |
61 | 63 | |
62 | 64 | return { | ... | ... |
src/router/guard/permissionGuard.ts
... | ... | @@ -7,8 +7,12 @@ import { useUserStoreWithOut } from '/@/store/modules/user'; |
7 | 7 | |
8 | 8 | import { PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic'; |
9 | 9 | |
10 | +import { RootRoute } from '/@/router/routes'; | |
11 | + | |
10 | 12 | const LOGIN_PATH = PageEnum.BASE_LOGIN; |
11 | 13 | |
14 | +const ROOT_PATH = RootRoute.path; | |
15 | + | |
12 | 16 | const whitePathList: PageEnum[] = [LOGIN_PATH]; |
13 | 17 | |
14 | 18 | export function createPermissionGuard(router: Router) { |
... | ... | @@ -17,7 +21,17 @@ export function createPermissionGuard(router: Router) { |
17 | 21 | router.beforeEach(async (to, from, next) => { |
18 | 22 | // Jump to the 404 page after processing the login |
19 | 23 | if (from.path === LOGIN_PATH && to.name === PAGE_NOT_FOUND_ROUTE.name) { |
20 | - next(PageEnum.BASE_HOME); | |
24 | + next(userStore.getUserInfo.homePath || PageEnum.BASE_HOME); | |
25 | + return; | |
26 | + } | |
27 | + | |
28 | + if ( | |
29 | + from.path === ROOT_PATH && | |
30 | + to.path === PageEnum.BASE_HOME && | |
31 | + userStore.getUserInfo.homePath && | |
32 | + userStore.getUserInfo.homePath !== PageEnum.BASE_HOME | |
33 | + ) { | |
34 | + next(userStore.getUserInfo.homePath); | |
21 | 35 | return; |
22 | 36 | } |
23 | 37 | ... | ... |
src/router/routes/modules/dashboard.ts
src/store/modules/multipleTab.ts
... | ... | @@ -13,6 +13,7 @@ import { getRawRoute } from '/@/utils'; |
13 | 13 | import { MULTIPLE_TABS_KEY } from '/@/enums/cacheEnum'; |
14 | 14 | |
15 | 15 | import projectSetting from '/@/settings/projectSetting'; |
16 | +import { useUserStore } from '/@/store/modules/user'; | |
16 | 17 | |
17 | 18 | export interface MultipleTabState { |
18 | 19 | cacheTabList: Set<string>; |
... | ... | @@ -181,7 +182,8 @@ export const useMultipleTabStore = defineStore({ |
181 | 182 | if (index === 0) { |
182 | 183 | // There is only one tab, then jump to the homepage, otherwise jump to the right tab |
183 | 184 | if (this.tabList.length === 1) { |
184 | - toTarget = PageEnum.BASE_HOME; | |
185 | + const userStore = useUserStore(); | |
186 | + toTarget = userStore.getUserInfo.homePath || PageEnum.BASE_HOME; | |
185 | 187 | } else { |
186 | 188 | // Jump to the right tab |
187 | 189 | const page = this.tabList[index + 1]; | ... | ... |
src/store/modules/permission.ts
... | ... | @@ -22,6 +22,7 @@ import { getMenuList } from '/@/api/sys/menu'; |
22 | 22 | import { getPermCode } from '/@/api/sys/user'; |
23 | 23 | |
24 | 24 | import { useMessage } from '/@/hooks/web/useMessage'; |
25 | +import { PageEnum } from '/@/enums/pageEnum'; | |
25 | 26 | |
26 | 27 | interface PermissionState { |
27 | 28 | // Permission code list |
... | ... | @@ -117,6 +118,32 @@ export const usePermissionStore = defineStore({ |
117 | 118 | return !ignoreRoute; |
118 | 119 | }; |
119 | 120 | |
121 | + /** | |
122 | + * @description 根据设置的首页path,修正routes中的affix标记(固定首页) | |
123 | + * */ | |
124 | + const patchHomeAffix = (routes: AppRouteRecordRaw[]) => { | |
125 | + if (!routes || routes.length === 0) return; | |
126 | + const homePath = userStore.getUserInfo.homePath || PageEnum.BASE_HOME; | |
127 | + function patcher(routes: AppRouteRecordRaw[], parentPath = '') { | |
128 | + if (parentPath) parentPath = parentPath + '/'; | |
129 | + routes.forEach((route: AppRouteRecordRaw) => { | |
130 | + const { path, children } = route; | |
131 | + const currentPath = path.startsWith('/') ? path : parentPath + path; | |
132 | + if (currentPath === homePath) { | |
133 | + route.meta = Object.assign({}, route.meta, { affix: true }); | |
134 | + throw new Error('end'); | |
135 | + } | |
136 | + children && children.length > 0 && patcher(children, currentPath); | |
137 | + }); | |
138 | + } | |
139 | + try { | |
140 | + patcher(routes); | |
141 | + } catch (e) { | |
142 | + // 已处理完毕跳出循环 | |
143 | + } | |
144 | + return; | |
145 | + }; | |
146 | + | |
120 | 147 | switch (permissionMode) { |
121 | 148 | case PermissionModeEnum.ROLE: |
122 | 149 | routes = filter(asyncRoutes, routeFilter); |
... | ... | @@ -176,6 +203,7 @@ export const usePermissionStore = defineStore({ |
176 | 203 | } |
177 | 204 | |
178 | 205 | routes.push(ERROR_LOG_ROUTE); |
206 | + patchHomeAffix(routes); | |
179 | 207 | return routes; |
180 | 208 | }, |
181 | 209 | }, | ... | ... |
src/store/modules/user.ts
... | ... | @@ -88,13 +88,15 @@ export const useUserStore = defineStore({ |
88 | 88 | |
89 | 89 | const sessionTimeout = this.sessionTimeout; |
90 | 90 | sessionTimeout && this.setSessionTimeout(false); |
91 | - !sessionTimeout && goHome && (await router.replace(PageEnum.BASE_HOME)); | |
91 | + !sessionTimeout && | |
92 | + goHome && | |
93 | + (await router.replace(userInfo.homePath || PageEnum.BASE_HOME)); | |
92 | 94 | return userInfo; |
93 | 95 | } catch (error) { |
94 | 96 | return Promise.reject(error); |
95 | 97 | } |
96 | 98 | }, |
97 | - async getUserInfoAction() { | |
99 | + async getUserInfoAction(): Promise<UserInfo> { | |
98 | 100 | const userInfo = await getUserInfo(); |
99 | 101 | const { roles } = userInfo; |
100 | 102 | const roleList = roles.map((item) => item.value) as RoleEnum[]; | ... | ... |
types/store.d.ts
1 | 1 | import { ErrorTypeEnum } from '/@/enums/exceptionEnum'; |
2 | 2 | import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum'; |
3 | +import { RoleInfo } from '/@/api/sys/model/userModel'; | |
3 | 4 | |
4 | 5 | // Lock screen information |
5 | 6 | export interface LockInfo { |
... | ... | @@ -35,6 +36,8 @@ export interface UserInfo { |
35 | 36 | realName: string; |
36 | 37 | avatar: string; |
37 | 38 | desc?: string; |
39 | + homePath?: string; | |
40 | + roles: RoleInfo[]; | |
38 | 41 | } |
39 | 42 | |
40 | 43 | export interface BeforeMiniState { | ... | ... |