From 8042e2b044fd3e1eaca7e32ef2f2fb9cc4ae376a Mon Sep 17 00:00:00 2001 From: vince Date: Fri, 5 Jul 2024 23:15:46 +0800 Subject: [PATCH] perf: Improve the use of store in the app --- .../src/modules/mock/mock.service.ts | 11 ++- apps/web-antd/src/forward/request.ts | 5 +- apps/web-antd/src/layouts/basic.vue | 30 +++--- apps/web-antd/src/router/guard.ts | 11 ++- .../src/router/routes/modules/demos.ts | 11 +++ apps/web-antd/src/store/index.ts | 7 +- apps/web-antd/src/store/modules/access.ts | 99 +++++++++++++++++++ apps/web-antd/src/store/modules/app.ts | 20 ++++ .../src/store/modules/example.test.ts | 17 ---- apps/web-antd/src/store/modules/example.ts | 14 --- .../views/_essential/authentication/login.vue | 58 ++--------- .../src/views/dashboard/workspace/index.vue | 6 +- .../demos/access/frontend/access-test-3.vue | 13 +++ .../src/views/demos/access/frontend/index.vue | 69 ++++++++++--- .../lint-configs/eslint-config/package.json | 2 +- .../forward/stores/src/modules/access.test.ts | 22 ++--- .../forward/stores/src/modules/access.ts | 10 +- .../forward/stores/src/modules/tabbar.test.ts | 38 +++---- .../forward/stores/src/modules/tabbar.ts | 8 +- packages/@core/shared/toolkit/package.json | 2 + packages/@core/shared/toolkit/src/index.ts | 1 + .../src/generate-menu-and-routes/index.ts | 4 + packages/business/access/src/index.ts | 2 +- .../src/{authority.vue => role-authority.vue} | 6 +- packages/business/access/src/use-access.ts | 19 +--- .../layouts/src/basic/content/content.vue | 10 +- .../layouts/src/basic/header/header.vue | 4 +- .../layouts/src/basic/menu/use-extra-menu.ts | 4 +- .../layouts/src/basic/menu/use-mixed-menu.ts | 4 +- .../layouts/src/basic/tabbar/use-tabs.ts | 40 ++++---- .../layouts/src/iframe/iframe-router-view.vue | 4 +- packages/business/universal-ui/package.json | 1 + .../src/authentication/code-login.vue | 3 +- .../src/authentication/forget-password.vue | 3 +- .../src/authentication/qrcode-login.vue | 3 +- .../src/authentication/register.vue | 3 +- packages/locales/src/langs/en-US.yaml | 5 +- packages/locales/src/langs/zh-CN.yaml | 5 +- pnpm-lock.yaml | 37 +++++-- 39 files changed, 387 insertions(+), 224 deletions(-) create mode 100644 apps/web-antd/src/store/modules/access.ts create mode 100644 apps/web-antd/src/store/modules/app.ts delete mode 100644 apps/web-antd/src/store/modules/example.test.ts delete mode 100644 apps/web-antd/src/store/modules/example.ts create mode 100644 apps/web-antd/src/views/demos/access/frontend/access-test-3.vue rename packages/business/access/src/{authority.vue => role-authority.vue} (86%) diff --git a/apps/backend-mock/src/modules/mock/mock.service.ts b/apps/backend-mock/src/modules/mock/mock.service.ts index 8d2e364e3..ec800f74d 100644 --- a/apps/backend-mock/src/modules/mock/mock.service.ts +++ b/apps/backend-mock/src/modules/mock/mock.service.ts @@ -51,16 +51,25 @@ export class MockService implements OnModuleInit { // 密码哈希 const hashPassword = await bcrypt.hash('123456', 10); + await this.addItem('users', { id: 0, password: hashPassword, realName: 'Vben', - roles: ['admin'], + roles: ['super'], username: 'vben', }); + await this.addItem('users', { id: 1, password: hashPassword, + realName: 'Admin', + roles: ['admin'], + username: 'admin', + }); + await this.addItem('users', { + id: 2, + password: hashPassword, realName: 'Jack', roles: ['user'], username: 'jack', diff --git a/apps/web-antd/src/forward/request.ts b/apps/web-antd/src/forward/request.ts index 49f3dcb56..cb69ad87c 100644 --- a/apps/web-antd/src/forward/request.ts +++ b/apps/web-antd/src/forward/request.ts @@ -5,7 +5,7 @@ import type { AxiosResponse } from '@vben-core/request'; import { RequestClient, isCancelError } from '@vben-core/request'; -import { useAccessStore } from '@vben-core/stores'; +import { useCoreAccessStore } from '@vben-core/stores'; import { message } from 'ant-design-vue'; @@ -30,7 +30,8 @@ function createRequestClient() { makeAuthorization: () => { return { handler: () => { - const accessStore = useAccessStore(); + // 这里不能用 useAccessStore,因为 useAccessStore 会导致循环引用 + const accessStore = useCoreAccessStore(); return { refreshToken: `Bearer ${accessStore.getRefreshToken}`, token: `Bearer ${accessStore.getAccessToken}`, diff --git a/apps/web-antd/src/layouts/basic.vue b/apps/web-antd/src/layouts/basic.vue index a59de8719..d8bbf910a 100644 --- a/apps/web-antd/src/layouts/basic.vue +++ b/apps/web-antd/src/layouts/basic.vue @@ -4,16 +4,16 @@ import type { NotificationItem } from '@vben/widgets'; import { computed, ref } from 'vue'; import { useRouter } from 'vue-router'; +import { LOGIN_PATH } from '@vben/constants'; import { IcRoundCreditScore, MdiDriveDocument, MdiGithub } from '@vben/icons'; import { BasicLayout } from '@vben/layouts'; import { $t } from '@vben/locales'; import { openWindow } from '@vben/utils'; import { Notification, UserDropdown } from '@vben/widgets'; import { preferences } from '@vben-core/preferences'; -import { useRequest } from '@vben-core/request'; -import { useAccessStore } from '@vben-core/stores'; -import { getUserInfo } from '#/apis'; +import { resetRoutes } from '#/router'; +import { useAppStore } from '#/store'; // https://avatar.vercel.sh/vercel.svg?text=Vaa // https://avatar.vercel.sh/1 @@ -80,20 +80,22 @@ const menus = computed(() => [ }, ]); -const accessStore = useAccessStore(); +const appStore = useAppStore(); const router = useRouter(); +// // 每次刷新页面都会进行用户信息获取 +// // 如果不需要,可以删除 +// const { runAsync: runGetUserInfo } = useRequest(getUserInfo, { +// manual: true, +// }); -const { runAsync: runGetUserInfo } = useRequest(getUserInfo, { - manual: true, -}); +// runGetUserInfo().then((userInfo) => { +// accessStore.setUserInfo(userInfo); +// }); -runGetUserInfo().then((userInfo) => { - accessStore.setUserInfo(userInfo); -}); - -function handleLogout() { - accessStore.$reset(); - router.replace('/auth/login'); +async function handleLogout() { + await appStore.resetAppState(); + resetRoutes(); + router.replace(LOGIN_PATH); } function handleNoticeClear() { diff --git a/apps/web-antd/src/router/guard.ts b/apps/web-antd/src/router/guard.ts index 96c8dfc4e..cc554c3d1 100644 --- a/apps/web-antd/src/router/guard.ts +++ b/apps/web-antd/src/router/guard.ts @@ -4,12 +4,12 @@ import { LOGIN_PATH } from '@vben/constants'; import { $t } from '@vben/locales'; import { startProgress, stopProgress } from '@vben/utils'; import { preferences } from '@vben-core/preferences'; -import { useAccessStore } from '@vben-core/stores'; import { useTitle } from '@vueuse/core'; import { generateAccess } from '#/forward/access'; import { dynamicRoutes, essentialsRouteNames } from '#/router/routes'; +import { useAccessStore } from '#/store'; /** * 通用守卫配置 @@ -57,7 +57,7 @@ function setupCommonGuard(router: Router) { function setupAccessGuard(router: Router) { router.beforeEach(async (to, from) => { const accessStore = useAccessStore(); - const accessToken = accessStore.getAccessToken; + const accessToken = accessStore.accessToken; // accessToken 检查 if (!accessToken) { @@ -83,7 +83,7 @@ function setupAccessGuard(router: Router) { return to; } - const accessRoutes = accessStore.getAccessRoutes; + const accessRoutes = accessStore.accessRoutes; // 是否已经生成过动态路由 if (accessRoutes && accessRoutes.length > 0) { @@ -92,7 +92,10 @@ function setupAccessGuard(router: Router) { // 生成路由表 // 当前登录用户拥有的角色标识列表 - const userRoles = accessStore.getUserRoles; + const userInfo = + accessStore.userInfo || (await accessStore.fetchUserInfo()); + + const userRoles = userInfo.roles ?? []; // 生成菜单和路由 const { accessibleMenus, accessibleRoutes } = await generateAccess({ diff --git a/apps/web-antd/src/router/routes/modules/demos.ts b/apps/web-antd/src/router/routes/modules/demos.ts index bc5fd1dde..288d80439 100644 --- a/apps/web-antd/src/router/routes/modules/demos.ts +++ b/apps/web-antd/src/router/routes/modules/demos.ts @@ -77,6 +77,17 @@ const routes: RouteRecordRaw[] = [ title: $t('page.demos.access.access-test-2'), }, }, + { + name: 'AccessFrontendTest3', + path: 'access-test-3', + component: () => + import('#/views/demos/access/frontend/access-test-3.vue'), + meta: { + authority: ['super'], + icon: 'mdi:button-cursor', + title: $t('page.demos.access.access-test-3'), + }, + }, ], }, { diff --git a/apps/web-antd/src/store/index.ts b/apps/web-antd/src/store/index.ts index 8711a3eec..593e4fd60 100644 --- a/apps/web-antd/src/store/index.ts +++ b/apps/web-antd/src/store/index.ts @@ -2,7 +2,7 @@ import type { InitStoreOptions } from '@vben-core/stores'; import type { App } from 'vue'; -import { initStore, useAccessStore, useTabbarStore } from '@vben-core/stores'; +import { initStore } from '@vben-core/stores'; /** * @zh_CN 初始化pinia @@ -13,4 +13,7 @@ async function setupStore(app: App, options: InitStoreOptions) { app.use(pinia); } -export { setupStore, useAccessStore, useTabbarStore }; +export { setupStore }; + +export { useAccessStore } from './modules/access'; +export { useAppStore } from './modules/app'; diff --git a/apps/web-antd/src/store/modules/access.ts b/apps/web-antd/src/store/modules/access.ts new file mode 100644 index 000000000..7b50ba42e --- /dev/null +++ b/apps/web-antd/src/store/modules/access.ts @@ -0,0 +1,99 @@ +import type { MenuRecordRaw, UserInfo } from '@vben/types'; +import type { LoginAndRegisterParams } from '@vben/universal-ui'; +import type { RouteRecordRaw } from 'vue-router'; + +import { computed, ref } from 'vue'; +import { useRouter } from 'vue-router'; + +import { DEFAULT_HOME_PATH } from '@vben/constants'; +import { useCoreAccessStore } from '@vben-core/stores'; + +import { defineStore } from 'pinia'; + +import { getUserInfo, userLogin } from '#/apis'; + +export const useAccessStore = defineStore('access', () => { + const coreStoreAccess = useCoreAccessStore(); + const router = useRouter(); + const loading = ref(false); + + const accessToken = computed(() => coreStoreAccess.getAccessToken); + const userRoles = computed(() => coreStoreAccess.getUserRoles); + const userInfo = computed(() => coreStoreAccess.getUserInfo); + const accessRoutes = computed(() => coreStoreAccess.getAccessRoutes); + + function setAccessMenus(menus: MenuRecordRaw[]) { + coreStoreAccess.setAccessMenus(menus); + } + + function setAccessRoutes(routes: RouteRecordRaw[]) { + coreStoreAccess.setAccessRoutes(routes); + } + + /** + * 异步处理登录操作 + * Asynchronously handle the login process + * @param params 登录表单数据 + */ + async function authLogin( + params: LoginAndRegisterParams, + onSuccess?: () => Promise, + ) { + // 异步处理用户登录操作并获取 accessToken + let userInfo: UserInfo | null = null; + try { + loading.value = true; + const { accessToken, refreshToken } = await userLogin(params); + + // 如果成功获取到 accessToken + // If accessToken is successfully obtained + if (accessToken) { + // 将 accessToken 存储到 accessStore 中 + // Store the accessToken in accessStore + coreStoreAccess.setAccessToken(accessToken); + coreStoreAccess.setRefreshToken(refreshToken); + + // 获取用户信息并存储到 accessStore 中 + // Get user information and store it in accessStore + userInfo = await fetchUserInfo(); + + coreStoreAccess.setUserInfo(userInfo); + + onSuccess + ? await onSuccess?.() + : await router.push(userInfo.homePath || DEFAULT_HOME_PATH); + } + } finally { + loading.value = false; + } + + return { + accessToken, + userInfo, + }; + } + + async function fetchUserInfo() { + let userInfo: UserInfo | null = null; + userInfo = await getUserInfo(); + coreStoreAccess.setUserInfo(userInfo); + return userInfo; + } + + function reset() { + coreStoreAccess.$reset(); + } + + return { + accessRoutes, + accessToken, + authLogin, + fetchUserInfo, + loading, + reset, + setAccessMenus, + setAccessRoutes, + userInfo, + userRoles, + }; +}); diff --git a/apps/web-antd/src/store/modules/app.ts b/apps/web-antd/src/store/modules/app.ts new file mode 100644 index 000000000..4064ae49b --- /dev/null +++ b/apps/web-antd/src/store/modules/app.ts @@ -0,0 +1,20 @@ +import { useCoreAccessStore, useCoreTabbarStore } from '@vben-core/stores'; + +import { defineStore } from 'pinia'; + +export const useAppStore = defineStore('app', () => { + const coreStoreAccess = useCoreAccessStore(); + const coreTabbarStore = useCoreTabbarStore(); + + /** + * 重置所有 状态 + */ + async function resetAppState() { + coreStoreAccess.$reset(); + coreTabbarStore.$reset(); + } + + return { + resetAppState, + }; +}); diff --git a/apps/web-antd/src/store/modules/example.test.ts b/apps/web-antd/src/store/modules/example.test.ts deleted file mode 100644 index c8dfe6d95..000000000 --- a/apps/web-antd/src/store/modules/example.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { createPinia, setActivePinia } from 'pinia'; -import { beforeEach, describe, expect, it } from 'vitest'; - -import { useCounterStore } from './example'; - -describe('useCounterStore', () => { - beforeEach(() => { - setActivePinia(createPinia()); - }); - - it('count test', () => { - setActivePinia(createPinia()); - const counterStore = useCounterStore(); - - expect(counterStore.count).toBe(0); - }); -}); diff --git a/apps/web-antd/src/store/modules/example.ts b/apps/web-antd/src/store/modules/example.ts deleted file mode 100644 index ceb1e81be..000000000 --- a/apps/web-antd/src/store/modules/example.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { defineStore } from 'pinia'; - -export const useCounterStore = defineStore('counter', { - actions: { - increment() { - this.count++; - }, - }, - getters: { - double: (state) => state.count * 2, - }, - persist: [], - state: () => ({ count: 0 }), -}); diff --git a/apps/web-antd/src/views/_essential/authentication/login.vue b/apps/web-antd/src/views/_essential/authentication/login.vue index 1cf2290bd..45ae94e5f 100644 --- a/apps/web-antd/src/views/_essential/authentication/login.vue +++ b/apps/web-antd/src/views/_essential/authentication/login.vue @@ -1,80 +1,36 @@