浏览代码

wip: refactor layout

vben 4 年之前
父节点
当前提交
ba068ba1df
共有 79 个文件被更改,包括 1381 次插入1184 次删除
  1. 2 2
      .eslintrc.js
  2. 4 3
      src/components/Application/index.ts
  3. 0 60
      src/components/Application/src/AppFooterToolbar.vue
  4. 26 5
      src/components/Application/src/AppLocalPicker.vue
  5. 72 0
      src/components/Application/src/AppLogo.vue
  6. 46 0
      src/components/Application/src/AppPageFooter.vue
  7. 6 10
      src/components/Authority/src/index.vue
  8. 1 1
      src/components/Description/src/index.tsx
  9. 1 1
      src/components/Dropdown/src/Dropdown.tsx
  10. 1 1
      src/components/Excel/src/ImportExcel.vue
  11. 1 1
      src/components/Form/src/componentMap.ts
  12. 1 1
      src/components/Form/src/hooks/useLabelWidth.ts
  13. 2 2
      src/components/Form/src/types/formItem.ts
  14. 5 1
      src/components/Menu/index.ts
  15. 15 13
      src/components/Menu/src/BasicMenu.tsx
  16. 3 3
      src/components/Menu/src/MenuContent.tsx
  17. 4 3
      src/components/Menu/src/hooks/useOpenKeys.ts
  18. 1 1
      src/components/Menu/src/hooks/useSearchInput.ts
  19. 5 5
      src/components/Menu/src/props.ts
  20. 0 0
      src/components/Menu/src/types.ts
  21. 1 1
      src/components/Modal/src/props.ts
  22. 1 1
      src/components/Table/src/types/column.ts
  23. 1 1
      src/components/Table/src/types/pagination.ts
  24. 1 1
      src/components/Tinymce/src/plugins.ts
  25. 2 0
      src/components/registerGlobComp.ts
  26. 3 0
      src/components/types.ts
  27. 1 1
      src/design/var/index.less
  28. 2 0
      src/enums/pageEnum.ts
  29. 20 0
      src/hooks/core/useDebouncedRef.ts
  30. 0 0
      src/hooks/setting/index.ts
  31. 67 0
      src/hooks/setting/useHeaderSetting.ts
  32. 5 13
      src/hooks/setting/useLocaleSetting.ts
  33. 120 0
      src/hooks/setting/useMenuSetting.ts
  34. 25 0
      src/hooks/setting/useMultipleTabSetting.ts
  35. 53 0
      src/hooks/setting/useRootSetting.ts
  36. 2 2
      src/hooks/web/useLocale.ts
  37. 1 0
      src/hooks/web/usePage.ts
  38. 0 20
      src/hooks/web/useSideBar.ts
  39. 0 214
      src/layouts/default/LayoutSideBar.tsx
  40. 14 16
      src/layouts/default/LayoutTrigger.tsx
  41. 50 39
      src/layouts/default/header/LayoutBreadcrumb.tsx
  42. 155 180
      src/layouts/default/header/LayoutHeader.tsx
  43. 0 1
      src/layouts/default/header/LockActionItem.less
  44. 14 22
      src/layouts/default/header/LockActionItem.tsx
  45. 47 33
      src/layouts/default/header/UserDropdown.tsx
  46. 6 2
      src/layouts/default/header/notice/NoticeActionItem.vue
  47. 12 24
      src/layouts/default/header/notice/NoticeList.vue
  48. 0 8
      src/layouts/default/header/notice/data.ts
  49. 0 43
      src/layouts/default/index.less
  50. 4 4
      src/layouts/default/index.tsx
  51. 72 171
      src/layouts/default/menu/LayoutMenu.tsx
  52. 0 12
      src/layouts/default/menu/index.less
  53. 113 0
      src/layouts/default/menu/useLayoutMenu.ts
  54. 1 1
      src/layouts/default/multitabs/index.tsx
  55. 77 0
      src/layouts/default/sider/LayoutSideBar.tsx
  56. 44 0
      src/layouts/default/sider/index.less
  57. 163 0
      src/layouts/default/sider/useLayoutSider.tsx
  58. 19 19
      src/layouts/iframe/useFrameKeepAlive.ts
  59. 0 105
      src/layouts/logo/index.vue
  60. 39 36
      src/layouts/page/index.tsx
  61. 5 7
      src/layouts/page/useTransition.ts
  62. 1 1
      src/main.ts
  63. 1 1
      src/router/guard/index.ts
  64. 1 1
      src/router/menus/index.ts
  65. 10 3
      src/settings/projectSetting.ts
  66. 1 1
      src/setup/error-handle/index.ts
  67. 1 1
      src/setup/i18n/index.ts
  68. 1 1
      src/store/modules/error.ts
  69. 0 67
      src/store/modules/menu.ts
  70. 0 5
      src/store/modules/permission.ts
  71. 0 1
      src/types/config.d.ts
  72. 2 1
      src/utils/file/download.ts
  73. 1 1
      src/utils/helper/envHelper.ts
  74. 1 1
      src/utils/http/axios/index.ts
  75. 14 0
      src/utils/index.ts
  76. 3 1
      src/views/demo/feat/icon/index.vue
  77. 4 4
      src/views/demo/page/form/high/index.vue
  78. 1 1
      src/views/sys/login/Login.vue
  79. 3 3
      vite.config.ts

+ 2 - 2
.eslintrc.js

