Kaynağa Gözat

refactor: refactor store

Vben 3 yıl önce
ebeveyn
işleme
215d8bab38
80 değiştirilmiş dosya ile 1546 ekleme ve 1497 silme
  1. 1 0
      build/generate/generateModifyVars.ts
  2. 5 3
      index.html
  3. 6 6
      package.json
  4. 9 7
      src/components/Application/src/AppProvider.vue
  5. 2 6
      src/components/Application/src/search/AppSearchKeyItem.vue
  6. 0 7
      src/components/SimpleMenu/src/SimpleMenu.vue
  7. 56 61
      src/hooks/setting/useHeaderSetting.ts
  8. 87 85
      src/hooks/setting/useMenuSetting.ts
  9. 11 14
      src/hooks/setting/useMultipleTabSetting.ts
  10. 36 37
      src/hooks/setting/useRootSetting.ts
  11. 13 15
      src/hooks/setting/useTransitionSetting.ts
  12. 0 0
      src/hooks/web/useEcharts/echarts.ts
  13. 6 4
      src/hooks/web/useEcharts/index.ts
  14. 2 1
      src/hooks/web/useFullContent.ts
  15. 11 6
      src/hooks/web/useLockPage.ts
  16. 17 7
      src/hooks/web/usePage.ts
  17. 19 12
      src/hooks/web/usePermission.ts
  18. 74 17
      src/hooks/web/useTabs.ts
  19. 5 6
      src/layouts/default/header/components/ErrorAction.vue
  20. 6 6
      src/layouts/default/header/components/lock/LockModal.vue
  21. 3 2
      src/layouts/default/header/components/user-dropdown/index.vue
  22. 0 4
      src/layouts/default/index.vue
  23. 3 2
      src/layouts/default/menu/useLayoutMenu.ts
  24. 17 13
      src/layouts/default/setting/components/SettingFooter.vue
  25. 7 4
      src/layouts/default/setting/handler.ts
  26. 10 15
      src/layouts/default/tabs/components/FoldButton.vue
  27. 0 21
      src/layouts/default/tabs/components/QuickButton.vue
  28. 19 22
      src/layouts/default/tabs/components/TabContent.vue
  29. 7 11
      src/layouts/default/tabs/components/TabRedo.vue
  30. 8 1
      src/layouts/default/tabs/index.less
  31. 22 15
      src/layouts/default/tabs/index.vue
  32. 0 10
      src/layouts/default/tabs/types.ts
  33. 10 6
      src/layouts/default/tabs/useMultipleTabs.ts
  34. 23 28
      src/layouts/default/tabs/useTabDropdown.ts
  35. 5 4
      src/layouts/iframe/useFrameKeepAlive.ts
  36. 3 6
      src/layouts/page/index.vue
  37. 2 1
      src/locales/setupI18n.ts
  38. 7 2
      src/locales/useLocale.ts
  39. 20 12
      src/logics/error-handle/index.ts
  40. 6 4
      src/logics/initAppConfig.ts
  41. 8 5
      src/logics/theme/updateBackground.ts
  42. 7 5
      src/main.ts
  43. 0 10
      src/router/constant.ts
  44. 10 8
      src/router/guard/index.ts
  45. 7 5
      src/router/guard/pageLoadingGuard.ts
  46. 7 5
      src/router/guard/permissionGuard.ts
  47. 1 2
      src/router/guard/progressGuard.ts
  48. 12 8
      src/router/guard/stateGuard.ts
  49. 5 3
      src/router/menus/index.ts
  50. 2 3
      src/settings/designSetting.ts
  51. 0 1
      src/settings/projectSetting.ts
  52. 2 0
      src/settings/siteSetting.ts
  53. 3 10
      src/store/index.ts
  54. 92 99
      src/store/modules/app.ts
  55. 0 81
      src/store/modules/error.ts
  56. 77 0
      src/store/modules/errorLog.ts
  57. 44 33
      src/store/modules/locale.ts
  58. 52 54
      src/store/modules/lock.ts
  59. 288 0
      src/store/modules/multipleTab.ts
  60. 125 124
      src/store/modules/permission.ts
  61. 0 294
      src/store/modules/tab.ts
  62. 118 129
      src/store/modules/user.ts
  63. 0 24
      src/store/types.ts
  64. 2 3
      src/utils/cache/persistent.ts
  65. 1 0
      src/utils/env.ts
  66. 4 2
      src/utils/http/axios/index.ts
  67. 3 4
      src/views/demo/permission/CurrentPermissionMode.vue
  68. 3 2
      src/views/demo/permission/back/Btn.vue
  69. 6 4
      src/views/demo/permission/front/Btn.vue
  70. 6 4
      src/views/demo/permission/front/index.vue
  71. 3 4
      src/views/sys/error-log/DetailModal.vue
  72. 1 1
      src/views/sys/error-log/data.tsx
  73. 7 5
      src/views/sys/error-log/index.vue
  74. 21 19
      src/views/sys/lock/LockPage.vue
  75. 3 7
      src/views/sys/lock/index.vue
  76. 2 1
      src/views/sys/lock/useNow.ts
  77. 2 1
      src/views/sys/login/Login.vue
  78. 2 1
      src/views/sys/login/LoginForm.vue
  79. 44 0
      types/store.ts
  80. 38 53
      yarn.lock

+ 1 - 0
build/generate/generateModifyVars.ts

