Commit 27e50b47479af8eaeb4be020aeb0fcbdb4308295

Authored by vben
1 parent ed41e508

perf(tabs): perf multiple-tabs

Showing 33 changed files with 575 additions and 383 deletions
src/components/Dropdown/index.ts
1 import { withInstall } from '../util'; 1 import { withInstall } from '../util';
2 2
3 -import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';  
4 -export const Dropdown = createAsyncComponent(() => import('./src/Dropdown')); 3 +import Dropdown from './src/Dropdown';
5 4
6 withInstall(Dropdown); 5 withInstall(Dropdown);
7 export * from './src/types'; 6 export * from './src/types';
  7 +export { Dropdown };
src/components/Menu/src/BasicMenu.tsx
@@ -243,6 +243,7 @@ export default defineComponent({ @@ -243,6 +243,7 @@ export default defineComponent({
243 onOpenChange={handleOpenChange} 243 onOpenChange={handleOpenChange}
244 class={unref(getMenuClass)} 244 class={unref(getMenuClass)}
245 onClick={handleMenuClick} 245 onClick={handleMenuClick}
  246 + subMenuOpenDelay={0.2}
246 {...unref(getInlineCollapseOptions)} 247 {...unref(getInlineCollapseOptions)}
247 > 248 >
248 {{ 249 {{
src/hooks/setting/useMenuSetting.ts
@@ -6,6 +6,7 @@ import { appStore } from '/@/store/modules/app'; @@ -6,6 +6,7 @@ import { appStore } from '/@/store/modules/app';
6 6
7 import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum'; 7 import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum';
8 import { MenuModeEnum, MenuTypeEnum, TriggerEnum } from '/@/enums/menuEnum'; 8 import { MenuModeEnum, MenuTypeEnum, TriggerEnum } from '/@/enums/menuEnum';
  9 +import { useFullContent } from '/@/hooks/web/useFullContent';
9 10
10 // Get menu configuration 11 // Get menu configuration
11 const getMenuSetting = computed(() => appStore.getProjectConfig.menuSetting); 12 const getMenuSetting = computed(() => appStore.getProjectConfig.menuSetting);
@@ -78,6 +79,15 @@ const getCalcContentWidth = computed(() => { @@ -78,6 +79,15 @@ const getCalcContentWidth = computed(() => {
78 return `calc(100% - ${unref(width)}px)`; 79 return `calc(100% - ${unref(width)}px)`;
79 }); 80 });
80 81
  82 +const { getFullContent: fullContent } = useFullContent();
  83 +
  84 +const getShowSidebar = computed(() => {
  85 + return (
  86 + unref(getSplit) ||
  87 + (unref(getShowMenu) && unref(getMenuMode) !== MenuModeEnum.HORIZONTAL && !unref(fullContent))
  88 + );
  89 +});
  90 +
81 // Set menu configuration 91 // Set menu configuration
82 function setMenuSetting(menuSetting: Partial<MenuSetting>): void { 92 function setMenuSetting(menuSetting: Partial<MenuSetting>): void {
83 appStore.commitProjectConfigState({ menuSetting }); 93 appStore.commitProjectConfigState({ menuSetting });
@@ -119,5 +129,6 @@ export function useMenuSetting() { @@ -119,5 +129,6 @@ export function useMenuSetting() {
119 getMenuHidden, 129 getMenuHidden,
120 getIsTopMenu, 130 getIsTopMenu,
121 getMenuBgColor, 131 getMenuBgColor,
  132 + getShowSidebar,
122 }; 133 };
123 } 134 }
src/layouts/default/LayoutTrigger.tsx deleted 100644 → 0
1 -import type { FunctionalComponent } from 'vue';  
2 -  
3 -import { defineComponent, unref } from 'vue';  
4 -  
5 -import {  
6 - DoubleRightOutlined,  
7 - DoubleLeftOutlined,  
8 - MenuUnfoldOutlined,  
9 - MenuFoldOutlined,  
10 -} from '@ant-design/icons-vue';  
11 -  
12 -import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';  
13 -import { propTypes } from '/@/utils/propTypes';  
14 -  
15 -const SiderTrigger: FunctionalComponent = () => {  
16 - const { getCollapsed } = useMenuSetting();  
17 - return unref(getCollapsed) ? <DoubleRightOutlined /> : <DoubleLeftOutlined />;  
18 -};  
19 -  
20 -const HeaderTrigger: FunctionalComponent<{  
21 - theme?: string;  
22 -}> = (props) => {  
23 - const { toggleCollapsed, getCollapsed } = useMenuSetting();  
24 - return (  
25 - <span class={['layout-trigger', props.theme]} onClick={toggleCollapsed}>  
26 - {unref(getCollapsed) ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}  
27 - </span>  
28 - );  
29 -};  
30 -  
31 -export default defineComponent({  
32 - name: 'LayoutTrigger',  
33 - props: {  
34 - sider: propTypes.bool.def(true),  
35 - theme: propTypes.oneOf(['light', 'dark']),  
36 - },  
37 - setup(props) {  
38 - return () => {  
39 - return props.sider ? <SiderTrigger /> : <HeaderTrigger theme={props.theme} />;  
40 - };  
41 - },  
42 -});  
src/layouts/default/content/index.vue
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 :loading="getPageLoading" 6 :loading="getPageLoading"
7 background="rgba(240, 242, 245, 0.6)" 7 background="rgba(240, 242, 245, 0.6)"
8 absolute 8 absolute
9 - :class="`${prefixCls}__loading`" 9 + :class="`${prefixCls}-loading`"
10 /> 10 />
11 </transition> 11 </transition>
12 <PageLayout /> 12 <PageLayout />
@@ -53,7 +53,7 @@ @@ -53,7 +53,7 @@
53 margin: 0 auto; 53 margin: 0 auto;
54 } 54 }
55 55
56 - &__loading { 56 + &-loading {
57 position: absolute; 57 position: absolute;
58 top: 200px; 58 top: 200px;
59 z-index: @page-loading-z-index; 59 z-index: @page-loading-z-index;
src/layouts/default/feature/index.vue 0 → 100644
  1 +<template>
  2 + <LayoutLockPage />
  3 + <BackTop v-if="getUseOpenBackTop" :target="getTarget" />
  4 + <SettingDrawer v-if="getShowSettingButton" />
  5 +</template>
  6 +<script lang="ts">
  7 + import { defineComponent } from 'vue';
  8 + import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
  9 + import { BackTop } from 'ant-design-vue';
  10 + import { useRootSetting } from '/@/hooks/setting/useRootSetting';
  11 +
  12 + export default defineComponent({
  13 + name: 'LayoutFeatures',
  14 + components: {
  15 + BackTop,
  16 + LayoutLockPage: createAsyncComponent(() => import('/@/views/sys/lock/index.vue')),
  17 + SettingDrawer: createAsyncComponent(() => import('/@/layouts/default/setting/index.vue')),
  18 + },
  19 + setup() {
  20 + const { getUseOpenBackTop, getShowSettingButton } = useRootSetting();
  21 +
  22 + return {
  23 + getTarget: () => document.body,
  24 + getUseOpenBackTop,
  25 + getShowSettingButton,
  26 + };
  27 + },
  28 + });
  29 +</script>
src/layouts/default/footer/index.less deleted 100644 → 0
1 -@normal-color: rgba(0, 0, 0, 0.45);  
2 -  
3 -@hover-color: rgba(0, 0, 0, 0.85);  
4 -  
5 -.layout-footer {  
6 - color: @normal-color;  
7 - text-align: center;  
8 -  
9 - &__links {  
10 - margin-bottom: 8px;  
11 -  
12 - a {  
13 - color: @normal-color;  
14 -  
15 - &:hover {  
16 - color: @hover-color;  
17 - }  
18 - }  
19 -  
20 - .github {  
21 - margin: 0 30px;  
22 -  
23 - &:hover {  
24 - color: @hover-color;  
25 - }  
26 - }  
27 - }  
28 -}  
src/layouts/default/footer/index.tsx deleted 100644 → 0
1 -import './index.less';  
2 -  
3 -import { defineComponent } from 'vue';  
4 -import { Layout } from 'ant-design-vue';  
5 -  
6 -import { GithubFilled } from '@ant-design/icons-vue';  
7 -  
8 -import { DOC_URL, GITHUB_URL, SITE_URL } from '/@/settings/siteSetting';  
9 -import { openWindow } from '/@/utils';  
10 -  
11 -import { useI18n } from '/@/hooks/web/useI18n';  
12 -  
13 -export default defineComponent({  
14 - name: 'LayoutContent',  
15 - setup() {  
16 - const { t } = useI18n();  
17 - return () => {  
18 - return (  
19 - <Layout.Footer class="layout-footer">  
20 - {() => (  
21 - <>  
22 - <div class="layout-footer__links">  
23 - <a onClick={() => openWindow(SITE_URL)}>{t('layout.footer.onlinePreview')}</a>  
24 - <GithubFilled onClick={() => openWindow(GITHUB_URL)} class="github" />  
25 - <a onClick={() => openWindow(DOC_URL)}>{t('layout.footer.onlineDocument')}</a>  
26 - </div>  
27 - <div>Copyright &copy;2020 Vben Admin</div>  
28 - </>  
29 - )}  
30 - </Layout.Footer>  
31 - );  
32 - };  
33 - },  
34 -});  
src/layouts/default/footer/index.vue 0 → 100644
  1 +<template>
  2 + <Footer :class="prefixCls" v-if="getShowLayoutFooter">
  3 + <div :class="`${prefixCls}__links`">
  4 + <a @click="openWindow(SITE_URL)">{{ t('layout.footer.onlinePreview') }}</a>
  5 + <GithubFilled @click="openWindow(GITHUB_URL)" :class="`${prefixCls}__github`" />
  6 + <a @click="openWindow(DOC_URL)">{{ t('layout.footer.onlineDocument') }}</a>
  7 + </div>
  8 + <div>Copyright &copy;2020 Vben Admin</div>
  9 + </Footer>
  10 +</template>
  11 +
  12 +<script lang="ts">
  13 + import { computed, defineComponent, unref } from 'vue';
  14 + import { Layout } from 'ant-design-vue';
  15 +
  16 + import { GithubFilled } from '@ant-design/icons-vue';
  17 +
  18 + import { DOC_URL, GITHUB_URL, SITE_URL } from '/@/settings/siteSetting';
  19 + import { openWindow } from '/@/utils';
  20 +
  21 + import { useI18n } from '/@/hooks/web/useI18n';
  22 + import { useRootSetting } from '/@/hooks/setting/useRootSetting';
  23 + import { useRouter } from 'vue-router';
  24 + import { useDesign } from '/@/hooks/web/useDesign';
  25 +
  26 + export default defineComponent({
  27 + name: 'LayoutFooter',
  28 + components: { Footer: Layout.Footer, GithubFilled },
  29 + setup() {
  30 + const { t } = useI18n();
  31 + const { getShowFooter } = useRootSetting();
  32 + const { currentRoute } = useRouter();
  33 + const { prefixCls } = useDesign('layout-footer');
  34 +
  35 + const getShowLayoutFooter = computed(() => {
  36 + return unref(getShowFooter) && !unref(currentRoute).meta?.hiddenFooter;
  37 + });
  38 + return { getShowLayoutFooter, prefixCls, t, DOC_URL, GITHUB_URL, SITE_URL, openWindow };
  39 + },
  40 + });
  41 +</script>
  42 +<style lang="less" scoped>
  43 + @import (reference) '../../../design/index.less';
  44 + @prefix-cls: ~'@{namespace}-layout-footer';
  45 +
  46 + @normal-color: rgba(0, 0, 0, 0.45);
  47 +
  48 + @hover-color: rgba(0, 0, 0, 0.85);
  49 +
  50 + .@{prefix-cls} {
  51 + color: @normal-color;
  52 + text-align: center;
  53 +
  54 + &__links {
  55 + margin-bottom: 8px;
  56 +
  57 + a {
  58 + color: @normal-color;
  59 +
  60 + &:hover {
  61 + color: @hover-color;
  62 + }
  63 + }
  64 + }
  65 +
  66 + &__github {
  67 + margin: 0 30px;
  68 +
  69 + &:hover {
  70 + color: @hover-color;
  71 + }
  72 + }
  73 + }
  74 +</style>
src/layouts/default/header/LayoutHeader.tsx
@@ -19,7 +19,7 @@ import UserDropdown from &#39;./UserDropdown&#39;; @@ -19,7 +19,7 @@ import UserDropdown from &#39;./UserDropdown&#39;;
19 import LayoutMenu from '../menu'; 19 import LayoutMenu from '../menu';
20 import LayoutBreadcrumb from './LayoutBreadcrumb.vue'; 20 import LayoutBreadcrumb from './LayoutBreadcrumb.vue';
21 import LockAction from './actions/LockAction'; 21 import LockAction from './actions/LockAction';
22 -import LayoutTrigger from '../LayoutTrigger'; 22 +import LayoutTrigger from '../trigger/index.vue';
23 import NoticeAction from './notice/NoticeActionItem.vue'; 23 import NoticeAction from './notice/NoticeActionItem.vue';
24 import { 24 import {
25 RedoOutlined, 25 RedoOutlined,
src/layouts/default/header/LayoutMultipleHeader.tsx
@@ -3,7 +3,7 @@ import &#39;./LayoutMultipleHeader.less&#39;; @@ -3,7 +3,7 @@ import &#39;./LayoutMultipleHeader.less&#39;;
3 import { defineComponent, unref, computed, ref, watch, nextTick, CSSProperties } from 'vue'; 3 import { defineComponent, unref, computed, ref, watch, nextTick, CSSProperties } from 'vue';
4 4
5 import LayoutHeader from './LayoutHeader'; 5 import LayoutHeader from './LayoutHeader';
6 -import MultipleTabs from '../multitabs/index'; 6 +import MultipleTabs from '../tabs/index.vue';
7 7
8 import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting'; 8 import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
9 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; 9 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
src/layouts/default/header/index.less
1 @import (reference) '../../../design/index.less'; 1 @import (reference) '../../../design/index.less';
  2 +@header-trigger-prefix-cls: ~'@{namespace}-layout-header-trigger';
2 3
3 .layout-header { 4 .layout-header {
4 display: flex; 5 display: flex;
@@ -24,7 +25,7 @@ @@ -24,7 +25,7 @@
24 height: 100%; 25 height: 100%;
25 align-items: center; 26 align-items: center;
26 27
27 - .layout-trigger { 28 + .@{header-trigger-prefix-cls} {
28 display: flex; 29 display: flex;
29 height: 100%; 30 height: 100%;
30 padding: 1px 10px 0 16px; 31 padding: 1px 10px 0 16px;
src/layouts/default/index.tsx
1 import './index.less'; 1 import './index.less';
2 2
3 -import { defineComponent, unref, computed, ref } from 'vue';  
4 -import { Layout, BackTop } from 'ant-design-vue';  
5 -import LayoutHeader from './header/LayoutHeader'; 3 +import { defineComponent, unref, ref } from 'vue';
  4 +import { Layout } from 'ant-design-vue';
  5 +import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
6 6
  7 +import LayoutHeader from './header/LayoutHeader';
7 import LayoutContent from './content/index.vue'; 8 import LayoutContent from './content/index.vue';
8 -import LayoutFooter from './footer';  
9 -import LayoutLockPage from '/@/views/sys/lock/index.vue';  
10 import LayoutSideBar from './sider'; 9 import LayoutSideBar from './sider';
11 -import SettingBtn from './setting/index.vue';  
12 import LayoutMultipleHeader from './header/LayoutMultipleHeader'; 10 import LayoutMultipleHeader from './header/LayoutMultipleHeader';
13 11
14 -import { MenuModeEnum } from '/@/enums/menuEnum';  
15 -  
16 -import { useRouter } from 'vue-router';  
17 -import { useFullContent } from '/@/hooks/web/useFullContent';  
18 import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting'; 12 import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
19 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; 13 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
20 -import { useRootSetting } from '/@/hooks/setting/useRootSetting';  
21 import { createLayoutContext } from './useLayoutContext'; 14 import { createLayoutContext } from './useLayoutContext';
22 15
23 import { registerGlobComp } from '/@/components/registerGlobComp'; 16 import { registerGlobComp } from '/@/components/registerGlobComp';
24 import { createBreakpointListen } from '/@/hooks/event/useBreakpoint'; 17 import { createBreakpointListen } from '/@/hooks/event/useBreakpoint';
25 import { isMobile } from '/@/utils/is'; 18 import { isMobile } from '/@/utils/is';
  19 +
  20 +const LayoutFeatures = createAsyncComponent(() => import('/@/layouts/default/feature/index.vue'));
  21 +const LayoutFooter = createAsyncComponent(() => import('/@/layouts/default/footer/index.vue'));
  22 +
26 export default defineComponent({ 23 export default defineComponent({
27 name: 'DefaultLayout', 24 name: 'DefaultLayout',
28 setup() { 25 setup() {
29 - const { currentRoute } = useRouter();  
30 const headerRef = ref<ComponentRef>(null); 26 const headerRef = ref<ComponentRef>(null);
31 const isMobileRef = ref(false); 27 const isMobileRef = ref(false);
32 28
@@ -43,56 +39,27 @@ export default defineComponent({ @@ -43,56 +39,27 @@ export default defineComponent({
43 39
44 const { getShowFullHeaderRef } = useHeaderSetting(); 40 const { getShowFullHeaderRef } = useHeaderSetting();
45 41
46 - const { getUseOpenBackTop, getShowSettingButton, getShowFooter } = useRootSetting();  
47 -  
48 - const { getShowMenu, getMenuMode, getSplit } = useMenuSetting();  
49 -  
50 - const { getFullContent } = useFullContent();  
51 -  
52 - const getShowLayoutFooter = computed(() => {  
53 - return unref(getShowFooter) && !unref(currentRoute).meta?.hiddenFooter;  
54 - });  
55 -  
56 - const showSideBarRef = computed(() => {  
57 - return (  
58 - unref(getSplit) ||  
59 - (unref(getShowMenu) &&  
60 - unref(getMenuMode) !== MenuModeEnum.HORIZONTAL &&  
61 - !unref(getFullContent))  
62 - );  
63 - });  
64 -  
65 - function renderFeatures() {  
66 - return (  
67 - <>  
68 - <LayoutLockPage />  
69 - {/* back top */}  
70 - {unref(getUseOpenBackTop) && <BackTop target={() => document.body} />}  
71 - {/* open setting drawer */}  
72 - {unref(getShowSettingButton) && <SettingBtn />}  
73 - </>  
74 - );  
75 - } 42 + const { getShowSidebar } = useMenuSetting();
76 43
77 return () => { 44 return () => {
78 return ( 45 return (
79 <Layout class="default-layout"> 46 <Layout class="default-layout">
80 {() => ( 47 {() => (
81 <> 48 <>
82 - {renderFeatures()} 49 + <LayoutFeatures />
83 50
84 {unref(getShowFullHeaderRef) && <LayoutHeader fixed={true} ref={headerRef} />} 51 {unref(getShowFullHeaderRef) && <LayoutHeader fixed={true} ref={headerRef} />}
85 52
86 <Layout> 53 <Layout>
87 {() => ( 54 {() => (
88 <> 55 <>
89 - {unref(showSideBarRef) && <LayoutSideBar />} 56 + {unref(getShowSidebar) && <LayoutSideBar />}
90 <Layout class="default-layout__main"> 57 <Layout class="default-layout__main">
91 {() => ( 58 {() => (
92 <> 59 <>
93 <LayoutMultipleHeader /> 60 <LayoutMultipleHeader />
94 <LayoutContent /> 61 <LayoutContent />
95 - {unref(getShowLayoutFooter) && <LayoutFooter />} 62 + <LayoutFooter />
96 </> 63 </>
97 )} 64 )}
98 </Layout> 65 </Layout>
src/layouts/default/index.vue 0 → 100644
  1 +<template>
  2 + <Layout :class="prefixCls">
  3 + <LayoutFeatures />
  4 + <LayoutHeader fixed ref="headerRef" v-if="getShowFullHeaderRef" />
  5 + <Layout>
  6 + <LayoutSideBar v-if="getShowSidebar" />
  7 + <Layout :class="`${prefixCls}__main`">
  8 + <LayoutMultipleHeader />
  9 + <LayoutContent />
  10 + <LayoutFooter />
  11 + </Layout>
  12 + </Layout>
  13 + </Layout>
  14 +</template>
  15 +
  16 +<script lang="ts">
  17 + import { defineComponent, ref } from 'vue';
  18 + import { Layout } from 'ant-design-vue';
  19 + import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
  20 +
  21 + import LayoutHeader from './header/LayoutHeader';
  22 + import LayoutContent from './content/index.vue';
  23 + import LayoutSideBar from './sider';
  24 + import LayoutMultipleHeader from './header/LayoutMultipleHeader';
  25 +
  26 + import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
  27 + import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
  28 + import { useDesign } from '/@/hooks/web/useDesign';
  29 + import { createLayoutContext } from './useLayoutContext';
  30 +
  31 + import { registerGlobComp } from '/@/components/registerGlobComp';
  32 + import { createBreakpointListen } from '/@/hooks/event/useBreakpoint';
  33 + import { isMobile } from '/@/utils/is';
  34 +
  35 + export default defineComponent({
  36 + name: 'DefaultLayout',
  37 + components: {
  38 + LayoutFeatures: createAsyncComponent(() => import('/@/layouts/default/feature/index.vue')),
  39 + LayoutFooter: createAsyncComponent(() => import('/@/layouts/default/footer/index.vue')),
  40 + LayoutHeader,
  41 + LayoutContent,
  42 + LayoutSideBar,
  43 + LayoutMultipleHeader,
  44 + Layout,
  45 + },
  46 + setup() {
  47 + const headerRef = ref<ComponentRef>(null);
  48 + const isMobileRef = ref(false);
  49 +
  50 + const { prefixCls } = useDesign('default-layout');
  51 +
  52 + createLayoutContext({ fullHeader: headerRef, isMobile: isMobileRef });
  53 +
  54 + createBreakpointListen(() => {
  55 + isMobileRef.value = isMobile();
  56 + });
  57 +
  58 + // ! Only register global components here
  59 + // ! Can reduce the size of the first screen code
  60 + // default layout It is loaded after login. So it won’t be packaged to the first screen
  61 + registerGlobComp();
  62 +
  63 + const { getShowFullHeaderRef } = useHeaderSetting();
  64 +
  65 + const { getShowSidebar } = useMenuSetting();
  66 +
  67 + return {
  68 + getShowFullHeaderRef,
  69 + getShowSidebar,
  70 + headerRef,
  71 + prefixCls,
  72 + };
  73 + },
  74 + });
  75 +</script>
  76 +<style lang="less">
  77 + @import (reference) '../../design/index.less';
  78 + @prefix-cls: ~'@{namespace}-default-layout';
  79 +
  80 + .@{prefix-cls} {
  81 + display: flex;
  82 + width: 100%;
  83 + min-height: 100%;
  84 + background: @content-bg;
  85 + flex-direction: column;
  86 +
  87 + > .ant-layout {
  88 + min-height: 100%;
  89 + }
  90 +
  91 + &__main {
  92 + margin-left: 1px;
  93 + }
  94 + }
  95 +</style>
src/layouts/default/setting/index.vue
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 import { useDesign } from '/@/hooks/web/useDesign'; 13 import { useDesign } from '/@/hooks/web/useDesign';
14 14
15 export default defineComponent({ 15 export default defineComponent({
16 - name: 'SettingBtn', 16 + name: 'SettingButton',
17 components: { SettingOutlined, SettingDrawer }, 17 components: { SettingOutlined, SettingDrawer },
18 setup() { 18 setup() {
19 const [register, { openDrawer }] = useDrawer(); 19 const [register, { openDrawer }] = useDrawer();
src/layouts/default/sider/useLayoutSider.tsx
1 import type { Ref } from 'vue'; 1 import type { Ref } from 'vue';
2 2
3 import { computed, unref, onMounted, nextTick, ref } from 'vue'; 3 import { computed, unref, onMounted, nextTick, ref } from 'vue';
4 -import LayoutTrigger from '/@/layouts/default/LayoutTrigger'; 4 +import LayoutTrigger from '/@/layouts/default/trigger/index.vue';
5 5
6 import { TriggerEnum } from '/@/enums/menuEnum'; 6 import { TriggerEnum } from '/@/enums/menuEnum';
7 7
src/layouts/default/tabs/components/QuickButton.vue 0 → 100644
  1 +<template>
  2 + <TabContent :type="TabContentEnum.EXTRA_TYPE" :tabItem="$route" />
  3 +</template>
  4 +<script lang="ts">
  5 + import { defineComponent } from 'vue';
  6 +
  7 + import { TabContentEnum } from '../types';
  8 +
  9 + import TabContent from './TabContent.vue';
  10 + export default defineComponent({
  11 + name: 'QuickButton',
  12 + components: {
  13 + TabContent,
  14 + },
  15 + setup() {
  16 + return {
  17 + TabContentEnum,
  18 + };
  19 + },
  20 + });
  21 +</script>
src/layouts/default/multitabs/TabContent.tsx renamed to src/layouts/default/tabs/components/TabContent.vue
1 -import type { PropType } from 'vue';  
2 -import { Dropdown } from '/@/components/Dropdown/index';  
3 -  
4 -import { defineComponent, unref, FunctionalComponent } from 'vue'; 1 +<template>
  2 + <Dropdown :dropMenuList="getDropMenuList" :trigger="getTrigger" @menuEvent="handleMenuEvent">
  3 + <div :class="`${prefixCls}__info`" @contextmenu="handleContext" v-if="isTabs">
  4 + <span class="ml-1">{{ getTitle }}</span>
  5 + </div>
5 6
6 -import { TabContentProps } from './types'; 7 + <span :class="`${prefixCls}__extra`" v-else>
  8 + <RightOutlined />
  9 + </span>
  10 + </Dropdown>
  11 +</template>
  12 +<script lang="ts">
  13 + import type { PropType } from 'vue';
7 14
8 -import { RightOutlined } from '@ant-design/icons-vue'; 15 + import { defineComponent, computed } from 'vue';
  16 + import { Dropdown } from '/@/components/Dropdown/index';
9 17
10 -import { TabContentEnum } from './types'; 18 + import { TabContentProps, TabContentEnum } from '../types';
11 19
12 -import { useTabDropdown } from './useTabDropdown';  
13 -import { useI18n } from '/@/hooks/web/useI18n'; 20 + import { RightOutlined } from '@ant-design/icons-vue';
14 21
15 -import { RouteLocationNormalized } from 'vue-router'; 22 + import { useDesign } from '/@/hooks/web/useDesign';
  23 + import { useTabDropdown } from '../useTabDropdown';
  24 + import { useI18n } from '/@/hooks/web/useI18n';
16 25
17 -const { t: titleT } = useI18n(); 26 + import { RouteLocationNormalized } from 'vue-router';
  27 + export default defineComponent({
  28 + name: 'TabContent',
  29 + components: { Dropdown, RightOutlined },
  30 + props: {
  31 + tabItem: {
  32 + type: Object as PropType<RouteLocationNormalized>,
  33 + default: null,
  34 + },
18 35
19 -const ExtraContent: FunctionalComponent = () => {  
20 - return (  
21 - <span class={`multiple-tabs-content__extra `}>  
22 - <RightOutlined />  
23 - </span>  
24 - );  
25 -}; 36 + type: {
  37 + type: Number as PropType<TabContentEnum>,
  38 + default: TabContentEnum.TAB_TYPE,
  39 + },
  40 + },
  41 + setup(props) {
  42 + const { prefixCls } = useDesign('multiple-tabs-content');
  43 + const { t } = useI18n();
26 44
27 -const TabContent: FunctionalComponent<{ tabItem: RouteLocationNormalized; handler: Fn }> = (  
28 - props  
29 -) => {  
30 - const { tabItem: { meta } = {} } = props; 45 + const getTitle = computed(() => {
  46 + const { tabItem: { meta } = {} } = props;
  47 + return meta && t(meta.title);
  48 + });
31 49
32 - return (  
33 - <div class={`multiple-tabs-content__content `} onContextmenu={props.handler(props.tabItem)}>  
34 - <span class="ml-1">{meta && titleT(meta.title)}</span>  
35 - </div>  
36 - );  
37 -}; 50 + const {
  51 + getDropMenuList,
  52 + handleMenuEvent,
  53 + handleContextMenu,
  54 + getTrigger,
  55 + isTabs,
  56 + } = useTabDropdown(props as TabContentProps);
38 57
39 -export default defineComponent({  
40 - name: 'TabContent',  
41 - props: {  
42 - tabItem: {  
43 - type: Object as PropType<RouteLocationNormalized>,  
44 - default: null, 58 + function handleContext(e: ChangeEvent) {
  59 + props.tabItem && handleContextMenu(props.tabItem)(e);
  60 + }
  61 + return {
  62 + prefixCls,
  63 + getDropMenuList,
  64 + handleMenuEvent,
  65 + handleContext,
  66 + getTrigger,
  67 + isTabs,
  68 + getTitle,
  69 + };
45 }, 70 },
46 -  
47 - type: {  
48 - type: Number as PropType<TabContentEnum>,  
49 - default: TabContentEnum.TAB_TYPE,  
50 - },  
51 - },  
52 - setup(props) {  
53 - const {  
54 - getDropMenuList,  
55 - handleMenuEvent,  
56 - handleContextMenu,  
57 - getTrigger,  
58 - isTabs,  
59 - } = useTabDropdown(props as TabContentProps);  
60 -  
61 - return () => {  
62 - return (  
63 - <Dropdown  
64 - dropMenuList={unref(getDropMenuList)}  
65 - trigger={unref(getTrigger)}  
66 - onMenuEvent={handleMenuEvent}  
67 - >  
68 - {() => {  
69 - if (!unref(isTabs)) {  
70 - return <ExtraContent />;  
71 - }  
72 - return <TabContent handler={handleContextMenu} tabItem={props.tabItem} />;  
73 - }}  
74 - </Dropdown>  
75 - );  
76 - };  
77 - },  
78 -}); 71 + });
  72 +</script>
src/layouts/default/multitabs/index.less renamed to src/layouts/default/tabs/index.less
1 @import (reference) '../../../design/index.less'; 1 @import (reference) '../../../design/index.less';
  2 +@prefix-cls: ~'@{namespace}-multiple-tabs';
2 3
3 -.multiple-tabs { 4 +.@{prefix-cls} {
4 z-index: 10; 5 z-index: 10;
5 height: @multiple-height + 2; 6 height: @multiple-height + 2;
6 - padding: 0 0 2px 0;  
7 - margin-left: -1px;  
8 line-height: @multiple-height + 2; 7 line-height: @multiple-height + 2;
9 background: @white; 8 background: @white;
10 box-shadow: 0 1px 2px 0 rgba(29, 35, 41, 0.05); 9 box-shadow: 0 1px 2px 0 rgba(29, 35, 41, 0.05);
@@ -32,13 +31,33 @@ @@ -32,13 +31,33 @@
32 line-height: calc(@multiple-height - 2px); 31 line-height: calc(@multiple-height - 2px);
33 color: @text-color-call-out; 32 color: @text-color-call-out;
34 background: @white; 33 background: @white;
35 - border: 1px solid darken(@border-color-light, 8%); 34 + border: 1px solid darken(@border-color-light, 6%);
36 transition: none; 35 transition: none;
37 36
  37 + &:not(.ant-tabs-tab-active)::before {
  38 + position: absolute;
  39 + top: -1px;
  40 + left: 50%;
  41 + width: 100%;
  42 + height: 2px;
  43 + background-color: @primary-color;
  44 + content: '';
  45 + opacity: 0;
  46 + transform: translate(-50%, 0) scaleX(0);
  47 + transform-origin: center;
  48 + transition: none;
  49 + }
  50 +
38 &:hover { 51 &:hover {
39 .ant-tabs-close-x { 52 .ant-tabs-close-x {
40 opacity: 1; 53 opacity: 1;
41 } 54 }
  55 +
  56 + &:not(.ant-tabs-tab-active)::before {
  57 + opacity: 1;
  58 + transform: translate(-50%, 0) scaleX(1);
  59 + transition: all 0.3s ease-in-out;
  60 + }
42 } 61 }
43 62
44 .ant-tabs-close-x { 63 .ant-tabs-close-x {
@@ -51,7 +70,7 @@ @@ -51,7 +70,7 @@
51 70
52 &:hover { 71 &:hover {
53 svg { 72 svg {
54 - width: 0.75em; 73 + width: 0.8em;
55 } 74 }
56 } 75 }
57 } 76 }
@@ -73,6 +92,7 @@ @@ -73,6 +92,7 @@
73 color: @white; 92 color: @white;
74 background: fade(@primary-color, 100%); 93 background: fade(@primary-color, 100%);
75 border: 0; 94 border: 0;
  95 + transition: none;
76 96
77 &::before { 97 &::before {
78 position: absolute; 98 position: absolute;
@@ -98,7 +118,7 @@ @@ -98,7 +118,7 @@
98 } 118 }
99 119
100 .ant-tabs-nav > div:nth-child(1) { 120 .ant-tabs-nav > div:nth-child(1) {
101 - padding: 0 10px; 121 + padding: 0 6px;
102 122
103 .ant-tabs-tab { 123 .ant-tabs-tab {
104 margin-right: 3px !important; 124 margin-right: 3px !important;
@@ -124,36 +144,42 @@ @@ -124,36 +144,42 @@
124 .ant-dropdown-trigger { 144 .ant-dropdown-trigger {
125 display: inline-flex; 145 display: inline-flex;
126 } 146 }
127 -}  
128 147
129 -.multiple-tabs-content {  
130 - &__extra {  
131 - display: inline-block;  
132 - width: @multiple-height;  
133 - height: @multiple-height;  
134 - line-height: @multiple-height;  
135 - color: #999;  
136 - text-align: center;  
137 - cursor: pointer;  
138 - border-left: 1px solid #eee;  
139 -  
140 - &:hover {  
141 - color: @text-color-base; 148 + &--hide-close {
  149 + .ant-tabs-close-x {
  150 + opacity: 0 !important;
142 } 151 }
  152 + }
  153 +
  154 + &-content {
  155 + &__extra {
  156 + display: inline-block;
  157 + width: @multiple-height;
  158 + height: @multiple-height;
  159 + line-height: @multiple-height;
  160 + color: #999;
  161 + text-align: center;
  162 + cursor: pointer;
  163 + border-left: 1px solid #eee;
  164 +
  165 + &:hover {
  166 + color: @text-color-base;
  167 + }
143 168
144 - span[role='img'] {  
145 - transform: rotate(90deg); 169 + span[role='img'] {
  170 + transform: rotate(90deg);
  171 + }
146 } 172 }
147 - }  
148 173
149 - &__content {  
150 - display: inline-block;  
151 - width: 100%;  
152 - height: @multiple-height - 2;  
153 - padding-left: 0;  
154 - margin-left: -10px;  
155 - font-size: 12px;  
156 - cursor: pointer;  
157 - user-select: none; 174 + &__info {
  175 + display: inline-block;
  176 + width: 100%;
  177 + height: @multiple-height - 2;
  178 + padding-left: 0;
  179 + margin-left: -10px;
  180 + font-size: 12px;
  181 + cursor: pointer;
  182 + user-select: none;
  183 + }
158 } 184 }
159 } 185 }
src/layouts/default/multitabs/index.tsx renamed to src/layouts/default/tabs/index.vue
1 -import './index.less';  
2 -  
3 -import type { TabContentProps } from './types';  
4 -  
5 -import { defineComponent, watch, computed, unref, ref } from 'vue';  
6 -import { useRouter } from 'vue-router';  
7 -  
8 -import { Tabs } from 'ant-design-vue';  
9 -import TabContent from './TabContent';  
10 -  
11 -import { useGo } from '/@/hooks/web/usePage';  
12 -  
13 -import { TabContentEnum } from './types';  
14 -  
15 -import { tabStore } from '/@/store/modules/tab';  
16 -import { userStore } from '/@/store/modules/user';  
17 -  
18 -import { initAffixTabs, useTabsDrag } from './useMultipleTabs';  
19 -import { REDIRECT_NAME } from '/@/router/constant';  
20 -  
21 -export default defineComponent({  
22 - name: 'MultipleTabs',  
23 - setup() {  
24 - const activeKeyRef = ref('');  
25 -  
26 - const affixTextList = initAffixTabs(); 1 +<template>
  2 + <div :class="getWrapClass">
  3 + <Tabs
  4 + type="editable-card"
  5 + size="small"
  6 + :animated="false"
  7 + :hideAdd="true"
  8 + :tabBarGutter="3"
  9 + :activeKey="activeKeyRef"
  10 + @change="handleChange"
  11 + @edit="handleEdit"
  12 + >
  13 + <template v-for="item in getTabsState" :key="item.query ? item.fullPath : item.path">
  14 + <TabPane :closable="!(item && item.meta && item.meta.affix)">
  15 + <template #tab>
  16 + <TabContent :tabItem="item" />
  17 + </template>
  18 + </TabPane>
  19 + </template>
  20 + <template #tabBarExtraContent>
  21 + <QuickButton />
  22 + </template>
  23 + </Tabs>
  24 + </div>
  25 +</template>
  26 +<script lang="ts">
  27 + import { defineComponent, watch, computed, unref, ref } from 'vue';
  28 +
  29 + import { Tabs } from 'ant-design-vue';
  30 + import TabContent from './components/TabContent.vue';
  31 +
  32 + import { useGo } from '/@/hooks/web/usePage';
  33 +
  34 + import { tabStore } from '/@/store/modules/tab';
  35 + import { userStore } from '/@/store/modules/user';
  36 +
  37 + import { initAffixTabs, useTabsDrag } from './useMultipleTabs';
  38 + import { REDIRECT_NAME } from '/@/router/constant';
  39 + import { useDesign } from '/@/hooks/web/useDesign';
  40 + import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
  41 +
  42 + export default defineComponent({
  43 + name: 'MultipleTabs',
  44 + components: {
  45 + QuickButton: createAsyncComponent(() => import('./components/QuickButton.vue')),
  46 + Tabs,
  47 + TabPane: Tabs.TabPane,
  48 + TabContent,
  49 + },
  50 + setup() {
  51 + const affixTextList = initAffixTabs();
  52 +
  53 + const activeKeyRef = ref('');
  54 +
  55 + useTabsDrag(affixTextList);
  56 + const { prefixCls } = useDesign('multiple-tabs');
  57 + const go = useGo();
  58 +
  59 + const getTabsState = computed(() => tabStore.getTabsState);
  60 +
  61 + const unClose = computed(() => {
  62 + return getTabsState.value.length === 1;
  63 + });
27 64
28 - useTabsDrag(affixTextList); 65 + const getWrapClass = computed(() => {
  66 + return [
  67 + prefixCls,
  68 + {
  69 + [`${prefixCls}--hide-close`]: unClose,
  70 + },
  71 + ];
  72 + });
29 73
30 - const go = useGo(); 74 + watch(
  75 + () => tabStore.getLastChangeRouteState?.path,
  76 + () => {
  77 + if (tabStore.getLastChangeRouteState?.name === REDIRECT_NAME) {
  78 + return;
  79 + }
  80 + const lastChangeRoute = unref(tabStore.getLastChangeRouteState);
  81 + if (!lastChangeRoute || !userStore.getTokenState) return;
  82 +
  83 + const { path, fullPath } = lastChangeRoute;
  84 + const p = fullPath || path;
  85 +
  86 + if (activeKeyRef.value !== p) {
  87 + activeKeyRef.value = p;
  88 + }
  89 +
  90 + tabStore.addTabAction(lastChangeRoute);
  91 + },
  92 + {
  93 + immediate: true,
  94 + }
  95 + );
31 96
32 - const { currentRoute } = useRouter(); 97 + function handleChange(activeKey: any) {
  98 + activeKeyRef.value = activeKey;
  99 + go(activeKey, false);
  100 + }
33 101
34 - const getTabsState = computed(() => tabStore.getTabsState); 102 + // Close the current tab
  103 + function handleEdit(targetKey: string) {
  104 + // Added operation to hide, currently only use delete operation
  105 + if (unref(unClose)) return;
35 106
36 - watch(  
37 - () => tabStore.getLastChangeRouteState?.path,  
38 - () => {  
39 - if (tabStore.getLastChangeRouteState?.name === REDIRECT_NAME) {  
40 - return;  
41 - }  
42 - const lastChangeRoute = unref(tabStore.getLastChangeRouteState);  
43 - if (!lastChangeRoute || !userStore.getTokenState) return;  
44 - const { path, fullPath } = lastChangeRoute;  
45 - const p = fullPath || path;  
46 - if (activeKeyRef.value !== p) {  
47 - activeKeyRef.value = p;  
48 - }  
49 - tabStore.addTabAction(lastChangeRoute);  
50 - },  
51 - {  
52 - immediate: true, 107 + tabStore.closeTabByKeyAction(targetKey);
53 } 108 }
54 - );  
55 -  
56 - function handleChange(activeKey: any) {  
57 - activeKeyRef.value = activeKey;  
58 - go(activeKey, false);  
59 - }  
60 -  
61 - // Close the current tab  
62 - function handleEdit(targetKey: string) {  
63 - // Added operation to hide, currently only use delete operation  
64 - tabStore.closeTabByKeyAction(targetKey);  
65 - }  
66 -  
67 - function renderQuick() {  
68 - const tabContentProps: TabContentProps = {  
69 - tabItem: currentRoute.value,  
70 - type: TabContentEnum.EXTRA_TYPE, 109 + return {
  110 + prefixCls,
  111 + unClose,
  112 + getWrapClass,
  113 + handleEdit,
  114 + handleChange,
  115 + activeKeyRef,
  116 + getTabsState,
71 }; 117 };
72 - return <TabContent {...tabContentProps} />;  
73 - }  
74 -  
75 - function renderTabs() {  
76 - return unref(getTabsState).map((item) => {  
77 - const key = item.query ? item.fullPath : item.path;  
78 - const closable = !(item && item.meta && item.meta.affix);  
79 -  
80 - const slots = {  
81 - tab: () => <TabContent tabItem={item} />,  
82 - };  
83 - return (  
84 - <Tabs.TabPane key={key} closable={closable}>  
85 - {slots}  
86 - </Tabs.TabPane>  
87 - );  
88 - });  
89 - }  
90 -  
91 - return () => {  
92 - const slots = {  
93 - default: () => renderTabs(),  
94 - tabBarExtraContent: () => renderQuick(),  
95 - };  
96 - return (  
97 - <div class="multiple-tabs">  
98 - <Tabs  
99 - type="editable-card"  
100 - size="small"  
101 - animated={false}  
102 - hideAdd={true}  
103 - tabBarGutter={3}  
104 - activeKey={unref(activeKeyRef)}  
105 - onChange={handleChange}  
106 - onEdit={handleEdit}  
107 - >  
108 - {slots}  
109 - </Tabs>  
110 - </div>  
111 - );  
112 - };  
113 - },  
114 -}); 118 + },
  119 + });
  120 +</script>
  121 +<style lang="less">
  122 + @import './index.less';
  123 +</style>
src/layouts/default/multitabs/types.ts renamed to src/layouts/default/tabs/types.ts
src/layouts/default/multitabs/useMultipleTabs.ts renamed to src/layouts/default/tabs/useMultipleTabs.ts
@@ -2,6 +2,7 @@ import Sortable from &#39;sortablejs&#39;; @@ -2,6 +2,7 @@ import Sortable from &#39;sortablejs&#39;;
2 import { toRaw, ref, nextTick, onMounted } from 'vue'; 2 import { toRaw, ref, nextTick, onMounted } from 'vue';
3 import { RouteLocationNormalized } from 'vue-router'; 3 import { RouteLocationNormalized } from 'vue-router';
4 import { useProjectSetting } from '/@/hooks/setting'; 4 import { useProjectSetting } from '/@/hooks/setting';
  5 +import { useDesign } from '/@/hooks/web/useDesign';
5 import router from '/@/router'; 6 import router from '/@/router';
6 import { tabStore } from '/@/store/modules/tab'; 7 import { tabStore } from '/@/store/modules/tab';
7 import { isNullAndUnDef } from '/@/utils/is'; 8 import { isNullAndUnDef } from '/@/utils/is';
@@ -48,12 +49,12 @@ export function initAffixTabs(): string[] { @@ -48,12 +49,12 @@ export function initAffixTabs(): string[] {
48 export function useTabsDrag(affixTextList: string[]) { 49 export function useTabsDrag(affixTextList: string[]) {
49 const { multiTabsSetting } = useProjectSetting(); 50 const { multiTabsSetting } = useProjectSetting();
50 51
  52 + const { prefixCls } = useDesign('multiple-tabs');
  53 +
51 function initSortableTabs() { 54 function initSortableTabs() {
52 if (!multiTabsSetting.canDrag) return; 55 if (!multiTabsSetting.canDrag) return;
53 nextTick(() => { 56 nextTick(() => {
54 - const el = document.querySelectorAll(  
55 - '.multiple-tabs .ant-tabs-nav > div'  
56 - )?.[0] as HTMLElement; 57 + const el = document.querySelectorAll(`.${prefixCls} .ant-tabs-nav > div`)?.[0] as HTMLElement;
57 58
58 if (!el) return; 59 if (!el) return;
59 Sortable.create(el, { 60 Sortable.create(el, {
src/layouts/default/multitabs/useTabDropdown.ts renamed to src/layouts/default/tabs/useTabDropdown.ts
src/layouts/default/trigger/HeaderTrigger.vue 0 → 100644
  1 +<template>
  2 + <span :class="[prefixCls, theme]" @click="toggleCollapsed">
  3 + <MenuUnfoldOutlined v-if="getCollapsed" /> <MenuFoldOutlined v-else />
  4 + </span>
  5 +</template>
  6 +<script lang="ts">
  7 + import { defineComponent } from 'vue';
  8 + import { MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons-vue';
  9 + import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
  10 + import { useDesign } from '/@/hooks/web/useDesign';
  11 + import { propTypes } from '/@/utils/propTypes';
  12 +
  13 + export default defineComponent({
  14 + name: 'SiderTrigger',
  15 + components: { MenuUnfoldOutlined, MenuFoldOutlined },
  16 + props: {
  17 + theme: propTypes.oneOf(['light', 'dark']),
  18 + },
  19 + setup() {
  20 + const { getCollapsed, toggleCollapsed } = useMenuSetting();
  21 + const { prefixCls } = useDesign('layout-header-trigger');
  22 + return { getCollapsed, toggleCollapsed, prefixCls };
  23 + },
  24 + });
  25 +</script>
src/layouts/default/trigger/SiderTrigger.vue 0 → 100644
  1 +<template>
  2 + <DoubleRightOutlined v-if="getCollapsed" />
  3 + <DoubleLeftOutlined v-else />
  4 +</template>
  5 +<script lang="ts">
  6 + import { defineComponent } from 'vue';
  7 + import { DoubleRightOutlined, DoubleLeftOutlined } from '@ant-design/icons-vue';
  8 + import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
  9 +
  10 + export default defineComponent({
  11 + name: 'SiderTrigger',
  12 + components: { DoubleRightOutlined, DoubleLeftOutlined },
  13 + setup() {
  14 + const { getCollapsed } = useMenuSetting();
  15 + return { getCollapsed };
  16 + },
  17 + });
  18 +</script>
src/layouts/default/trigger/index.vue 0 → 100644
  1 +<template>
  2 + <SiderTrigger v-if="sider" />
  3 + <HeaderTrigger v-else :theme="theme" />
  4 +</template>
  5 +<script lang="ts">
  6 + import { defineComponent } from 'vue';
  7 + import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
  8 + import { propTypes } from '/@/utils/propTypes';
  9 +
  10 + export default defineComponent({
  11 + name: 'LayoutTrigger',
  12 + components: {
  13 + SiderTrigger: createAsyncComponent(() => import('./SiderTrigger.vue')),
  14 + HeaderTrigger: createAsyncComponent(() => import('./HeaderTrigger.vue'), { loading: true }),
  15 + },
  16 + props: {
  17 + sider: propTypes.bool.def(true),
  18 + theme: propTypes.oneOf(['light', 'dark']),
  19 + },
  20 + });
  21 +</script>
src/layouts/iframe/index.vue
1 <template> 1 <template>
2 - <template v-for="frame in getFramePages" :key="frame.path">  
3 - <FramePage  
4 - v-if="frame.meta.frameSrc && hasRenderFrame(frame.name)"  
5 - v-show="showIframe(frame)"  
6 - :frameSrc="frame.meta.frameSrc"  
7 - />  
8 - </template> 2 + <div>
  3 + <template v-for="frame in getFramePages" :key="frame.path">
  4 + <FramePage
  5 + v-if="frame.meta.frameSrc && hasRenderFrame(frame.name)"
  6 + v-show="showIframe(frame)"
  7 + :frameSrc="frame.meta.frameSrc"
  8 + />
  9 + </template>
  10 + </div>
9 </template> 11 </template>
10 <script lang="ts"> 12 <script lang="ts">
11 import { defineComponent } from 'vue'; 13 import { defineComponent } from 'vue';
src/layouts/iframe/useFrameKeepAlive.ts
1 import type { AppRouteRecordRaw } from '/@/router/types'; 1 import type { AppRouteRecordRaw } from '/@/router/types';
2 2
3 import { computed, toRaw, unref } from 'vue'; 3 import { computed, toRaw, unref } from 'vue';
4 -import { useRouter } from 'vue-router';  
5 -import router from '/@/router';  
6 4
7 import { tabStore } from '/@/store/modules/tab'; 5 import { tabStore } from '/@/store/modules/tab';
8 6
@@ -10,8 +8,10 @@ import { unique } from &#39;/@/utils&#39;; @@ -10,8 +8,10 @@ import { unique } from &#39;/@/utils&#39;;
10 8
11 import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting'; 9 import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
12 10
  11 +import router from '/@/router';
  12 +
13 export function useFrameKeepAlive() { 13 export function useFrameKeepAlive() {
14 - const { currentRoute } = useRouter(); 14 + const { currentRoute } = router;
15 const { getShowMultipleTab } = useMultipleTabSetting(); 15 const { getShowMultipleTab } = useMultipleTabSetting();
16 16
17 const getFramePages = computed(() => { 17 const getFramePages = computed(() => {
src/layouts/page/index.tsx
@@ -10,12 +10,14 @@ import { useRootSetting } from &#39;/@/hooks/setting/useRootSetting&#39;; @@ -10,12 +10,14 @@ import { useRootSetting } from &#39;/@/hooks/setting/useRootSetting&#39;;
10 import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting'; 10 import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
11 import { useCache } from './useCache'; 11 import { useCache } from './useCache';
12 import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting'; 12 import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
  13 +// import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
13 14
14 interface DefaultContext { 15 interface DefaultContext {
15 Component: FunctionalComponent & { type: { [key: string]: any } }; 16 Component: FunctionalComponent & { type: { [key: string]: any } };
16 route: RouteLocation; 17 route: RouteLocation;
17 } 18 }
18 19
  20 +// const FrameLayout=createAsyncComponent(()=>'/@/layouts/iframe/index.vue')
19 export default defineComponent({ 21 export default defineComponent({
20 name: 'PageLayout', 22 name: 'PageLayout',
21 setup() { 23 setup() {
src/layouts/page/useCache.ts
@@ -32,7 +32,6 @@ export function useCache(isPage: boolean) { @@ -32,7 +32,6 @@ export function useCache(isPage: boolean) {
32 32
33 if (isPage) { 33 if (isPage) {
34 // page Layout 34 // page Layout
35 - // not parent layout  
36 return cached.get(PAGE_LAYOUT_KEY) || []; 35 return cached.get(PAGE_LAYOUT_KEY) || [];
37 } 36 }
38 const cacheSet = new Set<string>(); 37 const cacheSet = new Set<string>();
src/locales/lang/en/layout/multipleTab.ts
1 export default { 1 export default {
2 - redo: 'Refresh',  
3 - close: 'Close', 2 + redo: 'Refresh current',
  3 + close: 'Close current',
4 closeLeft: 'Close Left', 4 closeLeft: 'Close Left',
5 closeRight: 'Close Right', 5 closeRight: 'Close Right',
6 closeOther: 'Close Other', 6 closeOther: 'Close Other',
src/locales/lang/zh_CN/layout/multipleTab.ts
1 export default { 1 export default {
2 - redo: '刷新',  
3 - close: '关闭', 2 + redo: '刷新当前',
  3 + close: '关闭当前',
4 closeLeft: '关闭左侧', 4 closeLeft: '关闭左侧',
5 closeRight: '关闭右侧', 5 closeRight: '关闭右侧',
6 closeOther: '关闭其他', 6 closeOther: '关闭其他',
src/router/constant.ts
@@ -6,7 +6,7 @@ const EXCEPTION_COMPONENT = () =&gt; import(&#39;../views/sys/exception/Exception&#39;); @@ -6,7 +6,7 @@ const EXCEPTION_COMPONENT = () =&gt; import(&#39;../views/sys/exception/Exception&#39;);
6 /** 6 /**
7 * @description: default layout 7 * @description: default layout
8 */ 8 */
9 -export const LAYOUT = () => import('/@/layouts/default/index'); 9 +export const LAYOUT = () => import('/@/layouts/default/index.vue');
10 10
11 /** 11 /**
12 * @description: page-layout 12 * @description: page-layout