@@ -23,7 +23,7 @@ module.exports = {
     '@typescript-eslint/no-empty-function': 'off',
     'vue/custom-event-name-casing': 'off',
     'no-use-before-define': 'off',
-    // 'no-use-before-define': [
+    // 'no-setting-before-define': [
     //   'error',
     //   {
     //     functions: false,
@@ -31,7 +31,7 @@ module.exports = {
     //   },
     // ],
     '@typescript-eslint/no-use-before-define': 'off',
-    // '@typescript-eslint/no-use-before-define': [
+    // '@typescript-eslint/no-setting-before-define': [
     //   'error',
     //   {
     //     functions: false,

+ 4 - 3
src/components/Application/index.ts

@@ -1,7 +1,8 @@
 import AppLocalPicker from './src/AppLocalPicker.vue';
-import AppFooterToolbar from './src/AppFooterToolbar.vue';
+import AppPageFooter from './src/AppPageFooter.vue';
+import AppLogo from './src/AppLogo.vue';
 import { withInstall } from '../util';
 
-export { AppLocalPicker, AppFooterToolbar };
+export { AppLocalPicker, AppPageFooter, AppLogo };
 
-export default withInstall(AppLocalPicker, AppFooterToolbar);
+export default withInstall(AppLocalPicker, AppPageFooter, AppLogo);

+ 0 - 60
src/components/Application/src/AppFooterToolbar.vue

@@ -1,60 +0,0 @@
-<template>
-  <div class="app-footer" :style="{ width: getWidth }">
-    <div class="app-footer__left">
-      <slot name="left" />
-    </div>
-    <div class="app-footer__right">
-      <slot name="right" />
-    </div>
-  </div>
-</template>
-<script lang="ts">
-  import { defineComponent, computed, unref } from 'vue';
-
-  import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum';
-
-  import { appStore } from '/@/store/modules/app';
-  import { menuStore } from '/@/store/modules/menu';
-
-  export default defineComponent({
-    name: 'AppFooterToolbar',
-    setup() {
-      const getMiniWidth = computed(() => {
-        const {
-          menuSetting: { collapsedShowTitle },
-        } = appStore.getProjectConfig;
-        return collapsedShowTitle ? SIDE_BAR_SHOW_TIT_MINI_WIDTH : SIDE_BAR_MINI_WIDTH;
-      });
-
-      const getWidth = computed(() => {
-        const { getCollapsedState, getMenuWidthState } = menuStore;
-        const width = getCollapsedState ? unref(getMiniWidth) : getMenuWidthState;
-        return `calc(100% - ${width}px)`;
-      });
-
-      return { getWidth };
-    },
-  });
-</script>
-<style lang="less" scoped>
-  .app-footer {
-    position: fixed;
-    right: 0;
-    bottom: 0;
-    z-index: 99;
-    display: flex;
-    width: 100%;
-    align-items: center;
-    padding: 0 24px;
-    line-height: 44px;
-    background: #fff;
-    border-top: 1px solid #f0f0f0;
-    box-shadow: 0 -6px 16px -8px rgba(0, 0, 0, 0.08), 0 -9px 28px 0 rgba(0, 0, 0, 0.05),
-      0 -12px 48px 16px rgba(0, 0, 0, 0.03);
-    transition: width 0.3s;
-
-    &__left {
-      flex: 1 1;
-    }
-  }
-</style>

+ 26 - 5
src/components/Application/src/AppLocalPicker.vue

@@ -5,29 +5,44 @@
     :selectedKeys="selectedKeys"
     @menuEvent="handleMenuEvent"
   >
-    <GlobalOutlined class="app-locale" />
+    <span class="app-local-picker">
+      <GlobalOutlined class="app-local-picker__icon" />
+      <span v-if="showText">{{ getLangText }}</span>
+    </span>
   </Dropdown>
 </template>
 <script lang="ts">
-  import { defineComponent, ref, watchEffect, unref } from 'vue';
+  import { defineComponent, ref, watchEffect, unref, computed } from 'vue';
 
   import { Dropdown, DropMenu } from '/@/components/Dropdown';
   import { GlobalOutlined } from '@ant-design/icons-vue';
 
   import { useLocale } from '/@/hooks/web/useLocale';
-  import { useLocaleSetting } from '/@/settings/use/useLocaleSetting';
+  import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting';
 
   import { LocaleType } from '/@/locales/types';
 
   export default defineComponent({
     name: 'AppLocalPicker',
     components: { GlobalOutlined, Dropdown },
+    props: {
+      showText: {
+        type: Boolean,
+        default: true,
+      },
+    },
     setup() {
       const { localeList } = useLocaleSetting();
       const selectedKeys = ref<string[]>([]);
 
       const { changeLocale, getLang } = useLocale();
 
+      const getLangText = computed(() => {
+        const key = selectedKeys.value[0];
+        if (!key) return '';
+        return localeList.find((item) => item.event === key)?.text;
+      });
+
       watchEffect(() => {
         selectedKeys.value = [unref(getLang)];
       });
@@ -41,13 +56,19 @@
         toggleLocale(menu.event as string);
       }
 
-      return { localeList, handleMenuEvent, selectedKeys };
+      return { localeList, handleMenuEvent, selectedKeys, getLangText };
     },
   });
 </script>
 
 <style lang="less" scoped>
-  .app-locale {
+  .app-local-picker {
+    display: flex;
+    align-items: center;
     cursor: pointer;
+
+    &__icon {
+      margin-right: 4px;
+    }
   }
 </style>

+ 72 - 0
src/components/Application/src/AppLogo.vue

@@ -0,0 +1,72 @@
+<template>
+  <div class="app-logo anticon" :class="theme" @click="handleGoHome">
+    <img src="/@/assets/images/logo.png" />
+    <div class="app-logo__title ml-2 ellipsis">{{ globSetting.title }}</div>
+  </div>
+</template>
+<script lang="ts">
+  import type { PropType } from 'vue';
+  import { defineComponent } from 'vue';
+
+  import { useGlobSetting } from '/@/hooks/setting';
+  import { useGo } from '/@/hooks/web/usePage';
+
+  import { PageEnum } from '/@/enums/pageEnum';
+
+  export default defineComponent({
+    name: 'AppLogo',
+    props: {
+      /**
+       * The theme of the current parent component
+       */
+      theme: {
+        type: String as PropType<string>,
+      },
+    },
+    setup() {
+      const globSetting = useGlobSetting();
+      const go = useGo();
+
+      function handleGoHome(): void {
+        go(PageEnum.BASE_HOME);
+      }
+
+      return {
+        handleGoHome,
+        globSetting,
+      };
+    },
+  });
+</script>
+<style lang="less" scoped>
+  @import (reference) '../../../design/index.less';
+
+  .app-logo {
+    display: flex;
+    align-items: center;
+    padding-left: 16px;
+    cursor: pointer;
+
+    &.light {
+      border-bottom: 1px solid @border-color-base;
+    }
+
+    &.light &__title {
+      color: @primary-color;
+    }
+
+    &.dark &__title {
+      color: @white;
+    }
+
+    &__title {
+      font-size: 18px;
+      font-weight: 700;
+      opacity: 0;
+      transition: all 0.5s;
+      .respond-to(medium,{
+       opacity: 1;
+      });
+    }
+  }
+</style>

+ 46 - 0
src/components/Application/src/AppPageFooter.vue

@@ -0,0 +1,46 @@
+<template>
+  <div class="app-footer" :style="{ width: getCalcContentWidth }">
+    <div class="app-footer__left">
+      <slot name="left" />
+    </div>
+    <div class="app-footer__right">
+      <slot name="right" />
+    </div>
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent } from 'vue';
+
+  import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
+
+  export default defineComponent({
+    name: 'AppFooterToolbar',
+    setup() {
+      const { getCalcContentWidth } = useMenuSetting();
+
+      return { getCalcContentWidth };
+    },
+  });
+</script>
+<style lang="less" scoped>
+  .app-footer {
+    position: fixed;
+    right: 0;
+    bottom: 0;
+    z-index: 99;
+    display: flex;
+    width: 100%;
+    align-items: center;
+    padding: 0 24px;
+    line-height: 44px;
+    background: #fff;
+    border-top: 1px solid #f0f0f0;
+    box-shadow: 0 -6px 16px -8px rgba(0, 0, 0, 0.08), 0 -9px 28px 0 rgba(0, 0, 0, 0.05),
+      0 -12px 48px 16px rgba(0, 0, 0, 0.03);
+    transition: width 0.4s;
+
+    &__left {
+      flex: 1 1;
+    }
+  }
+</style>

+ 6 - 10
src/components/Authority/src/index.vue

@@ -1,16 +1,15 @@
 <!--
- * @Author: Vben
- * @Description:Access control component for fine-grained access control.
+ Access control component for fine-grained access control.
 -->
 <script lang="ts">
   import type { PropType } from 'vue';
-  import { defineComponent, computed, unref } from 'vue';
+  import { defineComponent, unref } from 'vue';
 
   import { PermissionModeEnum } from '/@/enums/appEnum';
   import { RoleEnum } from '/@/enums/roleEnum';
+  import { useRootSetting } from '/@/hooks/setting/useRootSetting';
 
   import { usePermission } from '/@/hooks/web/usePermission';
-  import { appStore } from '/@/store/modules/app';
 
   import { getSlot } from '/@/utils/helper/tsxHelper';
 
@@ -29,9 +28,8 @@
       },
     },
     setup(props, { slots }) {
-      const getModeRef = computed(() => {
-        return appStore.getProjectConfig.permissionMode;
-      });
+      const { getPermissionMode } = useRootSetting();
+      const { hasPermission } = usePermission();
 
       /**
        * Render role button
@@ -41,7 +39,6 @@
         if (!value) {
           return getSlot(slots);
         }
-        const { hasPermission } = usePermission();
         return hasPermission(value) ? getSlot(slots) : null;
       }
 
@@ -52,12 +49,11 @@
         if (!value) {
           return getSlot(slots);
         }
-        const { hasPermission } = usePermission();
         return hasPermission(value) ? getSlot(slots) : null;
       }
 
       return () => {
-        const mode = unref(getModeRef);
+        const mode = unref(getPermissionMode);
         // Role-based value control
         if (mode === PermissionModeEnum.ROLE) {
           return renderRoleAuth();

+ 1 - 1
src/components/Description/src/index.tsx

@@ -33,7 +33,7 @@ export default defineComponent({
     });
 
     /**
-     * @description: Whether to use title
+     * @description: Whether to setting title
      */
     const useWrapper = computed(() => {
       return !!unref(getMergeProps).title;

+ 1 - 1
src/components/Dropdown/src/Dropdown.tsx

@@ -15,7 +15,7 @@ export default defineComponent({
     const getMenuList = computed(() => props.dropMenuList);
 
     function handleClickMenu({ key }: any) {
-      const menu = unref(getMenuList).find((item) => item.event === key);
+      const menu = unref(getMenuList).find((item) => `${item.event}` === `${key}`);
       emit('menuEvent', menu);
     }
 

+ 1 - 1
src/components/Excel/src/ImportExcel.vue

@@ -104,7 +104,7 @@
        */
       function handleInputClick(e: Event) {
         const files = e && (e.target as HTMLInputElement).files;
-        const rawFile = files && files[0]; // only use files[0]
+        const rawFile = files && files[0]; // only setting files[0]
         if (!rawFile) return;
         upload(rawFile);
       }

+ 1 - 1
src/components/Form/src/componentMap.ts

@@ -2,7 +2,7 @@ import { Component } from 'vue';
 import type { ComponentType } from './types/index';
 
 /**
- * Component list, register here to use it in the form
+ * Component list, register here to setting it in the form
  */
 import {
   Input,

+ 1 - 1
src/components/Form/src/hooks/useLabelWidth.ts

@@ -31,7 +31,7 @@ export function useItemLabelWidth(schemaItemRef: Ref<FormSchema>, propsRef: Ref<
       wrapperCol: globWrapperCol,
     } = unref(propsRef) as any;
 
-    // If labelWidth is set globally, all items use
+    // If labelWidth is set globally, all items setting
     if ((!globalLabelWidth && !labelWidth && !globalLabelCol) || disabledLabelWidth) {
       return { labelCol, wrapperCol };
     }

+ 2 - 2
src/components/Form/src/types/formItem.ts

@@ -68,7 +68,7 @@ export interface FormItem {
    */
   labelAlign?: 'left' | 'right';
   /**
-   * a key of model. In the use of validate and resetFields method, the attribute is required
+   * a key of model. In the setting of validate and resetFields method, the attribute is required
    */
   name?: NamePath;
   /**
@@ -76,7 +76,7 @@ export interface FormItem {
    */
   rules?: object | object[];
   /**
-   * Whether to automatically associate form fields. In most cases, you can use automatic association.
+   * Whether to automatically associate form fields. In most cases, you can setting automatic association.
    * If the conditions for automatic association are not met, you can manually associate them. See the notes below.
    */
   autoLink?: boolean;

+ 5 - 1
src/components/Menu/index.ts

@@ -1 +1,5 @@
-export { default as BasicMenu } from './src/BasicMenu';
+import BasicMenu from './src/BasicMenu';
+import { withInstall } from '../util';
+
+export default withInstall(BasicMenu);
+export { BasicMenu };

+ 15 - 13
src/components/Menu/src/BasicMenu.tsx

@@ -1,3 +1,5 @@
+import './index.less';
+
 import type { MenuState } from './types';
 import type { Menu as MenuType } from '/@/router/types';
 
@@ -9,11 +11,10 @@ import MenuContent from './MenuContent';
 import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
 import { ThemeEnum } from '/@/enums/appEnum';
 
-import { menuStore } from '/@/store/modules/menu';
 import { appStore } from '/@/store/modules/app';
 
-import { useSearchInput } from './useSearchInput';
-import { useOpenKeys } from './useOpenKeys';
+import { useSearchInput } from './hooks/useSearchInput';
+import { useOpenKeys } from './hooks/useOpenKeys';
 import { useRouter } from 'vue-router';
 
 import { isFunction } from '/@/utils/is';
@@ -23,7 +24,7 @@ import { menuHasChildren } from './helper';
 import { getCurrentParentPath } from '/@/router/menus';
 
 import { basicProps } from './props';
-import './index.less';
+import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
 export default defineComponent({
   name: 'BasicMenu',
   props: basicProps,
@@ -39,6 +40,8 @@ export default defineComponent({
       selectedKeys: [],
       collapsedOpenKeys: [],
     });
+
+    const { getCollapsed } = useMenuSetting();
     const { currentRoute } = useRouter();
 
     const { items, flatItems, isAppMenu, mode, accordion } = toRefs(props);
@@ -61,7 +64,7 @@ export default defineComponent({
 
     const getOpenKeys = computed(() => {
       if (props.isAppMenu) {
-        return menuStore.getCollapsedState ? menuState.collapsedOpenKeys : menuState.openKeys;
+        return unref(getCollapsed) ? menuState.collapsedOpenKeys : menuState.openKeys;
       }
       return menuState.openKeys;
     });
@@ -95,20 +98,20 @@ export default defineComponent({
         cls.push('basic-menu__sidebar-hor');
       }
 
-      if (!props.isTop && props.isAppMenu && appStore.getProjectConfig.menuSetting.split) {
+      if (!props.isHorizontal && props.isAppMenu && appStore.getProjectConfig.menuSetting.split) {
         cls.push('basic-menu__second');
       }
       return cls;
     });
 
-    const showTitle = computed(() => props.collapsedShowTitle && menuStore.getCollapsedState);
+    const showTitle = computed(() => props.collapsedShowTitle && unref(getCollapsed));
 
     watch(
       () => currentRoute.value.name,
       (name: string) => {
         if (name === 'Redirect') return;
         handleMenuChange();
-        props.isTop && appStore.getProjectConfig.menuSetting.split && getParentPath();
+        props.isHorizontal && appStore.getProjectConfig.menuSetting.split && getParentPath();
       }
     );
 
@@ -180,7 +183,7 @@ export default defineComponent({
                 <MenuContent
                   item={menu}
                   level={index}
-                  isTop={props.isTop}
+                  isHorizontal={props.isHorizontal}
                   showTitle={unref(showTitle)}
                   searchValue={menuState.searchValue}
                 />,
@@ -196,7 +199,7 @@ export default defineComponent({
                   showTitle={unref(showTitle)}
                   item={menu}
                   level={index}
-                  isTop={props.isTop}
+                  isHorizontal={props.isHorizontal}
                   searchValue={menuState.searchValue}
                 />,
               ],
@@ -214,7 +217,7 @@ export default defineComponent({
       const inlineCollapsedObj = isInline
         ? props.isAppMenu
           ? {
-              inlineCollapsed: menuStore.getCollapsedState,
+              inlineCollapsed: unref(getCollapsed),
             }
           : { inlineCollapsed: props.inlineCollapsed }
         : {};
@@ -246,7 +249,6 @@ export default defineComponent({
     });
 
     return () => {
-      const { getCollapsedState } = menuStore;
       const { mode } = props;
       return mode === MenuModeEnum.HORIZONTAL ? (
         renderMenu()
@@ -258,7 +260,7 @@ export default defineComponent({
             theme={props.theme as ThemeEnum}
             onChange={handleInputChange}
             onClick={handleInputClick}
-            collapsed={getCollapsedState}
+            collapsed={unref(getCollapsed)}
           />
           <section style={unref(getMenuWrapStyle)} class="basic-menu__content">
             {renderMenu()}

+ 3 - 3
src/components/Menu/src/MenuContent.tsx

@@ -26,7 +26,7 @@ export default defineComponent({
       type: Number as PropType<number>,
       default: 0,
     },
-    isTop: {
+    isHorizontal: {
       type: Boolean as PropType<boolean>,
       default: true,
     },
@@ -40,8 +40,8 @@ export default defineComponent({
     }
 
     function renderTag() {
-      const { item, showTitle, isTop } = props;
-      if (!item || showTitle || isTop) return null;
+      const { item, showTitle, isHorizontal } = props;
+      if (!item || showTitle || isHorizontal) return null;
 
       const { tag } = item;
       if (!tag) return null;

+ 4 - 3
src/components/Menu/src/useOpenKeys.ts → src/components/Menu/src/hooks/useOpenKeys.ts

@@ -1,12 +1,12 @@
 import { MenuModeEnum } from '/@/enums/menuEnum';
 import type { Menu as MenuType } from '/@/router/types';
-import type { MenuState } from './types';
+import type { MenuState } from '../types';
 import type { Ref } from 'vue';
 
 import { unref } from 'vue';
-import { menuStore } from '/@/store/modules/menu';
 import { getAllParentPath } from '/@/utils/helper/menuHelper';
 import { es6Unique } from '/@/utils';
+import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
 
 export function useOpenKeys(
   menuState: MenuState,
@@ -16,6 +16,7 @@ export function useOpenKeys(
   mode: Ref<MenuModeEnum>,
   accordion: Ref<boolean>
 ) {
+  const { getCollapsed } = useMenuSetting();
   /**
    * @description:设置展开
    */
@@ -49,7 +50,7 @@ export function useOpenKeys(
           rootSubMenuKeys.push(path);
         }
       }
-      if (!menuStore.getCollapsedState || !unref(isAppMenu)) {
+      if (!unref(getCollapsed) || !unref(isAppMenu)) {
         const latestOpenKey = openKeys.find((key) => menuState.openKeys.indexOf(key) === -1);
         if (rootSubMenuKeys.indexOf(latestOpenKey as string) === -1) {
           menuState.openKeys = openKeys;

+ 1 - 1
src/components/Menu/src/useSearchInput.ts → src/components/Menu/src/hooks/useSearchInput.ts

@@ -1,5 +1,5 @@
 import type { Menu as MenuType } from '/@/router/types';
-import type { MenuState } from './types';
+import type { MenuState } from '../types';
 import type { Ref } from 'vue';
 
 import { isString } from '/@/utils/is';

+ 5 - 5
src/components/Menu/src/props.ts

@@ -8,6 +8,10 @@ export const basicProps = {
     type: Array as PropType<Menu[]>,
     default: () => [],
   },
+  flatItems: {
+    type: Array as PropType<Menu[]>,
+    default: () => [],
+  },
   appendClass: {
     type: Boolean as PropType<boolean>,
     default: false,
@@ -16,10 +20,6 @@ export const basicProps = {
     type: Boolean as PropType<boolean>,
     default: false,
   },
-  flatItems: {
-    type: Array as PropType<Menu[]>,
-    default: () => [],
-  },
   // 是否显示搜索框
   search: {
     type: Boolean as PropType<boolean>,
@@ -55,7 +55,7 @@ export const basicProps = {
     type: Boolean as PropType<boolean>,
     default: true,
   },
-  isTop: {
+  isHorizontal: {
     type: Boolean as PropType<boolean>,
     default: false,
   },

+ 0 - 0
src/components/Menu/src/types.d.ts → src/components/Menu/src/types.ts


+ 1 - 1
src/components/Modal/src/props.ts

@@ -35,7 +35,7 @@ export const basicProps = Object.assign({}, modalProps, {
   },
   // Warm reminder message
   helpMessage: [String, Array] as PropType<string | string[]>,
-  // Whether to use wrapper
+  // Whether to setting wrapper
   useWrapper: {
     type: Boolean as PropType<boolean>,
     default: true,

+ 1 - 1
src/components/Table/src/types/column.ts

@@ -190,7 +190,7 @@ export interface ColumnProps<T> {
   onFilterDropdownVisibleChange?: (visible: boolean) => void;
 
   /**
-   * When using columns, you can use this property to configure the properties that support the slot,
+   * When using columns, you can setting this property to configure the properties that support the slot,
    * such as slots: { filterIcon: 'XXX'}
    * @type object
    */

+ 1 - 1
src/components/Table/src/types/pagination.ts

@@ -86,7 +86,7 @@ export interface PaginationProps {
   size?: string;
 
   /**
-   * whether to use simple mode
+   * whether to setting simple mode
    * @type boolean
    */
   simple?: boolean;

+ 1 - 1
src/components/Tinymce/src/plugins.ts

@@ -1,4 +1,4 @@
-// Any plugins you want to use has to be imported
+// Any plugins you want to setting has to be imported
 // Detail plugins list see https://www.tinymce.com/docs/plugins/
 // Custom builds see https://www.tinymce.com/download/custom-builds/
 // colorpicker/contextmenu/textcolor plugin is now built in to the core editor, please remove it from your editor configuration

+ 2 - 0
src/components/registerGlobComp.ts

@@ -31,6 +31,7 @@ import {
   PageHeader,
   Result,
   Empty,
+  Avatar,
 } from 'ant-design-vue';
 import { getApp } from '/@/setup/App';
 
@@ -76,5 +77,6 @@ export function registerGlobComp() {
     .use(PageHeader)
     .use(Result)
     .use(Empty)
+    .use(Avatar)
     .use(Tabs);
 }

+ 3 - 0
src/components/types.ts

@@ -0,0 +1,3 @@
+import { defineComponent } from 'vue';
+
+export type Component = ReturnType<typeof defineComponent>;

+ 1 - 1
src/design/var/index.less

@@ -16,4 +16,4 @@
 @page-loading-z-index: 10000;
 
 // left-menu
-@app-menu-item-height: 44px;
+@app-menu-item-height: 42px;

+ 2 - 0
src/enums/pageEnum.ts

@@ -5,4 +5,6 @@ export enum PageEnum {
   BASE_HOME = '/dashboard',
   // error page path
   ERROR_PAGE = '/exception',
+  // error log page path
+  ERROR_LOG_PAGE = '/exception/error-log',
 }

+ 20 - 0
src/hooks/core/useDebouncedRef.ts

@@ -0,0 +1,20 @@
+import { customRef } from 'vue';
+
+export function useDebouncedRef<T = any>(value: T, delay = 100) {
+  let timeout: TimeoutHandle;
+  return customRef((track, trigger) => {
+    return {
+      get() {
+        track();
+        return value;
+      },
+      set(newValue: T) {
+        clearTimeout(timeout);
+        timeout = setTimeout(() => {
+          value = newValue;
+          trigger();
+        }, delay);
+      },
+    };
+  });
+}

+ 0 - 0
src/settings/use/index.ts → src/hooks/setting/index.ts


+ 67 - 0
src/hooks/setting/useHeaderSetting.ts

@@ -0,0 +1,67 @@
+import type { HeaderSetting } from '/@/types/config';
+
+import { computed, unref } from 'vue';
+
+import { appStore } from '/@/store/modules/app';
+
+import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
+import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
+import { useRootSetting } from '/@/hooks/setting/useRootSetting';
+
+import { MenuModeEnum } from '/@/enums/menuEnum';
+
+export function useHeaderSetting() {
+  const { getShow: getShowMultipleTab } = useMultipleTabSetting();
+  const { getMode, getSplit, getShowHeaderTrigger, getIsSidebarType } = useMenuSetting();
+  const { getShowBreadCrumb, getShowLogo } = useRootSetting();
+
+  // Get header configuration
+  const getHeaderSetting = computed(() => appStore.getProjectConfig.headerSetting);
+
+  const getShowDoc = computed(() => unref(getHeaderSetting).showDoc);
+
+  const getTheme = computed(() => unref(getHeaderSetting).theme);
+
+  const getShowRedo = computed(() => unref(getHeaderSetting).showRedo && unref(getShowMultipleTab));
+
+  const getUseLockPage = computed(() => unref(getHeaderSetting).useLockPage);
+
+  const getShowFullScreen = computed(() => unref(getHeaderSetting).showFullScreen);
+
+  const getShowNotice = computed(() => unref(getHeaderSetting).showNotice);
+
+  const getShowBread = computed(() => {
+    return (
+      unref(getMode) !== MenuModeEnum.HORIZONTAL && unref(getShowBreadCrumb) && !unref(getSplit)
+    );
+  });
+
+  const getShowHeaderLogo = computed(() => {
+    return unref(getShowLogo) && !unref(getIsSidebarType);
+  });
+
+  const getShowContent = computed(() => {
+    return unref(getShowBread) || unref(getShowHeaderTrigger);
+  });
+
+  // Set header configuration
+  function setHeaderSetting(headerSetting: Partial<HeaderSetting>): void {
+    appStore.commitProjectConfigState({ headerSetting });
+  }
+
+  return {
+    setHeaderSetting,
+
+    getHeaderSetting,
+
+    getShowDoc,
+    getTheme,
+    getShowRedo,
+    getUseLockPage,
+    getShowFullScreen,
+    getShowNotice,
+    getShowBread,
+    getShowContent,
+    getShowHeaderLogo,
+  };
+}

+ 5 - 13
src/settings/use/useLocaleSetting.ts → src/hooks/setting/useLocaleSetting.ts

@@ -1,6 +1,6 @@
 import type { LocaleSetting } from '/@/types/config';
 
-import { computed } from 'vue';
+import { computed, unref } from 'vue';
 import { appStore } from '/@/store/modules/app';
 
 import getProjectSetting from '/@/settings/projectSetting';
@@ -8,24 +8,16 @@ import { localeList } from '/@/locales';
 
 export function useLocaleSetting() {
   // Get locale configuration
-  const getLocale = computed(() => {
-    return appStore.getProjectConfig.locale || getProjectSetting.locale;
-  });
+  const getLocale = computed(() => appStore.getProjectConfig.locale || getProjectSetting.locale);
 
   // get current language
-  const getLang = computed(() => {
-    return getLocale.value.lang;
-  });
+  const getLang = computed(() => unref(getLocale).lang);
 
   // get Available Locales
-  const getAvailableLocales = computed((): string[] => {
-    return getLocale.value.availableLocales;
-  });
+  const getAvailableLocales = computed((): string[] => unref(getLocale).availableLocales);
 
   // get Fallback Locales
-  const getFallbackLocale = computed((): string => {
-    return getLocale.value.fallback;
-  });
+  const getFallbackLocale = computed((): string => unref(getLocale).fallback);
 
   // Set locale configuration
   function setLocale(locale: Partial<LocaleSetting>): void {

+ 120 - 0
src/hooks/setting/useMenuSetting.ts

@@ -0,0 +1,120 @@
+import type { MenuSetting } from '/@/types/config';
+
+import { computed, unref } from 'vue';
+
+import { appStore } 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';
+
+export function useMenuSetting() {
+  // Get menu configuration
+  const getMenuSetting = computed(() => appStore.getProjectConfig.menuSetting);
+
+  const getMiniWidth = computed(() => unref(getMenuSetting).menuWidth);
+
+  const getCollapsed = computed(() => unref(getMenuSetting).collapsed);
+
+  const getType = computed(() => unref(getMenuSetting).type);
+
+  const getMode = computed(() => unref(getMenuSetting).mode);
+
+  const getShow = computed(() => unref(getMenuSetting).show);
+
+  const getMenuWidth = computed(() => unref(getMenuSetting).menuWidth);
+
+  const getTrigger = computed(() => unref(getMenuSetting).trigger);
+
+  const getTheme = computed(() => unref(getMenuSetting).theme);
+
+  const getSplit = computed(() => unref(getMenuSetting).split);
+
+  const getHasDrag = computed(() => unref(getMenuSetting).hasDrag);
+
+  const getAccordion = computed(() => unref(getMenuSetting).accordion);
+
+  const getCollapsedShowTitle = computed(() => unref(getMenuSetting).collapsedShowTitle);
+
+  const getCollapsedShowSearch = computed(() => unref(getMenuSetting).collapsedShowSearch);
+
+  const getTopMenuAlign = computed(() => unref(getMenuSetting).topMenuAlign);
+
+  const getIsSidebarType = computed(() => unref(getType) === MenuTypeEnum.SIDEBAR);
+
+  const getShowTopMenu = computed(() => {
+    return unref(getMode) === MenuModeEnum.HORIZONTAL || unref(getSplit);
+  });
+
+  const getShowHeaderTrigger = computed(() => {
+    if (
+      unref(getType) === MenuTypeEnum.TOP_MENU ||
+      !unref(getShow) ||
+      !unref(getMenuSetting).hidden
+    ) {
+      return false;
+    }
+
+    return unref(getTrigger) === TriggerEnum.HEADER;
+  });
+
+  const getShowSearch = computed(() => {
+    return (
+      unref(getMenuSetting).showSearch &&
+      !(unref(getType) === MenuTypeEnum.MIX && unref(getMode) === MenuModeEnum.HORIZONTAL)
+    );
+  });
+
+  const getIsHorizontal = computed(() => {
+    return unref(getMode) === MenuModeEnum.HORIZONTAL;
+  });
+
+  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(getCollapsed) ? unref(getMiniWidthNumber) : unref(getMiniWidth);
+    return `calc(100% - ${width}px)`;
+  });
+
+  // Set menu configuration
+  function setMenuSetting(menuSetting: Partial<MenuSetting>): void {
+    appStore.commitProjectConfigState({ menuSetting });
+  }
+
+  function toggleCollapsed() {
+    setMenuSetting({
+      collapsed: !unref(getCollapsed),
+    });
+  }
+
+  return {
+    setMenuSetting,
+
+    toggleCollapsed,
+
+    getMenuSetting,
+    getMiniWidth,
+    getType,
+    getMode,
+    getShow,
+    getCollapsed,
+    getMiniWidthNumber,
+    getCalcContentWidth,
+    getMenuWidth,
+    getTrigger,
+    getSplit,
+    getTheme,
+    getHasDrag,
+    getIsHorizontal,
+    getShowSearch,
+    getCollapsedShowTitle,
+    getCollapsedShowSearch,
+    getIsSidebarType,
+    getAccordion,
+    getShowTopMenu,
+    getShowHeaderTrigger,
+    getTopMenuAlign,
+  };
+}

+ 25 - 0
src/hooks/setting/useMultipleTabSetting.ts

@@ -0,0 +1,25 @@
+import type { MultiTabsSetting } from '/@/types/config';
+
+import { computed, unref } from 'vue';
+
+import { appStore } from '/@/store/modules/app';
+
+export function useMultipleTabSetting() {
+  const getMultipleTabSetting = computed(() => appStore.getProjectConfig.multiTabsSetting);
+
+  const getMax = computed(() => unref(getMultipleTabSetting).max);
+
+  const getShow = computed(() => unref(getMultipleTabSetting).show);
+
+  function setMultipleTabSetting(multiTabsSetting: Partial<MultiTabsSetting>) {
+    appStore.commitProjectConfigState({ multiTabsSetting });
+  }
+
+  return {
+    setMultipleTabSetting,
+
+    getMultipleTabSetting,
+    getMax,
+    getShow,
+  };
+}

+ 53 - 0
src/hooks/setting/useRootSetting.ts

@@ -0,0 +1,53 @@
+import type { ProjectConfig } from '/@/types/config';
+
+import { computed, unref } from 'vue';
+
+import { appStore } from '/@/store/modules/app';
+
+type RootSetting = Omit<
+  ProjectConfig,
+  'locale' | 'headerSetting' | 'menuSetting' | 'multiTabsSetting'
+>;
+export function useRootSetting() {
+  const getRootSetting = computed((): RootSetting => appStore.getProjectConfig);
+
+  const getOpenPageLoading = computed(() => unref(getRootSetting).openPageLoading);
+
+  const getOpenRouterTransition = computed(() => unref(getRootSetting).openRouterTransition);
+
+  const getOpenKeepAlive = computed(() => unref(getRootSetting).openKeepAlive);
+
+  const getRouterTransition = computed(() => unref(getRootSetting).routerTransition);
+
+  const getCanEmbedIFramePage = computed(() => unref(getRootSetting).canEmbedIFramePage);
+
+  const getPermissionMode = computed(() => unref(getRootSetting).permissionMode);
+
+  const getShowLogo = computed(() => unref(getRootSetting).showLogo);
+
+  const getUseErrorHandle = computed(() => unref(getRootSetting).useErrorHandle);
+
+  const getShowBreadCrumb = computed(() => unref(getRootSetting).showBreadCrumb);
+
+  const getShowBreadCrumbIcon = computed(() => unref(getRootSetting).showBreadCrumbIcon);
+
+  function setRootSetting(setting: RootSetting) {
+    appStore.commitProjectConfigState(setting);
+  }
+
+  return {
+    setRootSetting,
+
+    getRootSetting,
+    getOpenPageLoading,
+    getOpenRouterTransition,
+    getOpenKeepAlive,
+    getRouterTransition,
+    getCanEmbedIFramePage,
+    getPermissionMode,
+    getShowLogo,
+    getUseErrorHandle,
+    getShowBreadCrumb,
+    getShowBreadCrumbIcon,
+  };
+}

+ 2 - 2
src/hooks/web/useLocale.ts

@@ -7,7 +7,7 @@ import { unref, ref } from 'vue';
 
 import { getI18n } from '/@/setup/i18n';
 
-import { useLocaleSetting } from '/@/settings/use/useLocaleSetting';
+import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting';
 
 import moment from 'moment';
 
@@ -67,7 +67,7 @@ export function useLocale() {
 }
 
 /**
- * For non-setup use
+ * For non-setup setting
  */
 export function useExternalI18n() {
   return getI18n().global;

+ 1 - 0
src/hooks/web/usePage.ts

@@ -20,6 +20,7 @@ function handleError(e: Error) {
 export function useGo() {
   const { push, replace } = useRouter();
   function go(opt: PageEnum | RouteLocationRawEx | string = PageEnum.BASE_HOME, isReplace = false) {
+    if (!opt) return;
     if (isString(opt)) {
       isReplace ? replace(opt).catch(handleError) : push(opt).catch(handleError);
     } else {

+ 0 - 20
src/hooks/web/useSideBar.ts

@@ -1,20 +0,0 @@
-import { computed } from 'vue';
-import { appStore } from '/@/store/modules/app';
-
-export function useSideBar() {
-  const currentCollapsedRef = computed(() => {
-    return appStore.getProjectConfig.menuSetting.collapsed;
-  });
-  const changeCollapsed = (collapsed: boolean) => {
-    appStore.commitProjectConfigState({
-      menuSetting: {
-        collapsed: collapsed,
-      },
-    });
-  };
-  return {
-    openSider: changeCollapsed(false),
-    closeSider: changeCollapsed(true),
-    currentCollapsedRef,
-  };
-}

+ 0 - 214
src/layouts/default/LayoutSideBar.tsx

@@ -1,214 +0,0 @@
-import { computed, defineComponent, nextTick, onMounted, ref, unref } from 'vue';
-
-import { Layout } from 'ant-design-vue';
-import LayoutTrigger from './LayoutTrigger';
-import LayoutMenu from '/@/layouts/default/menu/LayoutMenu';
-
-import { menuStore } from '/@/store/modules/menu';
-import { appStore } from '/@/store/modules/app';
-
-import { MenuModeEnum, MenuSplitTyeEnum, TriggerEnum } from '/@/enums/menuEnum';
-import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum';
-
-import { useDebounce } from '/@/hooks/core/useDebounce';
-
-export default defineComponent({
-  name: 'DefaultLayoutSideBar',
-  setup() {
-    const initRef = ref(false);
-    const brokenRef = ref(false);
-    const collapseRef = ref(true);
-    const dragBarRef = ref<Nullable<HTMLDivElement>>(null);
-    const sideRef = ref<any>(null);
-
-    const getProjectConfigRef = computed(() => {
-      return appStore.getProjectConfig;
-    });
-
-    const getMiniWidth = computed(() => {
-      const {
-        menuSetting: { collapsedShowTitle },
-      } = unref(getProjectConfigRef);
-      return collapsedShowTitle ? SIDE_BAR_SHOW_TIT_MINI_WIDTH : SIDE_BAR_MINI_WIDTH;
-    });
-
-    function onCollapseChange(val: boolean) {
-      if (initRef.value) {
-        collapseRef.value = val;
-        menuStore.commitCollapsedState(val);
-      } else {
-        const collapsed = appStore.getProjectConfig.menuSetting.collapsed;
-        !collapsed && menuStore.commitCollapsedState(val);
-      }
-      initRef.value = true;
-    }
-
-    // Menu area drag and drop-mouse movement
-    function handleMouseMove(ele: any, wrap: any, clientX: number) {
-      document.onmousemove = function (innerE) {
-        let iT = ele.left + ((innerE || event).clientX - clientX);
-        innerE = innerE || window.event;
-        // let tarnameb = innerE.target || innerE.srcElement;
-        const maxT = 600;
-        const minT = unref(getMiniWidth);
-        iT < 0 && (iT = 0);
-        iT > maxT && (iT = maxT);
-        iT < minT && (iT = minT);
-        ele.style.left = wrap.style.width = iT + 'px';
-        return false;
-      };
-    }
-
-    // 菜单区域拖拽 - 鼠标松开
-    function removeMouseup(ele: any) {
-      const wrap = unref(sideRef).$el;
-      document.onmouseup = function () {
-        document.onmousemove = null;
-        document.onmouseup = null;
-        const width = parseInt(wrap.style.width);
-        menuStore.commitDragStartState(false);
-        if (!menuStore.getCollapsedState) {
-          if (width > unref(getMiniWidth) + 20) {
-            setMenuWidth(width);
-          } else {
-            menuStore.commitCollapsedState(true);
-          }
-        } else {
-          if (width > unref(getMiniWidth)) {
-            setMenuWidth(width);
-            menuStore.commitCollapsedState(false);
-          }
-        }
-
-        ele.releaseCapture && ele.releaseCapture();
-      };
-    }
-
-    function setMenuWidth(width: number) {
-      appStore.commitProjectConfigState({
-        menuSetting: {
-          menuWidth: width,
-        },
-      });
-    }
-
-    function changeWrapWidth() {
-      const ele = unref(dragBarRef) as any;
-      const side = unref(sideRef);
-
-      const wrap = (side || {}).$el;
-      ele &&
-        (ele.onmousedown = (e: any) => {
-          menuStore.commitDragStartState(true);
-          wrap.style.transition = 'unset';
-          const clientX = (e || event).clientX;
-          ele.left = ele.offsetLeft;
-          handleMouseMove(ele, wrap, clientX);
-          removeMouseup(ele);
-          ele.setCapture && ele.setCapture();
-          return false;
-        });
-    }
-    function handleBreakpoint(broken: boolean) {
-      brokenRef.value = broken;
-    }
-
-    const getDragBarStyle = computed(() => {
-      if (menuStore.getCollapsedState) {
-        return { left: `${unref(getMiniWidth)}px` };
-      }
-      return {};
-    });
-
-    const getCollapsedWidth = computed(() => {
-      return unref(brokenRef) ? 0 : unref(getMiniWidth);
-    });
-
-    const showTrigger = computed(() => {
-      const {
-        menuSetting: { trigger },
-      } = unref(getProjectConfigRef);
-      return trigger !== TriggerEnum.NONE && trigger === TriggerEnum.FOOTER;
-    });
-
-    onMounted(() => {
-      nextTick(() => {
-        const [exec] = useDebounce(changeWrapWidth, 20);
-        exec();
-      });
-    });
-
-    function handleSiderClick(e: ChangeEvent) {
-      if (!e || !e.target || e.target.className !== 'basic-menu__content') return;
-
-      const { collapsed, show } = appStore.getProjectConfig.menuSetting;
-      if (!collapsed || !show) return;
-      appStore.commitProjectConfigState({
-        menuSetting: {
-          collapsed: false,
-        },
-      });
-    }
-
-    function renderDragLine() {
-      const { menuSetting: { hasDrag = true } = {} } = unref(getProjectConfigRef);
-      return (
-        <div
-          class={[`layout-sidebar__dargbar`, !hasDrag ? 'hide' : '']}
-          style={unref(getDragBarStyle)}
-          ref={dragBarRef}
-        />
-      );
-    }
-
-    return () => {
-      const {
-        menuSetting: { theme, split: splitMenu },
-      } = unref(getProjectConfigRef);
-      const { getCollapsedState, getMenuWidthState } = menuStore;
-
-      const triggerDom = unref(showTrigger)
-        ? {
-            trigger: () => <LayoutTrigger />,
-          }
-        : {};
-
-      const triggerAttr = unref(showTrigger)
-        ? {}
-        : {
-            trigger: null,
-          };
-
-      return (
-        <Layout.Sider
-          onClick={handleSiderClick}
-          onCollapse={onCollapseChange}
-          breakpoint="md"
-          width={getMenuWidthState}
-          collapsed={getCollapsedState}
-          collapsible
-          collapsedWidth={unref(getCollapsedWidth)}
-          theme={theme}
-          class="layout-sidebar"
-          ref={sideRef}
-          onBreakpoint={handleBreakpoint}
-          {...triggerAttr}
-        >
-          {{
-            ...triggerDom,
-            default: () => (
-              <>
-                <LayoutMenu
-                  theme={theme}
-                  menuMode={splitMenu ? MenuModeEnum.INLINE : null}
-                  splitType={splitMenu ? MenuSplitTyeEnum.LEFT : MenuSplitTyeEnum.NONE}
-                />
-                {renderDragLine()}
-              </>
-            ),
-          }}
-        </Layout.Sider>
-      );
-    };
-  },
-});

+ 14 - 16
src/layouts/default/LayoutTrigger.tsx

@@ -1,41 +1,39 @@
+import type { PropType } from 'vue';
+
+import { defineComponent, unref } from 'vue';
 import {
   DoubleRightOutlined,
   DoubleLeftOutlined,
   MenuUnfoldOutlined,
   MenuFoldOutlined,
 } from '@ant-design/icons-vue';
-import { defineComponent } from 'vue';
 
-// store
-import { menuStore } from '/@/store/modules/menu';
+import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
 
 export default defineComponent({
   name: 'LayoutTrigger',
   props: {
     sider: {
-      type: Boolean,
+      type: Boolean as PropType<boolean>,
       default: true,
     },
     theme: {
-      type: String,
+      type: String as PropType<string>,
     },
   },
   setup(props) {
-    function toggleMenu() {
-      menuStore.commitCollapsedState(!menuStore.getCollapsedState);
-    }
+    const { toggleCollapsed, getCollapsed } = useMenuSetting();
 
     return () => {
-      const siderTrigger = menuStore.getCollapsedState ? (
-        <DoubleRightOutlined />
-      ) : (
-        <DoubleLeftOutlined />
-      );
-      if (props.sider) return siderTrigger;
+      const siderTrigger = unref(getCollapsed) ? <DoubleRightOutlined /> : <DoubleLeftOutlined />;
+
+      if (props.sider) {
+        return siderTrigger;
+      }
 
       return (
-        <span class={['layout-trigger', props.theme]} onClick={toggleMenu}>
-          {menuStore.getCollapsedState ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
+        <span class={['layout-trigger', props.theme]} onClick={toggleCollapsed}>
+          {unref(getCollapsed) ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
         </span>
       );
     };

+ 50 - 39
src/layouts/default/header/LayoutBreadcrumb.tsx

@@ -4,14 +4,17 @@ import type { PropType } from 'vue';
 
 import { defineComponent, TransitionGroup, unref, watch, ref } from 'vue';
 import Breadcrumb from '/@/components/Breadcrumb/Breadcrumb.vue';
+import Icon from '/@/components/Icon';
 import BreadcrumbItem from '/@/components/Breadcrumb/BreadcrumbItem.vue';
+
 import { useRouter } from 'vue-router';
-import router from '/@/router';
-import { PageEnum } from '/@/enums/pageEnum';
-import { isBoolean } from '/@/utils/is';
 
+import { isBoolean } from '/@/utils/is';
 import { compile } from 'path-to-regexp';
-import Icon from '/@/components/Icon';
+
+import router from '/@/router';
+
+import { PageEnum } from '/@/enums/pageEnum';
 
 export default defineComponent({
   name: 'BasicBreadcrumb',
@@ -40,7 +43,6 @@ export default defineComponent({
       const matchedList = matched.filter((item) => item.meta && item.meta.title).slice(1);
       const firstItem = matchedList[0];
       const ret = getHomeRoute(firstItem);
-
       if (!isBoolean(ret)) {
         matchedList.unshift(ret);
       }
@@ -74,42 +76,51 @@ export default defineComponent({
       return push(pathCompile(path));
     }
 
+    function renderItemContent(item: AppRouteRecordRaw) {
+      return (
+        <>
+          {props.showIcon && item.meta.icon && item.meta.icon.trim() !== '' && (
+            <Icon
+              icon={item.meta.icon}
+              class="icon mr-1 "
+              style={{
+                marginBottom: '2px',
+              }}
+            />
+          )}
+          {item.meta.title}
+        </>
+      );
+    }
+
+    function renderBreadcrumbItemList() {
+      return unref(itemList).map((item) => {
+        const isLink =
+          (!!item.redirect && !item.meta.disabledRedirect) ||
+          !item.children ||
+          item.children.length === 0;
+
+        return (
+          <BreadcrumbItem
+            key={item.path}
+            isLink={isLink}
+            onClick={handleItemClick.bind(null, item)}
+          >
+            {() => renderItemContent(item as AppRouteRecordRaw)}
+          </BreadcrumbItem>
+        );
+      });
+    }
+
+    function renderBreadcrumbDefault() {
+      return (
+        <TransitionGroup name="breadcrumb">{() => renderBreadcrumbItemList()}</TransitionGroup>
+      );
+    }
+
     return () => (
       <Breadcrumb class={['layout-breadcrumb', unref(itemList).length === 0 ? 'hidden' : '']}>
-        {() => (
-          <TransitionGroup name="breadcrumb">
-            {() => {
-              return unref(itemList).map((item) => {
-                const isLink =
-                  (!!item.redirect && !item.meta.disabledRedirect) ||
-                  !item.children ||
-                  item.children.length === 0;
-                return (
-                  <BreadcrumbItem
-                    key={item.path}
-                    isLink={isLink}
-                    onClick={handleItemClick.bind(null, item)}
-                  >
-                    {() => (
-                      <>
-                        {props.showIcon && item.meta.icon && item.meta.icon.trim() !== '' && (
-                          <Icon
-                            icon={item.meta.icon}
-                            class="icon mr-1 "
-                            style={{
-                              marginBottom: '2px',
-                            }}
-                          />
-                        )}
-                        {item.meta.title}
-                      </>
-                    )}
-                  </BreadcrumbItem>
-                );
-              });
-            }}
-          </TransitionGroup>
-        )}
+        {() => renderBreadcrumbDefault()}
       </Breadcrumb>
     );
   },

+ 155 - 180
src/layouts/default/header/LayoutHeader.tsx

@@ -1,7 +1,9 @@
+import './index.less';
+
 import { defineComponent, unref, computed, ref } from 'vue';
 
 import { Layout, Tooltip, Badge } from 'ant-design-vue';
-import Logo from '/@/layouts/logo/index.vue';
+import { AppLogo } from '/@/components/Application';
 import UserDropdown from './UserDropdown';
 import LayoutMenu from '/@/layouts/default/menu/LayoutMenu';
 import LayoutBreadcrumb from './LayoutBreadcrumb';
@@ -12,50 +14,57 @@ import {
   RedoOutlined,
   FullscreenExitOutlined,
   FullscreenOutlined,
-  GithubFilled,
   LockOutlined,
   BugOutlined,
 } from '@ant-design/icons-vue';
+import { useModal } from '/@/components/Modal';
 
 import { useFullscreen } from '/@/hooks/web/useFullScreen';
 import { useTabs } from '/@/hooks/web/useTabs';
 import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
+import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
+import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
+import { useRootSetting } from '/@/hooks/setting/useRootSetting';
+
 import { useRouter } from 'vue-router';
-import { useModal } from '/@/components/Modal';
 
-import { appStore } from '/@/store/modules/app';
 import { errorStore } from '/@/store/modules/error';
 
-import { MenuModeEnum, MenuSplitTyeEnum, MenuTypeEnum, TriggerEnum } from '/@/enums/menuEnum';
-import { GITHUB_URL } from '/@/settings/siteSetting';
+import { PageEnum } from '/@/enums/pageEnum';
+import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
+import { Component } from '/@/components/types';
 
-import './index.less';
 export default defineComponent({
-  name: 'DefaultLayoutHeader',
+  name: 'LayoutHeader',
   setup() {
-    const widthRef = ref(200);
     let logoEl: Element | null;
 
+    const widthRef = ref(200);
+
     const { refreshPage } = useTabs();
+
+    const { getShowTopMenu, getShowHeaderTrigger, getSplit, getTopMenuAlign } = useMenuSetting();
+
+    const { getUseErrorHandle, getShowBreadCrumbIcon } = useRootSetting();
+
+    const {
+      getTheme,
+      getShowRedo,
+      getUseLockPage,
+      getShowFullScreen,
+      getShowNotice,
+      getShowContent,
+      getShowBread,
+      getShowHeaderLogo,
+    } = useHeaderSetting();
+
     const { push } = useRouter();
     const [register, { openModal }] = useModal();
     const { toggleFullscreen, isFullscreenRef } = useFullscreen();
 
-    const getProjectConfigRef = computed(() => {
-      return appStore.getProjectConfig;
-    });
-
-    const showTopMenu = computed(() => {
-      const getProjectConfig = unref(getProjectConfigRef);
-      const {
-        menuSetting: { mode, split: splitMenu },
-      } = getProjectConfig;
-      return mode === MenuModeEnum.HORIZONTAL || splitMenu;
-    });
-
     useWindowSizeFn(
       () => {
-        if (!unref(showTopMenu)) return;
+        if (!unref(getShowTopMenu)) return;
         let width = 0;
         if (!logoEl) {
           logoEl = document.querySelector('.layout-header__logo');
@@ -69,24 +78,23 @@ export default defineComponent({
       { immediate: true }
     );
 
-    function goToGithub() {
-      window.open(GITHUB_URL, '__blank');
-    }
-
     const headerClass = computed(() => {
-      const theme = unref(getProjectConfigRef).headerSetting.theme;
+      const theme = unref(getTheme);
       return theme ? `layout-header__header--${theme}` : '';
     });
 
-    const showHeaderTrigger = computed(() => {
-      const { show, trigger, hidden, type } = unref(getProjectConfigRef).menuSetting;
-      if (type === MenuTypeEnum.TOP_MENU || !show || !hidden) return false;
-      return trigger === TriggerEnum.HEADER;
+    const getSplitType = computed(() => {
+      return unref(getSplit) ? MenuSplitTyeEnum.TOP : MenuSplitTyeEnum.NONE;
+    });
+
+    const getMenuMode = computed(() => {
+      return unref(getSplit) ? MenuModeEnum.HORIZONTAL : null;
     });
 
     function handleToErrorList() {
-      errorStore.commitErrorListCountState(0);
-      push('/exception/error-log');
+      push(PageEnum.ERROR_LOG_PAGE).then(() => {
+        errorStore.commitErrorListCountState(0);
+      });
     }
 
     /**
@@ -96,162 +104,129 @@ export default defineComponent({
       openModal(true);
     }
 
-    return () => {
-      const getProjectConfig = unref(getProjectConfigRef);
-      const {
-        useErrorHandle,
-        showLogo,
-        multiTabsSetting: { show: showTab },
-        headerSetting: {
-          theme: headerTheme,
-          useLockPage,
-          showRedo,
-          showGithub,
-          showFullScreen,
-          showNotice,
-        },
-        menuSetting: { mode, type: menuType, split: splitMenu, topMenuAlign },
-        showBreadCrumb,
-        showBreadCrumbIcon,
-      } = getProjectConfig;
+    function renderHeaderContent() {
+      const width = unref(widthRef);
+      return (
+        <div class="layout-header__content ">
+          {unref(getShowHeaderLogo) && (
+            <AppLogo class={`layout-header__logo`} theme={unref(getTheme)} />
+          )}
 
-      const isSidebarType = menuType === MenuTypeEnum.SIDEBAR;
+          {unref(getShowContent) && (
+            <div class="layout-header__left">
+              {unref(getShowHeaderTrigger) && (
+                <LayoutTrigger theme={unref(getTheme)} sider={false} />
+              )}
+              {unref(getShowBread) && <LayoutBreadcrumb showIcon={unref(getShowBreadCrumbIcon)} />}
+            </div>
+          )}
 
-      const width = unref(widthRef);
+          {unref(getShowTopMenu) && (
+            <div class={[`layout-header__menu `]} style={{ width: `calc(100% - ${width}px)` }}>
+              <LayoutMenu
+                isHorizontal={true}
+                class={`justify-${unref(getTopMenuAlign)}`}
+                theme={unref(getTheme)}
+                splitType={unref(getSplitType)}
+                menuMode={unref(getMenuMode)}
+                showSearch={false}
+              />
+            </div>
+          )}
+        </div>
+      );
+    }
 
-      const showLeft =
-        (mode !== MenuModeEnum.HORIZONTAL && showBreadCrumb && !splitMenu) ||
-        unref(showHeaderTrigger);
+    function renderActionDefault(Comp: Component | any, event: Fn) {
       return (
-        <Layout.Header class={['layout-header', 'flex p-0 px-4 ', unref(headerClass)]}>
-          {() => (
-            <>
-              <div class="layout-header__content ">
-                {showLogo && !isSidebarType && (
-                  <Logo class={`layout-header__logo`} theme={headerTheme} />
-                )}
-
-                {showLeft && (
-                  <div class="layout-header__left">
-                    {unref(showHeaderTrigger) && (
-                      <LayoutTrigger theme={headerTheme} sider={false} />
-                    )}
-                    {mode !== MenuModeEnum.HORIZONTAL && showBreadCrumb && !splitMenu && (
-                      <LayoutBreadcrumb showIcon={showBreadCrumbIcon} />
-                    )}
-                  </div>
-                )}
+        <div class={`layout-header__action-item`} onClick={event}>
+          <Comp class={`layout-header__action-icon`} />
+        </div>
+      );
+    }
 
-                {unref(showTopMenu) && (
-                  <div
-                    class={[`layout-header__menu `]}
-                    style={{ width: `calc(100% - ${unref(width)}px)` }}
+    function renderAction() {
+      return (
+        <div class={`layout-header__action`}>
+          {unref(getUseErrorHandle) && (
+            <Tooltip>
+              {{
+                title: () => '错误日志',
+                default: () => (
+                  <Badge
+                    count={errorStore.getErrorListCountState}
+                    offset={[0, 10]}
+                    dot
+                    overflowCount={99}
                   >
-                    <LayoutMenu
-                      isTop={true}
-                      class={`justify-${topMenuAlign}`}
-                      theme={headerTheme}
-                      splitType={splitMenu ? MenuSplitTyeEnum.TOP : MenuSplitTyeEnum.NONE}
-                      menuMode={splitMenu ? MenuModeEnum.HORIZONTAL : null}
-                      showSearch={false}
-                    />
-                  </div>
-                )}
-              </div>
+                    {() => renderActionDefault(BugOutlined, handleToErrorList)}
+                  </Badge>
+                ),
+              }}
+            </Tooltip>
+          )}
 
-              <div class={`layout-header__action`}>
-                {useErrorHandle && (
-                  <Tooltip>
-                    {{
-                      title: () => '错误日志',
-                      default: () => (
-                        <Badge
-                          count={errorStore.getErrorListCountState}
-                          offset={[0, 10]}
-                          dot
-                          overflowCount={99}
-                        >
-                          {() => (
-                            <div class={`layout-header__action-item`} onClick={handleToErrorList}>
-                              <BugOutlined class={`layout-header__action-icon`} />
-                            </div>
-                          )}
-                        </Badge>
-                      ),
-                    }}
-                  </Tooltip>
-                )}
+          {unref(getUseLockPage) && (
+            <Tooltip>
+              {{
+                title: () => '锁定屏幕',
+                default: () => renderActionDefault(LockOutlined, handleLockPage),
+              }}
+            </Tooltip>
+          )}
+
+          {unref(getShowNotice) && (
+            <Tooltip>
+              {{
+                title: () => '消息通知',
+                default: () => <NoticeAction />,
+              }}
+            </Tooltip>
+          )}
 
-                {showGithub && (
-                  <Tooltip>
-                    {{
-                      title: () => 'github',
-                      default: () => (
-                        <div class={`layout-header__action-item`} onClick={goToGithub}>
-                          <GithubFilled class={`layout-header__action-icon`} />
-                        </div>
-                      ),
-                    }}
-                  </Tooltip>
-                )}
-                {useLockPage && (
-                  <Tooltip>
-                    {{
-                      title: () => '锁定屏幕',
-                      default: () => (
-                        <div class={`layout-header__action-item`} onClick={handleLockPage}>
-                          <LockOutlined class={`layout-header__action-icon`} />
-                        </div>
-                      ),
-                    }}
-                  </Tooltip>
-                )}
-                {showNotice && (
-                  <div>
-                    <Tooltip>
-                      {{
-                        title: () => '消息通知',
-                        default: () => <NoticeAction />,
-                      }}
-                    </Tooltip>
-                  </div>
-                )}
-                {showRedo && showTab && (
-                  <Tooltip>
-                    {{
-                      title: () => '刷新',
-                      default: () => (
-                        <div class={`layout-header__action-item`} onClick={refreshPage}>
-                          <RedoOutlined class={`layout-header__action-icon`} />
-                        </div>
-                      ),
-                    }}
-                  </Tooltip>
-                )}
-                {showFullScreen && (
-                  <Tooltip>
-                    {{
-                      title: () => (unref(isFullscreenRef) ? '退出全屏' : '全屏'),
-                      default: () => {
-                        const Icon: any = !unref(isFullscreenRef) ? (
-                          <FullscreenOutlined />
-                        ) : (
-                          <FullscreenExitOutlined />
-                        );
-                        return (
-                          <div class={`layout-header__action-item`} onClick={toggleFullscreen}>
-                            <Icon class={`layout-header__action-icon`} />
-                          </div>
-                        );
-                      },
-                    }}
-                  </Tooltip>
-                )}
-                <UserDropdown class={`layout-header__user-dropdown`} />
-              </div>
-              <LockAction onRegister={register} />
-            </>
+          {unref(getShowRedo) && (
+            <Tooltip>
+              {{
+                title: () => '刷新',
+                default: () => renderActionDefault(RedoOutlined, refreshPage),
+              }}
+            </Tooltip>
           )}
+
+          {unref(getShowFullScreen) && (
+            <Tooltip>
+              {{
+                title: () => (unref(isFullscreenRef) ? '退出全屏' : '全屏'),
+                default: () => {
+                  const Icon = !unref(isFullscreenRef) ? (
+                    <FullscreenOutlined />
+                  ) : (
+                    <FullscreenExitOutlined />
+                  );
+                  return renderActionDefault(Icon, toggleFullscreen);
+                },
+              }}
+            </Tooltip>
+          )}
+          <UserDropdown class={`layout-header__user-dropdown`} />
+        </div>
+      );
+    }
+
+    function renderHeaderDefault() {
+      return (
+        <>
+          {renderHeaderContent()}
+          {renderAction()}
+          <LockAction onRegister={register} />
+        </>
+      );
+    }
+
+    return () => {
+      return (
+        <Layout.Header class={['layout-header', 'flex p-0 px-4 ', unref(headerClass)]}>
+          {() => renderHeaderDefault()}
         </Layout.Header>
       );
     };

+ 0 - 1
src/layouts/default/header/LockActionItem.less

@@ -1,7 +1,6 @@
 .lock-modal {
   &__entry {
     position: relative;
-    // width: 500px;
     height: 240px;
     padding: 130px 30px 60px 30px;
     background: #fff;

+ 14 - 22
src/layouts/default/header/LockActionItem.tsx

@@ -1,41 +1,33 @@
-// 组件相关
+import './LockActionItem.less';
+
 import { defineComponent } from 'vue';
 import { BasicModal, useModalInner } from '/@/components/Modal/index';
-
-// hook
+import Button from '/@/components/Button/index.vue';
 import { BasicForm, useForm } from '/@/components/Form/index';
 
 import headerImg from '/@/assets/images/header.jpg';
 
 import { appStore } from '/@/store/modules/app';
 import { userStore } from '/@/store/modules/user';
-import Button from '/@/components/Button/index.vue';
-import './LockActionItem.less';
+
 const prefixCls = 'lock-modal';
 export default defineComponent({
   name: 'LockModal',
   setup(_, { attrs }) {
-    const [register, { setModalProps }] = useModalInner();
-    // 样式前缀
+    const [register, { closeModal }] = useModalInner();
+
     const [registerForm, { validateFields, resetFields }] = useForm({
-      // 隐藏按钮
       showActionButtonGroup: false,
-      // 表单项
       schemas: [
         {
           field: 'password',
-          label: '',
+          label: '锁屏密码',
           component: 'InputPassword',
-          componentProps: {
-            placeholder: '请输入锁屏密码',
-          },
-          rules: [{ required: true }],
+          required: true,
         },
       ],
     });
-    /**
-     * @description: lock
-     */
+
     async function lock(valid = true) {
       let password: string | undefined = '';
 
@@ -46,9 +38,7 @@ export default defineComponent({
           const values = (await validateFields()) as any;
           password = values.password;
         }
-        setModalProps({
-          visible: false,
-        });
+        closeModal();
 
         appStore.commitLockInfoState({
           isLock: true,
@@ -57,7 +47,7 @@ export default defineComponent({
         await resetFields();
       } catch (error) {}
     }
-    // 账号密码登录
+
     return () => (
       <BasicModal footer={null} title="锁定屏幕" {...attrs} class={prefixCls} onRegister={register}>
         {() => (
@@ -66,7 +56,9 @@ export default defineComponent({
               <img src={headerImg} class={`${prefixCls}__header-img`} />
               <p class={`${prefixCls}__header-name`}>{userStore.getUserInfoState.realName}</p>
             </div>
-            <BasicForm onRegister={registerForm} />
+
+            <BasicForm onRegister={registerForm} layout="vertical" />
+
             <div class={`${prefixCls}__footer`}>
               <Button type="primary" block class="mt-2" onClick={lock}>
                 {() => '锁屏'}

+ 47 - 33
src/layouts/default/header/UserDropdown.tsx

@@ -11,15 +11,23 @@ import Icon from '/@/components/Icon/index';
 import { userStore } from '/@/store/modules/user';
 
 import { DOC_URL } from '/@/settings/siteSetting';
-import { appStore } from '/@/store/modules/app';
+
+import { openWindow } from '/@/utils';
+
+import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
+
+interface RenderItemParams {
+  icon: string;
+  text: string;
+  key: string;
+}
 
 const prefixCls = 'user-dropdown';
+
 export default defineComponent({
   name: 'UserDropdown',
   setup() {
-    const getProjectConfigRef = computed(() => {
-      return appStore.getProjectConfig;
-    });
+    const { getShowDoc } = useHeaderSetting();
 
     const getUserInfo = computed(() => {
       const { realName = '', desc } = userStore.getUserInfoState || {};
@@ -33,7 +41,7 @@ export default defineComponent({
 
     // open doc
     function openDoc() {
-      window.open(DOC_URL, '__blank');
+      openWindow(DOC_URL);
     }
 
     function handleMenuClick(e: any) {
@@ -44,7 +52,7 @@ export default defineComponent({
       }
     }
 
-    function renderItem({ icon, text, key }: { icon: string; text: string; key: string }) {
+    function renderItem({ icon, text, key }: RenderItemParams) {
       return (
         <Menu.Item key={key}>
           {() => (
@@ -57,37 +65,43 @@ export default defineComponent({
       );
     }
 
-    return () => {
+    function renderSlotsDefault() {
       const { realName } = unref(getUserInfo);
-      const {
-        headerSetting: { showDoc },
-      } = unref(getProjectConfigRef);
+      return (
+        <section class={prefixCls}>
+          <img class={`${prefixCls}__header`} src={headerImg} />
+          <section class={`${prefixCls}__info`}>
+            <section class={`${prefixCls}__name`}>{realName}</section>
+          </section>
+        </section>
+      );
+    }
+
+    function renderSlotOverlay() {
+      const showDoc = unref(getShowDoc);
+      return (
+        <Menu onClick={handleMenuClick}>
+          {() => (
+            <>
+              {showDoc && renderItem({ key: 'doc', text: '文档', icon: 'gg:loadbar-doc' })}
+              {showDoc && <Divider />}
+              {renderItem({
+                key: 'loginOut',
+                text: '退出系统',
+                icon: 'ant-design:poweroff-outlined',
+              })}
+            </>
+          )}
+        </Menu>
+      );
+    }
+
+    return () => {
       return (
         <Dropdown placement="bottomLeft">
           {{
-            default: () => (
-              <section class={prefixCls}>
-                <img class={`${prefixCls}__header`} src={headerImg} />
-                <section class={`${prefixCls}__info`}>
-                  <section class={`${prefixCls}__name`}>{realName}</section>
-                </section>
-              </section>
-            ),
-            overlay: () => (
-              <Menu slot="overlay" onClick={handleMenuClick}>
-                {() => (
-                  <>
-                    {showDoc && renderItem({ key: 'doc', text: '文档', icon: 'gg:loadbar-doc' })}
-                    {showDoc && <Divider />}
-                    {renderItem({
-                      key: 'loginOut',
-                      text: '退出系统',
-                      icon: 'ant-design:poweroff-outlined',
-                    })}
-                  </>
-                )}
-              </Menu>
-            ),
+            default: () => renderSlotsDefault(),
+            overlay: () => renderSlotOverlay(),
           }}
         </Dropdown>
       );

+ 6 - 2
src/layouts/default/header/notice/NoticeActionItem.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="layout-header__action-item notify-action">
-    <Popover title="" trigger="click">
+    <Popover title="" trigger="click" overlayClassName="layout-header__notify-action">
       <Badge :count="count" dot :numberStyle="numberStyle">
         <BellOutlined class="layout-header__action-icon" />
       </Badge>
@@ -31,6 +31,7 @@
     components: { Popover, BellOutlined, Tabs, TabPane: Tabs.TabPane, Badge, NoticeList },
     setup() {
       let count = 0;
+
       for (let i = 0; i < tabListData.length; i++) {
         count += tabListData[i].list.length;
       }
@@ -44,6 +45,10 @@
   });
 </script>
 <style lang="less">
+  .layout-header__notify-action {
+    max-width: 360px;
+  }
+
   .notify-action {
     padding-top: 2px;
 
@@ -56,7 +61,6 @@
 
       .ant-badge-multiple-words {
         padding: 0 4px;
-        // transform: translate(26%, -40%);
       }
 
       svg {

+ 12 - 24
src/layouts/default/header/notice/NoticeList.vue

@@ -1,36 +1,37 @@
 <template>
-  <List class="list">
+  <a-list class="list">
     <template v-for="item in list" :key="item.id">
-      <ListItem class="list__item">
-        <ListItemMeta>
+      <a-list-item class="list-item">
+        <a-list-item-meta>
           <template #title>
             <div class="title">
               {{ item.title }}
               <div class="extra" v-if="item.extra">
-                <Tag class="tag" :color="item.color">
+                <a-tag class="tag" :color="item.color">
                   {{ item.extra }}
-                </Tag>
+                </a-tag>
               </div>
             </div>
           </template>
+
           <template #avatar>
-            <Avatar v-if="item.avatar" class="avatar" :src="item.avatar" />
+            <a-avatar v-if="item.avatar" class="avatar" :src="item.avatar" />
             <span v-else> {{ item.avatar }}</span>
           </template>
+
           <template #description>
             <div>
               <div class="description">{{ item.description }}</div>
               <div class="datetime">{{ item.datetime }}</div>
             </div>
           </template>
-        </ListItemMeta>
-      </ListItem>
+        </a-list-item-meta>
+      </a-list-item>
     </template>
-  </List>
+  </a-list>
 </template>
 <script lang="ts">
   import { defineComponent, PropType } from 'vue';
-  import { List, Avatar, Tag } from 'ant-design-vue';
   import { ListItem } from './data';
 
   export default defineComponent({
@@ -40,19 +41,6 @@
         default: () => [],
       },
     },
-    components: {
-      List,
-      ListItem: List.Item,
-      ListItemMeta: List.Item.Meta,
-      Avatar,
-      Tag,
-    },
-    setup(props) {
-      const { list = [] } = props;
-      return {
-        list,
-      };
-    },
   });
 </script>
 <style lang="less" scoped>
@@ -61,7 +49,7 @@
       display: none;
     }
 
-    &__item {
+    &-item {
       padding: 6px;
       overflow: hidden;
       cursor: pointer;

+ 0 - 8
src/layouts/default/header/notice/data.ts

@@ -56,14 +56,6 @@ export const tabListData: TabItem[] = [
         datetime: '2017-08-07',
         type: '1',
       },
-      // {
-      //   id: '000000005',
-      //   avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png',
-      //   title: '内容不要超过两行字,超出时自动截断',
-      //   description: '',
-      //   datetime: '2017-08-07',
-      //   type: '1',
-      // },
     ],
   },
   {

+ 0 - 43
src/layouts/default/index.less

@@ -36,47 +36,4 @@
       margin: 0 auto;
     }
   }
-
-  .layout-sidebar {
-    background-size: 100% 100%;
-
-    &.ant-layout-sider-dark {
-      background: @sider-dark-bg-color;
-    }
-
-    &:not(.ant-layout-sider-dark) {
-      border-right: 1px solid @border-color-light;
-    }
-
-    .ant-layout-sider-zero-width-trigger {
-      top: 40%;
-      z-index: 10;
-    }
-
-    &__dargbar {
-      position: absolute;
-      top: 0;
-      right: -2px;
-      z-index: @side-drag-z-index;
-      width: 2px;
-      height: 100%;
-      cursor: col-resize;
-      border-top: none;
-      border-bottom: none;
-
-      &.hide {
-        display: none;
-      }
-
-      &:hover {
-        background: @primary-color;
-        box-shadow: 0 0 4px 0 rgba(28, 36, 56, 0.15);
-      }
-    }
-  }
-}
-
-.ant-layout-sider-trigger {
-  height: 36px;
-  line-height: 36px;
 }

+ 4 - 4
src/layouts/default/index.tsx

@@ -4,7 +4,7 @@ import LayoutHeader from './header/LayoutHeader';
 
 import { appStore } from '/@/store/modules/app';
 import LayoutContent from './LayoutContent';
-import LayoutSideBar from './LayoutSideBar';
+import LayoutSideBar from './sider/LayoutSideBar';
 import SettingBtn from './setting/index.vue';
 import MultipleTabs from './multitabs/index';
 
@@ -36,7 +36,7 @@ export default defineComponent({
       return show;
     });
 
-    const isShowMixHeaderRef = computed(() => {
+    const showMixHeaderRef = computed(() => {
       const {
         menuSetting: { type },
       } = unref(getProjectConfigRef);
@@ -57,11 +57,11 @@ export default defineComponent({
     });
 
     const showFullHeaderRef = computed(() => {
-      return !unref(getFullContent) && unref(isShowMixHeaderRef) && unref(showHeaderRef);
+      return !unref(getFullContent) && unref(showMixHeaderRef) && unref(showHeaderRef);
     });
 
     const showInsetHeaderRef = computed(() => {
-      return !unref(getFullContent) && !unref(isShowMixHeaderRef) && unref(showHeaderRef);
+      return !unref(getFullContent) && !unref(showMixHeaderRef) && unref(showHeaderRef);
     });
 
     const fixedHeaderClsRef = computed(() => {

+ 72 - 171
src/layouts/default/menu/LayoutMenu.tsx

@@ -1,29 +1,21 @@
-import type { PropType } from 'vue';
+import './index.less';
+
+import { PropType, toRef } from 'vue';
 import type { Menu } from '/@/router/types';
 
-import { computed, defineComponent, unref, ref, onMounted, watch } from 'vue';
+import { computed, defineComponent, unref } from 'vue';
 import { BasicMenu } from '/@/components/Menu/index';
-import Logo from '/@/layouts/logo/index.vue';
-
-import { MenuModeEnum, MenuSplitTyeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
-
-// store
-import { appStore } from '/@/store/modules/app';
-import { menuStore } from '/@/store/modules/menu';
-
-import {
-  getMenus,
-  getFlatMenus,
-  getShallowMenus,
-  getChildrenMenus,
-  getFlatChildrenMenus,
-  getCurrentParentPath,
-} from '/@/router/menus/index';
-import { useRouter } from 'vue-router';
-import { useThrottle } from '/@/hooks/core/useThrottle';
-import { permissionStore } from '/@/store/modules/permission';
+import { AppLogo } from '/@/components/Application';
+
+import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
+
+import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
+import { useRootSetting } from '/@/hooks/setting/useRootSetting';
+
+import { useGo } from '/@/hooks/web/usePage';
+import { useSplitMenu } from './useLayoutMenu';
+import { openWindow } from '/@/utils';
 
-import './index.less';
 export default defineComponent({
   name: 'DefaultLayoutMenu',
   props: {
@@ -43,7 +35,7 @@ export default defineComponent({
       type: Boolean as PropType<boolean>,
       default: true,
     },
-    isTop: {
+    isHorizontal: {
       type: Boolean as PropType<boolean>,
       default: false,
     },
@@ -53,190 +45,99 @@ export default defineComponent({
     },
   },
   setup(props) {
-    // Menu array
-    const menusRef = ref<Menu[]>([]);
-    // flat menu array
-    const flatMenusRef = ref<Menu[]>([]);
-    const { currentRoute, push } = useRouter();
-
-    // get app config
-    const getProjectConfigRef = computed(() => {
-      return appStore.getProjectConfig;
-    });
+    const go = useGo();
 
-    // get is Horizontal
-    const getIsHorizontalRef = computed(() => {
-      return unref(getProjectConfigRef).menuSetting.mode === MenuModeEnum.HORIZONTAL;
-    });
+    const {
+      setMenuSetting,
+      getShowSearch,
+      getMode,
+      getType,
+      getCollapsedShowTitle,
+      getCollapsedShowSearch,
+      getIsSidebarType,
+      getTheme,
+      getCollapsed,
+      getAccordion,
+    } = useMenuSetting();
 
-    const [throttleHandleSplitLeftMenu] = useThrottle(handleSplitLeftMenu, 50);
-
-    // Route change split menu
-    watch(
-      [() => unref(currentRoute).path, () => props.splitType],
-      async ([path, splitType]: [string, MenuSplitTyeEnum]) => {
-        if (splitType !== MenuSplitTyeEnum.LEFT && !unref(getIsHorizontalRef)) return;
-        const parentPath = await getCurrentParentPath(path);
-        parentPath && throttleHandleSplitLeftMenu(parentPath);
-      },
-      {
-        immediate: true,
-      }
-    );
+    const { getShowLogo } = useRootSetting();
 
-    // Menu changes
-    watch(
-      [() => permissionStore.getLastBuildMenuTimeState, () => permissionStore.getBackMenuListState],
-      () => {
-        genMenus();
-      }
-    );
-
-    // split Menu changes
-    watch([() => appStore.getProjectConfig.menuSetting.split], () => {
-      if (props.splitType !== MenuSplitTyeEnum.LEFT && !unref(getIsHorizontalRef)) return;
-      genMenus();
-    });
+    const { flatMenusRef, menusRef } = useSplitMenu(toRef(props, 'splitType'));
 
-    // Handle left menu split
-    async function handleSplitLeftMenu(parentPath: string) {
-      const isSplitMenu = unref(getProjectConfigRef).menuSetting.split;
-      if (!isSplitMenu) return;
-      const { splitType } = props;
-      // spilt mode left
-      if (splitType === MenuSplitTyeEnum.LEFT) {
-        const children = await getChildrenMenus(parentPath);
-        if (!children) {
-          appStore.commitProjectConfigState({
-            menuSetting: {
-              hidden: false,
-            },
-          });
-          flatMenusRef.value = [];
-          menusRef.value = [];
-          return;
-        }
-        const flatChildren = await getFlatChildrenMenus(children);
-        appStore.commitProjectConfigState({
-          menuSetting: {
-            hidden: true,
-          },
-        });
-        flatMenusRef.value = flatChildren;
-        menusRef.value = children;
-      }
-    }
+    const showLogo = computed(() => unref(getShowLogo) && unref(getIsSidebarType));
 
-    // get menus
-    async function genMenus() {
-      const isSplitMenu = unref(getProjectConfigRef).menuSetting.split;
+    const getMenuMode = computed(() => props.menuMode || unref(getMode));
 
-      // normal mode
-      const { splitType } = props;
-      if (splitType === MenuSplitTyeEnum.NONE || !isSplitMenu) {
-        flatMenusRef.value = await getFlatMenus();
-        menusRef.value = await getMenus();
-        return;
-      }
+    const getMenuTheme = computed(() => props.theme || unref(getTheme));
 
-      // split-top
-      if (splitType === MenuSplitTyeEnum.TOP) {
-        const parentPath = await getCurrentParentPath(unref(currentRoute).path);
-        menuStore.commitCurrentTopSplitMenuPathState(parentPath);
-        const shallowMenus = await getShallowMenus();
+    const appendClass = computed(() => props.splitType === MenuSplitTyeEnum.TOP);
 
-        flatMenusRef.value = shallowMenus;
-        menusRef.value = shallowMenus;
-        return;
-      }
-    }
+    const showSearch = computed(() => {
+      return (
+        unref(getShowSearch) &&
+        props.showSearch &&
+        (unref(getCollapsedShowSearch) ? true : !unref(getCollapsed))
+      );
+    });
 
+    /**
+     * click menu
+     * @param menu
+     */
     function handleMenuClick(menu: Menu) {
-      const { path } = menu;
-      if (path) {
-        push(path);
-        const { splitType } = props;
-        // split mode top
-        if (splitType === MenuSplitTyeEnum.TOP) {
-          menuStore.commitCurrentTopSplitMenuPathState(path);
-        }
-      }
+      go(menu.path);
     }
 
+    /**
+     * before click menu
+     * @param menu
+     */
     async function beforeMenuClickFn(menu: Menu) {
       const { meta: { externalLink } = {} } = menu;
 
       if (externalLink) {
-        window.open(externalLink, '_blank');
+        openWindow(externalLink);
         return false;
       }
-
       return true;
     }
 
     function handleClickSearchInput() {
-      if (menuStore.getCollapsedState) {
-        menuStore.commitCollapsedState(false);
-      }
+      unref(getCollapsed) && setMenuSetting({ collapsed: false });
     }
 
-    const showSearchRef = computed(() => {
-      const { showSearch, type, mode } = unref(getProjectConfigRef).menuSetting;
+    function renderHeader() {
+      if (!unref(showLogo)) return null;
       return (
-        showSearch &&
-        props.showSearch &&
-        !(type === MenuTypeEnum.MIX && mode === MenuModeEnum.HORIZONTAL)
+        <AppLogo
+          showTitle={!unref(getCollapsed)}
+          class={[`layout-menu__logo`, unref(getMenuTheme)]}
+          theme={unref(getMenuTheme)}
+        />
       );
-    });
-
-    onMounted(() => {
-      genMenus();
-    });
+    }
 
     return () => {
-      const {
-        showLogo,
-        menuSetting: {
-          type: menuType,
-          mode,
-          theme,
-          collapsed,
-          collapsedShowTitle,
-          collapsedShowSearch,
-          accordion,
-        },
-      } = unref(getProjectConfigRef);
-
-      const isSidebarType = menuType === MenuTypeEnum.SIDEBAR;
-      const isShowLogo = showLogo && isSidebarType;
-      const themeData = props.theme || theme;
       return (
         <BasicMenu
-          beforeClickFn={beforeMenuClickFn}
-          onMenuClick={handleMenuClick}
-          type={menuType}
-          mode={props.menuMode || mode}
           class="layout-menu"
-          collapsedShowTitle={collapsedShowTitle}
-          theme={themeData}
-          showLogo={isShowLogo}
-          search={unref(showSearchRef) && (collapsedShowSearch ? true : !collapsed)}
+          beforeClickFn={beforeMenuClickFn}
+          isHorizontal={props.isHorizontal}
+          appendClass={unref(appendClass)}
+          type={unref(getType)}
+          mode={unref(getMenuMode)}
+          collapsedShowTitle={unref(getCollapsedShowTitle)}
+          theme={unref(getMenuTheme)}
+          showLogo={unref(showLogo)}
+          search={unref(showSearch)}
           items={unref(menusRef)}
           flatItems={unref(flatMenusRef)}
+          accordion={unref(getAccordion)}
+          onMenuClick={handleMenuClick}
           onClickSearchInput={handleClickSearchInput}
-          appendClass={props.splitType === MenuSplitTyeEnum.TOP}
-          isTop={props.isTop}
-          accordion={accordion}
         >
           {{
-            header: () =>
-              isShowLogo && (
-                <Logo
-                  showTitle={!collapsed}
-                  class={[`layout-menu__logo`, themeData]}
-                  theme={themeData}
-                />
-              ),
+            header: () => renderHeader(),
           }}
         </BasicMenu>
       );

+ 0 - 12
src/layouts/default/menu/index.less

@@ -9,17 +9,5 @@
       width: @logo-width;
       height: @logo-width;
     }
-
-    &.light {
-      .logo-title {
-        color: @text-color-base;
-      }
-    }
-
-    &.dark {
-      .logo-title {
-        color: @white;
-      }
-    }
   }
 }

+ 113 - 0
src/layouts/default/menu/useLayoutMenu.ts

@@ -0,0 +1,113 @@
+import type { Menu } from '/@/router/types';
+import type { Ref } from 'vue';
+
+import { watch, unref, ref, computed } from 'vue';
+import { useRouter } from 'vue-router';
+
+import { MenuSplitTyeEnum } from '/@/enums/menuEnum';
+import { useThrottle } from '/@/hooks/core/useThrottle';
+import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
+
+import {
+  getChildrenMenus,
+  getCurrentParentPath,
+  getFlatChildrenMenus,
+  getFlatMenus,
+  getMenus,
+  getShallowMenus,
+} from '/@/router/menus';
+import { permissionStore } from '/@/store/modules/permission';
+
+export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
+  // Menu array
+  const menusRef = ref<Menu[]>([]);
+  // flat menu array
+  const flatMenusRef = ref<Menu[]>([]);
+
+  const { currentRoute } = useRouter();
+
+  const { setMenuSetting, getIsHorizontal, getSplit } = useMenuSetting();
+
+  const [throttleHandleSplitLeftMenu] = useThrottle(handleSplitLeftMenu, 50);
+
+  const splitNotLeft = computed(
+    () => unref(splitType) !== MenuSplitTyeEnum.LEFT && !unref(getIsHorizontal)
+  );
+
+  const splitLeft = computed(() => !unref(getSplit) || unref(splitType) !== MenuSplitTyeEnum.LEFT);
+
+  const spiltTop = computed(() => unref(splitType) === MenuSplitTyeEnum.TOP);
+
+  const normalType = computed(() => {
+    return unref(splitType) === MenuSplitTyeEnum.NONE || !unref(getSplit);
+  });
+
+  watch(
+    [() => unref(currentRoute).path, () => unref(splitType)],
+    async ([path]: [string, MenuSplitTyeEnum]) => {
+      if (unref(splitNotLeft)) return;
+
+      const parentPath = await getCurrentParentPath(path);
+      parentPath && throttleHandleSplitLeftMenu(parentPath);
+    },
+    {
+      immediate: true,
+    }
+  );
+
+  // Menu changes
+  watch(
+    [() => permissionStore.getLastBuildMenuTimeState, () => permissionStore.getBackMenuListState],
+    () => {
+      genMenus();
+    },
+    {
+      immediate: true,
+    }
+  );
+
+  // split Menu changes
+  watch([() => getSplit.value], () => {
+    if (unref(splitNotLeft)) return;
+    genMenus();
+  });
+
+  // Handle left menu split
+  async function handleSplitLeftMenu(parentPath: string) {
+    if (unref(splitLeft)) return;
+
+    // spilt mode left
+    const children = await getChildrenMenus(parentPath);
+    if (!children) {
+      setMenuSetting({ hidden: false });
+      flatMenusRef.value = [];
+      menusRef.value = [];
+      return;
+    }
+
+    const flatChildren = await getFlatChildrenMenus(children);
+    setMenuSetting({ hidden: true });
+    flatMenusRef.value = flatChildren;
+    menusRef.value = children;
+  }
+
+  // get menus
+  async function genMenus() {
+    // normal mode
+    if (unref(normalType)) {
+      flatMenusRef.value = await getFlatMenus();
+      menusRef.value = await getMenus();
+      return;
+    }
+
+    // split-top
+    if (unref(spiltTop)) {
+      const shallowMenus = await getShallowMenus();
+
+      flatMenusRef.value = shallowMenus;
+      menusRef.value = shallowMenus;
+      return;
+    }
+  }
+  return { flatMenusRef, menusRef };
+}

+ 1 - 1
src/layouts/default/multitabs/index.tsx

@@ -33,7 +33,7 @@ export default defineComponent({
       return tabStore.getTabsState;
     });
 
-    // If you monitor routing changes, tab switching will be stuck. So use this method
+    // If you monitor routing changes, tab switching will be stuck. So setting this method
     watch(
       () => tabStore.getLastChangeRouteState,
       () => {

+ 77 - 0
src/layouts/default/sider/LayoutSideBar.tsx

@@ -0,0 +1,77 @@
+import './index.less';
+
+import { computed, defineComponent, ref, unref } from 'vue';
+
+import { Layout } from 'ant-design-vue';
+import LayoutMenu from '/@/layouts/default/menu/LayoutMenu';
+
+import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
+
+import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
+import { useTrigger, useDragLine, useSiderEvent } from './useLayoutSider';
+
+export default defineComponent({
+  name: 'LayoutSideBar',
+  setup() {
+    const dragBarRef = ref<Nullable<HTMLDivElement>>(null);
+    const sideRef = ref<Nullable<HTMLDivElement>>(null);
+
+    const { getCollapsed, getMenuWidth, getSplit, getTheme } = useMenuSetting();
+
+    const { getTriggerAttr, getTriggerSlot } = useTrigger();
+
+    const { renderDragLine } = useDragLine(sideRef, dragBarRef);
+
+    const {
+      getCollapsedWidth,
+      onBreakpointChange,
+      onCollapseChange,
+      onSiderClick,
+    } = useSiderEvent();
+
+    const getMode = computed(() => {
+      return unref(getSplit) ? MenuModeEnum.INLINE : null;
+    });
+
+    const getSplitType = computed(() => {
+      return unref(getSplit) ? MenuSplitTyeEnum.LEFT : MenuSplitTyeEnum.NONE;
+    });
+
+    function renderDefault() {
+      return (
+        <>
+          <LayoutMenu
+            theme={unref(getTheme)}
+            menuMode={unref(getMode)}
+            splitType={unref(getSplitType)}
+          />
+          {renderDragLine()}
+        </>
+      );
+    }
+
+    return () => {
+      return (
+        <Layout.Sider
+          ref={sideRef}
+          class="layout-sidebar"
+          breakpoint="md"
+          collapsible
+          width={unref(getMenuWidth)}
+          collapsed={unref(getCollapsed)}
+          collapsedWidth={unref(getCollapsedWidth)}
+          theme={unref(getTheme)}
+          onClick={onSiderClick}
+          onCollapse={onCollapseChange}
+          onBreakpoint={onBreakpointChange}
+          {...unref(getTriggerAttr)}
+        >
+          {{
+            ...unref(getTriggerSlot),
+            default: () => renderDefault(),
+          }}
+        </Layout.Sider>
+      );
+    };
+  },
+});

+ 44 - 0
src/layouts/default/sider/index.less

@@ -0,0 +1,44 @@
+@import (reference) '../../../design/index.less';
+
+.layout-sidebar {
+  background-size: 100% 100%;
+
+  &.ant-layout-sider-dark {
+    background: @sider-dark-bg-color;
+  }
+
+  &:not(.ant-layout-sider-dark) {
+    border-right: 1px solid @border-color-light;
+  }
+
+  .ant-layout-sider-zero-width-trigger {
+    top: 40%;
+    z-index: 10;
+  }
+
+  &__darg-bar {
+    position: absolute;
+    top: 0;
+    right: -2px;
+    z-index: @side-drag-z-index;
+    width: 2px;
+    height: 100%;
+    cursor: col-resize;
+    border-top: none;
+    border-bottom: none;
+
+    &.hide {
+      display: none;
+    }
+
+    &:hover {
+      background: @primary-color;
+      box-shadow: 0 0 4px 0 rgba(28, 36, 56, 0.15);
+    }
+  }
+}
+
+.ant-layout-sider-trigger {
+  height: 36px;
+  line-height: 36px;
+}

+ 163 - 0
src/layouts/default/sider/useLayoutSider.tsx

@@ -0,0 +1,163 @@
+import type { Ref } from 'vue';
+
+import { computed, unref, onMounted, nextTick, ref } from 'vue';
+import LayoutTrigger from '/@/layouts/default/LayoutTrigger';
+
+import { TriggerEnum } from '/@/enums/menuEnum';
+
+import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
+import { useDebounce } from '/@/hooks/core/useDebounce';
+
+/**
+ * Handle related operations of menu events
+ */
+export function useSiderEvent() {
+  const initRef = ref(false);
+  const brokenRef = ref(false);
+  const collapseRef = ref(true);
+
+  const { setMenuSetting, getCollapsed, getMiniWidthNumber, getShow } = useMenuSetting();
+
+  const getCollapsedWidth = computed(() => {
+    return unref(brokenRef) ? 0 : unref(getMiniWidthNumber);
+  });
+
+  function onCollapseChange(val: boolean) {
+    if (initRef.value) {
+      collapseRef.value = val;
+      setMenuSetting({ collapsed: val });
+    } else {
+      !unref(getCollapsed) && setMenuSetting({ collapsed: val });
+    }
+    initRef.value = true;
+  }
+
+  function onBreakpointChange(broken: boolean) {
+    brokenRef.value = broken;
+  }
+
+  function onSiderClick(e: ChangeEvent) {
+    if (!e || !e.target || e.target.className !== 'basic-menu__content') return;
+    if (!unref(getCollapsed) || !unref(getShow)) return;
+    setMenuSetting({ collapsed: false });
+  }
+  return { getCollapsedWidth, onCollapseChange, onBreakpointChange, onSiderClick };
+}
+
+/**
+ * Handle related operations of menu folding
+ */
+export function useTrigger() {
+  const { getTrigger } = useMenuSetting();
+
+  const showTrigger = computed(() => {
+    const trigger = unref(getTrigger);
+    return trigger !== TriggerEnum.NONE && trigger === TriggerEnum.FOOTER;
+  });
+
+  const getTriggerAttr = computed(() => {
+    if (unref(showTrigger)) {
+      return {};
+    }
+    return {
+      trigger: null,
+    };
+  });
+
+  const getTriggerSlot = computed(() => {
+    if (unref(showTrigger)) {
+      return {
+        trigger: () => <LayoutTrigger />,
+      };
+    }
+    return {};
+  });
+
+  return { getTriggerAttr, getTriggerSlot };
+}
+
+/**
+ * Handle menu drag and drop related operations
+ * @param siderRef
+ * @param dragBarRef
+ */
+export function useDragLine(siderRef: Ref<any>, dragBarRef: Ref<any>) {
+  const { getMiniWidthNumber, getCollapsed, setMenuSetting, getHasDrag } = useMenuSetting();
+
+  const getDragBarStyle = computed(() => {
+    if (unref(getCollapsed)) {
+      return { left: `${unref(getMiniWidthNumber)}px` };
+    }
+    return {};
+  });
+
+  onMounted(() => {
+    nextTick(() => {
+      const [exec] = useDebounce(changeWrapWidth, 20);
+      exec();
+    });
+  });
+
+  function renderDragLine() {
+    return (
+      <div
+        class={[`layout-sidebar__darg-bar`, !unref(getHasDrag) ? 'hide' : '']}
+        style={unref(getDragBarStyle)}
+        ref={dragBarRef}
+      />
+    );
+  }
+
+  function handleMouseMove(ele: HTMLElement, wrap: HTMLElement, clientX: number) {
+    document.onmousemove = function (innerE) {
+      let iT = (ele as any).left + (innerE.clientX - clientX);
+      innerE = innerE || window.event;
+      const maxT = 600;
+      const minT = unref(getMiniWidthNumber);
+      iT < 0 && (iT = 0);
+      iT > maxT && (iT = maxT);
+      iT < minT && (iT = minT);
+      ele.style.left = wrap.style.width = iT + 'px';
+      return false;
+    };
+  }
+
+  // Drag and drop in the menu area-release the mouse
+  function removeMouseup(ele: any) {
+    const wrap = unref(siderRef).$el;
+    document.onmouseup = function () {
+      document.onmousemove = null;
+      document.onmouseup = null;
+      const width = parseInt(wrap.style.width);
+      const miniWidth = unref(getMiniWidthNumber);
+
+      if (!unref(getCollapsed)) {
+        width > miniWidth + 20
+          ? setMenuSetting({ menuWidth: width })
+          : setMenuSetting({ collapsed: true });
+      } else {
+        width > miniWidth && setMenuSetting({ collapsed: false, menuWidth: width });
+      }
+      ele.releaseCapture?.();
+    };
+  }
+
+  function changeWrapWidth() {
+    const ele = unref(dragBarRef) as any;
+    const side = unref(siderRef);
+
+    const wrap = (side || {}).$el;
+    ele &&
+      (ele.onmousedown = (e: any) => {
+        wrap.style.transition = 'unset';
+        const clientX = e?.clientX;
+        ele.left = ele.offsetLeft;
+        handleMouseMove(ele, wrap, clientX);
+        removeMouseup(ele);
+        ele.setCapture?.();
+        return false;
+      });
+  }
+
+  return { renderDragLine };
+}

+ 19 - 19
src/layouts/iframe/useFrameKeepAlive.ts

@@ -5,12 +5,29 @@ import { useRouter } from 'vue-router';
 import router from '/@/router';
 
 import { tabStore } from '/@/store/modules/tab';
-import { appStore } from '/@/store/modules/app';
 
 import { unique } from '/@/utils';
 
+import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
+
 export function useFrameKeepAlive() {
   const { currentRoute } = useRouter();
+  const { getShow } = useMultipleTabSetting();
+
+  const getFramePages = computed(() => {
+    const ret =
+      getAllFramePages((toRaw(router.getRoutes()) as unknown) as AppRouteRecordRaw[]) || [];
+    return ret;
+  });
+
+  const getOpenTabList = computed((): string[] => {
+    return tabStore.getTabsState.reduce((prev: string[], next) => {
+      if (next.meta && Reflect.has(next.meta, 'frameSrc')) {
+        prev.push(next.path!);
+      }
+      return prev;
+    }, []);
+  });
 
   function getAllFramePages(routes: AppRouteRecordRaw[]): AppRouteRecordRaw[] {
     let res: AppRouteRecordRaw[] = [];
@@ -30,26 +47,9 @@ export function useFrameKeepAlive() {
   function showIframe(item: AppRouteRecordRaw) {
     return item.path === unref(currentRoute).path;
   }
-  const getFramePages = computed(() => {
-    const ret =
-      getAllFramePages((toRaw(router.getRoutes()) as unknown) as AppRouteRecordRaw[]) || [];
-    return ret;
-  });
-
-  const getOpenTabList = computed((): string[] => {
-    return tabStore.getTabsState.reduce((prev: string[], next) => {
-      if (next.meta && Reflect.has(next.meta, 'frameSrc')) {
-        prev.push(next.path!);
-      }
-      return prev;
-    }, []);
-  });
 
   function hasRenderFrame(path: string) {
-    const {
-      multiTabsSetting: { show },
-    } = appStore.getProjectConfig;
-    return show ? unref(getOpenTabList).includes(path) : true;
+    return unref(getShow) ? unref(getOpenTabList).includes(path) : true;
   }
   return { hasRenderFrame, getFramePages, showIframe, getAllFramePages };
 }

+ 0 - 105
src/layouts/logo/index.vue

@@ -1,105 +0,0 @@
-<template>
-  <div class="app-logo anticon" :class="theme" @click="handleGoHome" :style="wrapStyle">
-    <img src="/@/assets/images/logo.png" />
-    <div v-if="show" class="logo-title ml-2 ellipsis">{{ globSetting.title }}</div>
-  </div>
-</template>
-<script lang="ts">
-  import { computed, defineComponent, PropType, ref, watch } from 'vue';
-  // hooks
-  import { useGlobSetting } from '/@/settings/use';
-  import { useTimeoutFn } from '/@/hooks/core/useTimeout';
-  import { useGo } from '/@/hooks/web/usePage';
-
-  import { PageEnum } from '/@/enums/pageEnum';
-  import { MenuTypeEnum } from '/@/enums/menuEnum';
-
-  import { menuStore } from '/@/store/modules/menu';
-  import { appStore } from '/@/store/modules/app';
-
-  export default defineComponent({
-    name: 'Logo',
-    props: {
-      showTitle: {
-        type: Boolean as PropType<boolean>,
-        default: true,
-      },
-      theme: {
-        type: String,
-      },
-    },
-    setup(props) {
-      const showRef = ref<boolean>(!!props.showTitle);
-      const globSetting = useGlobSetting();
-      const go = useGo();
-
-      function handleGoHome() {
-        go(PageEnum.BASE_HOME);
-      }
-
-      watch(
-        () => props.showTitle,
-        (show: boolean) => {
-          if (show) {
-            useTimeoutFn(() => {
-              showRef.value = show;
-            }, 280);
-          } else {
-            showRef.value = show;
-          }
-        }
-      );
-
-      const wrapStyle = computed(() => {
-        const { getCollapsedState } = menuStore;
-        const {
-          menuSetting: { menuWidth, type },
-        } = appStore.getProjectConfig;
-        const miniWidth = { minWidth: `${menuWidth}px` };
-        if (type !== MenuTypeEnum.SIDEBAR) {
-          return miniWidth;
-        }
-        return getCollapsedState ? {} : miniWidth;
-      });
-
-      return {
-        handleGoHome,
-        globSetting,
-        show: showRef,
-        wrapStyle,
-      };
-    },
-  });
-</script>
-<style lang="less" scoped>
-  @import (reference) '../../design/index.less';
-
-  .app-logo {
-    display: flex;
-    align-items: center;
-    padding-left: 16px;
-    cursor: pointer;
-    // justify-content: center;
-    &.light {
-      border-bottom: 1px solid @border-color-base;
-    }
-
-    .logo-title {
-      font-size: 18px;
-      font-weight: 700;
-      opacity: 0;
-      transition: all 0.5s;
-      .respond-to(medium,{
-       opacity: 1;
-     });
-    }
-
-    // &.dark .logo-title {
-    //   font-weight: 400;
-    // }
-
-    &.light .logo-title {
-      color: @primary-color;
-    }
-  }
-</style>

+ 39 - 36
src/layouts/page/index.tsx

@@ -1,75 +1,78 @@
-import { computed, defineComponent, unref, Transition, KeepAlive, toRaw } from 'vue';
+import type { FunctionalComponent } from 'vue';
+
+import { computed, defineComponent, unref, Transition, KeepAlive } from 'vue';
 import { RouterView, RouteLocation } from 'vue-router';
 
 import FrameLayout from '/@/layouts/iframe/index.vue';
 
 import { useTransition } from './useTransition';
-import { useProjectSetting } from '/@/settings/use';
+import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
+import { useRootSetting } from '/@/hooks/setting/useRootSetting';
+import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
 
 import { tabStore } from '/@/store/modules/tab';
-import { appStore } from '/@/store/modules/app';
+
+interface DefaultContext {
+  Component: FunctionalComponent;
+  route: RouteLocation;
+}
 
 export default defineComponent({
   name: 'PageLayout',
   setup() {
-    const getProjectConfigRef = computed(() => appStore.getProjectConfig);
-    const openCacheRef = computed(() => {
-      const {
-        openKeepAlive,
-        multiTabsSetting: { show },
-      } = unref(getProjectConfigRef);
-      return openKeepAlive && show;
-    });
-    const getCacheTabsRef = computed(() => toRaw(tabStore.getKeepAliveTabsState) as string[]);
+    const { getShow } = useMenuSetting();
+    const {
+      getOpenKeepAlive,
+      getRouterTransition,
+      getOpenRouterTransition,
+      getCanEmbedIFramePage,
+    } = useRootSetting();
 
-    const { openPageLoading } = unref(getProjectConfigRef);
+    const { getMax } = useMultipleTabSetting();
 
-    let on = {};
-    if (openPageLoading) {
-      const { on: transitionOn } = useTransition();
-      on = transitionOn;
-    }
-    const projectSetting = useProjectSetting();
-    return () => {
-      const {
-        routerTransition,
-        openRouterTransition,
-        multiTabsSetting: { max },
-      } = unref(getProjectConfigRef);
+    const transitionEvent = useTransition();
+
+    const openCacheRef = computed(() => unref(getOpenKeepAlive) && unref(getShow));
 
+    const getCacheTabsRef = computed(() => tabStore.getKeepAliveTabsState as string[]);
+
+    return () => {
       return (
         <div>
           <RouterView>
             {{
-              default: ({ Component, route }: { Component: any; route: RouteLocation }) => {
+              default: ({ Component, route }: DefaultContext) => {
                 // No longer show animations that are already in the tab
                 const cacheTabs = unref(getCacheTabsRef);
                 const isInCache = cacheTabs.includes(route.name as string);
                 const name = isInCache && route.meta.inTab ? 'fade' : null;
 
-                const Content = unref(openCacheRef) ? (
-                  <KeepAlive max={max} include={cacheTabs}>
-                    <Component key={route.fullPath} />
+                const renderComp = () => <Component key={route.fullPath} />;
+
+                const PageContent = unref(openCacheRef) ? (
+                  <KeepAlive max={unref(getMax)} include={cacheTabs}>
+                    {renderComp()}
                   </KeepAlive>
                 ) : (
-                  <Component key={route.fullPath} />
+                  renderComp()
                 );
-                return openRouterTransition ? (
+
+                return unref(getOpenRouterTransition) ? (
                   <Transition
-                    {...on}
-                    name={name || route.meta.transitionName || routerTransition}
+                    {...transitionEvent}
+                    name={name || route.meta.transitionName || unref(getRouterTransition)}
                     mode="out-in"
                     appear={true}
                   >
-                    {() => Content}
+                    {() => PageContent}
                   </Transition>
                 ) : (
-                  Content
+                  PageContent
                 );
               },
             }}
           </RouterView>
-          {projectSetting.canEmbedIFramePage && <FrameLayout />}
+          {unref(getCanEmbedIFramePage) && <FrameLayout />}
         </div>
       );
     };

+ 5 - 7
src/layouts/page/useTransition.ts

@@ -1,10 +1,11 @@
+import { useRootSetting } from '/@/hooks/setting/useRootSetting';
 import { appStore } from '/@/store/modules/app';
 import { tryOnUnmounted } from '/@/utils/helper/vueHelper';
+
 export function useTransition() {
   function handleAfterEnter() {
-    const { openRouterTransition, openPageLoading } = appStore.getProjectConfig;
-
-    if (!openRouterTransition || !openPageLoading) return;
+    const { getOpenPageLoading, getOpenRouterTransition } = useRootSetting();
+    if (!getOpenPageLoading.value || !getOpenRouterTransition.value) return;
     // Close loading after the route switching animation ends
     appStore.setPageLoadingAction(false);
   }
@@ -15,9 +16,6 @@ export function useTransition() {
   });
 
   return {
-    handleAfterEnter,
-    on: {
-      onAfterEnter: handleAfterEnter,
-    },
+    onAfterEnter: handleAfterEnter,
   };
 }

+ 1 - 1
src/main.ts

@@ -49,7 +49,7 @@ if (isDevMode()) {
   window.__APP__ = app;
 }
 
-// If you do not need to use the mock service in the production environment, you can comment the code
+// If you do not need to setting the mock service in the production environment, you can comment the code
 if (isProdMode() && isUseMock()) {
   setupProdMockServer();
 }

+ 1 - 1
src/router/guard/index.ts

@@ -6,7 +6,7 @@ import { createProgressGuard } from './progressGuard';
 import { createPermissionGuard } from './permissionGuard';
 import { createPageLoadingGuard } from './pageLoadingGuard';
 
-import { useGlobSetting, useProjectSetting } from '/@/settings/use';
+import { useGlobSetting, useProjectSetting } from '/@/hooks/setting';
 
 import { getIsOpenTab, setCurrentTo } from '/@/utils/helper/routeHelper';
 import { setTitle } from '/@/utils/browser';

+ 1 - 1
src/router/menus/index.ts

@@ -7,6 +7,7 @@ import { filter } from '/@/utils/helper/treeHelper';
 import router from '/@/router';
 import { PermissionModeEnum } from '/@/enums/appEnum';
 import { pathToRegexp } from 'path-to-regexp';
+
 import modules from 'globby!/@/router/menus/modules/**/*.@(ts)';
 
 const menuModules: MenuModule[] = [];
@@ -44,7 +45,6 @@ async function getAsyncMenus() {
 // 获取深层扁平化菜单
 export const getFlatMenus = async () => {
   const menus = await getAsyncMenus();
-
   return flatMenus(menus);
 };
 

+ 10 - 3
src/settings/projectSetting.ts

@@ -9,7 +9,7 @@ import { isProdMode } from '/@/utils/env';
 const setting: ProjectConfig = {
   // locale setting
   locale: {
-    // Locales
+    // Locale
     lang: 'zh_CN',
     // Default locale
     fallback: 'zh_CN',
@@ -29,17 +29,22 @@ const setting: ProjectConfig = {
 
   // Whether to show the configuration button
   showSettingButton: true,
+
   // 权限模式
   permissionMode: PermissionModeEnum.ROLE,
+
   // 网站灰色模式,用于可能悼念的日期开启
   grayMode: false,
+
   // 色弱模式
   colorWeak: false,
 
   // 是否取消菜单,顶部,多标签页显示, 用于可能内嵌在别的系统内
   fullContent: false,
+
   // content mode
   contentMode: ContentEnum.FULL,
+
   // 是否显示logo
   showLogo: true,
 
@@ -58,11 +63,10 @@ const setting: ProjectConfig = {
     showFullScreen: true,
     // 显示文档按钮
     showDoc: true,
-    //  是否显示github
-    showGithub: true,
     // 显示消息中心按钮
     showNotice: true,
   },
+
   // 菜单配置
   menuSetting: {
     // 菜单折叠
@@ -108,13 +112,16 @@ const setting: ProjectConfig = {
     // 标签页缓存最大数量
     max: 12,
   },
+
   // 是否开启KeepAlive缓存  开发时候最好关闭,不然每次都需要清除缓存
   openKeepAlive: true,
 
   // 自动锁屏时间,为0不锁屏。 单位分钟 默认0
   lockTime: 0,
+
   // 显示面包屑
   showBreadCrumb: true,
+
   // 显示面包屑图标
   showBreadCrumbIcon: false,
 

+ 1 - 1
src/setup/error-handle/index.ts

@@ -3,7 +3,7 @@
  */
 
 import { errorStore, ErrorInfo } from '/@/store/modules/error';
-import { useProjectSetting } from '/@/settings/use';
+import { useProjectSetting } from '/@/hooks/setting';
 import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
 import { App } from 'vue';
 

+ 1 - 1
src/setup/i18n/index.ts

@@ -4,7 +4,7 @@ import type { I18n, I18nOptions } from 'vue-i18n';
 import { createI18n } from 'vue-i18n';
 import localeMessages from '/@/locales';
 import { useLocale } from '/@/hooks/web/useLocale';
-import { useLocaleSetting } from '/@/settings/use/useLocaleSetting';
+import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting';
 
 const { setupLocale } = useLocale();
 

+ 1 - 1
src/store/modules/error.ts

@@ -4,7 +4,7 @@ import { VuexModule, getModule, Module, Mutation, Action } from 'vuex-module-dec
 
 import { formatToDateTime } from '/@/utils/dateUtil';
 import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
-import { useProjectSetting } from '/@/settings/use';
+import { useProjectSetting } from '/@/hooks/setting';
 
 export interface ErrorInfo {
   type: ErrorTypeEnum;

+ 0 - 67
src/store/modules/menu.ts

@@ -1,67 +0,0 @@
-import store from '/@/store';
-import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper';
-import { VuexModule, Module, getModule, Mutation } from 'vuex-module-decorators';
-
-import { appStore } from '/@/store/modules/app';
-
-const NAME = 'menu';
-hotModuleUnregisterModule(NAME);
-@Module({ namespaced: true, name: NAME, dynamic: true, store })
-class Menu extends VuexModule {
-  // 是否开始拖拽
-  private dragStartState = false;
-
-  private currentTopSplitMenuPathState = '';
-
-  /**
-   * @description: 获取窗口名称
-   */
-  get getCollapsedState() {
-    return appStore.getProjectConfig.menuSetting.collapsed;
-  }
-
-  get getCurrentTopSplitMenuPathState() {
-    return this.currentTopSplitMenuPathState;
-  }
-
-  get getDragStartState() {
-    return this.dragStartState;
-  }
-
-  get getMenuWidthState() {
-    return appStore.getProjectConfig.menuSetting.menuWidth;
-  }
-
-  @Mutation
-  commitDragStartState(dragStart: boolean): void {
-    this.dragStartState = dragStart;
-  }
-
-  @Mutation
-  commitCurrentTopSplitMenuPathState(path: string): void {
-    this.currentTopSplitMenuPathState = path;
-  }
-
-  // 改变菜单展开状态
-  @Mutation
-  commitCollapsedState(collapsed: boolean): void {
-    // this.collapsedState = collapsed;
-    appStore.commitProjectConfigState({
-      menuSetting: {
-        collapsed: collapsed,
-      },
-    });
-  }
-
-  @Mutation
-  commitMenuWidthState(menuWidth: number): void {
-    // this.menuWidthState = menuWidth;
-    appStore.commitProjectConfigState({
-      menuSetting: {
-        menuWidth: menuWidth,
-      },
-    });
-  }
-}
-
-export const menuStore = getModule<Menu>(Menu);

+ 0 - 5
src/store/modules/permission.ts

@@ -97,11 +97,6 @@ class Permission extends VuexModule {
         if (!roles) return true;
         return roleList.some((role) => roles.includes(role));
       });
-      // this.commitRoutesState(routes);
-      // Background permissions
-      // warn(
-      //   `当前权限模式为:${PermissionModeEnum.ROLE},请将src/store/modules/permission.ts内的后台菜单获取函数注释,如果已注释可以忽略此信息!`
-      // );
       //  如果确定不需要做后台动态权限,请将下面整个判断注释
     } else if (permissionMode === PermissionModeEnum.BACK) {
       const messageKey = 'loadMenu';

+ 0 - 1
src/types/config.d.ts

@@ -44,7 +44,6 @@ export interface HeaderSetting {
   useLockPage: boolean;
   // 显示文档按钮
   showDoc: boolean;
-  showGithub: boolean;
   // 显示消息中心按钮
   showNotice: boolean;
 }

+ 2 - 1
src/utils/file/download.ts

@@ -1,3 +1,4 @@
+import { openWindow } from '..';
 import { dataURLtoBlob, urlToBase64 } from './base64Conver';
 
 /**
@@ -93,6 +94,6 @@ export function downloadByUrl({
     url += '?download';
   }
 
-  window.open(url, target);
+  openWindow(url, { target });
   return true;
 }

+ 1 - 1
src/utils/helper/envHelper.ts

@@ -1,5 +1,5 @@
 import { getEnv } from '/@/utils/env';
-import { useGlobSetting } from '/@/settings/use';
+import { useGlobSetting } from '/@/hooks/setting';
 import pkg from '../../../package.json';
 const globSetting = useGlobSetting();
 

+ 1 - 1
src/utils/http/axios/index.ts

@@ -10,7 +10,7 @@ import { AxiosTransform } from './axiosTransform';
 
 import { checkStatus } from './checkStatus';
 
-import { useGlobSetting } from '/@/settings/use';
+import { useGlobSetting } from '/@/hooks/setting';
 import { useMessage } from '/@/hooks/web/useMessage';
 
 import { RequestEnum, ResultEnum, ContentTypeEnum } from '/@/enums/httpEnum';

+ 14 - 0
src/utils/index.ts

@@ -11,6 +11,7 @@ export function getPopupContainer(node?: HTMLElement): HTMLElement {
   }
   return document.body;
 }
+
 /**
  * Add the object as a parameter to the URL
  * @param baseUrl url
@@ -64,3 +65,16 @@ export function unique<T = any>(arr: T[], key: string): T[] {
 export function es6Unique<T>(arr: T[]): T[] {
   return Array.from(new Set(arr));
 }
+
+export function openWindow(
+  url: string,
+  opt?: { target?: TargetContext | string; noopener?: boolean; noreferrer?: boolean }
+) {
+  const { target = '__blank', noopener = true, noreferrer = true } = opt || {};
+  const feature: string[] = [];
+
+  noopener && feature.push('noopener=yes');
+  noreferrer && feature.push('noreferrer=yes');
+
+  window.open(url, target, feature.join(','));
+}

+ 3 - 1
src/views/demo/feat/icon/index.vue

@@ -45,6 +45,8 @@
 
   import Icon from '/@/components/Icon/index';
 
+  import { openWindow } from '/@/utils';
+
   export default defineComponent({
     components: {
       CollapseContainer,
@@ -61,7 +63,7 @@
     setup() {
       return {
         toIconify: () => {
-          window.open('https://iconify.design/', '__blank');
+          openWindow('https://iconify.design/');
         },
       };
     },

+ 4 - 4
src/views/demo/page/form/high/index.vue

@@ -16,23 +16,23 @@
       </a-card>
     </div>
 
-    <AppFooterToolbar>
+    <AppPageFooter>
       <template #right>
         <a-button type="primary" @click="submitAll">提交</a-button>
       </template>
-    </AppFooterToolbar>
+    </AppPageFooter>
   </div>
 </template>
 <script lang="ts">
   import { BasicForm, useForm } from '/@/components/Form';
   import { defineComponent, ref } from 'vue';
   import PersonTable from './PersonTable.vue';
-  import { AppFooterToolbar } from '/@/components/Application';
+  import { AppPageFooter } from '/@/components/Application';
 
   import { schemas, taskSchemas } from './data';
 
   export default defineComponent({
-    components: { BasicForm, PersonTable, AppFooterToolbar },
+    components: { BasicForm, PersonTable, AppPageFooter },
     setup() {
       const tableRef = ref<{ getDataSource: () => any } | null>(null);
 

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

@@ -72,7 +72,7 @@
 
   // import { appStore } from '/@/store/modules/app';
   import { useMessage } from '/@/hooks/web/useMessage';
-  import { useGlobSetting } from '/@/settings/use';
+  import { useGlobSetting } from '/@/hooks/setting';
   import logo from '/@/assets/images/logo.png';
 
   export default defineComponent({

+ 3 - 3
vite.config.ts

@@ -30,7 +30,7 @@ function pathResolve(dir: string) {
 
 const viteConfig: UserConfig = {
   /**
-   * Entry. Use this to specify a js entry file in use cases where an
+   * Entry. Use this to specify a js entry file in setting cases where an
    * `index.html` does not exist (e.g. serving vite assets from a different host)
    * @default 'index.html'
    */
@@ -51,7 +51,7 @@ const viteConfig: UserConfig = {
    */
   open: false,
   /**
-   * Set to `false` to disable minification, or specify the minifier to use.
+   * Set to `false` to disable minification, or specify the minifier to setting.
    * Available options are 'terser' or 'esbuild'.
    * @default 'terser'
    */
@@ -112,7 +112,7 @@ const viteConfig: UserConfig = {
   },
   define: {
     __VERSION__: pkg.version,
-    // use vue-i18-next
+    // setting vue-i18-next
     // Suppress warning
     __VUE_I18N_LEGACY_API__: false,
     __VUE_I18N_FULL_INSTALL__: false,