@@ -28,6 +28,7 @@ export function generateModifyVars(dark = false) {
     'success-color': '#55D187', //  Success color
     'error-color': '#ED6F6F', //  False color
     'warning-color': '#EFBD47', //   Warning color
+    'border-color-base': '#EEEEEE',
     'font-size-base': '14px', //  Main font size
     'border-radius-base': '2px', //  Component/float fillet
     'link-color': primary, //   Link color

+ 5 - 3
index.html

@@ -16,9 +16,11 @@
     <script>
       (() => {
         var htmlRoot = document.getElementById('htmlRoot');
-        const theme = window.localStorage.getItem('__APP__DARK__MODE__');
-        if (!htmlRoot || !theme) return;
-        htmlRoot.setAttribute('data-theme', theme);
+        var theme = window.localStorage.getItem('__APP__DARK__MODE__');
+        if (htmlRoot && theme) {
+          htmlRoot.setAttribute('data-theme', theme);
+          theme = htmlRoot = null;
+        }
       })();
     </script>
     <div id="app">

+ 6 - 6
package.json

@@ -32,7 +32,7 @@
   },
   "dependencies": {
     "@iconify/iconify": "^2.0.0-rc.6",
-    "@vueuse/core": "^4.8.0",
+    "@vueuse/core": "^4.8.1",
     "@zxcvbn-ts/core": "^0.3.0",
     "ant-design-vue": "^2.1.2",
     "axios": "^0.21.1",
@@ -43,6 +43,7 @@
     "mockjs": "^1.1.0",
     "nprogress": "^0.2.0",
     "path-to-regexp": "^6.2.0",
+    "pinia": "^2.0.0-alpha.12",
     "print-js": "^1.6.0",
     "qrcode": "^1.4.4",
     "sortablejs": "^1.13.0",
@@ -52,8 +53,6 @@
     "vue-i18n": "9.0.0",
     "vue-router": "^4.0.6",
     "vue-types": "^3.0.2",
-    "vuex": "^4.0.0",
-    "vuex-module-decorators": "^1.0.1",
     "xlsx": "^0.16.9"
   },
   "devDependencies": {
@@ -81,7 +80,7 @@
     "conventional-changelog-cli": "^2.1.1",
     "cross-env": "^7.0.3",
     "dotenv": "^8.2.0",
-    "eslint": "^7.23.0",
+    "eslint": "^7.24.0",
     "eslint-config-prettier": "^8.1.0",
     "eslint-define-config": "^1.0.7",
     "eslint-plugin-prettier": "^3.3.1",
@@ -115,13 +114,14 @@
     "vite-plugin-style-import": "^0.9.2",
     "vite-plugin-svg-icons": "^0.4.1",
     "vite-plugin-theme": "^0.6.3",
-    "vite-plugin-windicss": "0.12.5",
+    "vite-plugin-windicss": "0.13.1",
     "vue-eslint-parser": "^7.6.0"
   },
   "resolutions": {
     "//": "Used to install imagemin dependencies, because imagemin may not be installed in China.If it is abroad, you can delete it",
     "bin-wrapper": "npm:bin-wrapper-china",
-    "rollup": "^2.44.0"
+    "rollup": "^2.45.1",
+    "esbuild": "^0.11.6"
   },
   "repository": {
     "type": "git",

+ 9 - 7
src/components/Application/src/AppProvider.vue

@@ -3,22 +3,24 @@
 
   import { createAppProviderContext } from './useAppContext';
 
-  import designSetting from '/@/settings/designSetting';
+  import { prefixCls } from '/@/settings/designSetting';
   import { createBreakpointListen } from '/@/hooks/event/useBreakpoint';
   import { propTypes } from '/@/utils/propTypes';
-  import { appStore } from '/@/store/modules/app';
+  import { useAppStore } from '/@/store/modules/app';
   import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
 
   export default defineComponent({
     name: 'AppProvider',
     inheritAttrs: false,
     props: {
-      prefixCls: propTypes.string.def(designSetting.prefixCls),
+      prefixCls: propTypes.string.def(prefixCls),
     },
     setup(props, { slots }) {
       const isMobile = ref(false);
       const isSetState = ref(false);
 
+      const appStore = useAppStore();
+
       createBreakpointListen(({ screenMap, sizeEnum, width }) => {
         const lgWidth = screenMap.get(sizeEnum.LG);
         if (lgWidth) {
@@ -42,20 +44,20 @@
                 split: menuSplit,
               },
             } = appStore.getProjectConfig;
-            appStore.commitProjectConfigState({
+            appStore.setProjectConfig({
               menuSetting: {
                 type: MenuTypeEnum.SIDEBAR,
                 mode: MenuModeEnum.INLINE,
                 split: false,
               },
             });
-            appStore.commitBeforeMiniState({ menuMode, menuCollapsed, menuType, menuSplit });
+            appStore.setBeforeMiniInfo({ menuMode, menuCollapsed, menuType, menuSplit });
           }
         } else {
           if (unref(isSetState)) {
             isSetState.value = false;
-            const { menuMode, menuCollapsed, menuType, menuSplit } = appStore.getBeforeMiniState;
-            appStore.commitProjectConfigState({
+            const { menuMode, menuCollapsed, menuType, menuSplit } = appStore.getBeforeMiniInfo;
+            appStore.setProjectConfig({
               menuSetting: {
                 type: menuType,
                 mode: menuMode,

+ 2 - 6
src/components/Application/src/search/AppSearchKeyItem.vue

@@ -1,17 +1,13 @@
 <template>
   <span :class="$attrs.class">
-    <Icon :icon="icon" />
+    <g-icon :icon="icon" />
   </span>
 </template>
 <script lang="ts">
   import { defineComponent } from 'vue';
-  import { Icon } from '/@/components/Icon';
-  import { propTypes } from '/@/utils/propTypes';
-
   export default defineComponent({
-    components: { Icon },
     props: {
-      icon: propTypes.string,
+      icon: String,
     },
   });
 </script>

+ 0 - 7
src/components/SimpleMenu/src/SimpleMenu.vue

@@ -6,7 +6,6 @@
     :class="prefixCls"
     :activeSubMenuNames="activeSubMenuNames"
     @select="handleSelect"
-    @open-change="handleOpenChange"
   >
     <template v-for="item in items" :key="item.path">
       <SimpleSubMenu
@@ -140,17 +139,11 @@
         menuState.activeName = key;
       }
 
-      function handleOpenChange(v) {
-        console.log('======================');
-        console.log(v);
-        console.log('======================');
-      }
       return {
         prefixCls,
         getBindValues,
         handleSelect,
         getOpenKeys,
-        handleOpenChange,
         ...toRefs(menuState),
       };
     },

+ 56 - 61
src/hooks/setting/useHeaderSetting.ts

@@ -2,94 +2,89 @@ import type { HeaderSetting } from '/#/config';
 
 import { computed, unref } from 'vue';
 
-import { appStore } from '/@/store/modules/app';
+import { useAppStore } from '/@/store/modules/app';
 
 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
 import { useRootSetting } from '/@/hooks/setting/useRootSetting';
 import { useFullContent } from '/@/hooks/web/useFullContent';
-
 import { MenuModeEnum } from '/@/enums/menuEnum';
 
-const { getFullContent } = useFullContent();
-const {
-  getMenuMode,
-  getSplit,
-  getShowHeaderTrigger,
-  getIsSidebarType,
-  getIsMixSidebar,
-  getIsTopMenu,
-} = useMenuSetting();
-const { getShowBreadCrumb, getShowLogo } = useRootSetting();
-
-const getShowMixHeaderRef = computed(() => !unref(getIsSidebarType) && unref(getShowHeader));
+export function useHeaderSetting() {
+  const { getFullContent } = useFullContent();
+  const appStore = useAppStore();
 
-const getShowFullHeaderRef = computed(() => {
-  return (
-    !unref(getFullContent) &&
-    unref(getShowMixHeaderRef) &&
-    unref(getShowHeader) &&
-    !unref(getIsTopMenu) &&
-    !unref(getIsMixSidebar)
-  );
-});
+  const getShowFullHeaderRef = computed(() => {
+    return (
+      !unref(getFullContent) &&
+      unref(getShowMixHeaderRef) &&
+      unref(getShowHeader) &&
+      !unref(getIsTopMenu) &&
+      !unref(getIsMixSidebar)
+    );
+  });
 
-const getShowInsetHeaderRef = computed(() => {
-  const need = !unref(getFullContent) && unref(getShowHeader);
-  return (
-    (need && !unref(getShowMixHeaderRef)) ||
-    (need && unref(getIsTopMenu)) ||
-    (need && unref(getIsMixSidebar))
-  );
-});
+  const getUnFixedAndFull = computed(() => !unref(getFixed) && !unref(getShowFullHeaderRef));
 
-// Get header configuration
-const getHeaderSetting = computed(() => appStore.getProjectConfig.headerSetting);
+  const getShowInsetHeaderRef = computed(() => {
+    const need = !unref(getFullContent) && unref(getShowHeader);
+    return (
+      (need && !unref(getShowMixHeaderRef)) ||
+      (need && unref(getIsTopMenu)) ||
+      (need && unref(getIsMixSidebar))
+    );
+  });
 
-const getShowDoc = computed(() => unref(getHeaderSetting).showDoc);
+  const {
+    getMenuMode,
+    getSplit,
+    getShowHeaderTrigger,
+    getIsSidebarType,
+    getIsMixSidebar,
+    getIsTopMenu,
+  } = useMenuSetting();
+  const { getShowBreadCrumb, getShowLogo } = useRootSetting();
 
-const getHeaderTheme = computed(() => unref(getHeaderSetting).theme);
+  const getShowMixHeaderRef = computed(() => !unref(getIsSidebarType) && unref(getShowHeader));
 
-const getShowHeader = computed(() => unref(getHeaderSetting).show);
+  const getShowDoc = computed(() => appStore.getHeaderSetting.showDoc);
 
-const getFixed = computed(() => unref(getHeaderSetting).fixed);
+  const getHeaderTheme = computed(() => appStore.getHeaderSetting.theme);
 
-const getHeaderBgColor = computed(() => unref(getHeaderSetting).bgColor);
+  const getShowHeader = computed(() => appStore.getHeaderSetting.show);
 
-const getShowSearch = computed(() => unref(getHeaderSetting).showSearch);
+  const getFixed = computed(() => appStore.getHeaderSetting.fixed);
 
-const getUseLockPage = computed(() => unref(getHeaderSetting).useLockPage);
+  const getHeaderBgColor = computed(() => appStore.getHeaderSetting.bgColor);
 
-const getShowFullScreen = computed(() => unref(getHeaderSetting).showFullScreen);
+  const getShowSearch = computed(() => appStore.getHeaderSetting.showSearch);
 
-const getShowNotice = computed(() => unref(getHeaderSetting).showNotice);
+  const getUseLockPage = computed(() => appStore.getHeaderSetting.useLockPage);
 
-const getUnFixedAndFull = computed(() => !unref(getFixed) && !unref(getShowFullHeaderRef));
+  const getShowFullScreen = computed(() => appStore.getHeaderSetting.showFullScreen);
 
-const getShowBread = computed(() => {
-  return (
-    unref(getMenuMode) !== MenuModeEnum.HORIZONTAL && unref(getShowBreadCrumb) && !unref(getSplit)
-  );
-});
+  const getShowNotice = computed(() => appStore.getHeaderSetting.showNotice);
 
-const getShowHeaderLogo = computed(() => {
-  return unref(getShowLogo) && !unref(getIsSidebarType) && !unref(getIsMixSidebar);
-});
+  const getShowBread = computed(() => {
+    return (
+      unref(getMenuMode) !== MenuModeEnum.HORIZONTAL && unref(getShowBreadCrumb) && !unref(getSplit)
+    );
+  });
 
-const getShowContent = computed(() => {
-  return unref(getShowBread) || unref(getShowHeaderTrigger);
-});
+  const getShowHeaderLogo = computed(() => {
+    return unref(getShowLogo) && !unref(getIsSidebarType) && !unref(getIsMixSidebar);
+  });
 
-// Set header configuration
-function setHeaderSetting(headerSetting: Partial<HeaderSetting>): void {
-  appStore.commitProjectConfigState({ headerSetting });
-}
+  const getShowContent = computed(() => {
+    return unref(getShowBread) || unref(getShowHeaderTrigger);
+  });
 
-export function useHeaderSetting() {
+  // Set header configuration
+  function setHeaderSetting(headerSetting: Partial<HeaderSetting>) {
+    appStore.setProjectConfig({ headerSetting });
+  }
   return {
     setHeaderSetting,
 
-    getHeaderSetting,
-
     getShowDoc,
     getShowSearch,
     getHeaderTheme,

+ 87 - 85
src/hooks/setting/useMenuSetting.ts

@@ -2,7 +2,7 @@ import type { MenuSetting } from '/#/config';
 
 import { computed, unref, ref } from 'vue';
 
-import { appStore } from '/@/store/modules/app';
+import { useAppStore } from '/@/store/modules/app';
 
 import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum';
 import { MenuModeEnum, MenuTypeEnum, TriggerEnum } from '/@/enums/menuEnum';
@@ -10,127 +10,129 @@ import { useFullContent } from '/@/hooks/web/useFullContent';
 
 const mixSideHasChildren = ref(false);
 
-// Get menu configuration
-const getMenuSetting = computed(() => appStore.getProjectConfig.menuSetting);
+export function useMenuSetting() {
+  const { getFullContent: fullContent } = useFullContent();
+  const appStore = useAppStore();
+
+  const getShowSidebar = computed(() => {
+    return (
+      unref(getSplit) ||
+      (unref(getShowMenu) && unref(getMenuMode) !== MenuModeEnum.HORIZONTAL && !unref(fullContent))
+    );
+  });
 
-const getCollapsed = computed(() => unref(getMenuSetting).collapsed);
+  const getCollapsed = computed(() => appStore.getMenuSetting.collapsed);
 
-const getMenuType = computed(() => unref(getMenuSetting).type);
+  const getMenuType = computed(() => appStore.getMenuSetting.type);
 
-const getMenuMode = computed(() => unref(getMenuSetting).mode);
+  const getMenuMode = computed(() => appStore.getMenuSetting.mode);
 
-const getMenuFixed = computed(() => unref(getMenuSetting).fixed);
+  const getMenuFixed = computed(() => appStore.getMenuSetting.fixed);
 
-const getShowMenu = computed(() => unref(getMenuSetting).show);
+  const getShowMenu = computed(() => appStore.getMenuSetting.show);
 
-const getMenuHidden = computed(() => unref(getMenuSetting).hidden);
+  const getMenuHidden = computed(() => appStore.getMenuSetting.hidden);
 
-const getMenuWidth = computed(() => unref(getMenuSetting).menuWidth);
+  const getMenuWidth = computed(() => appStore.getMenuSetting.menuWidth);
 
-const getTrigger = computed(() => unref(getMenuSetting).trigger);
+  const getTrigger = computed(() => appStore.getMenuSetting.trigger);
 
-const getMenuTheme = computed(() => unref(getMenuSetting).theme);
+  const getMenuTheme = computed(() => appStore.getMenuSetting.theme);
 
-const getSplit = computed(() => unref(getMenuSetting).split);
+  const getSplit = computed(() => appStore.getMenuSetting.split);
 
-const getMenuBgColor = computed(() => unref(getMenuSetting).bgColor);
+  const getMenuBgColor = computed(() => appStore.getMenuSetting.bgColor);
 
-const getMixSideTrigger = computed(() => unref(getMenuSetting).mixSideTrigger);
+  const getMixSideTrigger = computed(() => appStore.getMenuSetting.mixSideTrigger);
 
-const getCanDrag = computed(() => unref(getMenuSetting).canDrag);
+  const getCanDrag = computed(() => appStore.getMenuSetting.canDrag);
 
-const getAccordion = computed(() => unref(getMenuSetting).accordion);
+  const getAccordion = computed(() => appStore.getMenuSetting.accordion);
 
-const getMixSideFixed = computed(() => unref(getMenuSetting).mixSideFixed);
+  const getMixSideFixed = computed(() => appStore.getMenuSetting.mixSideFixed);
 
-const getTopMenuAlign = computed(() => unref(getMenuSetting).topMenuAlign);
+  const getTopMenuAlign = computed(() => appStore.getMenuSetting.topMenuAlign);
 
-const getCloseMixSidebarOnChange = computed(() => unref(getMenuSetting).closeMixSidebarOnChange);
+  const getCloseMixSidebarOnChange = computed(
+    () => appStore.getMenuSetting.closeMixSidebarOnChange
+  );
 
-const getIsSidebarType = computed(() => unref(getMenuType) === MenuTypeEnum.SIDEBAR);
+  const getIsSidebarType = computed(() => unref(getMenuType) === MenuTypeEnum.SIDEBAR);
 
-const getIsTopMenu = computed(() => unref(getMenuType) === MenuTypeEnum.TOP_MENU);
+  const getIsTopMenu = computed(() => unref(getMenuType) === MenuTypeEnum.TOP_MENU);
 
-const getCollapsedShowTitle = computed(() => unref(getMenuSetting).collapsedShowTitle);
+  const getCollapsedShowTitle = computed(() => appStore.getMenuSetting.collapsedShowTitle);
 
-const getShowTopMenu = computed(() => {
-  return unref(getMenuMode) === MenuModeEnum.HORIZONTAL || unref(getSplit);
-});
+  const getShowTopMenu = computed(() => {
+    return unref(getMenuMode) === MenuModeEnum.HORIZONTAL || unref(getSplit);
+  });
 
-const getShowHeaderTrigger = computed(() => {
-  if (unref(getMenuType) === MenuTypeEnum.TOP_MENU || !unref(getShowMenu) || unref(getMenuHidden)) {
-    return false;
-  }
+  const getShowHeaderTrigger = computed(() => {
+    if (
+      unref(getMenuType) === MenuTypeEnum.TOP_MENU ||
+      !unref(getShowMenu) ||
+      unref(getMenuHidden)
+    ) {
+      return false;
+    }
 
-  return unref(getTrigger) === TriggerEnum.HEADER;
-});
+    return unref(getTrigger) === TriggerEnum.HEADER;
+  });
 
-const getIsHorizontal = computed(() => {
-  return unref(getMenuMode) === MenuModeEnum.HORIZONTAL;
-});
+  const getIsHorizontal = computed(() => {
+    return unref(getMenuMode) === MenuModeEnum.HORIZONTAL;
+  });
 
-const getIsMixSidebar = computed(() => {
-  return unref(getMenuType) === MenuTypeEnum.MIX_SIDEBAR;
-});
+  const getIsMixSidebar = computed(() => {
+    return unref(getMenuType) === MenuTypeEnum.MIX_SIDEBAR;
+  });
 
-const getIsMixMode = computed(() => {
-  return unref(getMenuMode) === MenuModeEnum.INLINE && unref(getMenuType) === MenuTypeEnum.MIX;
-});
+  const getIsMixMode = computed(() => {
+    return unref(getMenuMode) === MenuModeEnum.INLINE && unref(getMenuType) === MenuTypeEnum.MIX;
+  });
 
-const getRealWidth = computed(() => {
-  if (unref(getIsMixSidebar)) {
-    return unref(getCollapsed) && !unref(getMixSideFixed)
-      ? unref(getMiniWidthNumber)
-      : unref(getMenuWidth);
-  }
-  return unref(getCollapsed) ? unref(getMiniWidthNumber) : unref(getMenuWidth);
-});
-
-const getMiniWidthNumber = computed(() => {
-  const { collapsedShowTitle } = unref(getMenuSetting);
-  return collapsedShowTitle ? SIDE_BAR_SHOW_TIT_MINI_WIDTH : SIDE_BAR_MINI_WIDTH;
-});
-
-const getCalcContentWidth = computed(() => {
-  const width =
-    unref(getIsTopMenu) || !unref(getShowMenu) || (unref(getSplit) && unref(getMenuHidden))
-      ? 0
-      : unref(getIsMixSidebar)
-      ? (unref(getCollapsed) ? SIDE_BAR_MINI_WIDTH : SIDE_BAR_SHOW_TIT_MINI_WIDTH) +
-        (unref(getMixSideFixed) && unref(mixSideHasChildren) ? unref(getRealWidth) : 0)
-      : unref(getRealWidth);
-
-  return `calc(100% - ${unref(width)}px)`;
-});
-
-const { getFullContent: fullContent } = useFullContent();
-
-const getShowSidebar = computed(() => {
-  return (
-    unref(getSplit) ||
-    (unref(getShowMenu) && unref(getMenuMode) !== MenuModeEnum.HORIZONTAL && !unref(fullContent))
-  );
-});
+  const getRealWidth = computed(() => {
+    if (unref(getIsMixSidebar)) {
+      return unref(getCollapsed) && !unref(getMixSideFixed)
+        ? unref(getMiniWidthNumber)
+        : unref(getMenuWidth);
+    }
+    return unref(getCollapsed) ? unref(getMiniWidthNumber) : unref(getMenuWidth);
+  });
 
-// Set menu configuration
-function setMenuSetting(menuSetting: Partial<MenuSetting>): void {
-  appStore.commitProjectConfigState({ menuSetting });
-}
+  const getMiniWidthNumber = computed(() => {
+    const { collapsedShowTitle } = appStore.getMenuSetting;
+    return collapsedShowTitle ? SIDE_BAR_SHOW_TIT_MINI_WIDTH : SIDE_BAR_MINI_WIDTH;
+  });
+
+  const getCalcContentWidth = computed(() => {
+    const width =
+      unref(getIsTopMenu) || !unref(getShowMenu) || (unref(getSplit) && unref(getMenuHidden))
+        ? 0
+        : unref(getIsMixSidebar)
+        ? (unref(getCollapsed) ? SIDE_BAR_MINI_WIDTH : SIDE_BAR_SHOW_TIT_MINI_WIDTH) +
+          (unref(getMixSideFixed) && unref(mixSideHasChildren) ? unref(getRealWidth) : 0)
+        : unref(getRealWidth);
 
-function toggleCollapsed() {
-  setMenuSetting({
-    collapsed: !unref(getCollapsed),
+    return `calc(100% - ${unref(width)}px)`;
   });
-}
 
-export function useMenuSetting() {
+  // Set menu configuration
+  function setMenuSetting(menuSetting: Partial<MenuSetting>): void {
+    appStore.setProjectConfig({ menuSetting });
+  }
+
+  function toggleCollapsed() {
+    setMenuSetting({
+      collapsed: !unref(getCollapsed),
+    });
+  }
   return {
     setMenuSetting,
 
     toggleCollapsed,
 
     getMenuFixed,
-    getMenuSetting,
     getRealWidth,
     getMenuType,
     getMenuMode,

+ 11 - 14
src/hooks/setting/useMultipleTabSetting.ts

@@ -1,28 +1,25 @@
 import type { MultiTabsSetting } from '/#/config';
 
-import { computed, unref } from 'vue';
+import { computed } from 'vue';
 
-import { appStore } from '/@/store/modules/app';
+import { useAppStore } from '/@/store/modules/app';
 
-const getMultipleTabSetting = computed(() => appStore.getProjectConfig.multiTabsSetting);
-
-const getShowMultipleTab = computed(() => unref(getMultipleTabSetting).show);
+export function useMultipleTabSetting() {
+  const appStore = useAppStore();
 
-const getShowQuick = computed(() => unref(getMultipleTabSetting).showQuick);
+  const getShowMultipleTab = computed(() => appStore.getMultiTabsSetting.show);
 
-const getShowRedo = computed(() => unref(getMultipleTabSetting).showRedo);
+  const getShowQuick = computed(() => appStore.getMultiTabsSetting.showQuick);
 
-const getShowFold = computed(() => unref(getMultipleTabSetting).showFold);
+  const getShowRedo = computed(() => appStore.getMultiTabsSetting.showRedo);
 
-function setMultipleTabSetting(multiTabsSetting: Partial<MultiTabsSetting>) {
-  appStore.commitProjectConfigState({ multiTabsSetting });
-}
+  const getShowFold = computed(() => appStore.getMultiTabsSetting.showFold);
 
-export function useMultipleTabSetting() {
+  function setMultipleTabSetting(multiTabsSetting: Partial<MultiTabsSetting>) {
+    appStore.setProjectConfig({ multiTabsSetting });
+  }
   return {
     setMultipleTabSetting,
-
-    getMultipleTabSetting,
     getShowMultipleTab,
     getShowQuick,
     getShowRedo,

+ 36 - 37
src/hooks/setting/useRootSetting.ts

@@ -1,71 +1,71 @@
 import type { ProjectConfig } from '/#/config';
 
-import { computed, unref } from 'vue';
+import { computed } from 'vue';
 
-import { appStore } from '/@/store/modules/app';
-import { ContentEnum } from '/@/enums/appEnum';
-import { ThemeEnum } from '../../enums/appEnum';
+import { useAppStore } from '/@/store/modules/app';
+import { ContentEnum, ThemeEnum } from '/@/enums/appEnum';
 
 type RootSetting = Omit<
   ProjectConfig,
   'locale' | 'headerSetting' | 'menuSetting' | 'multiTabsSetting'
 >;
 
-const getRootSetting = computed((): RootSetting => appStore.getProjectConfig);
+export function useRootSetting() {
+  const appStore = useAppStore();
 
-const getPageLoading = computed(() => appStore.getPageLoading);
+  const getPageLoading = computed(() => appStore.getPageLoading);
 
-const getOpenKeepAlive = computed(() => unref(getRootSetting).openKeepAlive);
+  const getOpenKeepAlive = computed(() => appStore.getProjectConfig.openKeepAlive);
 
-const getSettingButtonPosition = computed(() => unref(getRootSetting).settingButtonPosition);
+  const getSettingButtonPosition = computed(() => appStore.getProjectConfig.settingButtonPosition);
 
-const getCanEmbedIFramePage = computed(() => unref(getRootSetting).canEmbedIFramePage);
+  const getCanEmbedIFramePage = computed(() => appStore.getProjectConfig.canEmbedIFramePage);
 
-const getPermissionMode = computed(() => unref(getRootSetting).permissionMode);
+  const getPermissionMode = computed(() => appStore.getProjectConfig.permissionMode);
 
-const getShowLogo = computed(() => unref(getRootSetting).showLogo);
+  const getShowLogo = computed(() => appStore.getProjectConfig.showLogo);
 
-const getContentMode = computed(() => unref(getRootSetting).contentMode);
+  const getContentMode = computed(() => appStore.getProjectConfig.contentMode);
 
-const getUseOpenBackTop = computed(() => unref(getRootSetting).useOpenBackTop);
+  const getUseOpenBackTop = computed(() => appStore.getProjectConfig.useOpenBackTop);
 
-const getShowSettingButton = computed(() => unref(getRootSetting).showSettingButton);
+  const getShowSettingButton = computed(() => appStore.getProjectConfig.showSettingButton);
 
-const getUseErrorHandle = computed(() => unref(getRootSetting).useErrorHandle);
+  const getUseErrorHandle = computed(() => appStore.getProjectConfig.useErrorHandle);
 
-const getShowFooter = computed(() => unref(getRootSetting).showFooter);
+  const getShowFooter = computed(() => appStore.getProjectConfig.showFooter);
 
-const getShowBreadCrumb = computed(() => unref(getRootSetting).showBreadCrumb);
+  const getShowBreadCrumb = computed(() => appStore.getProjectConfig.showBreadCrumb);
 
-const getThemeColor = computed(() => unref(getRootSetting).themeColor);
+  const getThemeColor = computed(() => appStore.getProjectConfig.themeColor);
 
-const getShowBreadCrumbIcon = computed(() => unref(getRootSetting).showBreadCrumbIcon);
+  const getShowBreadCrumbIcon = computed(() => appStore.getProjectConfig.showBreadCrumbIcon);
 
-const getFullContent = computed(() => unref(getRootSetting).fullContent);
+  const getFullContent = computed(() => appStore.getProjectConfig.fullContent);
 
-const getColorWeak = computed(() => unref(getRootSetting).colorWeak);
+  const getColorWeak = computed(() => appStore.getProjectConfig.colorWeak);
 
-const getGrayMode = computed(() => unref(getRootSetting).grayMode);
+  const getGrayMode = computed(() => appStore.getProjectConfig.grayMode);
 
-const getLockTime = computed(() => unref(getRootSetting).lockTime);
+  const getLockTime = computed(() => appStore.getProjectConfig.lockTime);
 
-const getShowDarkModeToggle = computed(() => unref(getRootSetting).showDarkModeToggle);
+  const getShowDarkModeToggle = computed(() => appStore.getProjectConfig.showDarkModeToggle);
 
-const getDarkMode = computed(() => appStore.getDarkMode);
+  const getDarkMode = computed(() => appStore.getDarkMode);
 
-const getLayoutContentMode = computed(() =>
-  unref(getRootSetting).contentMode === ContentEnum.FULL ? ContentEnum.FULL : ContentEnum.FIXED
-);
+  const getLayoutContentMode = computed(() =>
+    appStore.getProjectConfig.contentMode === ContentEnum.FULL
+      ? ContentEnum.FULL
+      : ContentEnum.FIXED
+  );
 
-function setRootSetting(setting: Partial<RootSetting>) {
-  appStore.commitProjectConfigState(setting);
-}
+  function setRootSetting(setting: Partial<RootSetting>) {
+    appStore.setProjectConfig(setting);
+  }
 
-function setDarkMode(mode: ThemeEnum) {
-  appStore.commitDarkMode(mode);
-}
-
-export function useRootSetting() {
+  function setDarkMode(mode: ThemeEnum) {
+    appStore.setDarkMode(mode);
+  }
   return {
     setRootSetting,
 
@@ -73,7 +73,6 @@ export function useRootSetting() {
     getFullContent,
     getColorWeak,
     getGrayMode,
-    getRootSetting,
     getLayoutContentMode,
     getPageLoading,
     getOpenKeepAlive,

+ 13 - 15
src/hooks/setting/useTransitionSetting.ts

@@ -1,30 +1,28 @@
 import type { TransitionSetting } from '/#/config';
 
-import { computed, unref } from 'vue';
+import { computed } from 'vue';
 
-import { appStore } from '/@/store/modules/app';
+import { useAppStore } from '/@/store/modules/app';
 
-const getTransitionSetting = computed(() => appStore.getProjectConfig.transitionSetting);
-
-const getEnableTransition = computed(() => unref(getTransitionSetting)?.enable);
+export function useTransitionSetting() {
+  const appStore = useAppStore();
 
-const getOpenNProgress = computed(() => unref(getTransitionSetting)?.openNProgress);
+  const getEnableTransition = computed(() => appStore.getTransitionSetting?.enable);
 
-const getOpenPageLoading = computed((): boolean => {
-  return !!unref(getTransitionSetting)?.openPageLoading;
-});
+  const getOpenNProgress = computed(() => appStore.getTransitionSetting?.openNProgress);
 
-const getBasicTransition = computed(() => unref(getTransitionSetting)?.basicTransition);
+  const getOpenPageLoading = computed((): boolean => {
+    return !!appStore.getTransitionSetting?.openPageLoading;
+  });
 
-function setTransitionSetting(transitionSetting: Partial<TransitionSetting>) {
-  appStore.commitProjectConfigState({ transitionSetting });
-}
+  const getBasicTransition = computed(() => appStore.getTransitionSetting?.basicTransition);
 
-export function useTransitionSetting() {
+  function setTransitionSetting(transitionSetting: Partial<TransitionSetting>) {
+    appStore.setProjectConfig({ transitionSetting });
+  }
   return {
     setTransitionSetting,
 
-    getTransitionSetting,
     getEnableTransition,
     getOpenNProgress,
     getOpenPageLoading,

+ 0 - 0
src/plugins/echarts/index.ts → src/hooks/web/useEcharts/echarts.ts


+ 6 - 4
src/hooks/web/useECharts.ts → src/hooks/web/useEcharts/index.ts

@@ -1,13 +1,15 @@
+import type { EChartsOption } from 'echarts';
+import type { Ref } from 'vue';
+
 import { useTimeoutFn } from '/@/hooks/core/useTimeout';
 import { tryOnUnmounted } from '@vueuse/core';
-import { unref, Ref, nextTick, watch, computed, ref } from 'vue';
-import type { EChartsOption } from 'echarts';
+import { unref, nextTick, watch, computed, ref } from 'vue';
 import { useDebounce } from '/@/hooks/core/useDebounce';
 import { useEventListener } from '/@/hooks/event/useEventListener';
 import { useBreakpoint } from '/@/hooks/event/useBreakpoint';
 
-import echarts from '/@/plugins/echarts';
-import { useRootSetting } from '../setting/useRootSetting';
+import echarts from './echarts';
+import { useRootSetting } from '/@/hooks/setting/useRootSetting';
 
 export function useECharts(
   elRef: Ref<HTMLDivElement>,

+ 2 - 1
src/hooks/web/useFullContent.ts

@@ -1,6 +1,6 @@
 import { computed, unref } from 'vue';
 
-import { appStore } from '/@/store/modules/app';
+import { useAppStore } from '/@/store/modules/app';
 
 import router from '/@/router';
 
@@ -8,6 +8,7 @@ import router from '/@/router';
  * @description: Full screen display content
  */
 export const useFullContent = () => {
+  const appStore = useAppStore();
   const { currentRoute } = router;
 
   // Whether to display the content in full screen without displaying the menu

+ 11 - 6
src/hooks/web/useLockPage.ts

@@ -1,13 +1,18 @@
 import { computed, onUnmounted, unref, watchEffect } from 'vue';
 import { useThrottle } from '/@/hooks/core/useThrottle';
 
-import { appStore } from '/@/store/modules/app';
-import { lockStore } from '/@/store/modules/lock';
-import { userStore } from '/@/store/modules/user';
+import { useAppStore } from '/@/store/modules/app';
+import { useLockStore } from '/@/store/modules/lock';
+
+import { useUserStore } from '/@/store/modules/user';
 import { useRootSetting } from '../setting/useRootSetting';
 
 export function useLockPage() {
   const { getLockTime } = useRootSetting();
+  const lockStore = useLockStore();
+  const userStore = useUserStore();
+  const appStore = useAppStore();
+
   let timeId: TimeoutHandle;
 
   function clear(): void {
@@ -16,7 +21,7 @@ export function useLockPage() {
 
   function resetCalcLockTimeout(): void {
     // not login
-    if (!userStore.getTokenState) {
+    if (!userStore.getToken) {
       clear();
       return;
     }
@@ -33,14 +38,14 @@ export function useLockPage() {
   }
 
   function lockPage(): void {
-    lockStore.commitLockInfoState({
+    lockStore.setLockInfo({
       isLock: true,
       pwd: undefined,
     });
   }
 
   watchEffect((onClean) => {
-    if (userStore.getTokenState) {
+    if (userStore.getToken) {
       resetCalcLockTimeout();
     } else {
       clear();

+ 17 - 7
src/hooks/web/usePage.ts

@@ -1,10 +1,10 @@
-import type { RouteLocationRaw } from 'vue-router';
+import type { RouteLocationRaw, Router } from 'vue-router';
 
 import { PageEnum } from '/@/enums/pageEnum';
 import { isString } from '/@/utils/is';
 import { unref } from 'vue';
 
-import router from '/@/router';
+import { useRouter } from 'vue-router';
 
 export type RouteLocationRawEx = Omit<RouteLocationRaw, 'path'> & { path: PageEnum };
 
@@ -13,10 +13,16 @@ function handleError(e: Error) {
 }
 
 // page switch
-export function useGo() {
-  const { push, replace } = router;
+export function useGo(_router?: Router) {
+  let router;
+  if (!_router) {
+    router = useRouter();
+  }
+  const { push, replace } = _router || router;
   function go(opt: PageEnum | RouteLocationRawEx | string = PageEnum.BASE_HOME, isReplace = false) {
-    if (!opt) return;
+    if (!opt) {
+      return;
+    }
     if (isString(opt)) {
       isReplace ? replace(opt).catch(handleError) : push(opt).catch(handleError);
     } else {
@@ -30,8 +36,12 @@ export function useGo() {
 /**
  * @description: redo current page
  */
-export const useRedo = () => {
-  const { push, currentRoute } = router;
+export const useRedo = (_router?: Router) => {
+  let router;
+  if (!_router) {
+    router = useRouter();
+  }
+  const { push, currentRoute } = _router || router;
   const { query, params } = currentRoute.value;
   function redo(): Promise<boolean> {
     return new Promise((resolve) => {

+ 19 - 12
src/hooks/web/usePermission.ts

@@ -1,8 +1,8 @@
 import type { RouteRecordRaw } from 'vue-router';
 
-import { appStore } from '/@/store/modules/app';
-import { permissionStore } from '/@/store/modules/permission';
-import { userStore } from '/@/store/modules/user';
+import { useAppStore } from '/@/store/modules/app';
+import { usePermissionStore } from '/@/store/modules/permission';
+import { useUserStore } from '/@/store/modules/user';
 
 import { useTabs } from './useTabs';
 
@@ -15,15 +15,20 @@ import { RoleEnum } from '/@/enums/roleEnum';
 
 import { intersection } from 'lodash-es';
 import { isArray } from '/@/utils/is';
-import { tabStore } from '/@/store/modules/tab';
+import { useMultipleTabStore } from '/@/store/modules/multipleTab';
 
 // User permissions related operations
 export function usePermission() {
+  const userStore = useUserStore();
+  const appStore = useAppStore();
+  const permissionStore = usePermissionStore();
+  const { closeAll } = useTabs(router);
+
   /**
    * Change permission mode
    */
   async function togglePermissionMode() {
-    appStore.commitProjectConfigState({
+    appStore.setProjectConfig({
       permissionMode:
         projectSetting.permissionMode === PermissionModeEnum.BACK
           ? PermissionModeEnum.ROLE
@@ -37,14 +42,14 @@ export function usePermission() {
    * @param id
    */
   async function resume(id?: string | number) {
-    tabStore.commitClearCache();
+    const tabStore = useMultipleTabStore();
+    tabStore.clearCacheTabs();
     resetRouter();
     const routes = await permissionStore.buildRoutesAction(id);
     routes.forEach((route) => {
       router.addRoute((route as unknown) as RouteRecordRaw);
     });
-    permissionStore.commitLastBuildMenuTimeState();
-    const { closeAll } = useTabs();
+    permissionStore.setLastBuildMenuTime();
     closeAll();
   }
 
@@ -53,22 +58,24 @@ export function usePermission() {
    */
   function hasPermission(value?: RoleEnum | RoleEnum[] | string | string[], def = true): boolean {
     const permMode = projectSetting.permissionMode;
+
     if (PermissionModeEnum.ROLE === permMode) {
       // Visible by default
       if (!value) {
         return def;
       }
       if (!isArray(value)) {
-        return userStore.getRoleListState?.includes(value as RoleEnum);
+        return userStore.getRoleList?.includes(value as RoleEnum);
       }
-      return (intersection(value, userStore.getRoleListState) as RoleEnum[]).length > 0;
+      return (intersection(value, userStore.getRoleList) as RoleEnum[]).length > 0;
     }
+
     if (PermissionModeEnum.BACK === permMode) {
       // Visible by default
       if (!value) {
         return def;
       }
-      const allCodeList = permissionStore.getPermCodeListState;
+      const allCodeList = permissionStore.getPermCodeList;
       if (!isArray(value)) {
         return allCodeList.includes(value as string);
       }
@@ -90,7 +97,7 @@ export function usePermission() {
     if (!isArray(roles)) {
       roles = [roles];
     }
-    userStore.commitRoleListState(roles);
+    userStore.setRoleList(roles);
     await resume();
   }
 

+ 74 - 17
src/hooks/web/useTabs.ts

@@ -1,28 +1,85 @@
-import { tabStore } from '/@/store/modules/tab';
-import { appStore } from '/@/store/modules/app';
-import type { RouteLocationNormalized } from 'vue-router';
+import type { RouteLocationNormalized, Router } from 'vue-router';
 
-export function useTabs() {
-  function canIUseFn(): boolean {
-    const { multiTabsSetting: { show } = {} } = appStore.getProjectConfig;
+import { useRouter } from 'vue-router';
+import { unref } from 'vue';
+
+import { useMultipleTabStore } from '/@/store/modules/multipleTab';
+import { useAppStore } from '/@/store/modules/app';
+
+enum TableActionEnum {
+  REFRESH,
+  CLOSE_ALL,
+  CLOSE_LEFT,
+  CLOSE_RIGHT,
+  CLOSE_OTHER,
+  CLOSE_CURRENT,
+  CLOSE,
+}
+
+export function useTabs(_router: Router) {
+  const appStore = useAppStore();
+
+  function canIUseTabs(): boolean {
+    const { show } = appStore.getMultiTabsSetting;
     if (!show) {
       throw new Error('The multi-tab page is currently not open, please open it in the settings!');
     }
     return !!show;
   }
 
+  const tabStore = useMultipleTabStore();
+  const router = _router || useRouter();
+
+  const { currentRoute } = router;
+
+  function getCurrentTab() {
+    const route = unref(currentRoute);
+    return tabStore.getTabList.find((item) => item.path === route.path)!;
+  }
+
+  async function handleTabAction(action: TableActionEnum, tab?: RouteLocationNormalized) {
+    const canIUse = canIUseTabs;
+    if (!canIUse) {
+      return;
+    }
+    const currentTab = getCurrentTab();
+    switch (action) {
+      case TableActionEnum.REFRESH:
+        await tabStore.refreshPage(router);
+        break;
+
+      case TableActionEnum.CLOSE_ALL:
+        await tabStore.closeAllTab(router);
+        break;
+
+      case TableActionEnum.CLOSE_LEFT:
+        await tabStore.closeLeftTabs(currentTab, router);
+        break;
+
+      case TableActionEnum.CLOSE_RIGHT:
+        await tabStore.closeRightTabs(currentTab, router);
+        break;
+
+      case TableActionEnum.CLOSE_OTHER:
+        await tabStore.closeOtherTabs(currentTab, router);
+        break;
+
+      case TableActionEnum.CLOSE_CURRENT:
+      case TableActionEnum.CLOSE:
+        await tabStore.closeTab(tab || currentTab, router);
+        break;
+    }
+  }
+
   return {
-    refreshPage: async () => {
-      if (canIUseFn()) {
-        await tabStore.commitRedoPage();
-      }
+    refreshPage: () => handleTabAction(TableActionEnum.REFRESH),
+    closeAll: () => handleTabAction(TableActionEnum.CLOSE_ALL),
+    closeLeft: () => handleTabAction(TableActionEnum.CLOSE_LEFT),
+    closeRight: () => handleTabAction(TableActionEnum.CLOSE_RIGHT),
+    closeOther: () => handleTabAction(TableActionEnum.CLOSE_OTHER),
+    closeCurrent: () => handleTabAction(TableActionEnum.CLOSE_CURRENT),
+    close: (tab?: RouteLocationNormalized) => {
+      handleTabAction(TableActionEnum.CLOSE, tab);
     },
-    closeAll: () => canIUseFn() && tabStore.closeAllTabAction(),
-    closeLeft: () => canIUseFn() && tabStore.closeLeftTabAction(tabStore.getCurrentTab),
-    closeRight: () => canIUseFn() && tabStore.closeRightTabAction(tabStore.getCurrentTab),
-    closeOther: () => canIUseFn() && tabStore.closeOtherTabAction(tabStore.getCurrentTab),
-    closeCurrent: () => canIUseFn() && tabStore.closeTabAction(tabStore.getCurrentTab),
-    close: (tab?: RouteLocationNormalized) =>
-      canIUseFn() && tabStore.closeTabAction(tab || tabStore.getCurrentTab),
   };
 }

+ 5 - 6
src/layouts/default/header/components/ErrorAction.vue

@@ -5,7 +5,7 @@
     :mouseEnterDelay="0.5"
     @click="handleToErrorList"
   >
-    <Badge :count="getCount" :offset="[0, 10]" dot :overflowCount="99">
+    <Badge :count="getCount" :offset="[0, 10]" :overflowCount="99">
       <Icon icon="ion:bug-outline" />
     </Badge>
   </Tooltip>
@@ -16,7 +16,7 @@
   import Icon from '/@/components/Icon';
 
   import { useI18n } from '/@/hooks/web/useI18n';
-  import { errorStore } from '/@/store/modules/error';
+  import { useErrorLogStore } from '/@/store/modules/errorLog';
   import { PageEnum } from '/@/enums/pageEnum';
 
   import { useRouter } from 'vue-router';
@@ -28,14 +28,13 @@
     setup() {
       const { t } = useI18n();
       const { push } = useRouter();
+      const errorLogStore = useErrorLogStore();
 
-      const getCount = computed(() => {
-        return errorStore.getErrorListCountState;
-      });
+      const getCount = computed(() => errorLogStore.getErrorLogListCount);
 
       function handleToErrorList() {
         push(PageEnum.ERROR_LOG_PAGE).then(() => {
-          errorStore.commitErrorListCountState(0);
+          errorLogStore.setErrorLogListCount(0);
         });
       }
 

+ 6 - 6
src/layouts/default/header/components/lock/LockModal.vue

@@ -31,8 +31,8 @@
   import { BasicModal, useModalInner } from '/@/components/Modal/index';
   import { BasicForm, useForm } from '/@/components/Form/index';
 
-  import { userStore } from '/@/store/modules/user';
-  import { lockStore } from '/@/store/modules/lock';
+  import { useUserStore } from '/@/store/modules/user';
+  import { useLockStore } from '/@/store/modules/lock';
   import headerImg from '/@/assets/images/header.jpg';
   export default defineComponent({
     name: 'LockModal',
@@ -41,10 +41,10 @@
     setup() {
       const { t } = useI18n();
       const { prefixCls } = useDesign('header-lock-modal');
+      const userStore = useUserStore();
+      const lockStore = useLockStore();
 
-      const getRealName = computed(() => {
-        return userStore.getUserInfoState?.realName;
-      });
+      const getRealName = computed(() => userStore.getUserInfo?.realName);
       const [register, { closeModal }] = useModalInner();
 
       const [registerForm, { validateFields, resetFields }] = useForm({
@@ -64,7 +64,7 @@
         const password: string | undefined = values.password;
         closeModal();
 
-        lockStore.commitLockInfoState({
+        lockStore.setLockInfo({
           isLock: true,
           pwd: password,
         });

+ 3 - 2
src/layouts/default/header/components/user-dropdown/index.vue

@@ -41,7 +41,7 @@
 
   import { DOC_URL } from '/@/settings/siteSetting';
 
-  import { userStore } from '/@/store/modules/user';
+  import { useUserStore } from '/@/store/modules/user';
   import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
   import { useI18n } from '/@/hooks/web/useI18n';
   import { useDesign } from '/@/hooks/web/useDesign';
@@ -71,9 +71,10 @@
       const { prefixCls } = useDesign('header-user-dropdown');
       const { t } = useI18n();
       const { getShowDoc } = useHeaderSetting();
+      const userStore = useUserStore();
 
       const getUserInfo = computed(() => {
-        const { realName = '', desc } = userStore.getUserInfoState || {};
+        const { realName = '', desc } = userStore.getUserInfo || {};
         return { realName, desc };
       });
 

+ 0 - 4
src/layouts/default/index.vue

@@ -42,13 +42,9 @@
     },
     setup() {
       const { prefixCls } = useDesign('default-layout');
-
       const { getIsMobile } = useAppInject();
-
       const { getShowFullHeaderRef } = useHeaderSetting();
-
       const { getShowSidebar, getIsMixSidebar } = useMenuSetting();
-
       const layoutClass = computed(() => ({ 'ant-layout-has-sider': unref(getIsMixSidebar) }));
 
       return {

+ 3 - 2
src/layouts/default/menu/useLayoutMenu.ts

@@ -9,7 +9,7 @@ import { useThrottle } from '/@/hooks/core/useThrottle';
 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
 
 import { getChildrenMenus, getCurrentParentPath, getMenus, getShallowMenus } from '/@/router/menus';
-import { permissionStore } from '/@/store/modules/permission';
+import { usePermissionStore } from '/@/store/modules/permission';
 import { useAppInject } from '/@/hooks/web/useAppInject';
 
 export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
@@ -17,6 +17,7 @@ export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
   const menusRef = ref<Menu[]>([]);
   const { currentRoute } = useRouter();
   const { getIsMobile } = useAppInject();
+  const permissionStore = usePermissionStore();
   const { setMenuSetting, getIsHorizontal, getSplit } = useMenuSetting();
 
   const [throttleHandleSplitLeftMenu] = useThrottle(handleSplitLeftMenu, 50);
@@ -55,7 +56,7 @@ export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
 
   // Menu changes
   watch(
-    [() => permissionStore.getLastBuildMenuTimeState, () => permissionStore.getBackMenuListState],
+    [() => permissionStore.getLastBuildMenuTime, () => permissionStore.getBackMenuList],
     () => {
       genMenus();
     },

+ 17 - 13
src/layouts/default/setting/components/SettingFooter.vue

@@ -21,32 +21,36 @@
 
   import { CopyOutlined, RedoOutlined } from '@ant-design/icons-vue';
 
-  import { appStore } from '/@/store/modules/app';
-  import { permissionStore } from '/@/store/modules/permission';
-  import { tabStore } from '/@/store/modules/tab';
-  import { userStore } from '/@/store/modules/user';
+  import { useAppStore } from '/@/store/modules/app';
+  import { usePermissionStore } from '/@/store/modules/permission';
+  import { useMultipleTabStore } from '/@/store/modules/multipleTab';
+  import { useUserStore } from '/@/store/modules/user';
 
   import { useDesign } from '/@/hooks/web/useDesign';
   import { useI18n } from '/@/hooks/web/useI18n';
   import { useMessage } from '/@/hooks/web/useMessage';
   import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
-  import { useRootSetting } from '/@/hooks/setting/useRootSetting';
 
   import { updateColorWeak } from '/@/logics/theme/updateColorWeak';
   import { updateGrayMode } from '/@/logics/theme/updateGrayMode';
-
   import defaultSetting from '/@/settings/projectSetting';
+
   export default defineComponent({
     name: 'SettingFooter',
     components: { CopyOutlined, RedoOutlined },
     setup() {
-      const { getRootSetting } = useRootSetting();
+      const permissionStore = usePermissionStore();
       const { prefixCls } = useDesign('setting-footer');
       const { t } = useI18n();
       const { createSuccessModal, createMessage } = useMessage();
+      const tabStore = useMultipleTabStore();
+      const userStore = useUserStore();
+      const appStore = useAppStore();
 
       function handleCopy() {
-        const { isSuccessRef } = useCopyToClipboard(JSON.stringify(unref(getRootSetting), null, 2));
+        const { isSuccessRef } = useCopyToClipboard(
+          JSON.stringify(unref(appStore.getProjectConfig), null, 2)
+        );
         unref(isSuccessRef) &&
           createSuccessModal({
             title: t('layout.setting.operatingTitle'),
@@ -55,7 +59,7 @@
       }
       function handleResetSetting() {
         try {
-          appStore.commitProjectConfigState(defaultSetting);
+          appStore.setProjectConfig(defaultSetting);
           const { colorWeak, grayMode } = defaultSetting;
           // updateTheme(themeColor);
           updateColorWeak(colorWeak);
@@ -68,10 +72,10 @@
 
       function handleClearAndRedo() {
         localStorage.clear();
-        appStore.resumeAllState();
-        permissionStore.commitResetState();
-        tabStore.commitResetState();
-        userStore.commitResetState();
+        appStore.resetAllState();
+        permissionStore.resetState();
+        tabStore.resetState();
+        userStore.resetState();
         location.reload();
       }
       return {

+ 7 - 4
src/layouts/default/setting/handler.ts

@@ -3,15 +3,16 @@ import { updateHeaderBgColor, updateSidebarBgColor } from '/@/logics/theme/updat
 import { updateColorWeak } from '/@/logics/theme/updateColorWeak';
 import { updateGrayMode } from '/@/logics/theme/updateGrayMode';
 
-import { appStore } from '/@/store/modules/app';
+import { useAppStore } from '/@/store/modules/app';
 import { ProjectConfig } from '/#/config';
 import { changeTheme } from '/@/logics/theme';
 import { updateDarkTheme } from '/@/logics/theme/dark';
 import { useRootSetting } from '/@/hooks/setting/useRootSetting';
 
 export function baseHandler(event: HandlerEnum, value: any) {
+  const appStore = useAppStore();
   const config = handler(event, value);
-  appStore.commitProjectConfigState(config);
+  appStore.setProjectConfig(config);
   if (event === HandlerEnum.CHANGE_THEME) {
     updateHeaderBgColor();
     updateSidebarBgColor();
@@ -19,6 +20,8 @@ export function baseHandler(event: HandlerEnum, value: any) {
 }
 
 export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConfig> {
+  const appStore = useAppStore();
+
   const { getThemeColor, getDarkMode } = useRootSetting();
   switch (event) {
     case HandlerEnum.CHANGE_LAYOUT:
@@ -50,7 +53,7 @@ export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConf
       }
       updateDarkTheme(value);
 
-      return { darkMode: value };
+      return {};
 
     case HandlerEnum.MENU_HAS_DRAG:
       return { menuSetting: { canDrag: value } };
@@ -97,7 +100,7 @@ export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConf
 
     // ============transition==================
     case HandlerEnum.OPEN_PAGE_LOADING:
-      appStore.commitPageLoadingState(false);
+      appStore.setPageLoading(false);
       return { transitionSetting: { openPageLoading: value } };
 
     case HandlerEnum.ROUTER_TRANSITION:

+ 10 - 15
src/layouts/default/tabs/components/FoldButton.vue

@@ -5,38 +5,33 @@
 </template>
 <script lang="ts">
   import { defineComponent, unref, computed } from 'vue';
+  import { Icon } from '/@/components/Icon';
+
   import { useDesign } from '/@/hooks/web/useDesign';
   import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
   import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
 
-  import Icon from '/@/components/Icon';
-
   export default defineComponent({
     name: 'FoldButton',
     components: { Icon },
-
     setup() {
       const { prefixCls } = useDesign('multiple-tabs-content');
       const { getShowMenu, setMenuSetting } = useMenuSetting();
       const { getShowHeader, setHeaderSetting } = useHeaderSetting();
 
-      const getIsUnFold = computed(() => {
-        return !unref(getShowMenu) && !unref(getShowHeader);
-      });
+      const getIsUnFold = computed(() => !unref(getShowMenu) && !unref(getShowHeader));
 
-      const getIcon = computed(() => {
-        return unref(getIsUnFold) ? 'codicon:screen-normal' : 'codicon:screen-full';
-      });
+      const getIcon = computed(() =>
+        unref(getIsUnFold) ? 'codicon:screen-normal' : 'codicon:screen-full'
+      );
 
       function handleFold() {
-        const isScale = !unref(getShowMenu) && !unref(getShowHeader);
+        const isUnFold = unref(getIsUnFold);
         setMenuSetting({
-          show: isScale,
-          hidden: !isScale,
-        });
-        setHeaderSetting({
-          show: isScale,
+          show: isUnFold,
+          hidden: !isUnFold,
         });
+        setHeaderSetting({ show: isUnFold });
       }
 
       return { prefixCls, getIcon, handleFold };

+ 0 - 21
src/layouts/default/tabs/components/QuickButton.vue

@@ -1,21 +0,0 @@
-<template>
-  <TabContent :type="TabContentEnum.EXTRA_TYPE" :tabItem="$route" />
-</template>
-<script lang="ts">
-  import { defineComponent } from 'vue';
-
-  import { TabContentEnum } from '../types';
-
-  import TabContent from './TabContent.vue';
-  export default defineComponent({
-    name: 'QuickButton',
-    components: {
-      TabContent,
-    },
-    setup() {
-      return {
-        TabContentEnum,
-      };
-    },
-  });
-</script>

+ 19 - 22
src/layouts/default/tabs/components/TabContent.vue

@@ -1,9 +1,8 @@
 <template>
   <Dropdown :dropMenuList="getDropMenuList" :trigger="getTrigger" @menuEvent="handleMenuEvent">
-    <div :class="`${prefixCls}__info`" @contextmenu="handleContext" v-if="isTabs">
+    <div :class="`${prefixCls}__info`" @contextmenu="handleContext" v-if="getIsTabs">
       <span class="ml-1">{{ getTitle }}</span>
     </div>
-
     <span :class="`${prefixCls}__extra-quick`" v-else @click="handleContext">
       <Icon icon="ion:chevron-down" />
     </span>
@@ -11,18 +10,18 @@
 </template>
 <script lang="ts">
   import type { PropType } from 'vue';
+  import type { RouteLocationNormalized } from 'vue-router';
 
-  import { defineComponent, computed } from 'vue';
+  import { defineComponent, computed, unref } from 'vue';
   import { Dropdown } from '/@/components/Dropdown/index';
-  import Icon from '/@/components/Icon';
+  import { Icon } from '/@/components/Icon';
 
-  import { TabContentProps, TabContentEnum } from '../types';
+  import { TabContentProps } from '../types';
 
   import { useDesign } from '/@/hooks/web/useDesign';
-  import { useTabDropdown } from '../useTabDropdown';
   import { useI18n } from '/@/hooks/web/useI18n';
+  import { useTabDropdown } from '../useTabDropdown';
 
-  import { RouteLocationNormalized } from 'vue-router';
   export default defineComponent({
     name: 'TabContent',
     components: { Dropdown, Icon },
@@ -31,11 +30,7 @@
         type: Object as PropType<RouteLocationNormalized>,
         default: null,
       },
-
-      type: {
-        type: Number as PropType<TabContentEnum>,
-        default: TabContentEnum.TAB_TYPE,
-      },
+      isExtra: Boolean,
     },
     setup(props) {
       const { prefixCls } = useDesign('multiple-tabs-content');
@@ -43,27 +38,29 @@
 
       const getTitle = computed(() => {
         const { tabItem: { meta } = {} } = props;
-        return meta && t(meta.title);
+        return meta && t(meta.title as string);
       });
 
-      const {
-        getDropMenuList,
-        handleMenuEvent,
-        handleContextMenu,
-        getTrigger,
-        isTabs,
-      } = useTabDropdown(props as TabContentProps);
+      const getIsTabs = computed(() => !props.isExtra);
+
+      const getTrigger = computed(() => (unref(getIsTabs) ? ['contextmenu'] : ['click']));
 
-      function handleContext(e: ChangeEvent) {
+      const { getDropMenuList, handleMenuEvent, handleContextMenu } = useTabDropdown(
+        props as TabContentProps,
+        getIsTabs
+      );
+
+      function handleContext(e) {
         props.tabItem && handleContextMenu(props.tabItem)(e);
       }
+
       return {
         prefixCls,
         getDropMenuList,
         handleMenuEvent,
         handleContext,
         getTrigger,
-        isTabs,
+        getIsTabs,
         getTitle,
       };
     },

+ 7 - 11
src/layouts/default/tabs/components/TabRedo.vue

@@ -1,26 +1,22 @@
 <template>
-  <Tooltip :title="t('common.redo')" placement="bottom" :mouseEnterDelay="0.5">
-    <span :class="`${prefixCls}__extra-redo`" @click="handleRedo">
-      <RedoOutlined :spin="loading" />
-    </span>
-  </Tooltip>
+  <span :class="`${prefixCls}__extra-redo`" @click="handleRedo">
+    <RedoOutlined :spin="loading" />
+  </span>
 </template>
 <script lang="ts">
   import { defineComponent, ref } from 'vue';
   import { RedoOutlined } from '@ant-design/icons-vue';
   import { useDesign } from '/@/hooks/web/useDesign';
-  import { Tooltip } from 'ant-design-vue';
-  import { useI18n } from '/@/hooks/web/useI18n';
   import { useTabs } from '/@/hooks/web/useTabs';
 
   export default defineComponent({
     name: 'TabRedo',
-    components: { RedoOutlined, Tooltip },
+    components: { RedoOutlined },
 
     setup() {
       const loading = ref(false);
+
       const { prefixCls } = useDesign('multiple-tabs-content');
-      const { t } = useI18n();
       const { refreshPage } = useTabs();
 
       async function handleRedo() {
@@ -29,9 +25,9 @@
         setTimeout(() => {
           loading.value = false;
           // Animation execution time
-        }, 1000);
+        }, 1200);
       }
-      return { prefixCls, t, handleRedo, loading };
+      return { prefixCls, handleRedo, loading };
     },
   });
 </script>

+ 8 - 1
src/layouts/default/tabs/index.less

@@ -8,13 +8,20 @@ html[data-theme='dark'] {
   }
 }
 
+html[data-theme='light'] {
+  .@{prefix-cls} {
+    .ant-tabs-tab:not(.ant-tabs-tab-active) {
+      border: 1px solid #d9d9d9 !important;
+    }
+  }
+}
+
 .@{prefix-cls} {
   z-index: 10;
   height: @multiple-height + 2;
   line-height: @multiple-height + 2;
   background: @component-background;
   border-bottom: 1px solid @border-color-base;
-  box-shadow: 0 1px 2px 0 rgba(29, 35, 41, 0.05);
 
   .ant-tabs-small {
     height: @multiple-height;

+ 22 - 15
src/layouts/default/tabs/index.vue

@@ -20,26 +20,26 @@
 
       <template #tabBarExtraContent v-if="getShowRedo || getShowQuick">
         <TabRedo v-if="getShowRedo" />
-        <QuickButton v-if="getShowQuick" />
+        <TabContent isExtra :tabItem="$route" v-if="getShowQuick" />
         <FoldButton v-if="getShowFold" />
       </template>
     </Tabs>
   </div>
 </template>
 <script lang="ts">
+  import type { RouteLocationNormalized } from 'vue-router';
+
   import { defineComponent, computed, unref, ref } from 'vue';
 
   import { Tabs } from 'ant-design-vue';
   import TabContent from './components/TabContent.vue';
-  import QuickButton from './components/QuickButton.vue';
   import FoldButton from './components/FoldButton.vue';
   import TabRedo from './components/TabRedo.vue';
-  import type { RouteLocationNormalized } from 'vue-router';
 
   import { useGo } from '/@/hooks/web/usePage';
 
-  import { tabStore } from '/@/store/modules/tab';
-  import { userStore } from '/@/store/modules/user';
+  import { useMultipleTabStore } from '/@/store/modules/multipleTab';
+  import { useUserStore } from '/@/store/modules/user';
 
   import { initAffixTabs, useTabsDrag } from './useMultipleTabs';
   import { useDesign } from '/@/hooks/web/useDesign';
@@ -48,13 +48,12 @@
   import { REDIRECT_NAME } from '/@/router/constant';
   import { listenerRouteChange } from '/@/logics/mitt/routeChange';
 
-  import router from '/@/router';
+  import { useRouter } from 'vue-router';
 
   export default defineComponent({
     name: 'MultipleTabs',
     components: {
-      QuickButton,
-      TabRedo: TabRedo,
+      TabRedo,
       FoldButton,
       Tabs,
       TabPane: Tabs.TabPane,
@@ -65,12 +64,16 @@
       const activeKeyRef = ref('');
 
       useTabsDrag(affixTextList);
+      const tabStore = useMultipleTabStore();
+      const userStore = useUserStore();
+      const router = useRouter();
+
       const { prefixCls } = useDesign('multiple-tabs');
       const go = useGo();
       const { getShowQuick, getShowRedo, getShowFold } = useMultipleTabSetting();
 
       const getTabsState = computed(() => {
-        return tabStore.getTabsState.filter((item) => !item.meta?.hideTab);
+        return tabStore.getTabList.filter((item) => !item.meta?.hideTab);
       });
 
       const unClose = computed(() => unref(getTabsState).length === 1);
@@ -86,10 +89,11 @@
 
       listenerRouteChange((route) => {
         const { name } = route;
-        if (name === REDIRECT_NAME || !route || !userStore.getTokenState) return;
+        if (name === REDIRECT_NAME || !route || !userStore.getToken) {
+          return;
+        }
 
         const { path, fullPath, meta = {} } = route;
-
         const { currentActiveMenu, hideTab } = meta;
         const isHide = !hideTab ? null : currentActiveMenu;
         const p = isHide || fullPath || path;
@@ -101,10 +105,11 @@
           const findParentRoute = router
             .getRoutes()
             .find((item) => item.path === currentActiveMenu);
+
           findParentRoute &&
-            tabStore.addTabAction((findParentRoute as unknown) as RouteLocationNormalized);
+            tabStore.addTab((findParentRoute as unknown) as RouteLocationNormalized);
         } else {
-          tabStore.addTabAction(unref(route));
+          tabStore.addTab(unref(route));
         }
       });
 
@@ -116,9 +121,11 @@
       // Close the current tab
       function handleEdit(targetKey: string) {
         // Added operation to hide, currently only use delete operation
-        if (unref(unClose)) return;
+        if (unref(unClose)) {
+          return;
+        }
 
-        tabStore.closeTabByKeyAction(targetKey);
+        tabStore.closeTabByKey(targetKey, router);
       }
       return {
         prefixCls,

+ 0 - 10
src/layouts/default/tabs/types.ts

@@ -14,22 +14,12 @@ export interface TabContentProps {
   trigger?: ('click' | 'hover' | 'contextmenu')[];
 }
 
-/**
- * @description: 右键:下拉菜单文字
- */
 export enum MenuEventEnum {
-  // 刷新
   REFRESH_PAGE,
-  // 关闭当前
   CLOSE_CURRENT,
-  // 关闭左侧
   CLOSE_LEFT,
-  // 关闭右侧
   CLOSE_RIGHT,
-  // 关闭其他
   CLOSE_OTHER,
-  // 关闭所有
   CLOSE_ALL,
-  // 放大
   SCALE,
 }

+ 10 - 6
src/layouts/default/tabs/useMultipleTabs.ts

@@ -1,14 +1,17 @@
 import { toRaw, ref, nextTick } from 'vue';
-import { RouteLocationNormalized } from 'vue-router';
+import type { RouteLocationNormalized } from 'vue-router';
 import { useDesign } from '/@/hooks/web/useDesign';
 import { useSortable } from '/@/hooks/web/useSortable';
-import router from '/@/router';
-import { tabStore } from '/@/store/modules/tab';
+import { useMultipleTabStore } from '/@/store/modules/multipleTab';
 import { isNullAndUnDef } from '/@/utils/is';
 import projectSetting from '/@/settings/projectSetting';
+import { useRouter } from 'vue-router';
 
 export function initAffixTabs(): string[] {
   const affixList = ref<RouteLocationNormalized[]>([]);
+
+  const tabStore = useMultipleTabStore();
+  const router = useRouter();
   /**
    * @description: Filter all fixed routes
    */
@@ -30,7 +33,7 @@ export function initAffixTabs(): string[] {
     const affixTabs = filterAffixTabs((router.getRoutes() as unknown) as RouteLocationNormalized[]);
     affixList.value = affixTabs;
     for (const tab of affixTabs) {
-      tabStore.addTabAction(({
+      tabStore.addTab(({
         meta: tab.meta,
         name: tab.name,
         path: tab.path,
@@ -39,6 +42,7 @@ export function initAffixTabs(): string[] {
   }
 
   let isAddAffix = false;
+
   if (!isAddAffix) {
     addAffixTabs();
     isAddAffix = true;
@@ -47,8 +51,8 @@ export function initAffixTabs(): string[] {
 }
 
 export function useTabsDrag(affixTextList: string[]) {
+  const tabStore = useMultipleTabStore();
   const { multiTabsSetting } = projectSetting;
-
   const { prefixCls } = useDesign('multiple-tabs');
   nextTick(() => {
     if (!multiTabsSetting.canDrag) return;
@@ -66,7 +70,7 @@ export function useTabsDrag(affixTextList: string[]) {
           return;
         }
 
-        tabStore.commitSortTabs({ oldIndex, newIndex });
+        tabStore.sortTabs(oldIndex, newIndex);
       },
     });
     initSortable();

+ 23 - 28
src/layouts/default/tabs/useTabDropdown.ts

@@ -1,29 +1,28 @@
 import type { TabContentProps } from './types';
 import type { DropMenu } from '/@/components/Dropdown';
+import type { ComputedRef } from 'vue';
 
 import { computed, unref, reactive } from 'vue';
-import { TabContentEnum, MenuEventEnum } from './types';
-import { tabStore } from '/@/store/modules/tab';
-import router from '/@/router';
-import { RouteLocationNormalized } from 'vue-router';
+import { MenuEventEnum } from './types';
+import { useMultipleTabStore } from '/@/store/modules/multipleTab';
+import { RouteLocationNormalized, useRouter } from 'vue-router';
 import { useTabs } from '/@/hooks/web/useTabs';
 import { useI18n } from '/@/hooks/web/useI18n';
 
-const { t } = useI18n();
-
-export function useTabDropdown(tabContentProps: TabContentProps) {
+export function useTabDropdown(tabContentProps: TabContentProps, getIsTabs: ComputedRef<boolean>) {
   const state = reactive({
     current: null as Nullable<RouteLocationNormalized>,
     currentIndex: 0,
   });
 
-  const { currentRoute } = router;
-
-  const isTabs = computed(() => tabContentProps.type === TabContentEnum.TAB_TYPE);
+  const { t } = useI18n();
+  const tabStore = useMultipleTabStore();
+  const { currentRoute } = useRouter();
+  const { refreshPage, closeAll, close, closeLeft, closeOther, closeRight } = useTabs();
 
-  const getCurrentTab = computed(
+  const getTargetTab = computed(
     (): RouteLocationNormalized => {
-      return unref(isTabs) ? tabContentProps.tabItem : unref(currentRoute);
+      return unref(getIsTabs) ? tabContentProps.tabItem : unref(currentRoute);
     }
   );
 
@@ -31,8 +30,10 @@ export function useTabDropdown(tabContentProps: TabContentProps) {
    * @description: drop-down list
    */
   const getDropMenuList = computed(() => {
-    if (!unref(getCurrentTab)) return;
-    const { meta } = unref(getCurrentTab);
+    if (!unref(getTargetTab)) {
+      return;
+    }
+    const { meta } = unref(getTargetTab);
     const { path } = unref(currentRoute);
 
     // Refresh button
@@ -42,11 +43,11 @@ export function useTabDropdown(tabContentProps: TabContentProps) {
     // Close left
     const closeLeftDisabled = index === 0;
 
-    const disabled = tabStore.getTabsState.length === 1;
+    const disabled = tabStore.getTabList.length === 1;
 
     // Close right
     const closeRightDisabled =
-      index === tabStore.getTabsState.length - 1 && tabStore.getLastDragEndIndexState >= 0;
+      index === tabStore.getTabList.length - 1 && tabStore.getLastDragEndIndex >= 0;
     const dropMenuList: DropMenu[] = [
       {
         icon: 'ion:reload-sharp',
@@ -58,7 +59,7 @@ export function useTabDropdown(tabContentProps: TabContentProps) {
         icon: 'clarity:close-line',
         event: MenuEventEnum.CLOSE_CURRENT,
         text: t('layout.multipleTab.close'),
-        disabled: meta?.affix || disabled,
+        disabled: !!meta?.affix || disabled,
         divider: true,
       },
       {
@@ -92,15 +93,13 @@ export function useTabDropdown(tabContentProps: TabContentProps) {
     return dropMenuList;
   });
 
-  const getTrigger = computed(() => {
-    return unref(isTabs) ? ['contextmenu'] : ['click'];
-  });
-
   function handleContextMenu(tabItem: RouteLocationNormalized) {
     return (e: Event) => {
-      if (!tabItem) return;
+      if (!tabItem) {
+        return;
+      }
       e?.preventDefault();
-      const index = tabStore.getTabsState.findIndex((tab) => tab.path === tabItem.path);
+      const index = tabStore.getTabList.findIndex((tab) => tab.path === tabItem.path);
       state.current = tabItem;
       state.currentIndex = index;
     };
@@ -108,12 +107,8 @@ export function useTabDropdown(tabContentProps: TabContentProps) {
 
   // Handle right click event
   function handleMenuEvent(menu: DropMenu): void {
-    const { refreshPage, closeAll, close, closeLeft, closeOther, closeRight } = useTabs();
     const { event } = menu;
     switch (event) {
-      case MenuEventEnum.SCALE:
-        scaleScreen();
-        break;
       case MenuEventEnum.REFRESH_PAGE:
         // refresh page
         refreshPage();
@@ -140,5 +135,5 @@ export function useTabDropdown(tabContentProps: TabContentProps) {
         break;
     }
   }
-  return { getDropMenuList, handleMenuEvent, handleContextMenu, getTrigger, isTabs };
+  return { getDropMenuList, handleMenuEvent, handleContextMenu };
 }

+ 5 - 4
src/layouts/iframe/useFrameKeepAlive.ts

@@ -2,18 +2,19 @@ import type { AppRouteRecordRaw } from '/@/router/types';
 
 import { computed, toRaw, unref } from 'vue';
 
-import { tabStore } from '/@/store/modules/tab';
+import { useMultipleTabStore } from '/@/store/modules/multipleTab';
 
 import { uniqBy } from 'lodash-es';
 
 import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
 
-import router from '/@/router';
+import { useRouter } from 'vue-router';
 
 export function useFrameKeepAlive() {
+  const router = useRouter();
   const { currentRoute } = router;
   const { getShowMultipleTab } = useMultipleTabSetting();
-
+  const tabStore = useMultipleTabStore();
   const getFramePages = computed(() => {
     const ret =
       getAllFramePages((toRaw(router.getRoutes()) as unknown) as AppRouteRecordRaw[]) || [];
@@ -21,7 +22,7 @@ export function useFrameKeepAlive() {
   });
 
   const getOpenTabList = computed((): string[] => {
-    return tabStore.getTabsState.reduce((prev: string[], next) => {
+    return tabStore.getTabList.reduce((prev: string[], next) => {
       if (next.meta && Reflect.has(next.meta, 'frameSrc')) {
         prev.push(next.name as string);
       }

+ 3 - 6
src/layouts/page/index.vue

@@ -35,13 +35,14 @@
   import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
   import { getTransitionName } from './transition';
 
-  import { useStore } from 'vuex';
+  import { useMultipleTabStore } from '/@/store/modules/multipleTab';
 
   export default defineComponent({
     name: 'PageLayout',
     components: { FrameLayout },
     setup() {
       const { getShowMultipleTab } = useMultipleTabSetting();
+      const tabStore = useMultipleTabStore();
 
       const { getOpenKeepAlive, getCanEmbedIFramePage } = useRootSetting();
 
@@ -49,15 +50,11 @@
 
       const openCache = computed(() => unref(getOpenKeepAlive) && unref(getShowMultipleTab));
 
-      const { getters } = useStore();
-
       const getCaches = computed((): string[] => {
         if (!unref(getOpenKeepAlive)) {
           return [];
         }
-        // TODO The useStore is used here mainly to solve the problem of circular dependency hot update
-        const cacheTabs = getters['app-tab/getCachedTabsState'];
-        return cacheTabs;
+        return tabStore.getCachedTabList;
       });
 
       return {

+ 2 - 1
src/locales/setupI18n.ts

@@ -3,14 +3,15 @@ import type { I18n, I18nOptions } from 'vue-i18n';
 
 import { createI18n } from 'vue-i18n';
 
-import { localeStore } from '/@/store/modules/locale';
 import { localeSetting } from '/@/settings/localeSetting';
+import { useLocaleStoreWithOut } from '/@/store/modules/locale';
 
 const { fallback, availableLocales } = localeSetting;
 
 export let i18n: ReturnType<typeof createI18n>;
 
 async function createI18nOptions(): Promise<I18nOptions> {
+  const localeStore = useLocaleStoreWithOut();
   const locale = localeStore.getLocale;
   const defaultLocal = await import(`./lang/${locale}.ts`);
   const message = defaultLocal.default?.message ?? {};

+ 7 - 2
src/locales/useLocale.ts

@@ -6,7 +6,7 @@ import type { LocaleType } from '/#/config';
 import moment from 'moment';
 
 import { i18n } from './setupI18n';
-import { localeStore } from '/@/store/modules/locale';
+import { useLocaleStoreWithOut } from '/@/store/modules/locale';
 import { unref, computed } from 'vue';
 
 interface LangModule {
@@ -18,6 +18,8 @@ interface LangModule {
 const loadLocalePool: LocaleType[] = [];
 
 function setI18nLanguage(locale: LocaleType) {
+  const localeStore = useLocaleStoreWithOut();
+
   if (i18n.mode === 'legacy') {
     i18n.global.locale = locale;
   } else {
@@ -28,6 +30,7 @@ function setI18nLanguage(locale: LocaleType) {
 }
 
 export function useLocale() {
+  const localeStore = useLocaleStoreWithOut();
   const getLocale = computed(() => localeStore.getLocale);
   const getShowLocalePicker = computed(() => localeStore.getShowPicker);
 
@@ -40,7 +43,9 @@ export function useLocale() {
   async function changeLocale(locale: LocaleType) {
     const globalI18n = i18n.global;
     const currentLocale = unref(globalI18n.locale);
-    if (currentLocale === locale) return locale;
+    if (currentLocale === locale) {
+      return locale;
+    }
 
     if (loadLocalePool.includes(locale)) {
       setI18nLanguage(locale);

+ 20 - 12
src/logics/error-handle/index.ts

@@ -2,7 +2,10 @@
  * Used to configure the global error handling function, which can monitor vue errors, script errors, static resource errors and Promise errors
  */
 
-import { errorStore, ErrorInfo } from '/@/store/modules/error';
+import type { ErrorLogInfo } from '/#/store';
+
+import { useErrorLogStoreWithOut } from '/@/store/modules/errorLog';
+
 import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
 import { App } from 'vue';
 import projectSetting from '/@/settings/projectSetting';
@@ -61,8 +64,9 @@ function formatComponentName(vm: any) {
  */
 
 function vueErrorHandler(err: Error, vm: any, info: string) {
+  const errorLogStore = useErrorLogStoreWithOut();
   const { name, path } = formatComponentName(vm);
-  errorStore.commitErrorInfoState({
+  errorLogStore.addErrorLogInfo({
     type: ErrorTypeEnum.VUE,
     name,
     file: path,
@@ -86,7 +90,7 @@ export function scriptErrorHandler(
   if (event === 'Script error.' && !source) {
     return false;
   }
-  const errorInfo: Partial<ErrorInfo> = {};
+  const errorInfo: Partial<ErrorLogInfo> = {};
   colno = colno || (window.event && (window.event as any).errorCharacter) || 0;
   errorInfo.message = event as string;
   if (error?.stack) {
@@ -95,13 +99,14 @@ export function scriptErrorHandler(
     errorInfo.stack = '';
   }
   const name = source ? source.substr(source.lastIndexOf('/') + 1) : 'script';
-  errorStore.commitErrorInfoState({
+  const errorLogStore = useErrorLogStoreWithOut();
+  errorLogStore.addErrorLogInfo({
     type: ErrorTypeEnum.SCRIPT,
     name: name,
     file: source as string,
     detail: 'lineno' + lineno,
     url: window.location.href,
-    ...(errorInfo as Pick<ErrorInfo, 'message' | 'stack'>),
+    ...(errorInfo as Pick<ErrorLogInfo, 'message' | 'stack'>),
   });
   return true;
 }
@@ -112,8 +117,9 @@ export function scriptErrorHandler(
 function registerPromiseErrorHandler() {
   window.addEventListener(
     'unhandledrejection',
-    function (event: any) {
-      errorStore.commitErrorInfoState({
+    function (event) {
+      const errorLogStore = useErrorLogStoreWithOut();
+      errorLogStore.addErrorLogInfo({
         type: ErrorTypeEnum.PROMISE,
         name: 'Promise Error!',
         file: 'none',
@@ -136,10 +142,10 @@ function registerResourceErrorHandler() {
     'error',
     function (e: Event) {
       const target = e.target ? e.target : (e.srcElement as any);
-
-      errorStore.commitErrorInfoState({
+      const errorLogStore = useErrorLogStoreWithOut();
+      errorLogStore.addErrorLogInfo({
         type: ErrorTypeEnum.RESOURCE,
-        name: 'Resouce Error!',
+        name: 'Resource Error!',
         file: (e.target || ({} as any)).currentSrc,
         detail: JSON.stringify({
           tagName: target.localName,
@@ -147,7 +153,7 @@ function registerResourceErrorHandler() {
           type: e.type,
         }),
         url: window.location.href,
-        stack: 'resouce is not found',
+        stack: 'resource is not found',
         message: (e.target || ({} as any)).localName + ' is load error',
       });
     },
@@ -161,7 +167,9 @@ function registerResourceErrorHandler() {
  */
 export function setupErrorHandle(app: App) {
   const { useErrorHandle } = projectSetting;
-  if (!useErrorHandle) return;
+  if (!useErrorHandle) {
+    return;
+  }
   // Vue exception monitoring;
   app.config.errorHandler = vueErrorHandler;
 

+ 6 - 4
src/logics/initAppConfig.ts

@@ -12,18 +12,20 @@ import { updateGrayMode } from '/@/logics/theme/updateGrayMode';
 import { updateDarkTheme } from '/@/logics/theme/dark';
 import { changeTheme } from '/@/logics/theme';
 
-import { appStore } from '/@/store/modules/app';
-import { localeStore } from '/@/store/modules/locale';
+import { useAppStore } from '/@/store/modules/app';
+import { useLocaleStore } from '/@/store/modules/locale';
 
 import { getCommonStoragePrefix, getStorageShortName } from '/@/utils/env';
 
 import { primaryColor } from '../../build/config/themeConfig';
 import { Persistent } from '/@/utils/cache/persistent';
 import { deepMerge } from '/@/utils';
-import { ThemeEnum } from '../enums/appEnum';
+import { ThemeEnum } from '/@/enums/appEnum';
 
 // Initial project configuration
 export function initAppConfigStore() {
+  const localeStore = useLocaleStore();
+  const appStore = useAppStore();
   let projCfg: ProjectConfig = Persistent.getLocal(PROJ_CFG_KEY) as ProjectConfig;
   projCfg = deepMerge(projectSetting, projCfg || {});
   const darkMode = appStore.getDarkMode;
@@ -45,7 +47,7 @@ export function initAppConfigStore() {
   } catch (error) {
     console.log(error);
   }
-  appStore.commitProjectConfigState(projCfg);
+  appStore.setProjectConfig(projCfg);
 
   // init dark mode
   updateDarkTheme(darkMode);

+ 8 - 5
src/logics/theme/updateBackground.ts

@@ -1,5 +1,5 @@
 import { colorIsDark, lighten, darken } from '/@/utils/color';
-import { appStore } from '/@/store/modules/app';
+import { useAppStore } from '/@/store/modules/app';
 import { ThemeEnum } from '/@/enums/appEnum';
 import { setCssVar } from './util';
 
@@ -16,12 +16,13 @@ const SIDER_LIGHTEN_BG_COLOR = '--sider-dark-lighten-bg-color';
  * @param color
  */
 export function updateHeaderBgColor(color?: string) {
+  const appStore = useAppStore();
   const darkMode = appStore.getDarkMode === ThemeEnum.DARK;
   if (!color) {
     if (darkMode) {
       color = '#151515';
     } else {
-      color = appStore.getProjectConfig.headerSetting.bgColor;
+      color = appStore.getHeaderSetting.bgColor;
     }
   }
   // bg color
@@ -35,7 +36,7 @@ export function updateHeaderBgColor(color?: string) {
   // Determine the depth of the color value and automatically switch the theme
   const isDark = colorIsDark(color!);
 
-  appStore.commitProjectConfigState({
+  appStore.setProjectConfig({
     headerSetting: {
       theme: isDark || darkMode ? ThemeEnum.DARK : ThemeEnum.LIGHT,
     },
@@ -47,13 +48,15 @@ export function updateHeaderBgColor(color?: string) {
  * @param color  bg color
  */
 export function updateSidebarBgColor(color?: string) {
+  const appStore = useAppStore();
+
   // if (!isHexColor(color)) return;
   const darkMode = appStore.getDarkMode === ThemeEnum.DARK;
   if (!color) {
     if (darkMode) {
       color = '#212121';
     } else {
-      color = appStore.getProjectConfig.menuSetting.bgColor;
+      color = appStore.getMenuSetting.bgColor;
     }
   }
   setCssVar(SIDER_DARK_BG_COLOR, color);
@@ -64,7 +67,7 @@ export function updateSidebarBgColor(color?: string) {
   // Only when the background color is #fff, the theme of the menu will be changed to light
   const isLight = ['#fff', '#ffffff'].includes(color!.toLowerCase());
 
-  appStore.commitProjectConfigState({
+  appStore.setProjectConfig({
     menuSetting: {
       theme: isLight && !darkMode ? ThemeEnum.LIGHT : ThemeEnum.DARK,
     },

+ 7 - 5
src/main.ts

@@ -5,15 +5,13 @@ import { createApp } from 'vue';
 import App from './App.vue';
 
 import router, { setupRouter } from '/@/router';
+import { setupRouterGuard } from '/@/router/guard';
 import { setupStore } from '/@/store';
 import { setupErrorHandle } from '/@/logics/error-handle';
 import { setupGlobDirectives } from '/@/directives';
 import { setupI18n } from '/@/locales/setupI18n';
 import { registerGlobComp } from '/@/components/registerGlobComp';
 
-// router-guard
-import '/@/router/guard';
-
 // Register icon Sprite
 import 'vite-plugin-svg-icons/register';
 
@@ -27,6 +25,10 @@ if (import.meta.env.DEV) {
 
 (async () => {
   const app = createApp(App);
+
+  // Configure vuex store
+  setupStore(app);
+
   // Register global components
   registerGlobComp(app);
 
@@ -36,8 +38,8 @@ if (import.meta.env.DEV) {
   // Configure routing
   setupRouter(app);
 
-  // Configure vuex store
-  setupStore(app);
+  // router-guard
+  setupRouterGuard();
 
   // Register global directive
   setupGlobDirectives(app);

+ 0 - 10
src/router/constant.ts

@@ -20,13 +20,3 @@ export const getParentLayout = (_name?: string) => {
       });
     });
 };
-
-// export const getParentLayout = (name: string) => {
-//   return () =>
-//     new Promise((resolve) => {
-//       resolve({
-//         ...ParentLayout,
-//         name,
-//       });
-//     });
-// };

+ 10 - 8
src/router/guard/index.ts

@@ -9,11 +9,13 @@ import { createHttpGuard } from './httpGuard';
 import { createPageGuard } from './pageGuard';
 import { createStateGuard } from './stateGuard';
 
-createPageGuard(router);
-createPageLoadingGuard(router);
-createHttpGuard(router);
-createScrollGuard(router);
-createMessageGuard(router);
-createProgressGuard(router);
-createPermissionGuard(router);
-createStateGuard(router);
+export function setupRouterGuard() {
+  createPageGuard(router);
+  createPageLoadingGuard(router);
+  createHttpGuard(router);
+  createScrollGuard(router);
+  createMessageGuard(router);
+  createProgressGuard(router);
+  createPermissionGuard(router);
+  createStateGuard(router);
+}

+ 7 - 5
src/router/guard/pageLoadingGuard.ts

@@ -1,13 +1,15 @@
 import type { Router } from 'vue-router';
-import { appStore } from '/@/store/modules/app';
-import { userStore } from '/@/store/modules/user';
+import { useAppStoreWidthOut } from '/@/store/modules/app';
+import { useUserStoreWidthOut } from '/@/store/modules/user';
 import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
 import { unref } from 'vue';
 
-const { getOpenPageLoading } = useTransitionSetting();
 export function createPageLoadingGuard(router: Router) {
+  const userStore = useUserStoreWidthOut();
+  const appStore = useAppStoreWidthOut();
+  const { getOpenPageLoading } = useTransitionSetting();
   router.beforeEach(async (to) => {
-    if (!userStore.getTokenState) {
+    if (!userStore.getToken) {
       return true;
     }
     if (to.meta.loaded) {
@@ -24,7 +26,7 @@ export function createPageLoadingGuard(router: Router) {
   router.afterEach(async () => {
     if (unref(getOpenPageLoading)) {
       setTimeout(() => {
-        appStore.commitPageLoadingState(false);
+        appStore.setPageLoading(false);
       }, 220);
     }
     return true;

+ 7 - 5
src/router/guard/permissionGuard.ts

@@ -1,9 +1,9 @@
 import type { Router, RouteRecordRaw } from 'vue-router';
 
-import { permissionStore } from '/@/store/modules/permission';
+import { usePermissionStoreWidthOut } from '/@/store/modules/permission';
 
 import { PageEnum } from '/@/enums/pageEnum';
-import { userStore } from '/@/store/modules/user';
+import { useUserStoreWidthOut } from '/@/store/modules/user';
 
 import { PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic';
 
@@ -12,6 +12,8 @@ const LOGIN_PATH = PageEnum.BASE_LOGIN;
 const whitePathList: PageEnum[] = [LOGIN_PATH];
 
 export function createPermissionGuard(router: Router) {
+  const userStore = useUserStoreWidthOut();
+  const permissionStore = usePermissionStoreWidthOut();
   router.beforeEach(async (to, from, next) => {
     // Jump to the 404 page after processing the login
     if (from.path === LOGIN_PATH && to.name === PAGE_NOT_FOUND_ROUTE.name) {
@@ -25,7 +27,7 @@ export function createPermissionGuard(router: Router) {
       return;
     }
 
-    const token = userStore.getTokenState;
+    const token = userStore.getToken;
 
     // token does not exist
     if (!token) {
@@ -51,7 +53,7 @@ export function createPermissionGuard(router: Router) {
       next(redirectData);
       return;
     }
-    if (permissionStore.getIsDynamicAddedRouteState) {
+    if (permissionStore.getIsDynamicAddedRoute) {
       next();
       return;
     }
@@ -64,7 +66,7 @@ export function createPermissionGuard(router: Router) {
     const redirectPath = (from.query.redirect || to.path) as string;
     const redirect = decodeURIComponent(redirectPath);
     const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect };
-    permissionStore.commitDynamicAddedRouteState(true);
+    permissionStore.setDynamicAddedRoute(true);
     next(nextData);
   });
 }

+ 1 - 2
src/router/guard/progressGuard.ts

@@ -6,9 +6,8 @@ import nProgress from 'nprogress';
 
 import { unref } from 'vue';
 
-const { getOpenNProgress } = useTransitionSetting();
-
 export function createProgressGuard(router: Router) {
+  const { getOpenNProgress } = useTransitionSetting();
   router.beforeEach(async (to) => {
     if (to.meta.loaded) return true;
     unref(getOpenNProgress) && nProgress.start();

+ 12 - 8
src/router/guard/stateGuard.ts

@@ -1,19 +1,23 @@
 import type { Router } from 'vue-router';
-import { appStore } from '/@/store/modules/app';
-import { tabStore } from '/@/store/modules/tab';
-import { userStore } from '/@/store/modules/user';
-import { permissionStore } from '/@/store/modules/permission';
+import { useAppStore } from '/@/store/modules/app';
+import { useMultipleTabStore } from '/@/store/modules/multipleTab';
+import { useUserStore } from '/@/store/modules/user';
+import { usePermissionStore } from '/@/store/modules/permission';
 import { PageEnum } from '/@/enums/pageEnum';
 import { removeTabChangeListener } from '/@/logics/mitt/routeChange';
 
 export function createStateGuard(router: Router) {
   router.afterEach((to) => {
+    const tabStore = useMultipleTabStore();
+    const userStore = useUserStore();
+    const appStore = useAppStore();
+    const permissionStore = usePermissionStore();
     // Just enter the login page and clear the authentication information
     if (to.path === PageEnum.BASE_LOGIN) {
-      appStore.resumeAllState();
-      permissionStore.commitResetState();
-      tabStore.commitResetState();
-      userStore.commitResetState();
+      appStore.resetAllState();
+      permissionStore.resetState();
+      tabStore.resetState();
+      userStore.resetState();
       removeTabChangeListener();
     }
   });

+ 5 - 3
src/router/menus/index.ts

@@ -1,8 +1,8 @@
 import type { Menu, MenuModule } from '/@/router/types';
 import type { RouteRecordNormalized } from 'vue-router';
 
-import { appStore } from '/@/store/modules/app';
-import { permissionStore } from '/@/store/modules/permission';
+import { useAppStoreWidthOut } from '/@/store/modules/app';
+import { usePermissionStore } from '/@/store/modules/permission';
 import { transformMenuModule, getAllParentPath } from '/@/router/helper/menuHelper';
 import { filter } from '/@/utils/helper/treeHelper';
 import { isUrl } from '/@/utils/is';
@@ -24,6 +24,7 @@ Object.keys(modules).forEach((key) => {
 // ==========Helper===========
 // ===========================
 const isBackMode = () => {
+  const appStore = useAppStoreWidthOut();
   return appStore.getProjectConfig.permissionMode === PermissionModeEnum.BACK;
 };
 
@@ -39,7 +40,8 @@ const staticMenus: Menu[] = [];
 })();
 
 async function getAsyncMenus() {
-  return !isBackMode() ? staticMenus : permissionStore.getBackMenuListState;
+  const permissionStore = usePermissionStore();
+  return !isBackMode() ? staticMenus : permissionStore.getBackMenuList;
 }
 
 export const getMenus = async (): Promise<Menu[]> => {

+ 2 - 3
src/settings/designSetting.ts

@@ -1,7 +1,6 @@
 import { ThemeEnum } from '../enums/appEnum';
-export default {
-  prefixCls: 'vben',
-};
+
+export const prefixCls = 'vben';
 
 export const darkMode = ThemeEnum.LIGHT;
 

+ 0 - 1
src/settings/projectSetting.ts

@@ -61,7 +61,6 @@ const setting: ProjectConfig = {
     theme: ThemeEnum.LIGHT,
     // Whether to enable the lock screen function
     useLockPage: true,
-
     // Whether to show the full screen button
     showFullScreen: true,
     // Whether to show the document button

+ 2 - 0
src/settings/siteSetting.ts

@@ -1,6 +1,8 @@
 // github repo url
 export const GITHUB_URL = 'https://github.com/anncwb/vue-vben-admin';
+
 // vue-vben-admin-next-doc
 export const DOC_URL = 'https://vvbin.cn/doc-next/';
+
 // site url
 export const SITE_URL = 'https://vvbin.cn/next/';

+ 3 - 10
src/store/index.ts

@@ -1,16 +1,9 @@
 import type { App } from 'vue';
-import { createStore } from 'vuex';
-import { config } from 'vuex-module-decorators';
-import { isDevMode } from '/@/utils/env';
-
-config.rawError = true;
-
-const store = createStore({
-  strict: isDevMode(),
-});
+import { createPinia } from 'pinia';
+const store = createPinia();
 
 export function setupStore(app: App<Element>) {
   app.use(store);
 }
 
-export default store;
+export { store };

+ 92 - 99
src/store/modules/app.ts

@@ -1,109 +1,102 @@
 import type { ProjectConfig } from '/#/config';
-import type { BeforeMiniState } from '../types';
+import type { BeforeMiniState } from '/#/store';
 
-import { VuexModule, getModule, Module, Mutation, Action } from 'vuex-module-decorators';
-import store from '/@/store';
+import { defineStore } from 'pinia';
+import { store } from '/@/store';
 
-import { PROJ_CFG_KEY, APP_DARK_MODE_KEY_ } from '/@/enums/cacheEnum';
-
-import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper';
+import { ThemeEnum } from '/@/enums/appEnum';
+import { APP_DARK_MODE_KEY_, PROJ_CFG_KEY } from '/@/enums/cacheEnum';
 import { Persistent } from '/@/utils/cache/persistent';
-import { deepMerge } from '/@/utils';
-
-import { resetRouter } from '/@/router';
-import { ThemeEnum } from '../../enums/appEnum';
-
 import { darkMode } from '/@/settings/designSetting';
+import { resetRouter } from '/@/router';
+import { deepMerge } from '/@/utils';
 
-export interface LockInfo {
-  pwd: string | undefined;
-  isLock: boolean;
-}
-
-let timeId: TimeoutHandle;
-const NAME = 'app';
-hotModuleUnregisterModule(NAME);
-@Module({ dynamic: true, namespaced: true, store, name: NAME })
-export default class App extends VuexModule {
-  private darkMode;
-
+interface AppState {
+  darkMode: ThemeEnum;
   // Page loading status
-  private pageLoadingState = false;
-
+  pageLoading: boolean;
   // project config
-  private projectConfigState: ProjectConfig | null = Persistent.getLocal(PROJ_CFG_KEY);
-
-  // set main overflow hidden
-  private lockMainScrollState = false;
-
+  projectConfig: ProjectConfig | null;
   // When the window shrinks, remember some states, and restore these states when the window is restored
-  private beforeMiniState: BeforeMiniState = {};
-
-  get getPageLoading() {
-    return this.pageLoadingState;
-  }
-
-  get getDarkMode() {
-    return this.darkMode || localStorage.getItem(APP_DARK_MODE_KEY_) || darkMode;
-  }
-
-  get getBeforeMiniState() {
-    return this.beforeMiniState;
-  }
-
-  get getLockMainScrollState() {
-    return this.lockMainScrollState;
-  }
-
-  get getProjectConfig(): ProjectConfig {
-    return this.projectConfigState || ({} as ProjectConfig);
-  }
-
-  @Mutation
-  commitPageLoadingState(loading: boolean): void {
-    this.pageLoadingState = loading;
-  }
-
-  @Mutation
-  commitDarkMode(mode: ThemeEnum): void {
-    this.darkMode = mode;
-    localStorage.setItem(APP_DARK_MODE_KEY_, mode);
-  }
-
-  @Mutation
-  commitBeforeMiniState(state: BeforeMiniState): void {
-    this.beforeMiniState = state;
-  }
-
-  @Mutation
-  commitLockMainScrollState(lock: boolean): void {
-    this.lockMainScrollState = lock;
-  }
-
-  @Mutation
-  commitProjectConfigState(proCfg: DeepPartial<ProjectConfig>): void {
-    this.projectConfigState = deepMerge(this.projectConfigState || {}, proCfg);
-    Persistent.setLocal(PROJ_CFG_KEY, this.projectConfigState);
-  }
-
-  @Action
-  async resumeAllState() {
-    resetRouter();
-    Persistent.clearAll();
-  }
-
-  @Action
-  public async setPageLoadingAction(loading: boolean): Promise<void> {
-    if (loading) {
-      clearTimeout(timeId);
-      // Prevent flicker
-      timeId = setTimeout(() => {
-        this.commitPageLoadingState(loading);
-      }, 50);
-    } else {
-      this.commitPageLoadingState(loading);
-      clearTimeout(timeId);
-    }
-  }
+  beforeMiniInfo: BeforeMiniState;
+}
+let timeId: TimeoutHandle;
+export const useAppStore = defineStore({
+  id: 'app',
+  state: (): AppState => ({
+    darkMode: ThemeEnum.LIGHT,
+    pageLoading: false,
+    projectConfig: Persistent.getLocal(PROJ_CFG_KEY),
+    beforeMiniInfo: {},
+  }),
+  getters: {
+    getPageLoading() {
+      return this.pageLoading;
+    },
+    getDarkMode() {
+      return this.darkMode || localStorage.getItem(APP_DARK_MODE_KEY_) || darkMode;
+    },
+
+    getBeforeMiniInfo() {
+      return this.beforeMiniInfo;
+    },
+
+    getProjectConfig(): ProjectConfig {
+      return this.projectConfig || ({} as ProjectConfig);
+    },
+
+    getHeaderSetting() {
+      return this.getProjectConfig.headerSetting;
+    },
+    getMenuSetting() {
+      return this.getProjectConfig.menuSetting;
+    },
+    getTransitionSetting() {
+      return this.getProjectConfig.transitionSetting;
+    },
+    getMultiTabsSetting() {
+      return this.getProjectConfig.multiTabsSetting;
+    },
+  },
+  actions: {
+    setPageLoading(loading: boolean): void {
+      this.pageLoading = loading;
+    },
+
+    setDarkMode(mode: ThemeEnum): void {
+      this.darkMode = mode;
+      localStorage.setItem(APP_DARK_MODE_KEY_, mode);
+    },
+
+    setBeforeMiniInfo(state: BeforeMiniState): void {
+      this.beforeMiniInfo = state;
+    },
+
+    setProjectConfig(config: DeepPartial<ProjectConfig>): void {
+      this.projectConfig = deepMerge(this.projectConfig || {}, config);
+      Persistent.setLocal(PROJ_CFG_KEY, this.projectConfig);
+    },
+
+    async resetAllState() {
+      resetRouter();
+      Persistent.clearAll();
+    },
+    async setPageLoadingAction(loading: boolean): Promise<void> {
+      if (loading) {
+        clearTimeout(timeId);
+        // Prevent flicker
+        timeId = setTimeout(() => {
+          this.setPageLoading(loading);
+        }, 50);
+      } else {
+        this.setPageLoading(loading);
+        clearTimeout(timeId);
+      }
+    },
+  },
+});
+
+// Need to be used outside the setup
+export function useAppStoreWidthOut() {
+  return useAppStore(store);
 }
-export const appStore = getModule<App>(App);

+ 0 - 81
src/store/modules/error.ts

@@ -1,81 +0,0 @@
-import store from '/@/store';
-import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper';
-import { VuexModule, getModule, Module, Mutation, Action } from 'vuex-module-decorators';
-
-import { formatToDateTime } from '/@/utils/dateUtil';
-import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
-import projectSetting from '/@/settings/projectSetting';
-
-export interface ErrorInfo {
-  type: ErrorTypeEnum;
-  file: string;
-  name?: string;
-  message: string;
-  stack?: string;
-  detail: string;
-  url: string;
-  time?: string;
-}
-
-export interface ErrorState {
-  errorInfoState: ErrorInfo[] | null;
-  errorListCountState: number;
-}
-
-const NAME = 'app-error';
-hotModuleUnregisterModule(NAME);
-@Module({ dynamic: true, namespaced: true, store, name: NAME })
-class Error extends VuexModule implements ErrorState {
-  // error log list
-  errorInfoState: ErrorInfo[] = [];
-
-  // error log count
-  errorListCountState = 0;
-
-  get getErrorInfoState() {
-    return this.errorInfoState;
-  }
-
-  get getErrorListCountState() {
-    return this.errorListCountState;
-  }
-
-  @Mutation
-  commitErrorInfoState(info: ErrorInfo): void {
-    const item = {
-      ...info,
-      time: formatToDateTime(new Date()),
-    };
-    this.errorInfoState = [item, ...this.errorInfoState];
-    this.errorListCountState += 1;
-  }
-
-  @Mutation
-  commitErrorListCountState(count: number): void {
-    this.errorListCountState = count;
-  }
-
-  @Action
-  setupErrorHandle(error: any) {
-    const { useErrorHandle } = projectSetting;
-    if (!useErrorHandle) return;
-
-    const errInfo: Partial<ErrorInfo> = {
-      message: error.message,
-      type: ErrorTypeEnum.AJAX,
-    };
-    if (error.response) {
-      const {
-        config: { url = '', data: params = '', method = 'get', headers = {} } = {},
-        data = {},
-      } = error.response;
-      errInfo.url = url;
-      errInfo.name = 'Ajax Error!';
-      errInfo.file = '-';
-      errInfo.stack = JSON.stringify(data);
-      errInfo.detail = JSON.stringify({ params, method, headers });
-    }
-    this.commitErrorInfoState(errInfo as ErrorInfo);
-  }
-}
-export const errorStore = getModule<Error>(Error);

+ 77 - 0
src/store/modules/errorLog.ts

@@ -0,0 +1,77 @@
+import type { ErrorLogInfo } from '/#/store';
+
+import { defineStore } from 'pinia';
+import { store } from '/@/store';
+
+import { formatToDateTime } from '/@/utils/dateUtil';
+import projectSetting from '/@/settings/projectSetting';
+
+import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
+
+export interface ErrorLogState {
+  errorLogInfoList: Nullable<ErrorLogInfo[]>;
+  errorLogListCount: number;
+}
+
+export const useErrorLogStore = defineStore({
+  id: 'app-error-log',
+  state: (): ErrorLogState => ({
+    errorLogInfoList: null,
+    errorLogListCount: 0,
+  }),
+  getters: {
+    getErrorLogInfoList() {
+      return this.errorLogInfoList || [];
+    },
+    getErrorLogListCount() {
+      return this.errorLogListCount;
+    },
+  },
+  actions: {
+    addErrorLogInfo(info: ErrorLogInfo) {
+      const item = {
+        ...info,
+        time: formatToDateTime(new Date()),
+      };
+      this.errorLogInfoList = [item, ...(this.errorLogInfoList || [])];
+      this.errorLogListCount += 1;
+    },
+
+    setErrorLogListCount(count: number): void {
+      this.errorLogListCount = count;
+    },
+
+    /**
+     * Triggered after ajax request error
+     * @param error
+     * @returns
+     */
+    addAjaxErrorInfo(error) {
+      const { useErrorHandle } = projectSetting;
+      if (!useErrorHandle) {
+        return;
+      }
+      const errInfo: Partial<ErrorLogInfo> = {
+        message: error.message,
+        type: ErrorTypeEnum.AJAX,
+      };
+      if (error.response) {
+        const {
+          config: { url = '', data: params = '', method = 'get', headers = {} } = {},
+          data = {},
+        } = error.response;
+        errInfo.url = url;
+        errInfo.name = 'Ajax Error!';
+        errInfo.file = '-';
+        errInfo.stack = JSON.stringify(data);
+        errInfo.detail = JSON.stringify({ params, method, headers });
+      }
+      this.addErrorLogInfo(errInfo as ErrorLogInfo);
+    },
+  },
+});
+
+// Need to be used outside the setup
+export function useErrorLogStoreWithOut() {
+  return useErrorLogStore(store);
+}

+ 44 - 33
src/store/modules/locale.ts

@@ -1,44 +1,55 @@
-import store from '/@/store';
+import type { LocaleSetting, LocaleType } from '/#/config';
 
-import { VuexModule, getModule, Module, Mutation, Action } from 'vuex-module-decorators';
+import { defineStore } from 'pinia';
+import { store } from '/@/store';
 
 import { LOCALE_KEY } from '/@/enums/cacheEnum';
-
-import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper';
-import { LocaleSetting, LocaleType } from '/#/config';
 import { createLocalStorage } from '/@/utils/cache';
 import { localeSetting } from '/@/settings/localeSetting';
 
 const ls = createLocalStorage();
 
-const lsSetting = (ls.get(LOCALE_KEY) || localeSetting) as LocaleSetting;
-
-const NAME = 'app-locale';
-hotModuleUnregisterModule(NAME);
-@Module({ dynamic: true, namespaced: true, store, name: NAME })
-class Locale extends VuexModule {
-  private info: LocaleSetting = lsSetting;
-
-  get getShowPicker(): boolean {
-    return !!this.info?.showPicker;
-  }
+const lsLocaleSetting = (ls.get(LOCALE_KEY) || localeSetting) as LocaleSetting;
 
-  get getLocale(): LocaleType {
-    return this.info?.locale;
-  }
-
-  @Mutation
-  setLocaleInfo(info: Partial<LocaleSetting>): void {
-    this.info = { ...this.info, ...info };
-    ls.set(LOCALE_KEY, this.info);
-  }
+interface LocaleState {
+  localInfo: LocaleSetting;
+}
 
-  @Action
-  initLocale(): void {
-    this.setLocaleInfo({
-      ...localeSetting,
-      ...this.info,
-    });
-  }
+export const useLocaleStore = defineStore({
+  id: 'app-locale',
+  state: (): LocaleState => ({
+    localInfo: lsLocaleSetting,
+  }),
+  getters: {
+    getShowPicker() {
+      return !!this.localInfo?.showPicker;
+    },
+    getLocale(): LocaleType {
+      return this.localInfo?.locale ?? 'zh_CN';
+    },
+  },
+  actions: {
+    /**
+     * Set up multilingual information and cache
+     * @param info multilingual info
+     */
+    setLocaleInfo(info: Partial<LocaleSetting>) {
+      this.localInfo = { ...this.localInfo, ...info };
+      ls.set(LOCALE_KEY, this.localInfo);
+    },
+    /**
+     * Initialize multilingual information and load the existing configuration from the local cache
+     */
+    initLocale() {
+      this.setLocaleInfo({
+        ...localeSetting,
+        ...this.localInfo,
+      });
+    },
+  },
+});
+
+// Need to be used outside the setup
+export function useLocaleStoreWithOut() {
+  return useLocaleStore(store);
 }
-export const localeStore = getModule<Locale>(Locale);

+ 52 - 54
src/store/modules/lock.ts

@@ -1,61 +1,59 @@
-import type { LockInfo } from '/@/store/types';
+import type { LockInfo } from '/#/store';
 
-import { VuexModule, getModule, Module, Mutation, Action } from 'vuex-module-decorators';
-import store from '/@/store';
+import { defineStore } from 'pinia';
 
 import { LOCK_INFO_KEY } from '/@/enums/cacheEnum';
-
-import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper';
 import { Persistent } from '/@/utils/cache/persistent';
+import { useUserStore } from './user';
 
-import { userStore } from './user';
-
-const NAME = 'app-lock';
-hotModuleUnregisterModule(NAME);
-@Module({ dynamic: true, namespaced: true, store, name: NAME })
-class Lock extends VuexModule {
-  // lock info
-  private lockInfoState: LockInfo | null = Persistent.getLocal(LOCK_INFO_KEY);
-
-  get getLockInfo(): LockInfo {
-    return this.lockInfoState || ({} as LockInfo);
-  }
-
-  @Mutation
-  commitLockInfoState(info: LockInfo): void {
-    this.lockInfoState = Object.assign({}, this.lockInfoState, info);
-    Persistent.setLocal(LOCK_INFO_KEY, this.lockInfoState);
-  }
-
-  @Mutation
-  resetLockInfo(): void {
-    Persistent.removeLocal(LOCK_INFO_KEY);
-    this.lockInfoState = null;
-  }
+interface LockState {
+  lockInfo: Nullable<LockInfo>;
+}
 
-  /**
-   * @description: unlock page
-   */
-  @Action
-  public async unLockAction({ password }: { password: string }) {
-    const tryLogin = async () => {
-      try {
-        const username = userStore.getUserInfoState.username;
-        const res = await userStore.login({ username, password, goHome: false, mode: 'none' });
-        if (res) {
-          this.resetLockInfo();
-        }
-        return res;
-      } catch (error) {
-        return false;
+export const useLockStore = defineStore({
+  id: 'app-lock',
+  state: (): LockState => ({
+    lockInfo: Persistent.getLocal(LOCK_INFO_KEY),
+  }),
+  getters: {
+    getLockInfo() {
+      return this.lockInfo;
+    },
+  },
+  actions: {
+    setLockInfo(info: LockInfo) {
+      this.lockInfo = Object.assign({}, this.lockInfo, info);
+      Persistent.setLocal(LOCK_INFO_KEY, this.lockInfo);
+    },
+    resetLockInfo() {
+      Persistent.removeLocal(LOCK_INFO_KEY);
+      this.lockInfo = null;
+    },
+    // Unlock
+    async unLock(password?: string) {
+      const userStore = useUserStore();
+      if (this.lockInfo?.pwd === password) {
+        this.resetLockInfo();
+        return true;
       }
-    };
-
-    if (this.getLockInfo?.pwd === password) {
-      this.resetLockInfo();
-      return true;
-    }
-    return await tryLogin();
-  }
-}
-export const lockStore = getModule<Lock>(Lock);
+      const tryLogin = async () => {
+        try {
+          const username = userStore.getUserInfo?.username;
+          const res = await userStore.login({
+            username,
+            password: password!,
+            goHome: false,
+            mode: 'none',
+          });
+          if (res) {
+            this.resetLockInfo();
+          }
+          return res;
+        } catch (error) {
+          return false;
+        }
+      };
+      return await tryLogin();
+    },
+  },
+});

+ 288 - 0
src/store/modules/multipleTab.ts

@@ -0,0 +1,288 @@
+import type { RouteLocationNormalized, RouteLocationRaw, Router } from 'vue-router';
+
+import { toRaw, unref } from 'vue';
+import { defineStore } from 'pinia';
+import { store } from '/@/store';
+
+import { useGo, useRedo } from '/@/hooks/web/usePage';
+
+import { PageEnum } from '/@/enums/pageEnum';
+import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '/@/router/routes/basic';
+import { getRawRoute } from '/@/utils';
+
+export interface MultipleTabState {
+  cacheTabList: Set<string>;
+  tabList: RouteLocationNormalized[];
+  lastDragEndIndex: number;
+}
+
+function handleGotoPage(router: Router) {
+  const go = useGo(router);
+  go(unref(router.currentRoute).path, true);
+}
+
+export const useMultipleTabStore = defineStore({
+  id: 'app-multiple-tab',
+  state: (): MultipleTabState => ({
+    // Tabs that need to be cached
+    cacheTabList: new Set(),
+    // multiple tab list
+    tabList: [],
+    // Index of the last moved tab
+    lastDragEndIndex: 0,
+  }),
+  getters: {
+    getTabList() {
+      return this.tabList;
+    },
+    getCachedTabList(): string[] {
+      return Array.from(this.cacheTabList);
+    },
+    getLastDragEndIndex(): number {
+      return this.lastDragEndIndex;
+    },
+  },
+  actions: {
+    /**
+     * Update the cache according to the currently opened tabs
+     */
+    async updateCacheTab() {
+      const cacheMap: Set<string> = new Set();
+
+      for (const tab of this.tabList) {
+        const item = getRawRoute(tab);
+        // Ignore the cache
+        const needCache = !item.meta?.ignoreKeepAlive;
+        if (!needCache) {
+          return;
+        }
+        const name = item.name as string;
+        cacheMap.add(name);
+      }
+      this.cacheTabList = cacheMap;
+    },
+
+    /**
+     * Refresh tabs
+     */
+    async refreshPage(router: Router) {
+      const { currentRoute } = router;
+      const route = unref(currentRoute);
+      const name = route.name;
+
+      const findTab = this.getCachedTabList.find((item) => item === name);
+      if (findTab) {
+        this.cacheTabList.delete(findTab);
+      }
+      const redo = useRedo(router);
+      await redo();
+    },
+    clearCacheTabs(): void {
+      this.cacheTabList = new Set();
+    },
+    resetState(): void {
+      this.tabList = [];
+      this.clearCacheTabs();
+    },
+    goToPage(router: Router) {
+      const go = useGo(router);
+      const len = this.tabList.length;
+      const { path } = unref(router.currentRoute);
+
+      let toPath: PageEnum | string = PageEnum.BASE_HOME;
+
+      if (len > 0) {
+        const page = this.tabList[len - 1];
+        const p = page.fullPath || page.path;
+        if (p) {
+          toPath = p;
+        }
+      }
+      // Jump to the current page and report an error
+      path !== toPath && go(toPath as PageEnum, true);
+    },
+
+    async addTab(route: RouteLocationNormalized) {
+      const { path, name, fullPath, params, query } = getRawRoute(route);
+      // 404  The page does not need to add a tab
+      if (
+        path === PageEnum.ERROR_PAGE ||
+        !name ||
+        [REDIRECT_ROUTE.name, PAGE_NOT_FOUND_ROUTE.name].includes(name as string)
+      ) {
+        return;
+      }
+
+      let updateIndex = -1;
+      // Existing pages, do not add tabs repeatedly
+      const tabHasExits = this.tabList.some((tab, index) => {
+        updateIndex = index;
+        return (tab.fullPath || tab.path) === (fullPath || path);
+      });
+
+      // If the tab already exists, perform the update operation
+      if (tabHasExits) {
+        const curTab = toRaw(this.tabList)[updateIndex];
+        if (!curTab) {
+          return;
+        }
+        curTab.params = params || curTab.params;
+        curTab.query = query || curTab.query;
+        curTab.fullPath = fullPath || curTab.fullPath;
+        this.tabList.splice(updateIndex, 1, curTab);
+        return;
+      }
+      // Add tab
+      this.tabList.push(route);
+      this.updateCacheTab();
+    },
+
+    async closeTab(tab: RouteLocationNormalized, router: Router) {
+      const getToTarget = (tabItem: RouteLocationNormalized) => {
+        const { params, path, query } = tabItem;
+        return {
+          params: params || {},
+          path,
+          query: query || {},
+        };
+      };
+
+      const close = (route: RouteLocationNormalized) => {
+        const { fullPath, meta: { affix } = {} } = route;
+        if (affix) {
+          return;
+        }
+        const index = this.tabList.findIndex((item) => item.fullPath === fullPath);
+        index !== -1 && this.tabList.splice(index, 1);
+      };
+
+      const { currentRoute, replace } = router;
+
+      const { path } = unref(currentRoute);
+      if (path !== tab.path) {
+        // Closed is not the activation tab
+        close(tab);
+        return;
+      }
+
+      // Closed is activated atb
+      let toTarget: RouteLocationRaw = {};
+
+      const index = this.tabList.findIndex((item) => item.path === path);
+
+      // If the current is the leftmost tab
+      if (index === 0) {
+        // There is only one tab, then jump to the homepage, otherwise jump to the right tab
+        if (this.tabList.length === 1) {
+          toTarget = PageEnum.BASE_HOME;
+        } else {
+          //  Jump to the right tab
+          const page = this.tabList[index + 1];
+          toTarget = getToTarget(page);
+        }
+      } else {
+        // Close the current tab
+        const page = this.tabList[index - 1];
+        toTarget = getToTarget(page);
+      }
+      close(currentRoute.value);
+      replace(toTarget);
+    },
+
+    // Close according to key
+    async closeTabByKey(key: string, router: Router) {
+      const index = this.tabList.findIndex((item) => (item.fullPath || item.path) === key);
+      index !== -1 && this.closeTab(this.tabList[index], router);
+    },
+
+    // Sort the tabs
+    async sortTabs(oldIndex: number, newIndex: number) {
+      const currentTab = this.tabList[oldIndex];
+      this.tabList.splice(oldIndex, 1);
+      this.tabList.splice(newIndex, 0, currentTab);
+      this.lastDragEndIndex = this.lastDragEndIndex + 1;
+    },
+
+    // Close the tab on the right and jump
+    async closeLeftTabs(route: RouteLocationNormalized, router: Router) {
+      const index = this.tabList.findIndex((item) => item.path === route.path);
+
+      if (index > 0) {
+        const leftTabs = this.tabList.slice(0, index);
+        const pathList: string[] = [];
+        for (const item of leftTabs) {
+          const affix = item?.meta?.affix ?? false;
+          if (!affix) {
+            pathList.push(item.fullPath);
+          }
+        }
+        this.bulkCloseTabs(pathList);
+      }
+      this.updateCacheTab();
+      handleGotoPage(router);
+    },
+
+    // Close the tab on the left and jump
+    async closeRightTabs(route: RouteLocationNormalized, router: Router) {
+      const index = this.tabList.findIndex((item) => item.fullPath === route.fullPath);
+
+      if (index >= 0 && index < this.tabList.length - 1) {
+        const rightTabs = this.tabList.slice(index + 1, this.tabList.length);
+
+        const pathList: string[] = [];
+        for (const item of rightTabs) {
+          const affix = item?.meta?.affix ?? false;
+          if (!affix) {
+            pathList.push(item.fullPath);
+          }
+        }
+        this.bulkCloseTabs(pathList);
+      }
+      this.updateCacheTab();
+      handleGotoPage(router);
+    },
+
+    async closeAllTab(router: Router) {
+      this.tabList = this.tabList.filter((item) => item?.meta?.affix ?? false);
+      this.clearCacheTabs();
+      this.goToPage(router);
+    },
+
+    /**
+     * Close other tabs
+     */
+    async closeOtherTabs(route: RouteLocationNormalized, router: Router) {
+      const closePathList = this.tabList.map((item) => item.fullPath);
+
+      const pathList: string[] = [];
+
+      for (const path of closePathList) {
+        if (path !== route.fullPath) {
+          const closeItem = this.tabList.find((item) => item.path === path);
+          if (!closeItem) {
+            return;
+          }
+          const affix = closeItem?.meta?.affix ?? false;
+          if (!affix) {
+            pathList.push(closeItem.fullPath);
+          }
+        }
+      }
+      this.bulkCloseTabs(pathList);
+      this.updateCacheTab();
+      handleGotoPage(router);
+    },
+
+    /**
+     * Close tabs in bulk
+     */
+    async bulkCloseTabs(pathList: string[]) {
+      this.tabList = this.tabList.filter((item) => !pathList.includes(item.fullPath));
+    },
+  },
+});
+
+// Need to be used outside the setup
+export function useMultipleTabWithOutStore() {
+  return useMultipleTabStore(store);
+}

+ 125 - 124
src/store/modules/permission.ts

@@ -1,21 +1,20 @@
 import type { AppRouteRecordRaw, Menu } from '/@/router/types';
 
-import store from '/@/store';
+import { defineStore } from 'pinia';
+import { store } from '/@/store';
+import { useI18n } from '/@/hooks/web/useI18n';
+import { useUserStore } from './user';
+import { useAppStoreWidthOut } from './app';
 import { toRaw } from 'vue';
-import { VuexModule, Mutation, Module, getModule, Action } from 'vuex-module-decorators';
+import { transformObjToRoute, flatMultiLevelRoutes } from '/@/router/helper/routeHelper';
+import { transformRouteToMenu } from '/@/router/helper/menuHelper';
 
-import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper';
+import projectSetting from '/@/settings/projectSetting';
 
 import { PermissionModeEnum } from '/@/enums/appEnum';
 
-import { appStore } from '/@/store/modules/app';
-import { userStore } from '/@/store/modules/user';
-import projectSetting from '/@/settings/projectSetting';
-
 import { asyncRoutes } from '/@/router/routes';
 import { ERROR_LOG_ROUTE, PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic';
-import { transformObjToRoute, flatMultiLevelRoutes } from '/@/router/helper/routeHelper';
-import { transformRouteToMenu } from '/@/router/helper/menuHelper';
 
 import { filter } from '/@/utils/helper/treeHelper';
 
@@ -23,125 +22,127 @@ import { getMenuListById } from '/@/api/sys/menu';
 import { getPermCodeByUserId } from '/@/api/sys/user';
 
 import { useMessage } from '/@/hooks/web/useMessage';
-import { useI18n } from '/@/hooks/web/useI18n';
 
-const NAME = 'app-permission';
-hotModuleUnregisterModule(NAME);
-@Module({ dynamic: true, namespaced: true, store, name: NAME })
-class Permission extends VuexModule {
+interface PermissionState {
   // Permission code list
-  private permCodeListState: string[] = [];
-
+  permCodeList: string[];
   // Whether the route has been dynamically added
-  private isDynamicAddedRouteState = false;
-
+  isDynamicAddedRoute: boolean;
   // To trigger a menu update
-  private lastBuildMenuTimeState = 0;
-
+  lastBuildMenuTime: number;
   // Backstage menu list
-  private backMenuListState: Menu[] = [];
-
-  get getPermCodeListState() {
-    return this.permCodeListState;
-  }
-
-  get getBackMenuListState() {
-    return this.backMenuListState;
-  }
-
-  get getLastBuildMenuTimeState() {
-    return this.lastBuildMenuTimeState;
-  }
-
-  get getIsDynamicAddedRouteState() {
-    return this.isDynamicAddedRouteState;
-  }
-
-  @Mutation
-  commitPermCodeListState(codeList: string[]): void {
-    this.permCodeListState = codeList;
-  }
-
-  @Mutation
-  commitBackMenuListState(list: Menu[]): void {
-    this.backMenuListState = list;
-  }
-
-  @Mutation
-  commitLastBuildMenuTimeState(): void {
-    this.lastBuildMenuTimeState = new Date().getTime();
-  }
-
-  @Mutation
-  commitDynamicAddedRouteState(added: boolean): void {
-    this.isDynamicAddedRouteState = added;
-  }
-
-  @Mutation
-  commitResetState(): void {
-    this.isDynamicAddedRouteState = false;
-    this.permCodeListState = [];
-    this.backMenuListState = [];
-    this.lastBuildMenuTimeState = 0;
-  }
-
-  @Action
-  async changePermissionCode(userId: string) {
-    const codeList = await getPermCodeByUserId({ userId });
-    this.commitPermCodeListState(codeList);
-  }
-
-  @Action
-  async buildRoutesAction(id?: number | string): Promise<AppRouteRecordRaw[]> {
-    const { t } = useI18n();
-    let routes: AppRouteRecordRaw[] = [];
-    const roleList = toRaw(userStore.getRoleListState);
-    const { permissionMode = projectSetting.permissionMode } = appStore.getProjectConfig;
-    // role permissions
-    if (permissionMode === PermissionModeEnum.ROLE) {
-      const routeFilter = (route: AppRouteRecordRaw) => {
-        const { meta } = route;
-        const { roles } = meta || {};
-        if (!roles) return true;
-        return roleList.some((role) => roles.includes(role));
-      };
-      routes = filter(asyncRoutes, routeFilter);
-      routes = routes.filter(routeFilter);
-      // Convert multi-level routing to level 2 routing
-      routes = flatMultiLevelRoutes(routes);
-      //  If you are sure that you do not need to do background dynamic permissions, please comment the entire judgment below
-    } else if (permissionMode === PermissionModeEnum.BACK) {
-      const { createMessage } = useMessage();
-
-      createMessage.loading({
-        content: t('sys.app.menuLoading'),
-        duration: 1,
-      });
-      // Here to get the background routing menu logic to modify by yourself
-      const paramId = id || userStore.getUserInfoState.userId;
-
-      // !Simulate to obtain permission codes from the background,
-      // this function may only need to be executed once, and the actual project can be put at the right time by itself
-      try {
-        this.changePermissionCode('1');
-      } catch (error) {}
-      if (!paramId) {
-        throw new Error('paramId is undefined!');
+  backMenuList: Menu[];
+}
+export const usePermissionStore = defineStore({
+  id: 'app-permission',
+  state: (): PermissionState => ({
+    permCodeList: [],
+    // Whether the route has been dynamically added
+    isDynamicAddedRoute: false,
+    // To trigger a menu update
+    lastBuildMenuTime: 0,
+    // Backstage menu list
+    backMenuList: [],
+  }),
+  getters: {
+    getPermCodeList() {
+      return this.permCodeList;
+    },
+    getBackMenuList() {
+      return this.backMenuList;
+    },
+    getLastBuildMenuTime() {
+      return this.lastBuildMenuTime;
+    },
+    getIsDynamicAddedRoute() {
+      return this.isDynamicAddedRoute;
+    },
+  },
+  actions: {
+    setPermCodeList(codeList: string[]) {
+      this.permCodeList = codeList;
+    },
+
+    setBackMenuList(list: Menu[]) {
+      this.backMenuList = list;
+    },
+
+    setLastBuildMenuTime() {
+      this.lastBuildMenuTime = new Date().getTime();
+    },
+
+    setDynamicAddedRoute(added: boolean) {
+      this.isDynamicAddedRoute = added;
+    },
+    resetState(): void {
+      this.isDynamicAddedRoute = false;
+      this.permCodeList = [];
+      this.backMenuList = [];
+      this.lastBuildMenuTime = 0;
+    },
+    async changePermissionCode(userId: string) {
+      const codeList = await getPermCodeByUserId({ userId });
+      this.setPermCodeList(codeList);
+    },
+    async buildRoutesAction(id?: number | string): Promise<AppRouteRecordRaw[]> {
+      const { t } = useI18n();
+      const userStore = useUserStore();
+      const appStore = useAppStoreWidthOut();
+
+      let routes: AppRouteRecordRaw[] = [];
+      const roleList = toRaw(userStore.getRoleList);
+      const { permissionMode = projectSetting.permissionMode } = appStore.getProjectConfig;
+      // role permissions
+      if (permissionMode === PermissionModeEnum.ROLE) {
+        const routeFilter = (route: AppRouteRecordRaw) => {
+          const { meta } = route;
+          const { roles } = meta || {};
+          if (!roles) return true;
+          return roleList.some((role) => roles.includes(role));
+        };
+        routes = filter(asyncRoutes, routeFilter);
+        routes = routes.filter(routeFilter);
+        // Convert multi-level routing to level 2 routing
+        routes = flatMultiLevelRoutes(routes);
+        //  If you are sure that you do not need to do background dynamic permissions, please comment the entire judgment below
+      } else if (permissionMode === PermissionModeEnum.BACK) {
+        const { createMessage } = useMessage();
+
+        createMessage.loading({
+          content: t('sys.app.menuLoading'),
+          duration: 1,
+        });
+        // Here to get the background routing menu logic to modify by yourself
+        const paramId = id || userStore.getUserInfo?.userId;
+
+        // !Simulate to obtain permission codes from the background,
+        // this function may only need to be executed once, and the actual project can be put at the right time by itself
+        try {
+          this.changePermissionCode('1');
+        } catch (error) {}
+
+        if (!paramId) {
+          throw new Error('paramId is undefined!');
+        }
+        let routeList = (await getMenuListById({ id: paramId })) as AppRouteRecordRaw[];
+
+        // Dynamically introduce components
+        routeList = transformObjToRoute(routeList);
+
+        //  Background routing to menu structure
+        const backMenuList = transformRouteToMenu(routeList);
+        this.setBackMenuList(backMenuList);
+
+        routeList = flatMultiLevelRoutes(routeList);
+        routes = [PAGE_NOT_FOUND_ROUTE, ...routeList];
       }
-      let routeList = (await getMenuListById({ id: paramId })) as AppRouteRecordRaw[];
-
-      // Dynamically introduce components
-      routeList = transformObjToRoute(routeList);
-
-      //  Background routing to menu structure
-      const backMenuList = transformRouteToMenu(routeList);
-      this.commitBackMenuListState(backMenuList);
-
-      routeList = flatMultiLevelRoutes(routeList);
-      routes = [PAGE_NOT_FOUND_ROUTE, ...routeList];
-    }
-    routes.push(ERROR_LOG_ROUTE);
-    return routes;
-  }
+      routes.push(ERROR_LOG_ROUTE);
+      return routes;
+    },
+  },
+});
+
+// Need to be used outside the setup
+export function usePermissionStoreWidthOut() {
+  return usePermissionStore(store);
 }
-export const permissionStore = getModule<Permission>(Permission);

+ 0 - 294
src/store/modules/tab.ts

@@ -1,294 +0,0 @@
-import type { RouteLocationNormalized, RouteLocationRaw } from 'vue-router';
-
-import { toRaw, unref } from 'vue';
-import { Action, Module, Mutation, VuexModule, getModule } from 'vuex-module-decorators';
-import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper';
-
-import { PageEnum } from '/@/enums/pageEnum';
-
-import store from '/@/store';
-import router from '/@/router';
-import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '/@/router/routes/basic';
-import { getRawRoute } from '/@/utils';
-
-import { useGo, useRedo } from '/@/hooks/web/usePage';
-import { cloneDeep } from 'lodash-es';
-
-const NAME = 'app-tab';
-
-hotModuleUnregisterModule(NAME);
-
-function isGotoPage() {
-  const go = useGo();
-  go(unref(router.currentRoute).path, true);
-}
-
-@Module({ namespaced: true, name: NAME, dynamic: true, store })
-class Tab extends VuexModule {
-  cachedTabsState: Set<string> = new Set();
-
-  // tab list
-  tabsState: RouteLocationNormalized[] = [];
-
-  lastDragEndIndexState = 0;
-
-  get getTabsState() {
-    return this.tabsState;
-  }
-
-  get getCurrentTab(): RouteLocationNormalized {
-    const route = unref(router.currentRoute);
-    return this.tabsState.find((item) => item.path === route.path)!;
-  }
-
-  get getCachedTabsState(): string[] {
-    return Array.from(this.cachedTabsState);
-  }
-
-  get getLastDragEndIndexState(): number {
-    return this.lastDragEndIndexState;
-  }
-
-  @Mutation
-  commitClearCache(): void {
-    this.cachedTabsState = new Set();
-  }
-
-  @Mutation
-  goToPage() {
-    const go = useGo();
-    const len = this.tabsState.length;
-    const { path } = unref(router.currentRoute);
-
-    let toPath: PageEnum | string = PageEnum.BASE_HOME;
-
-    if (len > 0) {
-      const page = this.tabsState[len - 1];
-      const p = page.fullPath || page.path;
-      if (p) {
-        toPath = p;
-      }
-    }
-    // Jump to the current page and report an error
-    path !== toPath && go(toPath as PageEnum, true);
-  }
-
-  @Mutation
-  commitCachedMapState(): void {
-    const cacheMap: Set<string> = new Set();
-
-    this.tabsState.forEach((tab) => {
-      const item = getRawRoute(tab);
-      const needCache = !item.meta?.ignoreKeepAlive;
-      if (!needCache) return;
-      const name = item.name as string;
-      cacheMap.add(name);
-    });
-    this.cachedTabsState = cacheMap;
-  }
-
-  @Mutation
-  commitTabRoutesState(route: RouteLocationNormalized) {
-    const { path, fullPath, params, query } = route;
-
-    let updateIndex = -1;
-    // Existing pages, do not add tabs repeatedly
-    const hasTab = this.tabsState.some((tab, index) => {
-      updateIndex = index;
-      return (tab.fullPath || tab.path) === (fullPath || path);
-    });
-    if (hasTab) {
-      const curTab = toRaw(this.tabsState)[updateIndex];
-      if (!curTab) return;
-      curTab.params = params || curTab.params;
-      curTab.query = query || curTab.query;
-      curTab.fullPath = fullPath || curTab.fullPath;
-      this.tabsState.splice(updateIndex, 1, curTab);
-      return;
-    }
-    this.tabsState = cloneDeep([...this.tabsState, route]);
-  }
-
-  /**
-   * @description: close tab
-   */
-  @Mutation
-  commitCloseTab(route: RouteLocationNormalized): void {
-    const { fullPath, meta: { affix } = {} } = route;
-    if (affix) return;
-    const index = this.tabsState.findIndex((item) => item.fullPath === fullPath);
-    index !== -1 && this.tabsState.splice(index, 1);
-  }
-
-  @Mutation
-  commitCloseAllTab(): void {
-    this.tabsState = this.tabsState.filter((item) => {
-      return item.meta && item.meta.affix;
-    });
-  }
-
-  @Mutation
-  commitResetState(): void {
-    this.tabsState = [];
-    this.cachedTabsState = new Set();
-  }
-
-  @Mutation
-  commitSortTabs({ oldIndex, newIndex }: { oldIndex: number; newIndex: number }): void {
-    const currentTab = this.tabsState[oldIndex];
-
-    this.tabsState.splice(oldIndex, 1);
-    this.tabsState.splice(newIndex, 0, currentTab);
-    this.lastDragEndIndexState = this.lastDragEndIndexState + 1;
-  }
-
-  @Mutation
-  closeMultipleTab({ pathList }: { pathList: string[] }): void {
-    this.tabsState = toRaw(this.tabsState).filter((item) => !pathList.includes(item.fullPath));
-  }
-
-  @Action
-  addTabAction(route: RouteLocationNormalized) {
-    const { path, name } = route;
-    // 404  The page does not need to add a tab
-    if (
-      path === PageEnum.ERROR_PAGE ||
-      !name ||
-      [REDIRECT_ROUTE.name, PAGE_NOT_FOUND_ROUTE.name].includes(name as string)
-    ) {
-      return;
-    }
-    this.commitTabRoutesState(getRawRoute(route));
-
-    this.commitCachedMapState();
-  }
-
-  @Mutation
-  async commitRedoPage() {
-    const route = router.currentRoute.value;
-    const name = route.name;
-
-    const findVal = Array.from(this.cachedTabsState).find((item) => item === name);
-    if (findVal) {
-      this.cachedTabsState.delete(findVal);
-      // this.cachedTabsState.splice(index, 1);
-    }
-    const redo = useRedo();
-    await redo();
-  }
-
-  @Action
-  closeAllTabAction() {
-    this.commitCloseAllTab();
-    this.commitClearCache();
-    this.goToPage();
-  }
-
-  @Action
-  closeTabAction(tab: RouteLocationNormalized) {
-    function getObj(tabItem: RouteLocationNormalized) {
-      const { params, path, query } = tabItem;
-      return {
-        params: params || {},
-        path,
-        query: query || {},
-      };
-    }
-    const { currentRoute, replace } = router;
-
-    const { path } = unref(currentRoute);
-    if (path !== tab.path) {
-      // Closed is not the activation tab
-      this.commitCloseTab(tab);
-      return;
-    }
-
-    // Closed is activated atb
-    let toObj: RouteLocationRaw = {};
-
-    const index = this.getTabsState.findIndex((item) => item.path === path);
-
-    // If the current is the leftmost tab
-    if (index === 0) {
-      // There is only one tab, then jump to the homepage, otherwise jump to the right tab
-      if (this.getTabsState.length === 1) {
-        toObj = PageEnum.BASE_HOME;
-      } else {
-        //  Jump to the right tab
-        const page = this.getTabsState[index + 1];
-        toObj = getObj(page);
-      }
-    } else {
-      // Close the current tab
-      const page = this.getTabsState[index - 1];
-      toObj = getObj(page);
-    }
-    this.commitCloseTab(currentRoute.value);
-    replace(toObj);
-  }
-
-  @Action
-  closeTabByKeyAction(key: string) {
-    const index = this.tabsState.findIndex((item) => (item.fullPath || item.path) === key);
-    index !== -1 && this.closeTabAction(this.tabsState[index]);
-  }
-
-  @Action
-  closeLeftTabAction(route: RouteLocationNormalized): void {
-    const index = this.tabsState.findIndex((item) => item.path === route.path);
-
-    if (index > 0) {
-      const leftTabs = this.tabsState.slice(0, index);
-      const pathList: string[] = [];
-      for (const item of leftTabs) {
-        const affix = item.meta ? item.meta.affix : false;
-        if (!affix) {
-          pathList.push(item.fullPath);
-        }
-      }
-      this.closeMultipleTab({ pathList });
-    }
-    this.commitCachedMapState();
-    isGotoPage();
-  }
-
-  @Action
-  closeRightTabAction(route: RouteLocationNormalized): void {
-    const index = this.tabsState.findIndex((item) => item.fullPath === route.fullPath);
-
-    if (index >= 0 && index < this.tabsState.length - 1) {
-      const rightTabs = this.tabsState.slice(index + 1, this.tabsState.length);
-
-      const pathList: string[] = [];
-      for (const item of rightTabs) {
-        const affix = item.meta ? item.meta.affix : false;
-        if (!affix) {
-          pathList.push(item.fullPath);
-        }
-      }
-      this.closeMultipleTab({ pathList });
-    }
-    this.commitCachedMapState();
-    isGotoPage();
-  }
-
-  @Action
-  closeOtherTabAction(route: RouteLocationNormalized): void {
-    const closePathList = this.tabsState.map((item) => item.fullPath);
-    const pathList: string[] = [];
-    closePathList.forEach((path) => {
-      if (path !== route.fullPath) {
-        const closeItem = this.tabsState.find((item) => item.path === path);
-        if (!closeItem) return;
-        const affix = closeItem.meta ? closeItem.meta.affix : false;
-        if (!affix) {
-          pathList.push(closeItem.fullPath);
-        }
-      }
-    });
-    this.closeMultipleTab({ pathList });
-    this.commitCachedMapState();
-    isGotoPage();
-  }
-}
-export const tabStore = getModule<Tab>(Tab);

+ 118 - 129
src/store/modules/user.ts

@@ -1,141 +1,130 @@
-import type {
-  LoginParams,
-  GetUserInfoByUserIdModel,
-  GetUserInfoByUserIdParams,
-} from '/@/api/sys/model/userModel';
-import type { UserInfo } from '/@/store/types';
+import type { UserInfo } from '/#/store';
+import type { ErrorMessageMode } from '/@/utils/http/axios/types';
 
-import store from '/@/store/index';
-import { VuexModule, Module, getModule, Mutation, Action } from 'vuex-module-decorators';
-import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper';
+import { defineStore } from 'pinia';
+import { store } from '/@/store';
 
-import { PageEnum } from '/@/enums/pageEnum';
 import { RoleEnum } from '/@/enums/roleEnum';
+import { PageEnum } from '/@/enums/pageEnum';
 import { ROLES_KEY, TOKEN_KEY, USER_INFO_KEY } from '/@/enums/cacheEnum';
 
-import { useMessage } from '/@/hooks/web/useMessage';
-
-import router from '/@/router';
+import { getAuthCache, setAuthCache } from '/@/utils/auth';
+import {
+  GetUserInfoByUserIdModel,
+  GetUserInfoByUserIdParams,
+  LoginParams,
+} from '/@/api/sys/model/userModel';
 
-import { loginApi, getUserInfoById } from '/@/api/sys/user';
+import { getUserInfoById, loginApi } from '/@/api/sys/user';
 
 import { useI18n } from '/@/hooks/web/useI18n';
-import { ErrorMessageMode } from '/@/utils/http/axios/types';
-import { getAuthCache, setAuthCache } from '/@/utils/auth/index';
-
-const NAME = 'app-user';
-hotModuleUnregisterModule(NAME);
-
-@Module({ namespaced: true, name: NAME, dynamic: true, store })
-class User extends VuexModule {
-  // user info
-  private userInfoState: UserInfo | null = null;
-
-  // token
-  private tokenState = '';
-
-  // roleList
-  private roleListState: RoleEnum[] = [];
-
-  get getUserInfoState(): UserInfo {
-    return this.userInfoState || getAuthCache<UserInfo>(USER_INFO_KEY) || {};
-  }
-
-  get getTokenState(): string {
-    return this.tokenState || getAuthCache<string>(TOKEN_KEY);
-  }
-
-  get getRoleListState(): RoleEnum[] {
-    return this.roleListState.length > 0 ? this.roleListState : getAuthCache<RoleEnum[]>(ROLES_KEY);
-  }
-
-  @Mutation
-  commitResetState(): void {
-    this.userInfoState = null;
-    this.tokenState = '';
-    this.roleListState = [];
-  }
-
-  @Mutation
-  commitUserInfoState(info: UserInfo): void {
-    this.userInfoState = info;
-    setAuthCache(USER_INFO_KEY, info);
-  }
-
-  @Mutation
-  commitRoleListState(roleList: RoleEnum[]): void {
-    this.roleListState = roleList;
-    setAuthCache(ROLES_KEY, roleList);
-  }
-
-  @Mutation
-  commitTokenState(info: string): void {
-    this.tokenState = info;
-    setAuthCache(TOKEN_KEY, info);
-  }
-
-  /**
-   * @description: login
-   */
-  @Action
-  async login(
-    params: LoginParams & {
-      goHome?: boolean;
-      mode?: ErrorMessageMode;
-    }
-  ): Promise<GetUserInfoByUserIdModel | null> {
-    try {
-      const { goHome = true, mode, ...loginParams } = params;
-      const data = await loginApi(loginParams, mode);
-
-      const { token, userId } = data;
-
-      // save token
-      this.commitTokenState(token);
+import { useMessage } from '/@/hooks/web/useMessage';
+import router from '/@/router';
 
-      // get user info
-      const userInfo = await this.getUserInfoAction({ userId });
+interface UserState {
+  userInfo: Nullable<UserInfo>;
+  token?: string;
+  roleList: RoleEnum[];
+}
 
-      goHome && (await router.replace(PageEnum.BASE_HOME));
+export const useUserStore = defineStore({
+  id: 'app-user',
+  state: (): UserState => ({
+    // user info
+    userInfo: null,
+    // token
+    token: undefined,
+    // roleList
+    roleList: [],
+  }),
+  getters: {
+    getUserInfo(): UserInfo {
+      return this.userInfo || getAuthCache<UserInfo>(USER_INFO_KEY) || {};
+    },
+    getToken(): string {
+      return this.token || getAuthCache<string>(TOKEN_KEY);
+    },
+    getRoleList(): RoleEnum[] {
+      return this.roleList.length > 0 ? this.roleList : getAuthCache<RoleEnum[]>(ROLES_KEY);
+    },
+  },
+  actions: {
+    setToken(info: string) {
+      this.token = info;
+      setAuthCache(TOKEN_KEY, info);
+    },
+    setRoleList(roleList: RoleEnum[]) {
+      this.roleList = roleList;
+      setAuthCache(ROLES_KEY, roleList);
+    },
+    setUserInfo(info: UserInfo) {
+      this.userInfo = info;
+      setAuthCache(USER_INFO_KEY, info);
+    },
+    resetState() {
+      this.userInfo = null;
+      this.token = '';
+      this.roleList = [];
+    },
+    /**
+     * @description: login
+     */
+    async login(
+      params: LoginParams & {
+        goHome?: boolean;
+        mode?: ErrorMessageMode;
+      }
+    ): Promise<GetUserInfoByUserIdModel | null> {
+      try {
+        const { goHome = true, mode, ...loginParams } = params;
+        const data = await loginApi(loginParams, mode);
+        const { token, userId } = data;
+
+        // save token
+        this.setToken(token);
+        // get user info
+        const userInfo = await this.getUserInfoAction({ userId });
+
+        goHome && (await router.replace(PageEnum.BASE_HOME));
+        return userInfo;
+      } catch (error) {
+        return null;
+      }
+    },
+    async getUserInfoAction({ userId }: GetUserInfoByUserIdParams) {
+      const userInfo = await getUserInfoById({ userId });
+      const { roles } = userInfo;
+      const roleList = roles.map((item) => item.value) as RoleEnum[];
+      this.setUserInfo(userInfo);
+      this.setRoleList(roleList);
       return userInfo;
-    } catch (error) {
-      return null;
-    }
-  }
-
-  @Action
-  async getUserInfoAction({ userId }: GetUserInfoByUserIdParams) {
-    const userInfo = await getUserInfoById({ userId });
-    const { roles } = userInfo;
-    const roleList = roles.map((item) => item.value) as RoleEnum[];
-    this.commitUserInfoState(userInfo);
-    this.commitRoleListState(roleList);
-    return userInfo;
-  }
-
-  /**
-   * @description: logout
-   */
-  @Action
-  async logout(goLogin = false) {
-    goLogin && router.push(PageEnum.BASE_LOGIN);
-  }
-
-  /**
-   * @description: Confirm before logging out
-   */
-  @Action
-  async confirmLoginOut() {
-    const { createConfirm } = useMessage();
-    const { t } = useI18n();
-    createConfirm({
-      iconType: 'warning',
-      title: t('sys.app.logoutTip'),
-      content: t('sys.app.logoutMessage'),
-      onOk: async () => {
-        await this.logout(true);
-      },
-    });
-  }
+    },
+    /**
+     * @description: logout
+     */
+    logout(goLogin = false) {
+      goLogin && router.push(PageEnum.BASE_LOGIN);
+    },
+
+    /**
+     * @description: Confirm before logging out
+     */
+    confirmLoginOut() {
+      const { createConfirm } = useMessage();
+      const { t } = useI18n();
+      createConfirm({
+        iconType: 'warning',
+        title: t('sys.app.logoutTip'),
+        content: t('sys.app.logoutMessage'),
+        onOk: async () => {
+          await this.logout(true);
+        },
+      });
+    },
+  },
+});
+
+// Need to be used outside the setup
+export function useUserStoreWidthOut() {
+  return useUserStore(store);
 }
-export const userStore = getModule<User>(User);

+ 0 - 24
src/store/types.ts

@@ -1,24 +0,0 @@
-import { MenuModeEnum, MenuTypeEnum } from '../enums/menuEnum';
-
-export interface LockInfo {
-  pwd: string | undefined;
-  isLock: boolean;
-}
-
-export interface UserInfo {
-  // 用户id
-  userId: string | number;
-  // 用户名
-  username: string;
-  // 真实名字
-  realName: string;
-  // 介绍
-  desc?: string;
-}
-
-export interface BeforeMiniState {
-  menuCollapsed?: boolean;
-  menuSplit?: boolean;
-  menuMode?: MenuModeEnum;
-  menuType?: MenuTypeEnum;
-}

+ 2 - 3
src/utils/cache/persistent.ts

@@ -1,6 +1,5 @@
-import type { LockInfo, UserInfo } from '/@/store/types';
-
-import { ProjectConfig } from '/#/config';
+import type { LockInfo, UserInfo } from '/#/store';
+import type { ProjectConfig } from '/#/config';
 
 import { createLocalStorage, createSessionStorage } from '/@/utils/cache';
 import { Memory } from './memory';

+ 1 - 0
src/utils/env.ts

@@ -35,6 +35,7 @@ export function getAppEnvConfig() {
       `VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.`
     );
   }
+
   return {
     VITE_GLOB_APP_TITLE,
     VITE_GLOB_API_URL,

+ 4 - 2
src/utils/http/axios/index.ts

@@ -16,7 +16,8 @@ import { RequestEnum, ResultEnum, ContentTypeEnum } from '/@/enums/httpEnum';
 import { isString } from '/@/utils/is';
 import { getToken } from '/@/utils/auth';
 import { setObjToUrlParams, deepMerge } from '/@/utils';
-import { errorStore } from '/@/store/modules/error';
+import { useErrorLogStoreWithOut } from '/@/store/modules/errorLog';
+
 import { errorResult } from './const';
 import { useI18n } from '/@/hooks/web/useI18n';
 import { createNow, formatRequestDate } from './helper';
@@ -150,7 +151,8 @@ const transform: AxiosTransform = {
    */
   responseInterceptorsCatch: (error: any) => {
     const { t } = useI18n();
-    errorStore.setupErrorHandle(error);
+    const errorLogStore = useErrorLogStoreWithOut();
+    errorLogStore.addAjaxErrorInfo(error);
     const { response, code, message } = error || {};
     const msg: string = response?.data?.error?.message ?? '';
     const err: string = error?.toString?.() ?? '';

+ 3 - 4
src/views/demo/permission/CurrentPermissionMode.vue

@@ -10,7 +10,7 @@
 </template>
 <script lang="ts">
   import { defineComponent, computed } from 'vue';
-  import { appStore } from '/@/store/modules/app';
+  import { useAppStore } from '/@/store/modules/app';
   import { PermissionModeEnum } from '/@/enums/appEnum';
   import { Divider } from 'ant-design-vue';
   import { usePermission } from '/@/hooks/web/usePermission';
@@ -18,9 +18,8 @@
     name: 'CurrentPermissionMode',
     components: { Divider },
     setup() {
-      const permissionMode = computed(() => {
-        return appStore.getProjectConfig.permissionMode;
-      });
+      const appStore = useAppStore();
+      const permissionMode = computed(() => appStore.getProjectConfig.permissionMode);
       const { togglePermissionMode } = usePermission();
 
       return {

+ 3 - 2
src/views/demo/permission/back/Btn.vue

@@ -5,7 +5,7 @@
     <CurrentPermissionMode />
 
     <p>
-      当前拥有的code列表: <a> {{ permissionStore.getPermCodeListState }} </a>
+      当前拥有的code列表: <a> {{ permissionStore.getPermCodeList }} </a>
     </p>
     <Divider />
     <Alert class="mt-4" type="info" message="点击后请查看按钮变化" show-icon />
@@ -59,7 +59,7 @@
   import CurrentPermissionMode from '../CurrentPermissionMode.vue';
   import { usePermission } from '/@/hooks/web/usePermission';
   import { Authority } from '/@/components/Authority';
-  import { permissionStore } from '/@/store/modules/permission';
+  import { usePermissionStore } from '/@/store/modules/permission';
   import { PermissionModeEnum } from '/@/enums/appEnum';
   import { PageWrapper } from '/@/components/Page';
 
@@ -67,6 +67,7 @@
     components: { Alert, PageWrapper, CurrentPermissionMode, Divider, Authority },
     setup() {
       const { hasPermission } = usePermission();
+      const permissionStore = usePermissionStore();
 
       function changePermissionCode(userId: string) {
         permissionStore.changePermissionCode(userId);

+ 6 - 4
src/views/demo/permission/front/Btn.vue

@@ -8,7 +8,7 @@
     <CurrentPermissionMode />
 
     <p>
-      当前角色: <a> {{ userStore.getRoleListState }} </a>
+      当前角色: <a> {{ userStore.getRoleList }} </a>
     </p>
     <Alert class="mt-4" type="info" message="点击后请查看按钮变化" show-icon />
 
@@ -63,7 +63,7 @@
   import { computed, defineComponent } from 'vue';
   import { Alert, Divider } from 'ant-design-vue';
   import CurrentPermissionMode from '../CurrentPermissionMode.vue';
-  import { userStore } from '/@/store/modules/user';
+  import { useUserStore } from '/@/store/modules/user';
   import { RoleEnum } from '/@/enums/roleEnum';
   import { usePermission } from '/@/hooks/web/usePermission';
   import { Authority } from '/@/components/Authority';
@@ -73,11 +73,13 @@
     components: { Alert, PageWrapper, CurrentPermissionMode, Divider, Authority },
     setup() {
       const { changeRole, hasPermission } = usePermission();
+      const userStore = useUserStore();
+
       return {
         userStore,
         RoleEnum,
-        isSuper: computed(() => userStore.getRoleListState.includes(RoleEnum.SUPER)),
-        isTest: computed(() => userStore.getRoleListState.includes(RoleEnum.TEST)),
+        isSuper: computed(() => userStore.getRoleList.includes(RoleEnum.SUPER)),
+        isTest: computed(() => userStore.getRoleList.includes(RoleEnum.TEST)),
         changeRole,
         hasPermission,
       };

+ 6 - 4
src/views/demo/permission/front/index.vue

@@ -8,7 +8,7 @@
     <CurrentPermissionMode />
 
     <p>
-      当前角色: <a> {{ userStore.getRoleListState }} </a>
+      当前角色: <a> {{ userStore.getRoleList }} </a>
     </p>
     <Alert class="mt-4" type="info" message="点击后请查看左侧菜单变化" show-icon />
 
@@ -29,7 +29,7 @@
   import { computed, defineComponent } from 'vue';
   import { Alert } from 'ant-design-vue';
   import CurrentPermissionMode from '../CurrentPermissionMode.vue';
-  import { userStore } from '/@/store/modules/user';
+  import { useUserStore } from '/@/store/modules/user';
   import { RoleEnum } from '/@/enums/roleEnum';
   import { usePermission } from '/@/hooks/web/usePermission';
   import { PageWrapper } from '/@/components/Page';
@@ -38,11 +38,13 @@
     components: { Alert, CurrentPermissionMode, PageWrapper },
     setup() {
       const { changeRole } = usePermission();
+      const userStore = useUserStore();
+
       return {
         userStore,
         RoleEnum,
-        isSuper: computed(() => userStore.getRoleListState.includes(RoleEnum.SUPER)),
-        isTest: computed(() => userStore.getRoleListState.includes(RoleEnum.TEST)),
+        isSuper: computed(() => userStore.getRoleList.includes(RoleEnum.SUPER)),
+        isTest: computed(() => userStore.getRoleList.includes(RoleEnum.TEST)),
         changeRole,
       };
     },

+ 3 - 4
src/views/sys/error-log/DetailModal.vue

@@ -5,6 +5,7 @@
 </template>
 <script lang="ts">
   import type { PropType } from 'vue';
+  import type { ErrorLogInfo } from '/#/store';
 
   import { defineComponent } from 'vue';
   import { BasicModal } from '/@/components/Modal/index';
@@ -12,8 +13,6 @@
 
   import { useI18n } from '/@/hooks/web/useI18n';
 
-  import { ErrorInfo } from '/@/store/modules/error';
-
   import { getDescSchema } from './data';
 
   export default defineComponent({
@@ -21,7 +20,7 @@
     components: { BasicModal, Description },
     props: {
       info: {
-        type: Object as PropType<ErrorInfo>,
+        type: Object as PropType<ErrorLogInfo>,
         default: null,
       },
     },
@@ -30,7 +29,7 @@
 
       const [register] = useDescription({
         column: 2,
-        schema: getDescSchema(),
+        schema: getDescSchema()!,
       });
 
       return {

+ 1 - 1
src/views/sys/error-log/data.tsx

@@ -57,7 +57,7 @@ export function getColumns(): BasicColumn[] {
   ];
 }
 
-export function getDescSchema() {
+export function getDescSchema(): any {
   return getColumns().map((column) => {
     return {
       field: column.dataIndex!,

+ 7 - 5
src/views/sys/error-log/index.vue

@@ -28,6 +28,8 @@
 </template>
 
 <script lang="ts">
+  import type { ErrorLogInfo } from '/#/store';
+
   import { defineComponent, watch, ref, nextTick } from 'vue';
 
   import DetailModal from './DetailModal.vue';
@@ -37,7 +39,7 @@
   import { useMessage } from '/@/hooks/web/useMessage';
   import { useI18n } from '/@/hooks/web/useI18n';
 
-  import { errorStore, ErrorInfo } from '/@/store/modules/error';
+  import { useErrorLogStore } from '/@/store/modules/errorLog';
 
   import { fireErrorApi } from '/@/api/demo/error';
 
@@ -49,11 +51,11 @@
     name: 'ErrorHandler',
     components: { DetailModal, BasicTable, TableAction },
     setup() {
-      const rowInfo = ref<ErrorInfo>();
+      const rowInfo = ref<ErrorLogInfo>();
       const imgList = ref<string[]>([]);
 
       const { t } = useI18n();
-
+      const errorLogStore = useErrorLogStore();
       const [register, { setTableData }] = useTable({
         title: t('sys.errorLog.tableTitle'),
         columns: getColumns(),
@@ -67,7 +69,7 @@
       const [registerModal, { openModal }] = useModal();
 
       watch(
-        () => errorStore.getErrorInfoState,
+        () => errorLogStore.getErrorLogInfoList,
         (list) => {
           nextTick(() => {
             setTableData(cloneDeep(list));
@@ -82,7 +84,7 @@
         createMessage.info(t('sys.errorLog.enableMessage'));
       }
       // 查看详情
-      function handleDetail(row: ErrorInfo) {
+      function handleDetail(row: ErrorLogInfo) {
         rowInfo.value = row;
         openModal(true);
       }

+ 21 - 19
src/views/sys/lock/LockPage.vue

@@ -38,7 +38,7 @@
             class="enter-x"
             v-model:value="password"
           />
-          <span :class="`${prefixCls}-entry__err-msg enter-x`" v-if="errMsgRef">
+          <span :class="`${prefixCls}-entry__err-msg enter-x`" v-if="errMsg">
             {{ t('sys.lock.alert') }}
           </span>
           <div :class="`${prefixCls}-entry__footer enter-x`">
@@ -46,7 +46,7 @@
               type="link"
               size="small"
               class="mt-2 mr-2 enter-x"
-              :disabled="loadingRef"
+              :disabled="loading"
               @click="handleShowForm(true)"
             >
               {{ t('common.back') }}
@@ -55,12 +55,12 @@
               type="link"
               size="small"
               class="mt-2 mr-2 enter-x"
-              :disabled="loadingRef"
+              :disabled="loading"
               @click="goLogin"
             >
               {{ t('sys.lock.backToLogin') }}
             </a-button>
-            <a-button class="mt-2" type="link" size="small" @click="unLock()" :loading="loadingRef">
+            <a-button class="mt-2" type="link" size="small" @click="unLock()" :loading="loading">
               {{ t('sys.lock.entry') }}
             </a-button>
           </div>
@@ -80,8 +80,8 @@
   import { defineComponent, ref, computed } from 'vue';
   import { Input } from 'ant-design-vue';
 
-  import { userStore } from '/@/store/modules/user';
-  import { lockStore } from '/@/store/modules/lock';
+  import { useUserStore } from '/@/store/modules/user';
+  import { useLockStore } from '/@/store/modules/lock';
   import { useI18n } from '/@/hooks/web/useI18n';
 
   import { useNow } from './useNow';
@@ -95,19 +95,21 @@
     components: { LockOutlined, InputPassword: Input.Password },
 
     setup() {
-      const passwordRef = ref('');
-      const loadingRef = ref(false);
-      const errMsgRef = ref(false);
+      const password = ref('');
+      const loading = ref(false);
+      const errMsg = ref(false);
       const showDate = ref(true);
 
       const { prefixCls } = useDesign('lock-page');
+      const lockStore = useLockStore();
+      const userStore = useUserStore();
 
       const { ...state } = useNow(true);
 
       const { t } = useI18n();
 
       const realName = computed(() => {
-        const { realName } = userStore.getUserInfoState || {};
+        const { realName } = userStore.getUserInfo || {};
         return realName;
       });
 
@@ -115,16 +117,16 @@
        * @description: unLock
        */
       async function unLock() {
-        if (!passwordRef.value) {
+        if (!password.value) {
           return;
         }
-        let password = passwordRef.value;
+        let pwd = password.value;
         try {
-          loadingRef.value = true;
-          const res = await lockStore.unLockAction({ password });
-          errMsgRef.value = !res;
+          loading.value = true;
+          const res = await lockStore.unLock(pwd);
+          errMsg.value = !res;
         } finally {
-          loadingRef.value = false;
+          loading.value = false;
         }
       }
 
@@ -141,12 +143,12 @@
         goLogin,
         realName,
         unLock,
-        errMsgRef,
-        loadingRef,
+        errMsg,
+        loading,
         t,
         prefixCls,
         showDate,
-        password: passwordRef,
+        password,
         handleShowForm,
         headerImg,
         ...state,

+ 3 - 7
src/views/sys/lock/index.vue

@@ -7,17 +7,13 @@
   import { defineComponent, computed } from 'vue';
   import LockPage from './LockPage.vue';
 
-  import { lockStore } from '/@/store/modules/lock';
+  import { useLockStore } from '/@/store/modules/lock';
   export default defineComponent({
     name: 'Lock',
     components: { LockPage },
     setup() {
-      const getIsLock = computed(() => {
-        const { getLockInfo } = lockStore;
-        const { isLock } = getLockInfo;
-        return isLock;
-      });
-
+      const lockStore = useLockStore();
+      const getIsLock = computed(() => lockStore?.getLockInfo?.isLock ?? false);
       return { getIsLock };
     },
   });

+ 2 - 1
src/views/sys/lock/useNow.ts

@@ -1,9 +1,10 @@
 import { dateUtil } from '/@/utils/dateUtil';
 import { reactive, toRefs } from 'vue';
-import { localeStore } from '/@/store/modules/locale';
+import { useLocaleStore } from '/@/store/modules/locale';
 import { tryOnMounted, tryOnUnmounted } from '@vueuse/core';
 
 export function useNow(immediate = true) {
+  const localeStore = useLocaleStore();
   const localData = dateUtil.localeData(localeStore.getLocale);
   let timer: IntervalHandle;
 

+ 2 - 1
src/views/sys/login/Login.vue

@@ -58,7 +58,7 @@
   import { useGlobSetting } from '/@/hooks/setting';
   import { useI18n } from '/@/hooks/web/useI18n';
   import { useDesign } from '/@/hooks/web/useDesign';
-  import { localeStore } from '/@/store/modules/locale';
+  import { useLocaleStore } from '/@/store/modules/locale';
 
   export default defineComponent({
     name: 'Login',
@@ -76,6 +76,7 @@
       const globSetting = useGlobSetting();
       const { prefixCls } = useDesign('login');
       const { t } = useI18n();
+      const localeStore = useLocaleStore();
 
       return {
         t,

+ 2 - 1
src/views/sys/login/LoginForm.vue

@@ -85,7 +85,7 @@
   import { useI18n } from '/@/hooks/web/useI18n';
   import { useMessage } from '/@/hooks/web/useMessage';
 
-  import { userStore } from '/@/store/modules/user';
+  import { useUserStore } from '/@/store/modules/user';
   import { LoginStateEnum, useLoginState, useFormRules, useFormValid } from './useLogin';
   import { useDesign } from '/@/hooks/web/useDesign';
   import { useKeyPress } from '/@/hooks/event/useKeyPress';
@@ -114,6 +114,7 @@
       const { t } = useI18n();
       const { notification } = useMessage();
       const { prefixCls } = useDesign('login');
+      const userStore = useUserStore();
 
       const { setLoginState, getLoginState } = useLoginState();
       const { getFormRules } = useFormRules();

+ 44 - 0
types/store.ts

@@ -0,0 +1,44 @@
+import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
+import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
+
+// Lock screen information
+export interface LockInfo {
+  // Password required
+  pwd?: string | undefined;
+  // Is it locked?
+  isLock?: boolean;
+}
+
+// Error-log information
+export interface ErrorLogInfo {
+  // Type of error
+  type: ErrorTypeEnum;
+  // Error file
+  file: string;
+  // Error name
+  name?: string;
+  // Error message
+  message: string;
+  // Error stack
+  stack?: string;
+  // Error detail
+  detail: string;
+  // Error url
+  url: string;
+  // Error time
+  time?: string;
+}
+
+export interface UserInfo {
+  userId: string | number;
+  username: string;
+  realName: string;
+  desc?: string;
+}
+
+export interface BeforeMiniState {
+  menuCollapsed?: boolean;
+  menuSplit?: boolean;
+  menuMode?: MenuModeEnum;
+  menuType?: MenuTypeEnum;
+}

+ 38 - 53
yarn.lock

@@ -1762,25 +1762,25 @@
   resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.0.11.tgz#20d22dd0da7d358bb21c17f9bde8628152642c77"
   integrity sha512-b+zB8A2so8eCE0JsxjL24J7vdGl8rzPQ09hZNhystm+KqSbKcAej1A+Hbva1rCMmTTqA+hFnUSDc5kouEo0JzA==
 
-"@vueuse/core@^4.8.0":
-  version "4.8.0"
-  resolved "https://registry.npmjs.org/@vueuse/core/-/core-4.8.0.tgz#d86e36956521c0f9b6571cb58b27f0e2535259b3"
-  integrity sha512-nUH4Hn1DN4kkuF1r5ZcfGnjoAKDD0Kw9oFnt/TUo1aueNijq4KujagtoQN8OC4Pei10TeTDdqhmZAWnaCE1NbA==
+"@vueuse/core@^4.8.1":
+  version "4.8.1"
+  resolved "https://registry.npmjs.org/@vueuse/core/-/core-4.8.1.tgz#d7a7fb2e72610d1962ecb9244bd93dacb96d921c"
+  integrity sha512-oXFEDaKNU69Rj20/Hd7ZlmTpEtA2M19cRkZaL4A0Nl0w5Wb5In/8aK+0vtdi1VyMUXXbq6h1OGKCJcIhg5cziA==
   dependencies:
-    "@vueuse/shared" "4.8.0"
+    "@vueuse/shared" "4.8.1"
     vue-demi latest
 
-"@vueuse/shared@4.8.0":
-  version "4.8.0"
-  resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-4.8.0.tgz#abf3da96ca81b4be82e885928193fef2c676cdbc"
-  integrity sha512-g1lSbHD4ptiS74qBUvffJ98QjRsoCH7ILjxVzJF488EPAmp5z3taLnoggt6NXfonnYve7fEPuqsJqd2BLOxT1A==
+"@vueuse/shared@4.8.1":
+  version "4.8.1"
+  resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-4.8.1.tgz#45fd5f64bf4e8944db42a5b72fa2705cfc74608a"
+  integrity sha512-ONKJoIvZPrGCA8loK7dX+ZcjgZLikI+vPiz1lWlXs6+jZiQiZSLkmvg1NjV6Cfb6OqbDCfEScTWLbZHB7EwrRw==
   dependencies:
     vue-demi latest
 
-"@windicss/plugin-utils@0.12.5":
-  version "0.12.5"
-  resolved "https://registry.npmjs.org/@windicss/plugin-utils/-/plugin-utils-0.12.5.tgz#d03517d1ae7a48b5b459e3d670e873d38b63e4a1"
-  integrity sha512-4ux2o4s6D/gRTD68os41oxs/0NFk/eSJxHhZL9nN2wy4RGt+pPMQJyOHV56l7zDh9B0ywU5+ZRxDjdw2cl5Yvg==
+"@windicss/plugin-utils@0.13.1":
+  version "0.13.1"
+  resolved "https://registry.npmjs.org/@windicss/plugin-utils/-/plugin-utils-0.13.1.tgz#e0e172855ebcf0b8a5f0f358befdcaf44bae5cf1"
+  integrity sha512-Vr7f7yWxmB5AWwe+iDPV3JbhTlZHbDvM89IfJ0hyP6PqYmZNTtUfMXMbHXZJHVAbQ54dWBMG23WmeC9X327ETA==
   dependencies:
     debug "^4.3.2"
     fast-glob "^3.2.5"
@@ -1788,7 +1788,7 @@
     micromatch "^4.0.2"
     pirates "^4.0.1"
     sucrase "^3.17.1"
-    windicss "^2.5.11"
+    windicss "^2.5.12"
 
 "@zxcvbn-ts/core@^0.3.0":
   version "0.3.0"
@@ -3670,21 +3670,11 @@ esbuild-register@^2.2.0:
     esbuild "^0.9.2"
     jsonc-parser "^3.0.0"
 
-esbuild@^0.11.4:
-  version "0.11.5"
-  resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.11.5.tgz#25b18a2ff2fb9580683edce26a48f64c08c2f2df"
-  integrity sha512-aRs6jAE+bVRp1tyfzUugAw1T/Y0Fwzp4Z2ROikF3h+UifoD5QlEbEYQGc6orNnnSIRhWR5VWBH7LozlAumaLHg==
-
-esbuild@^0.11.6:
+esbuild@^0.11.4, esbuild@^0.11.6, esbuild@^0.9.2, esbuild@^0.9.3:
   version "0.11.6"
   resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.11.6.tgz#20961309c4cfed00b71027e18806150358d0cbb0"
   integrity sha512-L+nKW9ftVS/N2CVJMR9YmXHbkm+vHzlNYuo09rzipQhF7dYNvRLfWoEPSDRTl10and4owFBV9rJ2CTFNtLIOiw==
 
-esbuild@^0.9.2, esbuild@^0.9.3:
-  version "0.9.7"
-  resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.9.7.tgz#ea0d639cbe4b88ec25fbed4d6ff00c8d788ef70b"
-  integrity sha512-VtUf6aQ89VTmMLKrWHYG50uByMF4JQlVysb8dmg6cOgW8JnFCipmz7p+HNBl+RR3LLCuBxFGVauAe2wfnF9bLg==
-
 escalade@^3.1.1:
   version "3.1.1"
   resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
@@ -3752,10 +3742,10 @@ eslint-visitor-keys@^2.0.0:
   resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8"
   integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
 
-eslint@^7.23.0:
-  version "7.23.0"
-  resolved "https://registry.npmjs.org/eslint/-/eslint-7.23.0.tgz#8d029d252f6e8cf45894b4bee08f5493f8e94325"
-  integrity sha512-kqvNVbdkjzpFy0XOszNwjkKzZ+6TcwCQ/h+ozlcIWwaimBBuhlQ4nN6kbiM2L+OjDcznkTJxzYfRFH92sx4a0Q==
+eslint@^7.24.0:
+  version "7.24.0"
+  resolved "https://registry.npmjs.org/eslint/-/eslint-7.24.0.tgz#2e44fa62d93892bfdb100521f17345ba54b8513a"
+  integrity sha512-k9gaHeHiFmGCDQ2rEfvULlSLruz6tgfA8DEn+rY9/oYPFFTlz55mM/Q/Rij1b2Y42jwZiK3lXvNTw6w6TXzcKQ==
   dependencies:
     "@babel/code-frame" "7.12.11"
     "@eslint/eslintrc" "^0.4.0"
@@ -6942,6 +6932,11 @@ pify@^4.0.1:
   resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
   integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
 
+pinia@^2.0.0-alpha.12:
+  version "2.0.0-alpha.12"
+  resolved "https://registry.npmjs.org/pinia/-/pinia-2.0.0-alpha.12.tgz#690e9a7b4c176bb9d95fe0dc8ec4ab8847b09493"
+  integrity sha512-qmcDpuoAwxQKAVp7/cOkXFYDaja+vyXMWR6kvdyzeJcGGMvZf1HQ2xFhUSW5lf1eW5IiQP0cBRdF3ZDyVa+JIQ==
+
 pinkie-promise@^2.0.0:
   version "2.0.1"
   resolved "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
@@ -7691,10 +7686,10 @@ rollup-plugin-visualizer@5.3.0:
     source-map "^0.7.3"
     yargs "^16.2.0"
 
-rollup@^2.25.0, rollup@^2.38.5, rollup@^2.44.0:
-  version "2.44.0"
-  resolved "https://registry.npmjs.org/rollup/-/rollup-2.44.0.tgz#8da324d1c4fd12beef9ae6e12f4068265b6d95eb"
-  integrity sha512-rGSF4pLwvuaH/x4nAS+zP6UNn5YUDWf/TeEU5IoXSZKBbKRNTCI3qMnYXKZgrC0D2KzS2baiOZt1OlqhMu5rnQ==
+rollup@^2.25.0, rollup@^2.38.5, rollup@^2.44.0, rollup@^2.45.1:
+  version "2.45.1"
+  resolved "https://registry.npmjs.org/rollup/-/rollup-2.45.1.tgz#eae2b94dc2088b4e0a3b7197a5a1ee0bdd589d5c"
+  integrity sha512-vPD+JoDj3CY8k6m1bLcAFttXMe78P4CMxoau0iLVS60+S9kLsv2379xaGy4NgYWu+h2WTlucpoLPAoUoixFBag==
   optionalDependencies:
     fsevents "~2.3.1"
 
@@ -9187,15 +9182,15 @@ vite-plugin-theme@^0.6.3:
     esbuild-plugin-alias "^0.1.2"
     tinycolor2 "^1.4.2"
 
-vite-plugin-windicss@0.12.5:
-  version "0.12.5"
-  resolved "https://registry.npmjs.org/vite-plugin-windicss/-/vite-plugin-windicss-0.12.5.tgz#74a5043db3615fe432855f6ecff13be36f7a6843"
-  integrity sha512-M/eEA+x94kxZNpEEkJLdY7M6Lp3WFhN0Kb/a2zhdPxBviMwaHSA5A7fUqN1xTYMxlQe4xM7D7naxL7EpnSNlmg==
+vite-plugin-windicss@0.13.1:
+  version "0.13.1"
+  resolved "https://registry.npmjs.org/vite-plugin-windicss/-/vite-plugin-windicss-0.13.1.tgz#82a488f3395be710ae2166b83b0612a5eaec7738"
+  integrity sha512-WmFfTLTMSY5gRC3MWX9o72Yni2HRdrtJ2im+cCyZ2W/p4WE6T702zFCScO8Tnz/E08GDx4OH6oFCZWeZYwgxzg==
   dependencies:
-    "@windicss/plugin-utils" "0.12.5"
+    "@windicss/plugin-utils" "0.13.1"
     chalk "^4.1.0"
     debug "^4.3.2"
-    windicss "^2.5.11"
+    windicss "^2.5.12"
 
 vite@2.1.5:
   version "2.1.5"
@@ -9265,16 +9260,6 @@ vue@^3.0.0:
     "@vue/runtime-dom" "3.0.10"
     "@vue/shared" "3.0.10"
 
-vuex-module-decorators@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.npmjs.org/vuex-module-decorators/-/vuex-module-decorators-1.0.1.tgz#d34dafb5428a3636f1c26d3d014c15fc9659ccd0"
-  integrity sha512-FLWZsXV5XAtl/bcKUyQFpnSBtpc3wK/7zSdy9oKbyp71mZd4ut5y2zSd219wWW9OG7WUOlVwac4rXFFDVnq7ug==
-
-vuex@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.npmjs.org/vuex/-/vuex-4.0.0.tgz#ac877aa76a9c45368c979471e461b520d38e6cf5"
-  integrity sha512-56VPujlHscP5q/e7Jlpqc40sja4vOhC4uJD1llBCWolVI8ND4+VzisDVkUMl+z5y0MpIImW6HjhNc+ZvuizgOw==
-
 warning@^4.0.0:
   version "4.0.3"
   resolved "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
@@ -9326,10 +9311,10 @@ which@^2.0.1:
   dependencies:
     isexe "^2.0.0"
 
-windicss@^2.5.11:
-  version "2.5.11"
-  resolved "https://registry.npmjs.org/windicss/-/windicss-2.5.11.tgz#dd4027c724c7b12a37746d1474b96a52239157d1"
-  integrity sha512-u7b4rOPb8MwO1glkf0gdDygZ+lIzXb/PYLNjqni5Fe2684DCEt6dWTKdk3iMxXgbKoqRNncKu7xt3pFwXHdSAw==
+windicss@^2.5.12:
+  version "2.5.12"
+  resolved "https://registry.npmjs.org/windicss/-/windicss-2.5.12.tgz#7bc469b05d7a8fa3905d49d6521a1ff9107d0ea4"
+  integrity sha512-BZ0Ps1C0RlCHBVOPcw/DAReeR9o/mKaoFgkBsVphQ23M5nsvVfVXgGlNJZssjAQsXnlDpj97pnIhtDn1ENBjXw==
 
 wmf@~1.0.1:
   version "1.0.2"