Commit 0692b4798c0f3391a2cb583e30dcca5345aba407

Authored by vben
1 parent 25d43a5f

refactor: refactor layout

Showing 50 changed files with 1148 additions and 817 deletions
CHANGELOG.zh_CN.md
1 1 ## Wip
2 2  
  3 +### ✨ Refactor
  4 +
  5 +- 重构整体 layout。更改代码实现方式。代码更精简
  6 +
3 7 ### ✨ Features
4 8  
5 9 - 缓存可以配置是否加密
... ... @@ -7,6 +11,7 @@
7 11 ### 🎫 Chores
8 12  
9 13 - 移除 messageSetting 配置
  14 +- 暂时删除 `@vueuse/core`.等稳定后在集成。目前不太稳定。
10 15  
11 16 ## 2.0.0-rc.11 (2020-11-18)
12 17  
... ...
build/vite/plugin/transform/globby/index.ts
... ... @@ -154,15 +154,17 @@ const globTransform = function (config: SharedConfig): Transform {
154 154  
155 155 const groups: Array<string>[] = [];
156 156 const replaceFiles = files.map((f, i) => {
157   - const fileNameWithAlias = resolver.fileToRequest(f);
158   -
159   - const file = bareExporter + fileNameWithAlias + bareExporter;
  157 + const filePath = resolver.fileToRequest(f);
  158 + const file = bareExporter + filePath + bareExporter;
160 159  
161 160 if (isLocale) {
162 161 const globrexRes = globrex(globPath, { extended: true, globstar: true });
163 162  
164 163 // Get segments for files like an en/system ch/modules for:
165 164 // ['en', 'system'] ['ch', 'modules']
  165 +
  166 + // TODO The window system and mac system path are inconsistent?
  167 + const fileNameWithAlias = filePath.replace(/^(\/src\/)/, '/@/');
166 168 const matchedGroups = globrexRes.regex.exec(fileNameWithAlias);
167 169  
168 170 if (matchedGroups && matchedGroups.length) {
... ...
package.json
... ... @@ -22,7 +22,6 @@
22 22 },
23 23 "dependencies": {
24 24 "@iconify/iconify": "^2.0.0-rc.2",
25   - "@vueuse/core": "^4.0.0-rc.3",
26 25 "ant-design-vue": "2.0.0-beta.15",
27 26 "apexcharts": "3.22.0",
28 27 "axios": "^0.21.0",
... ...
src/components/Application/src/AppLogo.vue
1 1 <template>
2   - <div class="app-logo anticon" :class="theme" @click="handleGoHome">
  2 + <div
  3 + class="app-logo anticon"
  4 + :class="{ theme, 'collapsed-show-title': getCollapsedShowTitle }"
  5 + @click="handleGoHome"
  6 + >
3 7 <img src="/@/assets/images/logo.png" />
4   - <div class="app-logo__title ml-2 ellipsis">{{ globSetting.title }}</div>
  8 + <div class="app-logo__title ml-2 ellipsis" v-show="showTitle">{{ globSetting.title }}</div>
5 9 </div>
6 10 </template>
7 11 <script lang="ts">
... ... @@ -10,6 +14,7 @@
10 14  
11 15 import { useGlobSetting } from '/@/hooks/setting';
12 16 import { useGo } from '/@/hooks/web/usePage';
  17 + import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
13 18  
14 19 import { PageEnum } from '/@/enums/pageEnum';
15 20  
... ... @@ -22,8 +27,13 @@
22 27 theme: {
23 28 type: String as PropType<string>,
24 29 },
  30 + showTitle: {
  31 + type: Boolean,
  32 + default: true,
  33 + },
25 34 },
26 35 setup() {
  36 + const { getCollapsedShowTitle } = useMenuSetting();
27 37 const globSetting = useGlobSetting();
28 38 const go = useGo();
29 39  
... ... @@ -34,6 +44,7 @@
34 44 return {
35 45 handleGoHome,
36 46 globSetting,
  47 + getCollapsedShowTitle,
37 48 };
38 49 },
39 50 });
... ... @@ -44,9 +55,13 @@
44 55 .app-logo {
45 56 display: flex;
46 57 align-items: center;
47   - padding-left: 16px;
  58 + padding-left: 10px;
48 59 cursor: pointer;
49 60  
  61 + &.collapsed-show-title {
  62 + padding-left: 20px;
  63 + }
  64 +
50 65 &.light {
51 66 border-bottom: 1px solid @border-color-base;
52 67 }
... ... @@ -64,6 +79,7 @@
64 79 font-weight: 700;
65 80 opacity: 0;
66 81 transition: all 0.5s;
  82 +
67 83 .respond-to(medium,{
68 84 opacity: 1;
69 85 });
... ...
src/components/Drawer/src/BasicDrawer.tsx
... ... @@ -49,7 +49,7 @@ export default defineComponent({
49 49 ? `${opt.wrapClassName} ${prefixCls}__detail`
50 50 : `${prefixCls}__detail`;
51 51 if (!opt.getContainer) {
52   - opt.getContainer = `.default-layout__main`;
  52 + opt.getContainer = '.layout-content';
53 53 }
54 54 }
55 55 return opt;
... ...
src/components/Menu/src/BasicMenu.tsx
... ... @@ -80,7 +80,7 @@ export default defineComponent({
80 80 offset += 46;
81 81 }
82 82 return {
83   - height: `calc(100% - ${offset - 12}px)`,
  83 + height: `calc(100% - ${offset}px)`,
84 84 position: 'relative',
85 85 overflowY: 'auto',
86 86 };
... ...
src/components/Modal/src/BasicModal.tsx
... ... @@ -219,11 +219,7 @@ export default defineComponent({
219 219 emit('register', modalMethods, uuid);
220 220  
221 221 return () => (
222   - <Modal
223   - onCancel={handleCancel}
224   - getContainer={() => document.querySelector('.default-layout__main')}
225   - {...{ ...attrs, ...props, ...unref(getProps) }}
226   - >
  222 + <Modal onCancel={handleCancel} {...{ ...attrs, ...props, ...unref(getProps) }}>
227 223 {{
228 224 footer: () => renderFooter(),
229 225 closeIcon: () => renderClose(),
... ...
src/components/Modal/src/useModal.ts
... ... @@ -33,6 +33,7 @@ export function useModal(): UseModalReturnType {
33 33  
34 34 modalRef.value = modalMethod;
35 35 }
  36 +
36 37 const getInstance = () => {
37 38 const instance = unref(modalRef);
38 39 if (!instance) {
... ... @@ -50,6 +51,7 @@ export function useModal(): UseModalReturnType {
50 51 getInstance().setModalProps({
51 52 visible: visible,
52 53 });
  54 +
53 55 if (data) {
54 56 dataTransferRef[unref(uidRef)] = openOnSet
55 57 ? {
... ...
src/components/Table/src/hooks/useTableScroll.ts
... ... @@ -43,7 +43,6 @@ export function useTableScroll(refProps: ComputedRef&lt;BasicTableProps&gt;, tableElRe
43 43 const tableEl: Element = table.$el;
44 44 if (!tableEl) return;
45 45 const headEl = tableEl.querySelector('.ant-table-thead ');
46   - // const layoutMain: Element | null = document.querySelector('.default-layout__main ');
47 46 if (!headEl) return;
48 47  
49 48 // 表格距离底部高度
... ...
src/design/index.less
... ... @@ -35,7 +35,7 @@ html,
35 35 body {
36 36 width: 100%;
37 37 height: 100%;
38   - overflow: hidden;
  38 + overflow-x: hidden;
39 39  
40 40 &.color-weak {
41 41 filter: invert(80%);
... ... @@ -160,9 +160,7 @@ object {
160 160 vertical-align: baseline !important;
161 161 }
162 162  
163   -#app,
164   -#app > div,
165   -.ant-layout {
  163 +#app {
166 164 width: 100%;
167 165 height: 100%;
168 166 }
... ... @@ -170,8 +168,8 @@ object {
170 168 .ant-layout {
171 169 background: #f0f2f5;
172 170  
173   - &-content {
174   - position: relative;
175   - overflow: hidden;
176   - }
  171 + // &-content {
  172 + // position: relative;
  173 + // overflow: hidden;
  174 + // }
177 175 }
... ...
src/design/reset.less
... ... @@ -101,14 +101,3 @@
101 101 }
102 102 }
103 103 }
104   -
105   -.reset-layout() {
106   - .ant-layout {
107   - background: #f1f1f6 !important;
108   -
109   - &-content {
110   - position: relative;
111   - overflow: hidden;
112   - }
113   - }
114   -}
... ...
src/hooks/core/useContext.ts 0 → 100644
  1 +import { InjectionKey, provide, inject, reactive, readonly } from 'vue';
  2 +
  3 +export const createContext = <T>(
  4 + context: any,
  5 + contextInjectKey: InjectionKey<T> = Symbol(),
  6 + _readonly = true
  7 +) => {
  8 + const state = reactive({
  9 + ...context,
  10 + });
  11 + const provideData = _readonly ? readonly(state) : state;
  12 + provide(contextInjectKey, provideData);
  13 +};
  14 +
  15 +export const useContext = <T>(
  16 + contextInjectKey: InjectionKey<T> = Symbol(),
  17 + defaultValue?: any,
  18 + _readonly = true
  19 +): T => {
  20 + const state = inject(contextInjectKey, defaultValue || {});
  21 + return _readonly ? readonly(state) : state;
  22 +};
... ...
src/hooks/setting/useHeaderSetting.ts
... ... @@ -7,20 +7,50 @@ import { appStore } from &#39;/@/store/modules/app&#39;;
7 7 import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
8 8 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
9 9 import { useRootSetting } from '/@/hooks/setting/useRootSetting';
  10 +import { useFullContent } from '/@/hooks/web/useFullContent';
10 11  
11 12 import { MenuModeEnum } from '/@/enums/menuEnum';
12 13  
13 14 export function useHeaderSetting() {
14   - const { getShow: getShowMultipleTab } = useMultipleTabSetting();
15   - const { getMode, getSplit, getShowHeaderTrigger, getIsSidebarType } = useMenuSetting();
  15 + const { getFullContent } = useFullContent();
  16 + const { getShowMultipleTab } = useMultipleTabSetting();
  17 + const {
  18 + getMenuMode,
  19 + getSplit,
  20 + getShowHeaderTrigger,
  21 + getIsSidebarType,
  22 + getIsTopMenu,
  23 + } = useMenuSetting();
16 24 const { getShowBreadCrumb, getShowLogo } = useRootSetting();
17 25  
  26 + const getShowMixHeaderRef = computed(() => !unref(getIsSidebarType) && unref(getShowHeader));
  27 +
  28 + const getShowFullHeaderRef = computed(() => {
  29 + return (
  30 + !unref(getFullContent) &&
  31 + unref(getShowMixHeaderRef) &&
  32 + unref(getShowHeader) &&
  33 + !unref(getIsTopMenu)
  34 + );
  35 + });
  36 +
  37 + const getShowInsetHeaderRef = computed(() => {
  38 + const need = !unref(getFullContent) && unref(getShowHeader);
  39 + return (need && !unref(getShowMixHeaderRef)) || (need && unref(getIsTopMenu));
  40 + });
  41 +
18 42 // Get header configuration
19 43 const getHeaderSetting = computed(() => appStore.getProjectConfig.headerSetting);
20 44  
21 45 const getShowDoc = computed(() => unref(getHeaderSetting).showDoc);
22 46  
23   - const getTheme = computed(() => unref(getHeaderSetting).theme);
  47 + const getHeaderTheme = computed(() => unref(getHeaderSetting).theme);
  48 +
  49 + const getShowHeader = computed(() => unref(getHeaderSetting).show);
  50 +
  51 + const getFixed = computed(() => unref(getHeaderSetting).fixed);
  52 +
  53 + const getHeaderBgColor = computed(() => unref(getHeaderSetting).bgColor);
24 54  
25 55 const getShowRedo = computed(() => unref(getHeaderSetting).showRedo && unref(getShowMultipleTab));
26 56  
... ... @@ -30,9 +60,11 @@ export function useHeaderSetting() {
30 60  
31 61 const getShowNotice = computed(() => unref(getHeaderSetting).showNotice);
32 62  
  63 + const getUnFixedAndFull = computed(() => !unref(getFixed) && !unref(getShowFullHeaderRef));
  64 +
33 65 const getShowBread = computed(() => {
34 66 return (
35   - unref(getMode) !== MenuModeEnum.HORIZONTAL && unref(getShowBreadCrumb) && !unref(getSplit)
  67 + unref(getMenuMode) !== MenuModeEnum.HORIZONTAL && unref(getShowBreadCrumb) && !unref(getSplit)
36 68 );
37 69 });
38 70  
... ... @@ -55,7 +87,7 @@ export function useHeaderSetting() {
55 87 getHeaderSetting,
56 88  
57 89 getShowDoc,
58   - getTheme,
  90 + getHeaderTheme,
59 91 getShowRedo,
60 92 getUseLockPage,
61 93 getShowFullScreen,
... ... @@ -63,5 +95,12 @@ export function useHeaderSetting() {
63 95 getShowBread,
64 96 getShowContent,
65 97 getShowHeaderLogo,
  98 + getShowHeader,
  99 + getFixed,
  100 + getShowMixHeaderRef,
  101 + getShowFullHeaderRef,
  102 + getShowInsetHeaderRef,
  103 + getUnFixedAndFull,
  104 + getHeaderBgColor,
66 105 };
67 106 }
... ...
src/hooks/setting/useMenuSetting.ts
... ... @@ -11,24 +11,28 @@ export function useMenuSetting() {
11 11 // Get menu configuration
12 12 const getMenuSetting = computed(() => appStore.getProjectConfig.menuSetting);
13 13  
14   - const getMiniWidth = computed(() => unref(getMenuSetting).menuWidth);
15   -
16 14 const getCollapsed = computed(() => unref(getMenuSetting).collapsed);
17 15  
18   - const getType = computed(() => unref(getMenuSetting).type);
  16 + const getMenuType = computed(() => unref(getMenuSetting).type);
  17 +
  18 + const getMenuMode = computed(() => unref(getMenuSetting).mode);
19 19  
20   - const getMode = computed(() => unref(getMenuSetting).mode);
  20 + const getMenuFixed = computed(() => unref(getMenuSetting).fixed);
21 21  
22   - const getShow = computed(() => unref(getMenuSetting).show);
  22 + const getShowMenu = computed(() => unref(getMenuSetting).show);
  23 +
  24 + const getMenuHidden = computed(() => unref(getMenuSetting).hidden);
23 25  
24 26 const getMenuWidth = computed(() => unref(getMenuSetting).menuWidth);
25 27  
26 28 const getTrigger = computed(() => unref(getMenuSetting).trigger);
27 29  
28   - const getTheme = computed(() => unref(getMenuSetting).theme);
  30 + const getMenuTheme = computed(() => unref(getMenuSetting).theme);
29 31  
30 32 const getSplit = computed(() => unref(getMenuSetting).split);
31 33  
  34 + const getMenuBgColor = computed(() => unref(getMenuSetting).bgColor);
  35 +
32 36 const getHasDrag = computed(() => unref(getMenuSetting).hasDrag);
33 37  
34 38 const getAccordion = computed(() => unref(getMenuSetting).accordion);
... ... @@ -39,17 +43,19 @@ export function useMenuSetting() {
39 43  
40 44 const getTopMenuAlign = computed(() => unref(getMenuSetting).topMenuAlign);
41 45  
42   - const getIsSidebarType = computed(() => unref(getType) === MenuTypeEnum.SIDEBAR);
  46 + const getIsSidebarType = computed(() => unref(getMenuType) === MenuTypeEnum.SIDEBAR);
  47 +
  48 + const getIsTopMenu = computed(() => unref(getMenuType) === MenuTypeEnum.TOP_MENU);
43 49  
44 50 const getShowTopMenu = computed(() => {
45   - return unref(getMode) === MenuModeEnum.HORIZONTAL || unref(getSplit);
  51 + return unref(getMenuMode) === MenuModeEnum.HORIZONTAL || unref(getSplit);
46 52 });
47 53  
48 54 const getShowHeaderTrigger = computed(() => {
49 55 if (
50   - unref(getType) === MenuTypeEnum.TOP_MENU ||
51   - !unref(getShow) ||
52   - !unref(getMenuSetting).hidden
  56 + unref(getMenuType) === MenuTypeEnum.TOP_MENU ||
  57 + !unref(getShowMenu) ||
  58 + !unref(getMenuHidden)
53 59 ) {
54 60 return false;
55 61 }
... ... @@ -60,12 +66,16 @@ export function useMenuSetting() {
60 66 const getShowSearch = computed(() => {
61 67 return (
62 68 unref(getMenuSetting).showSearch &&
63   - !(unref(getType) === MenuTypeEnum.MIX && unref(getMode) === MenuModeEnum.HORIZONTAL)
  69 + !(unref(getMenuType) === MenuTypeEnum.MIX && unref(getMenuMode) === MenuModeEnum.HORIZONTAL)
64 70 );
65 71 });
66 72  
67 73 const getIsHorizontal = computed(() => {
68   - return unref(getMode) === MenuModeEnum.HORIZONTAL;
  74 + return unref(getMenuMode) === MenuModeEnum.HORIZONTAL;
  75 + });
  76 +
  77 + const getRealWidth = computed(() => {
  78 + return unref(getCollapsed) ? unref(getMiniWidthNumber) : unref(getMenuWidth);
69 79 });
70 80  
71 81 const getMiniWidthNumber = computed(() => {
... ... @@ -74,8 +84,8 @@ export function useMenuSetting() {
74 84 });
75 85  
76 86 const getCalcContentWidth = computed(() => {
77   - const width = unref(getCollapsed) ? unref(getMiniWidthNumber) : unref(getMiniWidth);
78   - return `calc(100% - ${width}px)`;
  87 + const width = unref(getIsTopMenu) || !unref(getShowMenu) ? 0 : unref(getRealWidth);
  88 + return `calc(100% - ${unref(width)}px)`;
79 89 });
80 90  
81 91 // Set menu configuration
... ... @@ -94,18 +104,19 @@ export function useMenuSetting() {
94 104  
95 105 toggleCollapsed,
96 106  
  107 + getMenuFixed,
97 108 getMenuSetting,
98   - getMiniWidth,
99   - getType,
100   - getMode,
101   - getShow,
  109 + getRealWidth,
  110 + getMenuType,
  111 + getMenuMode,
  112 + getShowMenu,
102 113 getCollapsed,
103 114 getMiniWidthNumber,
104 115 getCalcContentWidth,
105 116 getMenuWidth,
106 117 getTrigger,
107 118 getSplit,
108   - getTheme,
  119 + getMenuTheme,
109 120 getHasDrag,
110 121 getIsHorizontal,
111 122 getShowSearch,
... ... @@ -116,5 +127,8 @@ export function useMenuSetting() {
116 127 getShowTopMenu,
117 128 getShowHeaderTrigger,
118 129 getTopMenuAlign,
  130 + getMenuHidden,
  131 + getIsTopMenu,
  132 + getMenuBgColor,
119 133 };
120 134 }
... ...
src/hooks/setting/useMultipleTabSetting.ts
... ... @@ -9,7 +9,9 @@ export function useMultipleTabSetting() {
9 9  
10 10 const getMax = computed(() => unref(getMultipleTabSetting).max);
11 11  
12   - const getShow = computed(() => unref(getMultipleTabSetting).show);
  12 + const getShowMultipleTab = computed(() => unref(getMultipleTabSetting).show);
  13 +
  14 + const getShowQuick = computed(() => unref(getMultipleTabSetting).showQuick);
13 15  
14 16 function setMultipleTabSetting(multiTabsSetting: Partial<MultiTabsSetting>) {
15 17 appStore.commitProjectConfigState({ multiTabsSetting });
... ... @@ -20,6 +22,7 @@ export function useMultipleTabSetting() {
20 22  
21 23 getMultipleTabSetting,
22 24 getMax,
23   - getShow,
  25 + getShowMultipleTab,
  26 + getShowQuick,
24 27 };
25 28 }
... ...
src/hooks/setting/useRootSetting.ts
... ... @@ -3,6 +3,7 @@ import type { ProjectConfig } from &#39;/@/types/config&#39;;
3 3 import { computed, unref } from 'vue';
4 4  
5 5 import { appStore } from '/@/store/modules/app';
  6 +import { ContentEnum } from '/@/enums/appEnum';
6 7  
7 8 type RootSetting = Omit<
8 9 ProjectConfig,
... ... @@ -13,6 +14,8 @@ export function useRootSetting() {
13 14  
14 15 const getOpenPageLoading = computed(() => unref(getRootSetting).openPageLoading);
15 16  
  17 + const getPageLoading = computed(() => appStore.getPageLoading);
  18 +
16 19 const getOpenRouterTransition = computed(() => unref(getRootSetting).openRouterTransition);
17 20  
18 21 const getOpenKeepAlive = computed(() => unref(getRootSetting).openKeepAlive);
... ... @@ -25,12 +28,30 @@ export function useRootSetting() {
25 28  
26 29 const getShowLogo = computed(() => unref(getRootSetting).showLogo);
27 30  
  31 + const getContentMode = computed(() => unref(getRootSetting).contentMode);
  32 +
  33 + const getUseOpenBackTop = computed(() => unref(getRootSetting).useOpenBackTop);
  34 +
  35 + const getShowSettingButton = computed(() => unref(getRootSetting).showSettingButton);
  36 +
28 37 const getUseErrorHandle = computed(() => unref(getRootSetting).useErrorHandle);
29 38  
  39 + const getShowFooter = computed(() => unref(getRootSetting).showFooter);
  40 +
30 41 const getShowBreadCrumb = computed(() => unref(getRootSetting).showBreadCrumb);
31 42  
32 43 const getShowBreadCrumbIcon = computed(() => unref(getRootSetting).showBreadCrumbIcon);
33 44  
  45 + const getFullContent = computed(() => unref(getRootSetting).fullContent);
  46 +
  47 + const getColorWeak = computed(() => unref(getRootSetting).colorWeak);
  48 +
  49 + const getGrayMode = computed(() => unref(getRootSetting).grayMode);
  50 +
  51 + const getLayoutContentMode = computed(() =>
  52 + unref(getRootSetting).contentMode === ContentEnum.FULL ? ContentEnum.FULL : ContentEnum.FIXED
  53 + );
  54 +
34 55 function setRootSetting(setting: RootSetting) {
35 56 appStore.commitProjectConfigState(setting);
36 57 }
... ... @@ -38,7 +59,12 @@ export function useRootSetting() {
38 59 return {
39 60 setRootSetting,
40 61  
  62 + getFullContent,
  63 + getColorWeak,
  64 + getGrayMode,
41 65 getRootSetting,
  66 + getLayoutContentMode,
  67 + getPageLoading,
42 68 getOpenPageLoading,
43 69 getOpenRouterTransition,
44 70 getOpenKeepAlive,
... ... @@ -49,5 +75,9 @@ export function useRootSetting() {
49 75 getUseErrorHandle,
50 76 getShowBreadCrumb,
51 77 getShowBreadCrumbIcon,
  78 + getUseOpenBackTop,
  79 + getShowSettingButton,
  80 + getShowFooter,
  81 + getContentMode,
52 82 };
53 83 }
... ...
src/layouts/default/LayoutContent.tsx deleted 100644 → 0
1   -import { computed, defineComponent, unref } from 'vue';
2   -import { Layout } from 'ant-design-vue';
3   -import { FullLoading } from '/@/components/Loading/index';
4   -
5   -import { RouterView } from 'vue-router';
6   -
7   -import { ContentEnum } from '/@/enums/appEnum';
8   -import { appStore } from '/@/store/modules/app';
9   -export default defineComponent({
10   - name: 'DefaultLayoutContent',
11   - setup() {
12   - const getProjectConfigRef = computed(() => {
13   - return appStore.getProjectConfig;
14   - });
15   -
16   - return () => {
17   - const { contentMode, openPageLoading } = unref(getProjectConfigRef);
18   - const { getPageLoading } = appStore;
19   - const wrapClass = contentMode === ContentEnum.FULL ? 'full' : 'fixed';
20   - return (
21   - <div class={[`default-layout__main`]}>
22   - {openPageLoading && (
23   - <FullLoading class={[`default-layout__loading`, !getPageLoading && 'hidden']} />
24   - )}
25   - <Layout.Content class={`layout-content ${wrapClass} `}>
26   - {() => <RouterView />}
27   - </Layout.Content>
28   - </div>
29   - );
30   - };
31   - },
32   -});
src/layouts/default/LayoutTrigger.tsx
1   -import type { PropType } from 'vue';
  1 +import type { PropType, FunctionalComponent } from 'vue';
2 2  
3 3 import { defineComponent, unref } from 'vue';
4 4 import {
... ... @@ -10,6 +10,22 @@ import {
10 10  
11 11 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
12 12  
  13 +const SiderTrigger: FunctionalComponent = () => {
  14 + const { getCollapsed } = useMenuSetting();
  15 + return unref(getCollapsed) ? <DoubleRightOutlined /> : <DoubleLeftOutlined />;
  16 +};
  17 +
  18 +const HeaderTrigger: FunctionalComponent<{
  19 + theme?: string;
  20 +}> = (props) => {
  21 + const { toggleCollapsed, getCollapsed } = useMenuSetting();
  22 + return (
  23 + <span class={['layout-trigger', props.theme]} onClick={toggleCollapsed}>
  24 + {unref(getCollapsed) ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
  25 + </span>
  26 + );
  27 +};
  28 +
13 29 export default defineComponent({
14 30 name: 'LayoutTrigger',
15 31 props: {
... ... @@ -22,20 +38,8 @@ export default defineComponent({
22 38 },
23 39 },
24 40 setup(props) {
25   - const { toggleCollapsed, getCollapsed } = useMenuSetting();
26   -
27 41 return () => {
28   - const siderTrigger = unref(getCollapsed) ? <DoubleRightOutlined /> : <DoubleLeftOutlined />;
29   -
30   - if (props.sider) {
31   - return siderTrigger;
32   - }
33   -
34   - return (
35   - <span class={['layout-trigger', props.theme]} onClick={toggleCollapsed}>
36   - {unref(getCollapsed) ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
37   - </span>
38   - );
  42 + return props.sider ? <SiderTrigger /> : <HeaderTrigger theme={props.theme} />;
39 43 };
40 44 },
41 45 });
... ...
src/layouts/default/content/index.less 0 → 100644
  1 +@import (reference) '../../../design/index.less';
  2 +
  3 +.layout-content {
  4 + position: relative;
  5 + flex: 1 1 auto;
  6 + min-height: 0;
  7 +
  8 + &.fixed {
  9 + width: 1200px;
  10 + margin: 0 auto;
  11 + }
  12 +
  13 + &__loading {
  14 + position: fixed;
  15 + z-index: @page-loading-z-index;
  16 +
  17 + > .basic-loading {
  18 + margin-bottom: 20%;
  19 + }
  20 + }
  21 +}
... ...
src/layouts/default/content/index.tsx 0 → 100644
  1 +import './index.less';
  2 +
  3 +import { defineComponent, unref } from 'vue';
  4 +import { FullLoading } from '/@/components/Loading/index';
  5 +
  6 +import { RouterView } from 'vue-router';
  7 +
  8 +import { useRootSetting } from '/@/hooks/setting/useRootSetting';
  9 +
  10 +export default defineComponent({
  11 + name: 'LayoutContent',
  12 + setup() {
  13 + const { getOpenPageLoading, getLayoutContentMode, getPageLoading } = useRootSetting();
  14 +
  15 + return () => {
  16 + return (
  17 + <div class={['layout-content', unref(getLayoutContentMode)]}>
  18 + {unref(getOpenPageLoading) && (
  19 + <FullLoading class={[`layout-content__loading`, { hidden: !unref(getPageLoading) }]} />
  20 + )}
  21 + <RouterView />
  22 + </div>
  23 + );
  24 + };
  25 + },
  26 +});
... ...
src/layouts/default/footer/index.less 0 → 100644
  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 0 → 100644
  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 +export default defineComponent({
  12 + name: 'LayoutContent',
  13 + setup() {
  14 + return () => {
  15 + return (
  16 + <Layout.Footer class="layout-footer">
  17 + {() => (
  18 + <>
  19 + <div class="layout-footer__links">
  20 + <a onClick={() => openWindow(SITE_URL)}>在线预览</a>
  21 + <GithubFilled onClick={() => openWindow(GITHUB_URL)} class="github" />
  22 + <a onClick={() => openWindow(DOC_URL)}>在线文档</a>
  23 + </div>
  24 + <div>Copyright &copy;2020 Vben Admin</div>
  25 + </>
  26 + )}
  27 + </Layout.Footer>
  28 + );
  29 + };
  30 + },
  31 +});
... ...
src/layouts/default/header/LayoutHeader.tsx
1 1 import './index.less';
2 2  
  3 +import type { FunctionalComponent } from 'vue';
  4 +
3 5 import { defineComponent, unref, computed, ref, nextTick } from 'vue';
4 6  
5 7 import { Layout, Tooltip, Badge } from 'ant-design-vue';
6 8 import { AppLogo } from '/@/components/Application';
7 9 import UserDropdown from './UserDropdown';
8   -import LayoutMenu from '/@/layouts/default/menu/LayoutMenu';
  10 +import LayoutMenu from '../menu';
9 11 import LayoutBreadcrumb from './LayoutBreadcrumb';
10   -import LockAction from './LockActionItem';
  12 +import LockAction from '../lock/LockAction';
11 13 import LayoutTrigger from '../LayoutTrigger';
12 14 import NoticeAction from './notice/NoticeActionItem.vue';
13 15 import {
... ... @@ -34,9 +36,30 @@ import { PageEnum } from &#39;/@/enums/pageEnum&#39;;
34 36 import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
35 37 import { Component } from '/@/components/types';
36 38  
  39 +interface TooltipItemProps {
  40 + title: string;
  41 +}
  42 +
  43 +const TooltipItem: FunctionalComponent<TooltipItemProps> = (props, { slots }) => {
  44 + return (
  45 + <Tooltip>
  46 + {{
  47 + title: () => props.title,
  48 + default: () => slots.default?.(),
  49 + }}
  50 + </Tooltip>
  51 + );
  52 +};
  53 +
37 54 export default defineComponent({
38 55 name: 'LayoutHeader',
39   - setup() {
  56 + props: {
  57 + fixed: {
  58 + type: Boolean,
  59 + default: false,
  60 + },
  61 + },
  62 + setup(props) {
40 63 let logoEl: Element | null;
41 64  
42 65 const logoWidthRef = ref(200);
... ... @@ -48,7 +71,7 @@ export default defineComponent({
48 71 const { getUseErrorHandle, getShowBreadCrumbIcon } = useRootSetting();
49 72  
50 73 const {
51   - getTheme,
  74 + getHeaderTheme,
52 75 getShowRedo,
53 76 getUseLockPage,
54 77 getShowFullScreen,
... ... @@ -69,8 +92,7 @@ export default defineComponent({
69 92 let width = 0;
70 93 if (!logoEl) {
71 94 logoEl = logoRef.value.$el;
72   - }
73   - if (logoEl) {
  95 + } else {
74 96 width += logoEl.clientWidth;
75 97 }
76 98 logoWidthRef.value = width + 80;
... ... @@ -81,7 +103,7 @@ export default defineComponent({
81 103 );
82 104  
83 105 const headerClass = computed(() => {
84   - const theme = unref(getTheme);
  106 + const theme = unref(getHeaderTheme);
85 107 return theme ? `layout-header__header--${theme}` : '';
86 108 });
87 109  
... ... @@ -99,9 +121,6 @@ export default defineComponent({
99 121 });
100 122 }
101 123  
102   - /**
103   - * @description: 锁定屏幕
104   - */
105 124 function handleLockPage() {
106 125 openModal(true);
107 126 }
... ... @@ -111,13 +130,13 @@ export default defineComponent({
111 130 return (
112 131 <div class="layout-header__content ">
113 132 {unref(getShowHeaderLogo) && (
114   - <AppLogo class={`layout-header__logo`} ref={logoRef} theme={unref(getTheme)} />
  133 + <AppLogo class={`layout-header__logo`} ref={logoRef} theme={unref(getHeaderTheme)} />
115 134 )}
116 135  
117 136 {unref(getShowContent) && (
118 137 <div class="layout-header__left">
119 138 {unref(getShowHeaderTrigger) && (
120   - <LayoutTrigger theme={unref(getTheme)} sider={false} />
  139 + <LayoutTrigger theme={unref(getHeaderTheme)} sider={false} />
121 140 )}
122 141 {unref(getShowBread) && <LayoutBreadcrumb showIcon={unref(getShowBreadCrumbIcon)} />}
123 142 </div>
... ... @@ -128,7 +147,7 @@ export default defineComponent({
128 147 <LayoutMenu
129 148 isHorizontal={true}
130 149 class={`justify-${unref(getTopMenuAlign)}`}
131   - theme={unref(getTheme)}
  150 + theme={unref(getHeaderTheme)}
132 151 splitType={unref(getSplitType)}
133 152 menuMode={unref(getMenuMode)}
134 153 showSearch={false}
... ... @@ -151,64 +170,47 @@ export default defineComponent({
151 170 return (
152 171 <div class={`layout-header__action`}>
153 172 {unref(getUseErrorHandle) && (
154   - <Tooltip>
155   - {{
156   - title: () => '错误日志',
157   - default: () => (
158   - <Badge
159   - count={errorStore.getErrorListCountState}
160   - offset={[0, 10]}
161   - dot
162   - overflowCount={99}
163   - >
164   - {() => renderActionDefault(BugOutlined, handleToErrorList)}
165   - </Badge>
166   - ),
167   - }}
168   - </Tooltip>
  173 + <TooltipItem title="错误日志">
  174 + {() => (
  175 + <Badge
  176 + count={errorStore.getErrorListCountState}
  177 + offset={[0, 10]}
  178 + dot
  179 + overflowCount={99}
  180 + >
  181 + {() => renderActionDefault(BugOutlined, handleToErrorList)}
  182 + </Badge>
  183 + )}
  184 + </TooltipItem>
169 185 )}
170 186  
171 187 {unref(getUseLockPage) && (
172   - <Tooltip>
173   - {{
174   - title: () => '锁定屏幕',
175   - default: () => renderActionDefault(LockOutlined, handleLockPage),
176   - }}
177   - </Tooltip>
  188 + <TooltipItem title="锁定屏幕">
  189 + {() => renderActionDefault(LockOutlined, handleLockPage)}
  190 + </TooltipItem>
178 191 )}
179 192  
180 193 {unref(getShowNotice) && (
181   - <Tooltip>
182   - {{
183   - title: () => '消息通知',
184   - default: () => <NoticeAction />,
185   - }}
186   - </Tooltip>
  194 + <TooltipItem title="消息通知">{() => <NoticeAction />}</TooltipItem>
187 195 )}
188 196  
189 197 {unref(getShowRedo) && (
190   - <Tooltip>
191   - {{
192   - title: () => '刷新',
193   - default: () => renderActionDefault(RedoOutlined, refreshPage),
194   - }}
195   - </Tooltip>
  198 + <TooltipItem title="刷新">
  199 + {() => renderActionDefault(RedoOutlined, refreshPage)}
  200 + </TooltipItem>
196 201 )}
197 202  
198 203 {unref(getShowFullScreen) && (
199   - <Tooltip>
200   - {{
201   - title: () => (unref(isFullscreenRef) ? '退出全屏' : '全屏'),
202   - default: () => {
203   - const Icon = !unref(isFullscreenRef) ? (
204   - <FullscreenOutlined />
205   - ) : (
206   - <FullscreenExitOutlined />
207   - );
208   - return renderActionDefault(Icon, toggleFullscreen);
209   - },
  204 + <TooltipItem title={unref(isFullscreenRef) ? '退出全屏' : '全屏'}>
  205 + {() => {
  206 + const Icon = !unref(isFullscreenRef) ? (
  207 + <FullscreenOutlined />
  208 + ) : (
  209 + <FullscreenExitOutlined />
  210 + );
  211 + return renderActionDefault(Icon, toggleFullscreen);
210 212 }}
211   - </Tooltip>
  213 + </TooltipItem>
212 214 )}
213 215 <UserDropdown class={`layout-header__user-dropdown`} />
214 216 </div>
... ... @@ -227,7 +229,9 @@ export default defineComponent({
227 229  
228 230 return () => {
229 231 return (
230   - <Layout.Header class={['layout-header', 'flex p-0 px-4 ', unref(headerClass)]}>
  232 + <Layout.Header
  233 + class={['layout-header', 'flex p-0 px-4 ', unref(headerClass), { fixed: props.fixed }]}
  234 + >
231 235 {() => renderHeaderDefault()}
232 236 </Layout.Header>
233 237 );
... ...
src/layouts/default/header/LayoutMultipleHeader.less 0 → 100644
  1 +.multiple-tab-header {
  2 + flex: 0 0 auto;
  3 +
  4 + &.fixed {
  5 + position: fixed;
  6 + top: 0;
  7 + z-index: 100;
  8 + width: 100%;
  9 + }
  10 +}
... ...
src/layouts/default/header/LayoutMultipleHeader.tsx 0 → 100644
  1 +import './LayoutMultipleHeader.less';
  2 +
  3 +import { defineComponent, unref, computed, ref, watch, nextTick, CSSProperties } from 'vue';
  4 +
  5 +import LayoutHeader from './LayoutHeader';
  6 +import MultipleTabs from '../multitabs/index';
  7 +
  8 +import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
  9 +import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
  10 +import { useFullContent } from '/@/hooks/web/useFullContent';
  11 +import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
  12 +import { useLayoutContext } from '../useLayoutContext';
  13 +
  14 +export default defineComponent({
  15 + name: 'LayoutMultipleHeader',
  16 + setup() {
  17 + const placeholderHeightRef = ref(0);
  18 + const fullHeaderHeightRef = ref(0);
  19 + const headerElRef = ref<ComponentRef>(null);
  20 + const tabElRef = ref<ComponentRef>(null);
  21 +
  22 + const injectValue = useLayoutContext();
  23 +
  24 + const { getCalcContentWidth } = useMenuSetting();
  25 +
  26 + const {
  27 + getFixed,
  28 + getShowInsetHeaderRef,
  29 + getShowFullHeaderRef,
  30 + getShowHeader,
  31 + getUnFixedAndFull,
  32 + } = useHeaderSetting();
  33 +
  34 + const { getFullContent } = useFullContent();
  35 +
  36 + const { getShowMultipleTab } = useMultipleTabSetting();
  37 +
  38 + const showTabsRef = computed(() => {
  39 + return unref(getShowMultipleTab) && !unref(getFullContent);
  40 + });
  41 +
  42 + const getPlaceholderDomStyle = computed(() => {
  43 + return {
  44 + height: `${unref(placeholderHeightRef)}px`,
  45 + };
  46 + });
  47 +
  48 + const getIsShowPlaceholderDom = computed(() => {
  49 + return unref(getFixed) || unref(getShowFullHeaderRef);
  50 + });
  51 +
  52 + const getWrapStyle = computed(() => {
  53 + const style: CSSProperties = {};
  54 + if (unref(getFixed)) {
  55 + style.width = unref(getCalcContentWidth);
  56 + }
  57 + if (unref(getShowFullHeaderRef)) {
  58 + style.top = `${unref(fullHeaderHeightRef)}px`;
  59 + }
  60 + return style;
  61 + });
  62 +
  63 + const getIsFixed = computed(() => {
  64 + return unref(getFixed) || unref(getShowFullHeaderRef);
  65 + });
  66 +
  67 + watch(
  68 + () => [
  69 + unref(getFixed),
  70 + unref(getShowFullHeaderRef),
  71 + unref(getShowHeader),
  72 + unref(getShowMultipleTab),
  73 + ],
  74 + () => {
  75 + if (unref(getUnFixedAndFull)) return;
  76 + nextTick(() => {
  77 + const headerEl = unref(headerElRef)?.$el;
  78 + const tabEl = unref(tabElRef)?.$el;
  79 + const fullHeaderEl = unref(injectValue.fullHeaderRef)?.$el;
  80 +
  81 + let height = 0;
  82 + if (headerEl && !unref(getShowFullHeaderRef)) {
  83 + height += headerEl.offsetHeight;
  84 + }
  85 +
  86 + if (tabEl) {
  87 + height += tabEl.offsetHeight;
  88 + }
  89 +
  90 + if (fullHeaderEl && unref(getShowFullHeaderRef)) {
  91 + const fullHeaderHeight = fullHeaderEl.offsetHeight;
  92 + height += fullHeaderHeight;
  93 + fullHeaderHeightRef.value = fullHeaderHeight;
  94 + }
  95 + placeholderHeightRef.value = height;
  96 + });
  97 + },
  98 + {
  99 + immediate: true,
  100 + }
  101 + );
  102 +
  103 + return () => {
  104 + return (
  105 + <>
  106 + {unref(getIsShowPlaceholderDom) && <div style={unref(getPlaceholderDomStyle)} />}
  107 + <div
  108 + style={unref(getWrapStyle)}
  109 + class={['multiple-tab-header', { fixed: unref(getIsFixed) }]}
  110 + >
  111 + {unref(getShowInsetHeaderRef) && <LayoutHeader ref={headerElRef} />}
  112 + {unref(showTabsRef) && <MultipleTabs ref={tabElRef} />}
  113 + </div>
  114 + </>
  115 + );
  116 + };
  117 + },
  118 +});
... ...
src/layouts/default/header/UserDropdown.tsx
... ... @@ -15,15 +15,31 @@ import { DOC_URL } from &#39;/@/settings/siteSetting&#39;;
15 15 import { openWindow } from '/@/utils';
16 16  
17 17 import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
  18 +import { FunctionalComponent } from 'vue';
18 19  
19   -interface RenderItemParams {
  20 +type MenuEvent = 'loginOut' | 'doc';
  21 +interface MenuItemProps {
20 22 icon: string;
21 23 text: string;
22   - key: string;
  24 + key: MenuEvent;
23 25 }
24 26  
25 27 const prefixCls = 'user-dropdown';
26 28  
  29 +const MenuItem: FunctionalComponent<MenuItemProps> = (props) => {
  30 + const { key, icon, text } = props;
  31 + return (
  32 + <Menu.Item key={key}>
  33 + {() => (
  34 + <span class="flex items-center">
  35 + <Icon icon={icon} class="mr-1" />
  36 + <span>{text}</span>
  37 + </span>
  38 + )}
  39 + </Menu.Item>
  40 + );
  41 +};
  42 +
27 43 export default defineComponent({
28 44 name: 'UserDropdown',
29 45 setup() {
... ... @@ -44,27 +60,17 @@ export default defineComponent({
44 60 openWindow(DOC_URL);
45 61 }
46 62  
47   - function handleMenuClick(e: any) {
48   - if (e.key === 'loginOut') {
49   - handleLoginOut();
50   - } else if (e.key === 'doc') {
51   - openDoc();
  63 + function handleMenuClick(e: { key: MenuEvent }) {
  64 + switch (e.key) {
  65 + case 'loginOut':
  66 + handleLoginOut();
  67 + break;
  68 + case 'doc':
  69 + openDoc();
  70 + break;
52 71 }
53 72 }
54 73  
55   - function renderItem({ icon, text, key }: RenderItemParams) {
56   - return (
57   - <Menu.Item key={key}>
58   - {() => (
59   - <span class="flex items-center">
60   - <Icon icon={icon} class="mr-1" />
61   - <span>{text}</span>
62   - </span>
63   - )}
64   - </Menu.Item>
65   - );
66   - }
67   -
68 74 function renderSlotsDefault() {
69 75 const { realName } = unref(getUserInfo);
70 76 return (
... ... @@ -83,13 +89,9 @@ export default defineComponent({
83 89 <Menu onClick={handleMenuClick}>
84 90 {() => (
85 91 <>
86   - {showDoc && renderItem({ key: 'doc', text: '文档', icon: 'gg:loadbar-doc' })}
  92 + {showDoc && <MenuItem key="doc" text="文档" icon="gg:loadbar-doc" />}
87 93 {showDoc && <Divider />}
88   - {renderItem({
89   - key: 'loginOut',
90   - text: '退出系统',
91   - icon: 'ant-design:poweroff-outlined',
92   - })}
  94 + <MenuItem key="loginOut" text="退出系统" icon="ant-design:poweroff-outlined" />
93 95 </>
94 96 )}
95 97 </Menu>
... ...
src/layouts/default/header/index.less
... ... @@ -10,13 +10,21 @@
10 10 align-items: center;
11 11 justify-content: space-between;
12 12  
  13 + &.fixed {
  14 + position: fixed;
  15 + top: 0;
  16 + left: 0;
  17 + z-index: 1000;
  18 + width: 100%;
  19 + }
  20 +
13 21 &__left {
14 22 display: flex;
15 23 // flex-grow: 1;
16 24 align-items: center;
17 25  
18 26 .layout-trigger {
19   - padding: 4px 10px 0 16px;
  27 + padding: 1px 10px 0 16px;
20 28 cursor: pointer;
21 29  
22 30 .anticon {
... ... @@ -150,6 +158,7 @@
150 158 }
151 159  
152 160 &__inner,
  161 + &__inner.is-link,
153 162 &__separator {
154 163 color: @white;
155 164 }
... ...
src/layouts/default/index.less
1 1 @import (reference) '../../design/index.less';
2 2  
3 3 .default-layout {
4   - &__content {
5   - position: relative;
  4 + display: flex;
  5 + flex-direction: column;
  6 + width: 100%;
  7 + min-height: 100%;
6 8  
7   - &.fixed {
8   - overflow: hidden;
9   - }
10   - }
11   -
12   - &__loading {
13   - position: absolute;
14   - z-index: @page-loading-z-index;
15   - }
16   -
17   - &__main {
18   - position: relative;
19   - height: 100%;
20   -
21   - &.fixed {
22   - overflow-x: hidden;
23   - overflow-y: auto;
24   - }
25   -
26   - &.fixed.lock {
27   - overflow: hidden;
28   - }
29   - }
30   -
31   - .layout-content {
32   - position: relative;
33   -
34   - &.fixed {
35   - width: 1200px;
36   - margin: 0 auto;
37   - }
  9 + > .ant-layout {
  10 + min-height: 100%;
38 11 }
39 12 }
... ...
src/layouts/default/index.tsx
1   -import { defineComponent, unref, computed } from 'vue';
  1 +import './index.less';
  2 +
  3 +import { defineComponent, unref, computed, ref } from 'vue';
2 4 import { Layout, BackTop } from 'ant-design-vue';
3 5 import LayoutHeader from './header/LayoutHeader';
4 6  
5   -import { appStore } from '/@/store/modules/app';
6   -import LayoutContent from './LayoutContent';
7   -import LayoutSideBar from './sider/LayoutSideBar';
  7 +import LayoutContent from './content';
  8 +import LayoutFooter from './footer';
  9 +import LayoutLockPage from './lock';
  10 +import LayoutSideBar from './sider';
8 11 import SettingBtn from './setting/index.vue';
9   -import MultipleTabs from './multitabs/index';
  12 +import LayoutMultipleHeader from './header/LayoutMultipleHeader';
10 13  
11   -import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
  14 +import { MenuModeEnum } from '/@/enums/menuEnum';
  15 +
  16 +import { useRouter } from 'vue-router';
12 17 import { useFullContent } from '/@/hooks/web/useFullContent';
  18 +import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
  19 +import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
  20 +import { useRootSetting } from '/@/hooks/setting/useRootSetting';
  21 +import { createLayoutContext } from './useLayoutContext';
13 22  
14   -import LockPage from '/@/views/sys/lock/index.vue';
15 23 import { registerGlobComp } from '/@/components/registerGlobComp';
16 24  
17   -import './index.less';
18 25 export default defineComponent({
19 26 name: 'DefaultLayout',
20 27 setup() {
  28 + const { currentRoute } = useRouter();
  29 + const headerRef = ref<ComponentRef>(null);
  30 +
  31 + createLayoutContext({ fullHeaderRef: headerRef });
  32 +
21 33 // ! Only register global components here
22 34 // ! Can reduce the size of the first screen code
23 35 // default layout It is loaded after login. So it won’t be packaged to the first screen
24 36 registerGlobComp();
25 37  
26   - const { getFullContent } = useFullContent();
27   -
28   - const getProjectConfigRef = computed(() => appStore.getProjectConfig);
  38 + const { getShowFullHeaderRef } = useHeaderSetting();
29 39  
30   - const getLockMainScrollStateRef = computed(() => appStore.getLockMainScrollState);
  40 + const { getUseOpenBackTop, getShowSettingButton, getShowFooter } = useRootSetting();
31 41  
32   - const showHeaderRef = computed(() => {
33   - const {
34   - headerSetting: { show },
35   - } = unref(getProjectConfigRef);
36   - return show;
37   - });
  42 + const { getShowMenu, getMenuMode, getSplit } = useMenuSetting();
38 43  
39   - const showMixHeaderRef = computed(() => {
40   - const {
41   - menuSetting: { type },
42   - } = unref(getProjectConfigRef);
43   - return type !== MenuTypeEnum.SIDEBAR && unref(showHeaderRef);
44   - });
  44 + const { getFullContent } = useFullContent();
45 45  
46   - const getIsLockRef = computed(() => {
47   - const { getLockInfo } = appStore;
48   - const { isLock } = getLockInfo;
49   - return isLock;
  46 + const getShowLayoutFooter = computed(() => {
  47 + return unref(getShowFooter) && !unref(currentRoute).meta?.hiddenFooter;
50 48 });
51 49  
52 50 const showSideBarRef = computed(() => {
53   - const {
54   - menuSetting: { show, mode, split },
55   - } = unref(getProjectConfigRef);
56   - return split || (show && mode !== MenuModeEnum.HORIZONTAL && !unref(getFullContent));
57   - });
58   -
59   - const showFullHeaderRef = computed(() => {
60   - return !unref(getFullContent) && unref(showMixHeaderRef) && unref(showHeaderRef);
61   - });
62   -
63   - const showInsetHeaderRef = computed(() => {
64   - return !unref(getFullContent) && !unref(showMixHeaderRef) && unref(showHeaderRef);
65   - });
66   -
67   - const fixedHeaderClsRef = computed(() => {
68   - const {
69   - headerSetting: { fixed },
70   - } = unref(getProjectConfigRef);
71   - const fixedHeaderCls = fixed
72   - ? 'fixed' + (unref(getLockMainScrollStateRef) ? ' lock' : '')
73   - : '';
74   - return fixedHeaderCls;
75   - });
76   -
77   - const showTabsRef = computed(() => {
78   - const {
79   - multiTabsSetting: { show },
80   - } = unref(getProjectConfigRef);
81   - return show && !unref(getFullContent);
82   - });
83   -
84   - const showClassSideBarRef = computed(() => {
85   - const {
86   - menuSetting: { split, hidden },
87   - } = unref(getProjectConfigRef);
88   - return split ? hidden : true;
  51 + return (
  52 + unref(getSplit) ||
  53 + (unref(getShowMenu) &&
  54 + unref(getMenuMode) !== MenuModeEnum.HORIZONTAL &&
  55 + !unref(getFullContent))
  56 + );
89 57 });
90 58  
91   - function getTarget(): any {
92   - const {
93   - headerSetting: { fixed },
94   - } = unref(getProjectConfigRef);
95   - return document.querySelector(`.default-layout__${fixed ? 'main' : 'content'}`);
  59 + function renderFeatures() {
  60 + return (
  61 + <>
  62 + <LayoutLockPage />
  63 + {/* back top */}
  64 + {unref(getUseOpenBackTop) && <BackTop target={() => document.body} />}
  65 + {/* open setting drawer */}
  66 + {unref(getShowSettingButton) && <SettingBtn />}
  67 + </>
  68 + );
96 69 }
97 70  
98 71 return () => {
99   - const { useOpenBackTop, showSettingButton } = unref(getProjectConfigRef);
100 72 return (
101   - <Layout class="default-layout relative">
  73 + <Layout class="default-layout">
102 74 {() => (
103 75 <>
104   - {/* lock page */}
105   - {unref(getIsLockRef) && <LockPage />}
106   - {/* back top */}
107   - {useOpenBackTop && <BackTop target={getTarget} />}
108   - {/* open setting drawer */}
109   - {showSettingButton && <SettingBtn />}
  76 + {renderFeatures()}
110 77  
111   - {unref(showFullHeaderRef) && <LayoutHeader />}
  78 + {unref(getShowFullHeaderRef) && <LayoutHeader fixed={true} ref={headerRef} />}
112 79  
113 80 <Layout>
114 81 {() => (
115 82 <>
116   - {unref(showSideBarRef) && (
117   - <LayoutSideBar class={unref(showClassSideBarRef) ? '' : 'hidden'} />
118   - )}
119   - <Layout class={[`default-layout__content`, unref(fixedHeaderClsRef)]}>
  83 + {unref(showSideBarRef) && <LayoutSideBar />}
  84 + <Layout>
120 85 {() => (
121 86 <>
122   - {unref(showInsetHeaderRef) && <LayoutHeader />}
123   -
124   - {unref(showTabsRef) && <MultipleTabs />}
125   -
126   - <LayoutContent class={unref(fixedHeaderClsRef)} />
  87 + <LayoutMultipleHeader />
  88 + <LayoutContent />
  89 + {unref(getShowLayoutFooter) && <LayoutFooter />}
127 90 </>
128 91 )}
129 92 </Layout>
... ...
src/layouts/default/header/LockActionItem.less renamed to src/layouts/default/lock/LockAction.less
src/layouts/default/header/LockActionItem.tsx renamed to src/layouts/default/lock/LockAction.tsx
1   -import './LockActionItem.less';
  1 +import './LockAction.less';
2 2  
3 3 import { defineComponent } from 'vue';
4 4 import { BasicModal, useModalInner } from '/@/components/Modal/index';
... ...
src/layouts/default/lock/index.tsx 0 → 100644
  1 +import { defineComponent, unref, computed } from 'vue';
  2 +import { appStore } from '/@/store/modules/app';
  3 +import LockPage from '/@/views/sys/lock/index.vue';
  4 +
  5 +export default defineComponent({
  6 + name: 'LayoutLockPage',
  7 + setup() {
  8 + const getIsLockRef = computed(() => {
  9 + const { getLockInfo } = appStore;
  10 + const { isLock } = getLockInfo;
  11 + return isLock;
  12 + });
  13 + return () => {
  14 + return unref(getIsLockRef) ? <LockPage /> : null;
  15 + };
  16 + },
  17 +});
... ...
src/layouts/default/menu/LayoutMenu.tsx renamed to src/layouts/default/menu/index.tsx
... ... @@ -17,7 +17,7 @@ import { useSplitMenu } from &#39;./useLayoutMenu&#39;;
17 17 import { openWindow } from '/@/utils';
18 18  
19 19 export default defineComponent({
20   - name: 'DefaultLayoutMenu',
  20 + name: 'LayoutMenu',
21 21 props: {
22 22 theme: {
23 23 type: String as PropType<string>,
... ... @@ -50,12 +50,12 @@ export default defineComponent({
50 50 const {
51 51 setMenuSetting,
52 52 getShowSearch,
53   - getMode,
54   - getType,
  53 + getMenuMode,
  54 + getMenuType,
55 55 getCollapsedShowTitle,
56 56 getCollapsedShowSearch,
57 57 getIsSidebarType,
58   - getTheme,
  58 + getMenuTheme,
59 59 getCollapsed,
60 60 getAccordion,
61 61 } = useMenuSetting();
... ... @@ -66,9 +66,9 @@ export default defineComponent({
66 66  
67 67 const showLogo = computed(() => unref(getShowLogo) && unref(getIsSidebarType));
68 68  
69   - const getMenuMode = computed(() => props.menuMode || unref(getMode));
  69 + const getComputedMenuMode = computed(() => props.menuMode || unref(getMenuMode));
70 70  
71   - const getMenuTheme = computed(() => props.theme || unref(getTheme));
  71 + const getComputedMenuTheme = computed(() => props.theme || unref(getMenuTheme));
72 72  
73 73 const appendClass = computed(() => props.splitType === MenuSplitTyeEnum.TOP);
74 74  
... ... @@ -111,8 +111,8 @@ export default defineComponent({
111 111 return (
112 112 <AppLogo
113 113 showTitle={!unref(getCollapsed)}
114   - class={[`layout-menu__logo`, unref(getMenuTheme)]}
115   - theme={unref(getMenuTheme)}
  114 + class={[`layout-menu__logo`, unref(getComputedMenuTheme)]}
  115 + theme={unref(getComputedMenuTheme)}
116 116 />
117 117 );
118 118 }
... ... @@ -124,10 +124,10 @@ export default defineComponent({
124 124 beforeClickFn={beforeMenuClickFn}
125 125 isHorizontal={props.isHorizontal}
126 126 appendClass={unref(appendClass)}
127   - type={unref(getType)}
128   - mode={unref(getMenuMode)}
  127 + type={unref(getMenuType)}
  128 + mode={unref(getComputedMenuMode)}
129 129 collapsedShowTitle={unref(getCollapsedShowTitle)}
130   - theme={unref(getMenuTheme)}
  130 + theme={unref(getComputedMenuTheme)}
131 131 showLogo={unref(showLogo)}
132 132 search={unref(showSearch)}
133 133 items={unref(menusRef)}
... ...
src/layouts/default/multitabs/TabContent.tsx
... ... @@ -6,7 +6,6 @@ import { TabItem, tabStore } from &#39;/@/store/modules/tab&#39;;
6 6 import { getScaleAction, TabContentProps } from './tab.data';
7 7  
8 8 import { Dropdown } from '/@/components/Dropdown/index';
9   -import Icon from '/@/components/Icon/index';
10 9 import { RightOutlined } from '@ant-design/icons-vue';
11 10 import { appStore } from '/@/store/modules/app';
12 11  
... ... @@ -57,18 +56,11 @@ export default defineComponent({
57 56 /**
58 57 * @description: 渲染图标
59 58 */
60   - function renderIcon() {
61   - const { tabItem } = props;
62   - if (!tabItem) return;
63   - const icon = tabItem.meta && tabItem.meta.icon;
64   - if (!icon || !unref(getProjectConfigRef).multiTabsSetting.showIcon) return null;
65   - return <Icon icon={icon} class="align-middle " style={{ marginBottom: '2px' }} />;
66   - }
  59 +
67 60 function renderTabContent() {
68 61 const { tabItem: { meta } = {} } = props;
69 62 return (
70 63 <div class={`multiple-tabs-content__content `} onContextmenu={handleContextMenu}>
71   - {renderIcon()}
72 64 <span class="ml-1">{meta && meta.title}</span>
73 65 </div>
74 66 );
... ...
src/layouts/default/setting/SettingDrawer.tsx
1   -import { defineComponent, computed, unref, ref } from 'vue';
  1 +import type { ProjectConfig } from '/@/types/config';
  2 +
  3 +import defaultSetting from '/@/settings/projectSetting';
  4 +
  5 +import { defineComponent, computed, unref, FunctionalComponent } from 'vue';
2 6 import { BasicDrawer } from '/@/components/Drawer/index';
3 7 import { Divider, Switch, Tooltip, InputNumber, Select } from 'ant-design-vue';
4 8 import Button from '/@/components/Button/index.vue';
5   -import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
6 9 import { CopyOutlined, RedoOutlined, CheckOutlined } from '@ant-design/icons-vue';
  10 +
  11 +import { MenuTypeEnum } from '/@/enums/menuEnum';
7 12 import { appStore } from '/@/store/modules/app';
8   -import { ProjectConfig } from '/@/types/config';
9 13  
10 14 import { useMessage } from '/@/hooks/web/useMessage';
11 15 import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
  16 +import { useRootSetting } from '/@/hooks/setting/useRootSetting';
  17 +import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
  18 +import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
  19 +import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
12 20  
13   -import defaultSetting from '/@/settings/projectSetting';
14   -
15   -import mixImg from '/@/assets/images/layout/menu-mix.svg';
16   -import sidebarImg from '/@/assets/images/layout/menu-sidebar.svg';
17   -import menuTopImg from '/@/assets/images/layout/menu-top.svg';
18 21 import { updateColorWeak, updateGrayMode } from '/@/setup/theme';
  22 +
19 23 import { baseHandler } from './handler';
  24 +
20 25 import {
21 26 HandlerEnum,
22 27 contentModeOptions,
23 28 topMenuAlignOptions,
24 29 menuTriggerOptions,
25 30 routerTransitionOptions,
26   -} from './const';
  31 + menuTypeList,
  32 +} from './enum';
  33 +
27 34 import { HEADER_PRESET_BG_COLOR_LIST, SIDE_BAR_BG_COLOR_LIST } from '/@/settings/colorSetting';
28 35  
29 36 interface SwitchOptions {
... ... @@ -40,215 +47,280 @@ interface SelectConfig {
40 47 handler?: Fn;
41 48 }
42 49  
43   -interface ThemeOptions {
44   - def?: string;
45   - handler?: Fn;
  50 +interface ThemePickerProps {
  51 + colorList: string[];
  52 + handler: Fn;
  53 + def: string;
46 54 }
47 55  
  56 +const { createSuccessModal, createMessage } = useMessage();
  57 +
  58 +/**
  59 + * Menu type Picker comp
  60 + */
  61 +const MenuTypePicker: FunctionalComponent = () => {
  62 + const { getIsHorizontal, getMenuType } = useMenuSetting();
  63 + return (
  64 + <div class={`setting-drawer__siderbar`}>
  65 + {menuTypeList.map((item) => {
  66 + const { title, type: ItemType, mode, src } = item;
  67 + return (
  68 + <Tooltip title={title} placement="bottom" key={title}>
  69 + {{
  70 + default: () => (
  71 + <div
  72 + onClick={baseHandler.bind(null, HandlerEnum.CHANGE_LAYOUT, {
  73 + mode: mode,
  74 + type: ItemType,
  75 + split: unref(getIsHorizontal) ? false : undefined,
  76 + })}
  77 + >
  78 + <CheckOutlined
  79 + class={['check-icon', unref(getMenuType) === ItemType ? 'active' : '']}
  80 + />
  81 + <img src={src} />
  82 + </div>
  83 + ),
  84 + }}
  85 + </Tooltip>
  86 + );
  87 + })}
  88 + </div>
  89 + );
  90 +};
  91 +
  92 +const ThemePicker: FunctionalComponent<ThemePickerProps> = (props) => {
  93 + return (
  94 + <div class={`setting-drawer__theme-item`}>
  95 + {props.colorList.map((color) => {
  96 + return (
  97 + <span
  98 + onClick={() => props.handler?.(color)}
  99 + key={color}
  100 + class={[props.def === color ? 'active' : '']}
  101 + style={{
  102 + background: color,
  103 + }}
  104 + >
  105 + <CheckOutlined class="icon" />
  106 + </span>
  107 + );
  108 + })}
  109 + </div>
  110 + );
  111 +};
  112 +
  113 +/**
  114 + * FooterButton component
  115 + */
  116 +const FooterButton: FunctionalComponent = () => {
  117 + const { getRootSetting } = useRootSetting();
  118 + function handleCopy() {
  119 + const { isSuccessRef } = useCopyToClipboard(JSON.stringify(unref(getRootSetting), null, 2));
  120 + unref(isSuccessRef) &&
  121 + createSuccessModal({
  122 + title: '操作成功',
  123 + content: '复制成功,请到 src/settings/projectSetting.ts 中修改配置!',
  124 + });
  125 + }
  126 + function handleResetSetting() {
  127 + try {
  128 + appStore.commitProjectConfigState(defaultSetting);
  129 + const { colorWeak, grayMode } = defaultSetting;
  130 + // updateTheme(themeColor);
  131 + updateColorWeak(colorWeak);
  132 + updateGrayMode(grayMode);
  133 + createMessage.success('重置成功!');
  134 + } catch (error) {
  135 + createMessage.error(error);
  136 + }
  137 + }
  138 +
  139 + function handleClearAndRedo() {
  140 + localStorage.clear();
  141 + appStore.resumeAllState();
  142 + location.reload();
  143 + }
  144 +
  145 + return (
  146 + <div class="setting-drawer__footer">
  147 + <Button type="primary" block onClick={handleCopy}>
  148 + {() => (
  149 + <>
  150 + <CopyOutlined class="mr-2" />
  151 + 拷贝
  152 + </>
  153 + )}
  154 + </Button>
  155 + <Button block class="mt-2" onClick={handleResetSetting} color="warning">
  156 + {() => (
  157 + <>
  158 + <RedoOutlined class="mr-2" />
  159 + 重置
  160 + </>
  161 + )}
  162 + </Button>
  163 + <Button block class="mt-2" onClick={handleClearAndRedo} color="error">
  164 + {() => (
  165 + <>
  166 + <RedoOutlined class="mr-2" />
  167 + 清空缓存并返回登录页
  168 + </>
  169 + )}
  170 + </Button>
  171 + </div>
  172 + );
  173 +};
  174 +
48 175 export default defineComponent({
49 176 name: 'SettingDrawer',
50 177 setup(_, { attrs }) {
51   - const { createSuccessModal, createMessage } = useMessage();
  178 + const {
  179 + getContentMode,
  180 + getRouterTransition,
  181 + getOpenRouterTransition,
  182 + getOpenPageLoading,
  183 + getShowFooter,
  184 + getShowBreadCrumb,
  185 + getShowBreadCrumbIcon,
  186 + getShowLogo,
  187 + getFullContent,
  188 + getColorWeak,
  189 + getGrayMode,
  190 + } = useRootSetting();
52 191  
53   - const getProjectConfigRef = computed(() => {
54   - return appStore.getProjectConfig;
55   - });
  192 + const {
  193 + getIsHorizontal,
  194 + getShowMenu,
  195 + getMenuType,
  196 + getTrigger,
  197 + getCollapsedShowTitle,
  198 + getMenuFixed,
  199 + getCollapsed,
  200 + getShowSearch,
  201 + getHasDrag,
  202 + getTopMenuAlign,
  203 + getAccordion,
  204 + getMenuWidth,
  205 + getMenuBgColor,
  206 + getIsTopMenu,
  207 + getSplit,
  208 + } = useMenuSetting();
56 209  
57   - const getIsHorizontalRef = computed(() => {
58   - return unref(getProjectConfigRef).menuSetting.mode === MenuModeEnum.HORIZONTAL;
59   - });
  210 + const { getShowHeader, getFixed: getHeaderFixed, getHeaderBgColor } = useHeaderSetting();
60 211  
61   - const getShowHeaderRef = computed(() => {
62   - return unref(getProjectConfigRef).headerSetting.show;
63   - });
  212 + const { getShowMultipleTab, getShowQuick } = useMultipleTabSetting();
64 213  
65 214 const getShowMenuRef = computed(() => {
66   - return unref(getProjectConfigRef).menuSetting.show && !unref(getIsHorizontalRef);
  215 + return unref(getShowMenu) && !unref(getIsHorizontal);
67 216 });
68 217  
69   - const getShowTabsRef = computed(() => {
70   - return unref(getProjectConfigRef).multiTabsSetting.show;
71   - });
72   -
73   - function handleCopy() {
74   - const { isSuccessRef } = useCopyToClipboard(
75   - JSON.stringify(unref(getProjectConfigRef), null, 2)
  218 + function renderSidebar() {
  219 + return (
  220 + <>
  221 + <MenuTypePicker />
  222 + {renderSwitchItem('分割菜单', {
  223 + handler: (e) => {
  224 + baseHandler(HandlerEnum.MENU_SPLIT, e);
  225 + },
  226 + def: unref(getSplit),
  227 + disabled: !unref(getShowMenuRef) || unref(getMenuType) !== MenuTypeEnum.MIX,
  228 + })}
  229 + </>
76 230 );
77   - unref(isSuccessRef) &&
78   - createSuccessModal({
79   - title: '操作成功',
80   - content: '复制成功,请到 src/settings/projectSetting.ts 中修改配置!',
81   - });
82 231 }
83 232  
84   - function handleResetSetting() {
85   - try {
86   - appStore.commitProjectConfigState(defaultSetting);
87   - const { colorWeak, grayMode } = defaultSetting;
88   - // updateTheme(themeColor);
89   - updateColorWeak(colorWeak);
90   - updateGrayMode(grayMode);
91   - createMessage.success('重置成功!');
92   - } catch (error) {
93   - createMessage.error(error);
94   - }
95   - }
96   -
97   - function handleClearAndRedo() {
98   - localStorage.clear();
99   - appStore.resumeAllState();
100   - location.reload();
  233 + function renderTheme() {
  234 + return (
  235 + <>
  236 + <Divider>{() => '顶栏主题'}</Divider>
  237 + <ThemePicker
  238 + colorList={HEADER_PRESET_BG_COLOR_LIST}
  239 + def={unref(getHeaderBgColor)}
  240 + handler={(e) => {
  241 + baseHandler(HandlerEnum.HEADER_THEME, e);
  242 + }}
  243 + />
  244 + <Divider>{() => '菜单主题'}</Divider>
  245 + <ThemePicker
  246 + colorList={SIDE_BAR_BG_COLOR_LIST}
  247 + def={unref(getMenuBgColor)}
  248 + handler={(e) => {
  249 + baseHandler(HandlerEnum.MENU_THEME, e);
  250 + }}
  251 + />
  252 + </>
  253 + );
101 254 }
102 255  
103   - function renderSidebar() {
104   - const {
105   - menuSetting: { type, split },
106   - } = unref(getProjectConfigRef);
107   -
108   - const typeList = ref([
109   - {
110   - title: '左侧菜单模式',
111   - mode: MenuModeEnum.INLINE,
112   - type: MenuTypeEnum.SIDEBAR,
113   - src: sidebarImg,
114   - },
115   - {
116   - title: '混合模式',
117   - mode: MenuModeEnum.INLINE,
118   - type: MenuTypeEnum.MIX,
119   - src: mixImg,
120   - },
121   -
122   - {
123   - title: '顶部菜单模式',
124   - mode: MenuModeEnum.HORIZONTAL,
125   - type: MenuTypeEnum.TOP_MENU,
126   - src: menuTopImg,
127   - },
128   - ]);
129   - return [
130   - <div class={`setting-drawer__siderbar`}>
131   - {unref(typeList).map((item) => {
132   - const { title, type: ItemType, mode, src } = item;
133   - return (
134   - <Tooltip title={title} placement="bottom" key={title}>
135   - {{
136   - default: () => (
137   - <div
138   - onClick={baseHandler.bind(null, HandlerEnum.CHANGE_LAYOUT, {
139   - mode: mode,
140   - type: ItemType,
141   - split: unref(getIsHorizontalRef) ? false : undefined,
142   - })}
143   - >
144   - <CheckOutlined class={['check-icon', type === ItemType ? 'active' : '']} />
145   - <img src={src} />
146   - </div>
147   - ),
148   - }}
149   - </Tooltip>
150   - );
151   - })}
152   - </div>,
153   - renderSwitchItem('分割菜单', {
154   - handler: (e) => {
155   - baseHandler(HandlerEnum.MENU_SPLIT, e);
156   - },
157   - def: split,
158   - disabled: !unref(getShowMenuRef) || type !== MenuTypeEnum.MIX,
159   - }),
160   - // renderSelectItem('顶栏主题', {
161   - // handler: (e) => {
162   - // baseHandler(HandlerEnum.HEADER_THEME, e);
163   - // },
164   - // def: headerTheme,
165   - // options: themeOptions,
166   - // disabled: !unref(getShowHeaderRef),
167   - // }),
168   - // renderSelectItem('菜单主题', {
169   - // handler: (e) => {
170   - // baseHandler(HandlerEnum.MENU_THEME, e);
171   - // },
172   - // def: menuTheme,
173   - // options: themeOptions,
174   - // disabled: !unref(getShowMenuRef),
175   - // }),
176   - ];
177   - }
178 256 /**
179 257 * @description:
180 258 */
181 259 function renderFeatures() {
182   - const {
183   - contentMode,
184   - headerSetting: { fixed },
185   - menuSetting: {
186   - hasDrag,
187   - collapsed,
188   - showSearch,
189   - menuWidth,
190   - topMenuAlign,
191   - collapsedShowTitle,
192   - trigger,
193   - accordion,
194   - } = {},
195   - } = appStore.getProjectConfig;
196 260 return [
197 261 renderSwitchItem('侧边菜单拖拽', {
198 262 handler: (e) => {
199 263 baseHandler(HandlerEnum.MENU_HAS_DRAG, e);
200 264 },
201   - def: hasDrag,
  265 + def: unref(getHasDrag),
202 266 disabled: !unref(getShowMenuRef),
203 267 }),
204 268 renderSwitchItem('侧边菜单搜索', {
205 269 handler: (e) => {
206 270 baseHandler(HandlerEnum.MENU_SHOW_SEARCH, e);
207 271 },
208   - def: showSearch,
  272 + def: unref(getShowSearch),
209 273 disabled: !unref(getShowMenuRef),
210 274 }),
211 275 renderSwitchItem('侧边菜单手风琴模式', {
212 276 handler: (e) => {
213 277 baseHandler(HandlerEnum.MENU_ACCORDION, e);
214 278 },
215   - def: accordion,
  279 + def: unref(getAccordion),
216 280 disabled: !unref(getShowMenuRef),
217 281 }),
218 282 renderSwitchItem('折叠菜单', {
219 283 handler: (e) => {
220 284 baseHandler(HandlerEnum.MENU_COLLAPSED, e);
221 285 },
222   - def: collapsed,
  286 + def: unref(getCollapsed),
223 287 disabled: !unref(getShowMenuRef),
224 288 }),
225 289 renderSwitchItem('折叠菜单显示名称', {
226 290 handler: (e) => {
227 291 baseHandler(HandlerEnum.MENU_COLLAPSED_SHOW_TITLE, e);
228 292 },
229   - def: collapsedShowTitle,
230   - disabled: !unref(getShowMenuRef) || !collapsed,
  293 + def: unref(getCollapsedShowTitle),
  294 + disabled: !unref(getShowMenuRef) || !unref(getCollapsed),
231 295 }),
232 296 renderSwitchItem('固定header', {
233 297 handler: (e) => {
234 298 baseHandler(HandlerEnum.HEADER_FIXED, e);
235 299 },
236   - def: fixed,
237   - disabled: !unref(getShowHeaderRef),
  300 + def: unref(getHeaderFixed),
  301 + disabled: !unref(getShowHeader),
  302 + }),
  303 + renderSwitchItem('固定Siderbar', {
  304 + handler: (e) => {
  305 + baseHandler(HandlerEnum.MENU_FIXED, e);
  306 + },
  307 + def: unref(getMenuFixed),
  308 + disabled: !unref(getShowMenuRef),
238 309 }),
239 310 renderSelectItem('顶部菜单布局', {
240 311 handler: (e) => {
241 312 baseHandler(HandlerEnum.MENU_TOP_ALIGN, e);
242 313 },
243   - def: topMenuAlign,
  314 + def: unref(getTopMenuAlign),
244 315 options: topMenuAlignOptions,
245   - disabled: !unref(getShowHeaderRef),
  316 + disabled: !unref(getShowHeader) || (!unref(getIsTopMenu) && !unref(getSplit)),
246 317 }),
247 318 renderSelectItem('菜单折叠按钮', {
248 319 handler: (e) => {
249 320 baseHandler(HandlerEnum.MENU_TRIGGER, e);
250 321 },
251   - def: trigger,
  322 + disabled: !unref(getShowMenuRef),
  323 + def: unref(getTrigger),
252 324 options: menuTriggerOptions,
253 325 }),
254 326  
... ... @@ -256,7 +328,7 @@ export default defineComponent({
256 328 handler: (e) => {
257 329 baseHandler(HandlerEnum.CONTENT_MODE, e);
258 330 },
259   - def: contentMode,
  331 + def: unref(getContentMode),
260 332 options: contentModeOptions,
261 333 }),
262 334 <div class={`setting-drawer__cell-item`}>
... ... @@ -286,7 +358,7 @@ export default defineComponent({
286 358 min={100}
287 359 step={10}
288 360 disabled={!unref(getShowMenuRef)}
289   - defaultValue={menuWidth}
  361 + defaultValue={unref(getMenuWidth)}
290 362 formatter={(value: string) => `${parseInt(value)}px`}
291 363 onChange={(e: any) => {
292 364 baseHandler(HandlerEnum.MENU_WIDTH, e);
... ... @@ -295,121 +367,112 @@ export default defineComponent({
295 367 </div>,
296 368 ];
297 369 }
298   - function renderTransition() {
299   - const { routerTransition, openRouterTransition, openPageLoading } = appStore.getProjectConfig;
300 370  
301   - return (
302   - <>
303   - {renderSwitchItem('页面切换loading', {
304   - handler: (e) => {
305   - baseHandler(HandlerEnum.OPEN_PAGE_LOADING, e);
306   - },
307   - def: openPageLoading,
308   - })}
309   - {renderSwitchItem('切换动画', {
310   - handler: (e) => {
311   - baseHandler(HandlerEnum.OPEN_ROUTE_TRANSITION, e);
312   - },
313   - def: openRouterTransition,
314   - })}
315   - {renderSelectItem('路由动画', {
316   - handler: (e) => {
317   - baseHandler(HandlerEnum.ROUTER_TRANSITION, e);
318   - },
319   - def: routerTransition,
320   - options: routerTransitionOptions,
321   - disabled: !openRouterTransition,
322   - })}
323   - </>
324   - );
325   - }
326 371 function renderContent() {
327   - const {
328   - grayMode,
329   - colorWeak,
330   - fullContent,
331   - showLogo,
332   - headerSetting: { show: showHeader },
333   - menuSetting: { show: showMenu },
334   - multiTabsSetting: { show: showMultiple, showQuick, showIcon: showTabIcon },
335   - showBreadCrumb,
336   - showBreadCrumbIcon,
337   - } = unref(getProjectConfigRef);
338 372 return [
339 373 renderSwitchItem('面包屑', {
340 374 handler: (e) => {
341 375 baseHandler(HandlerEnum.SHOW_BREADCRUMB, e);
342 376 },
343   - def: showBreadCrumb,
344   - disabled: !unref(getShowHeaderRef),
  377 + def: unref(getShowBreadCrumb),
  378 + disabled: !unref(getShowHeader),
345 379 }),
346 380 renderSwitchItem('面包屑图标', {
347 381 handler: (e) => {
348 382 baseHandler(HandlerEnum.SHOW_BREADCRUMB_ICON, e);
349 383 },
350   - def: showBreadCrumbIcon,
351   - disabled: !unref(getShowHeaderRef),
  384 + def: unref(getShowBreadCrumbIcon),
  385 + disabled: !unref(getShowHeader),
352 386 }),
353 387 renderSwitchItem('标签页', {
354 388 handler: (e) => {
355 389 baseHandler(HandlerEnum.TABS_SHOW, e);
356 390 },
357   - def: showMultiple,
  391 + def: unref(getShowMultipleTab),
358 392 }),
359 393 renderSwitchItem('标签页快捷按钮', {
360 394 handler: (e) => {
361 395 baseHandler(HandlerEnum.TABS_SHOW_QUICK, e);
362 396 },
363   - def: showQuick,
364   - disabled: !unref(getShowTabsRef),
365   - }),
366   - renderSwitchItem('标签页图标', {
367   - handler: (e) => {
368   - baseHandler(HandlerEnum.TABS_SHOW_ICON, e);
369   - },
370   - def: showTabIcon,
371   - disabled: !unref(getShowTabsRef),
  397 + def: unref(getShowQuick),
  398 + disabled: !unref(getShowMultipleTab),
372 399 }),
  400 +
373 401 renderSwitchItem('左侧菜单', {
374 402 handler: (e) => {
375 403 baseHandler(HandlerEnum.MENU_SHOW_SIDEBAR, e);
376 404 },
377   - def: showMenu,
378   - disabled: unref(getIsHorizontalRef),
  405 + def: unref(getShowMenu),
  406 + disabled: unref(getIsHorizontal),
379 407 }),
380 408 renderSwitchItem('顶栏', {
381 409 handler: (e) => {
382 410 baseHandler(HandlerEnum.HEADER_SHOW, e);
383 411 },
384   - def: showHeader,
  412 + def: unref(getShowHeader),
385 413 }),
386 414 renderSwitchItem('Logo', {
387 415 handler: (e) => {
388 416 baseHandler(HandlerEnum.SHOW_LOGO, e);
389 417 },
390   - def: showLogo,
  418 + def: unref(getShowLogo),
  419 + }),
  420 + renderSwitchItem('页脚', {
  421 + handler: (e) => {
  422 + baseHandler(HandlerEnum.SHOW_FOOTER, e);
  423 + },
  424 + def: unref(getShowFooter),
391 425 }),
392 426 renderSwitchItem('全屏内容', {
393 427 handler: (e) => {
394 428 baseHandler(HandlerEnum.FULL_CONTENT, e);
395 429 },
396   - def: fullContent,
  430 + def: unref(getFullContent),
397 431 }),
398 432 renderSwitchItem('灰色模式', {
399 433 handler: (e) => {
400 434 baseHandler(HandlerEnum.GRAY_MODE, e);
401 435 },
402   - def: grayMode,
  436 + def: unref(getGrayMode),
403 437 }),
404 438 renderSwitchItem('色弱模式', {
405 439 handler: (e) => {
406 440 baseHandler(HandlerEnum.COLOR_WEAK, e);
407 441 },
408   - def: colorWeak,
  442 + def: unref(getColorWeak),
409 443 }),
410 444 ];
411 445 }
412 446  
  447 + function renderTransition() {
  448 + return (
  449 + <>
  450 + {renderSwitchItem('页面切换loading', {
  451 + handler: (e) => {
  452 + baseHandler(HandlerEnum.OPEN_PAGE_LOADING, e);
  453 + },
  454 + def: unref(getOpenPageLoading),
  455 + })}
  456 +
  457 + {renderSwitchItem('切换动画', {
  458 + handler: (e) => {
  459 + baseHandler(HandlerEnum.OPEN_ROUTE_TRANSITION, e);
  460 + },
  461 + def: unref(getOpenRouterTransition),
  462 + })}
  463 +
  464 + {renderSelectItem('路由动画', {
  465 + handler: (e) => {
  466 + baseHandler(HandlerEnum.ROUTER_TRANSITION, e);
  467 + },
  468 + def: unref(getRouterTransition),
  469 + options: routerTransitionOptions,
  470 + disabled: !unref(getOpenRouterTransition),
  471 + })}
  472 + </>
  473 + );
  474 + }
  475 +
413 476 function renderSelectItem(text: string, config?: SelectConfig) {
414 477 const { handler, def, disabled = false, options } = config || {};
415 478 const opt = def ? { value: def, defaultValue: def } : {};
... ... @@ -449,50 +512,6 @@ export default defineComponent({
449 512 );
450 513 }
451 514  
452   - function renderTheme() {
453   - const { headerBgColor, menuBgColor } = unref(getProjectConfigRef);
454   - return (
455   - <>
456   - <Divider>{() => '顶栏主题'}</Divider>
457   - {renderThemeItem(HEADER_PRESET_BG_COLOR_LIST, {
458   - def: headerBgColor,
459   - handler: (e) => {
460   - baseHandler(HandlerEnum.HEADER_THEME, e);
461   - },
462   - })}
463   - <Divider>{() => '菜单主题'}</Divider>
464   - {renderThemeItem(SIDE_BAR_BG_COLOR_LIST, {
465   - def: menuBgColor,
466   - handler: (e) => {
467   - baseHandler(HandlerEnum.MENU_THEME, e);
468   - },
469   - })}
470   - </>
471   - );
472   - }
473   -
474   - function renderThemeItem(colorList: string[], opt: ThemeOptions) {
475   - const { def, handler } = opt;
476   - return (
477   - <div class={`setting-drawer__theme-item`}>
478   - {colorList.map((item) => {
479   - return (
480   - <span
481   - onClick={() => handler && handler(item)}
482   - key={item}
483   - class={[def === item ? 'active' : '']}
484   - style={{
485   - background: item,
486   - }}
487   - >
488   - <CheckOutlined class="icon" />
489   - </span>
490   - );
491   - })}
492   - </div>
493   - );
494   - }
495   -
496 515 return () => (
497 516 <BasicDrawer {...attrs} title="项目配置" width={300} wrapClassName="setting-drawer">
498 517 {{
... ... @@ -500,9 +519,7 @@ export default defineComponent({
500 519 <>
501 520 <Divider>{() => '导航栏模式'}</Divider>
502 521 {renderSidebar()}
503   -
504 522 {renderTheme()}
505   -
506 523 <Divider>{() => '界面功能'}</Divider>
507 524 {renderFeatures()}
508 525 <Divider>{() => '界面显示'}</Divider>
... ... @@ -510,32 +527,7 @@ export default defineComponent({
510 527 <Divider>{() => '切换动画'}</Divider>
511 528 {renderTransition()}
512 529 <Divider />
513   - <div class="setting-drawer__footer">
514   - <Button type="primary" block onClick={handleCopy}>
515   - {() => (
516   - <>
517   - <CopyOutlined class="mr-2" />
518   - 拷贝
519   - </>
520   - )}
521   - </Button>
522   - <Button block class="mt-2" onClick={handleResetSetting} color="warning">
523   - {() => (
524   - <>
525   - <RedoOutlined class="mr-2" />
526   - 重置
527   - </>
528   - )}
529   - </Button>
530   - <Button block class="mt-2" onClick={handleClearAndRedo} color="error">
531   - {() => (
532   - <>
533   - <RedoOutlined class="mr-2" />
534   - 清空缓存并返回登录页
535   - </>
536   - )}
537   - </Button>
538   - </div>
  530 + <FooterButton />
539 531 </>
540 532 ),
541 533 }}
... ...
src/layouts/default/setting/const.ts renamed to src/layouts/default/setting/enum.ts
1 1 import { ContentEnum, RouterTransitionEnum, ThemeEnum } from '/@/enums/appEnum';
2   -import { TopMenuAlignEnum, TriggerEnum } from '/@/enums/menuEnum';
  2 +import { MenuModeEnum, MenuTypeEnum, TopMenuAlignEnum, TriggerEnum } from '/@/enums/menuEnum';
  3 +
  4 +import mixImg from '/@/assets/images/layout/menu-mix.svg';
  5 +import sidebarImg from '/@/assets/images/layout/menu-sidebar.svg';
  6 +import menuTopImg from '/@/assets/images/layout/menu-top.svg';
3 7  
4 8 export enum HandlerEnum {
5 9 CHANGE_LAYOUT,
... ... @@ -15,6 +19,7 @@ export enum HandlerEnum {
15 19 MENU_THEME,
16 20 MENU_SPLIT,
17 21 MENU_SHOW_SEARCH,
  22 + MENU_FIXED,
18 23  
19 24 // header
20 25 HEADER_SHOW,
... ... @@ -23,7 +28,6 @@ export enum HandlerEnum {
23 28  
24 29 TABS_SHOW_QUICK,
25 30 TABS_SHOW,
26   - TABS_SHOW_ICON,
27 31  
28 32 OPEN_PAGE_LOADING,
29 33 OPEN_ROUTE_TRANSITION,
... ... @@ -36,6 +40,7 @@ export enum HandlerEnum {
36 40 GRAY_MODE,
37 41 COLOR_WEAK,
38 42 SHOW_LOGO,
  43 + SHOW_FOOTER,
39 44 }
40 45  
41 46 export const themeOptions = [
... ... @@ -102,3 +107,25 @@ export const routerTransitionOptions = [
102 107 value: item,
103 108 };
104 109 });
  110 +
  111 +export const menuTypeList = [
  112 + {
  113 + title: '左侧菜单模式',
  114 + mode: MenuModeEnum.INLINE,
  115 + type: MenuTypeEnum.SIDEBAR,
  116 + src: sidebarImg,
  117 + },
  118 + {
  119 + title: '混合模式',
  120 + mode: MenuModeEnum.INLINE,
  121 + type: MenuTypeEnum.MIX,
  122 + src: mixImg,
  123 + },
  124 +
  125 + {
  126 + title: '顶部菜单模式',
  127 + mode: MenuModeEnum.HORIZONTAL,
  128 + type: MenuTypeEnum.TOP_MENU,
  129 + src: menuTopImg,
  130 + },
  131 +];
... ...
src/layouts/default/setting/handler.ts
1   -import { HandlerEnum } from './const';
2   -// import { MenuThemeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
  1 +import { HandlerEnum } from './enum';
3 2 import {
4 3 updateColorWeak,
5 4 updateGrayMode,
... ... @@ -19,12 +18,7 @@ export function handler(event: HandlerEnum, value: any): DeepPartial&lt;ProjectConf
19 18 case HandlerEnum.CHANGE_LAYOUT:
20 19 const { mode, type, split } = value;
21 20 const splitOpt = split === undefined ? { split } : {};
22   - // let headerSetting = {};
23   - // if (type === MenuTypeEnum.TOP_MENU) {
24   - // headerSetting = {
25   - // theme: MenuThemeEnum.DARK,
26   - // };
27   - // }
  21 +
28 22 return {
29 23 menuSetting: {
30 24 mode,
... ... @@ -33,159 +27,103 @@ export function handler(event: HandlerEnum, value: any): DeepPartial&lt;ProjectConf
33 27 show: true,
34 28 ...splitOpt,
35 29 },
36   - // headerSetting,
37 30 };
38 31  
39 32 case HandlerEnum.MENU_HAS_DRAG:
40   - return {
41   - menuSetting: {
42   - hasDrag: value,
43   - },
44   - };
  33 + return { menuSetting: { hasDrag: value } };
45 34  
46 35 case HandlerEnum.MENU_ACCORDION:
47   - return {
48   - menuSetting: {
49   - accordion: value,
50   - },
51   - };
  36 + return { menuSetting: { accordion: value } };
  37 +
52 38 case HandlerEnum.MENU_TRIGGER:
53   - return {
54   - menuSetting: {
55   - trigger: value,
56   - },
57   - };
  39 + return { menuSetting: { trigger: value } };
  40 +
58 41 case HandlerEnum.MENU_TOP_ALIGN:
59   - return {
60   - menuSetting: {
61   - topMenuAlign: value,
62   - },
63   - };
  42 + return { menuSetting: { topMenuAlign: value } };
  43 +
64 44 case HandlerEnum.MENU_COLLAPSED:
65   - return {
66   - menuSetting: {
67   - collapsed: value,
68   - },
69   - };
  45 + return { menuSetting: { collapsed: value } };
  46 +
70 47 case HandlerEnum.MENU_WIDTH:
71   - return {
72   - menuSetting: {
73   - menuWidth: value,
74   - },
75   - };
  48 + return { menuSetting: { menuWidth: value } };
  49 +
76 50 case HandlerEnum.MENU_COLLAPSED_SHOW_TITLE:
77   - return {
78   - menuSetting: {
79   - collapsedShowTitle: value,
80   - },
81   - };
  51 + return { menuSetting: { collapsedShowTitle: value } };
  52 +
82 53 case HandlerEnum.MENU_SHOW_SIDEBAR:
83   - return {
84   - menuSetting: {
85   - show: value,
86   - },
87   - };
  54 + return { menuSetting: { show: value } };
  55 +
88 56 case HandlerEnum.MENU_THEME:
89 57 updateSidebarBgColor(value);
90   - return {
91   - menuBgColor: value,
92   - // menuSetting: {
93   - // theme: value,
94   - // },
95   - };
  58 + return { menuSetting: { bgColor: value } };
  59 +
96 60 case HandlerEnum.MENU_SPLIT:
97   - return {
98   - menuSetting: {
99   - split: value,
100   - },
101   - };
  61 + return { menuSetting: { split: value } };
  62 +
  63 + case HandlerEnum.MENU_FIXED:
  64 + return { menuSetting: { fixed: value } };
  65 +
102 66 case HandlerEnum.MENU_SHOW_SEARCH:
103   - return {
104   - menuSetting: {
105   - showSearch: value,
106   - },
107   - };
  67 + return { menuSetting: { showSearch: value } };
  68 +
  69 + // ============root==================
  70 +
108 71 case HandlerEnum.OPEN_PAGE_LOADING:
109   - return {
110   - openPageLoading: value,
111   - };
  72 + appStore.commitPageLoadingState(false);
  73 + return { openPageLoading: value };
  74 +
112 75 case HandlerEnum.OPEN_ROUTE_TRANSITION:
113   - return {
114   - openRouterTransition: value,
115   - };
  76 + return { openRouterTransition: value };
  77 +
116 78 case HandlerEnum.ROUTER_TRANSITION:
117   - return {
118   - routerTransition: value,
119   - };
  79 + return { routerTransition: value };
  80 +
120 81 case HandlerEnum.LOCK_TIME:
121   - return {
122   - lockTime: value,
123   - };
  82 + return { lockTime: value };
  83 +
124 84 case HandlerEnum.FULL_CONTENT:
125   - return {
126   - fullContent: value,
127   - };
  85 + return { fullContent: value };
  86 +
128 87 case HandlerEnum.CONTENT_MODE:
129   - return {
130   - contentMode: value,
131   - };
  88 + return { contentMode: value };
  89 +
132 90 case HandlerEnum.SHOW_BREADCRUMB:
133   - return {
134   - showBreadCrumb: value,
135   - };
  91 + return { showBreadCrumb: value };
  92 +
136 93 case HandlerEnum.SHOW_BREADCRUMB_ICON:
137   - return {
138   - showBreadCrumbIcon: value,
139   - };
  94 + return { showBreadCrumbIcon: value };
  95 +
140 96 case HandlerEnum.GRAY_MODE:
141 97 updateGrayMode(value);
142   - return {
143   - grayMode: value,
144   - };
  98 + return { grayMode: value };
  99 +
  100 + case HandlerEnum.SHOW_FOOTER:
  101 + return { showFooter: value };
  102 +
145 103 case HandlerEnum.COLOR_WEAK:
146 104 updateColorWeak(value);
147   - return {
148   - colorWeak: value,
149   - };
  105 + return { colorWeak: value };
  106 +
150 107 case HandlerEnum.SHOW_LOGO:
151   - return {
152   - showLogo: value,
153   - };
  108 + return { showLogo: value };
  109 +
  110 + // ============tabs==================
154 111 case HandlerEnum.TABS_SHOW_QUICK:
155   - return {
156   - multiTabsSetting: {
157   - showQuick: value,
158   - },
159   - };
160   - case HandlerEnum.TABS_SHOW_ICON:
161   - return {
162   - multiTabsSetting: {
163   - showIcon: value,
164   - },
165   - };
  112 + return { multiTabsSetting: { showQuick: value } };
  113 +
166 114 case HandlerEnum.TABS_SHOW:
167   - return {
168   - multiTabsSetting: {
169   - show: value,
170   - },
171   - };
  115 + return { multiTabsSetting: { show: value } };
  116 +
  117 + // ============header==================
172 118 case HandlerEnum.HEADER_THEME:
173 119 updateHeaderBgColor(value);
174   - return {
175   - headerBgColor: value,
176   - };
  120 + return { headerSetting: { bgColor: value } };
  121 +
177 122 case HandlerEnum.HEADER_FIXED:
178   - return {
179   - headerSetting: {
180   - fixed: value,
181   - },
182   - };
  123 + return { headerSetting: { fixed: value } };
  124 +
183 125 case HandlerEnum.HEADER_SHOW:
184   - return {
185   - headerSetting: {
186   - show: value,
187   - },
188   - };
  126 + return { headerSetting: { show: value } };
189 127 default:
190 128 return {};
191 129 }
... ...
src/layouts/default/setting/index.vue
... ... @@ -10,7 +10,7 @@
10 10 import SettingDrawer from './SettingDrawer';
11 11  
12 12 import { useDrawer } from '/@/components/Drawer';
13   - //
  13 +
14 14 export default defineComponent({
15 15 name: 'SettingBtn',
16 16 components: { SettingOutlined, SettingDrawer },
... ...
src/layouts/default/sider/index.less
1 1 @import (reference) '../../../design/index.less';
2 2  
3 3 .layout-sidebar {
4   - background-size: 100% 100%;
  4 + overflow: hidden;
  5 +
  6 + &.fixed {
  7 + position: fixed;
  8 + top: 0;
  9 + left: 0;
  10 + height: 100%;
  11 + }
5 12  
6 13 &.ant-layout-sider-dark {
7 14 background: @sider-dark-bg-color;
... ... @@ -9,6 +16,7 @@
9 16  
10 17 &:not(.ant-layout-sider-dark) {
11 18 border-right: 1px solid @border-color-light;
  19 + box-shadow: 2px 0 8px 0 rgba(29, 35, 41, 0.05);
12 20 }
13 21  
14 22 .ant-layout-sider-zero-width-trigger {
... ...
src/layouts/default/sider/LayoutSideBar.tsx renamed to src/layouts/default/sider/index.tsx
1 1 import './index.less';
2 2  
3   -import { computed, defineComponent, ref, unref } from 'vue';
  3 +import { computed, defineComponent, ref, unref, watch, nextTick } from 'vue';
4 4  
5 5 import { Layout } from 'ant-design-vue';
6   -import LayoutMenu from '/@/layouts/default/menu/LayoutMenu';
  6 +import LayoutMenu from '../menu';
7 7  
8 8 import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
9 9  
10 10 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
  11 +import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
11 12 import { useTrigger, useDragLine, useSiderEvent } from './useLayoutSider';
  13 +import { useLayoutContext } from '../useLayoutContext';
12 14  
13 15 export default defineComponent({
14 16 name: 'LayoutSideBar',
15 17 setup() {
16   - const dragBarRef = ref<Nullable<HTMLDivElement>>(null);
17   - const sideRef = ref<Nullable<HTMLDivElement>>(null);
  18 + const topRef = ref(0);
  19 + const dragBarRef = ref<ElRef>(null);
  20 + const sideRef = ref<ElRef>(null);
18 21  
19   - const { getCollapsed, getMenuWidth, getSplit, getTheme } = useMenuSetting();
  22 + const {
  23 + getCollapsed,
  24 + getMenuWidth,
  25 + getSplit,
  26 + getMenuTheme,
  27 + getRealWidth,
  28 + getMenuHidden,
  29 + getMenuFixed,
  30 + } = useMenuSetting();
  31 +
  32 + const { getShowFullHeaderRef, getUnFixedAndFull } = useHeaderSetting();
  33 +
  34 + const injectValue = useLayoutContext();
20 35  
21 36 const { getTriggerAttr, getTriggerSlot } = useTrigger();
22 37  
... ... @@ -37,11 +52,62 @@ export default defineComponent({
37 52 return unref(getSplit) ? MenuSplitTyeEnum.LEFT : MenuSplitTyeEnum.NONE;
38 53 });
39 54  
  55 + const showClassSideBarRef = computed(() => {
  56 + return unref(getSplit) ? unref(getMenuHidden) : true;
  57 + });
  58 +
  59 + const getSiderClass = computed(() => {
  60 + return {
  61 + 'layout-sidebar': true,
  62 + fixed: unref(getMenuFixed),
  63 + hidden: !unref(showClassSideBarRef),
  64 + };
  65 + });
  66 +
  67 + const getSiderStyle = computed(() => {
  68 + const top = `${unref(topRef)}px`;
  69 + if (!unref(getMenuFixed)) {
  70 + return { top };
  71 + }
  72 + return {
  73 + top,
  74 + height: `calc(100% - ${top})`,
  75 + };
  76 + });
  77 +
  78 + watch(
  79 + () => getShowFullHeaderRef.value,
  80 + () => {
  81 + topRef.value = 0;
  82 + if (unref(getUnFixedAndFull)) return;
  83 + nextTick(() => {
  84 + const fullHeaderEl = unref(injectValue.fullHeaderRef)?.$el;
  85 + if (!fullHeaderEl) return;
  86 + topRef.value = fullHeaderEl.offsetHeight;
  87 + });
  88 + },
  89 + {
  90 + immediate: true,
  91 + }
  92 + );
  93 +
  94 + const getHiddenDomStyle = computed(() => {
  95 + const width = `${unref(getRealWidth)}px`;
  96 + return {
  97 + width: width,
  98 + overflow: 'hidden',
  99 + flex: `0 0 ${width}`,
  100 + 'max-width': width,
  101 + 'min-width': width,
  102 + transition: 'all 0.2s',
  103 + };
  104 + });
  105 +
40 106 function renderDefault() {
41 107 return (
42 108 <>
43 109 <LayoutMenu
44   - theme={unref(getTheme)}
  110 + theme={unref(getMenuTheme)}
45 111 menuMode={unref(getMode)}
46 112 splitType={unref(getSplitType)}
47 113 />
... ... @@ -52,25 +118,32 @@ export default defineComponent({
52 118  
53 119 return () => {
54 120 return (
55   - <Layout.Sider
56   - ref={sideRef}
57   - class="layout-sidebar"
58   - breakpoint="md"
59   - collapsible
60   - width={unref(getMenuWidth)}
61   - collapsed={unref(getCollapsed)}
62   - collapsedWidth={unref(getCollapsedWidth)}
63   - theme={unref(getTheme)}
64   - onClick={onSiderClick}
65   - onCollapse={onCollapseChange}
66   - onBreakpoint={onBreakpointChange}
67   - {...unref(getTriggerAttr)}
68   - >
69   - {{
70   - ...unref(getTriggerSlot),
71   - default: () => renderDefault(),
72   - }}
73   - </Layout.Sider>
  121 + <>
  122 + {unref(getMenuFixed) && (
  123 + <div style={unref(getHiddenDomStyle)} class={{ hidden: !unref(showClassSideBarRef) }} />
  124 + )}
  125 +
  126 + <Layout.Sider
  127 + ref={sideRef}
  128 + breakpoint="md"
  129 + collapsible
  130 + class={unref(getSiderClass)}
  131 + style={unref(getSiderStyle)}
  132 + width={unref(getMenuWidth)}
  133 + collapsed={unref(getCollapsed)}
  134 + collapsedWidth={unref(getCollapsedWidth)}
  135 + theme={unref(getMenuTheme)}
  136 + onClick={onSiderClick}
  137 + onCollapse={onCollapseChange}
  138 + onBreakpoint={onBreakpointChange}
  139 + {...unref(getTriggerAttr)}
  140 + >
  141 + {{
  142 + ...unref(getTriggerSlot),
  143 + default: () => renderDefault(),
  144 + }}
  145 + </Layout.Sider>
  146 + </>
74 147 );
75 148 };
76 149 },
... ...
src/layouts/default/sider/useLayoutSider.tsx
... ... @@ -16,7 +16,7 @@ export function useSiderEvent() {
16 16 const brokenRef = ref(false);
17 17 const collapseRef = ref(true);
18 18  
19   - const { setMenuSetting, getCollapsed, getMiniWidthNumber, getShow } = useMenuSetting();
  19 + const { setMenuSetting, getCollapsed, getMiniWidthNumber, getShowMenu } = useMenuSetting();
20 20  
21 21 const getCollapsedWidth = computed(() => {
22 22 return unref(brokenRef) ? 0 : unref(getMiniWidthNumber);
... ... @@ -38,7 +38,7 @@ export function useSiderEvent() {
38 38  
39 39 function onSiderClick(e: ChangeEvent) {
40 40 if (!e || !e.target || e.target.className !== 'basic-menu__content') return;
41   - if (!unref(getCollapsed) || !unref(getShow)) return;
  41 + if (!unref(getCollapsed) || !unref(getShowMenu)) return;
42 42 setMenuSetting({ collapsed: false });
43 43 }
44 44 return { getCollapsedWidth, onCollapseChange, onBreakpointChange, onSiderClick };
... ...
src/layouts/default/useLayoutContext.ts 0 → 100644
  1 +import { InjectionKey, Ref } from 'vue';
  2 +import { createContext, useContext } from '/@/hooks/core/useContext';
  3 +
  4 +export interface LayoutContextProps {
  5 + fullHeaderRef: Ref<ComponentRef>;
  6 +}
  7 +
  8 +const layoutContextInjectKey: InjectionKey<LayoutContextProps> = Symbol();
  9 +
  10 +export function createLayoutContext(context: LayoutContextProps) {
  11 + return createContext<LayoutContextProps>(context, layoutContextInjectKey);
  12 +}
  13 +
  14 +export function useLayoutContext() {
  15 + return useContext<LayoutContextProps>(layoutContextInjectKey);
  16 +}
... ...
src/layouts/iframe/useFrameKeepAlive.ts
... ... @@ -12,7 +12,7 @@ import { useMultipleTabSetting } from &#39;/@/hooks/setting/useMultipleTabSetting&#39;;
12 12  
13 13 export function useFrameKeepAlive() {
14 14 const { currentRoute } = useRouter();
15   - const { getShow } = useMultipleTabSetting();
  15 + const { getShowMultipleTab } = useMultipleTabSetting();
16 16  
17 17 const getFramePages = computed(() => {
18 18 const ret =
... ... @@ -49,7 +49,7 @@ export function useFrameKeepAlive() {
49 49 }
50 50  
51 51 function hasRenderFrame(path: string) {
52   - return unref(getShow) ? unref(getOpenTabList).includes(path) : true;
  52 + return unref(getShowMultipleTab) ? unref(getOpenTabList).includes(path) : true;
53 53 }
54 54 return { hasRenderFrame, getFramePages, showIframe, getAllFramePages };
55 55 }
... ...
src/layouts/page/index.tsx
... ... @@ -20,7 +20,7 @@ interface DefaultContext {
20 20 export default defineComponent({
21 21 name: 'PageLayout',
22 22 setup() {
23   - const { getShow } = useMenuSetting();
  23 + const { getShowMenu } = useMenuSetting();
24 24 const {
25 25 getOpenKeepAlive,
26 26 getRouterTransition,
... ... @@ -32,7 +32,7 @@ export default defineComponent({
32 32  
33 33 const transitionEvent = useTransition();
34 34  
35   - const openCacheRef = computed(() => unref(getOpenKeepAlive) && unref(getShow));
  35 + const openCacheRef = computed(() => unref(getOpenKeepAlive) && unref(getShowMenu));
36 36  
37 37 const getCacheTabsRef = computed(() => tabStore.getKeepAliveTabsState as string[]);
38 38  
... ...
src/settings/projectSetting.ts
... ... @@ -21,12 +21,6 @@ const setting: ProjectConfig = {
21 21 // TODO 主题色
22 22 themeColor: primaryColor,
23 23  
24   - // header bg color
25   - headerBgColor: '#ffffff',
26   -
27   - // sidebar menu bg color
28   - menuBgColor: '#273352',
29   -
30 24 // Whether to show the configuration button
31 25 showSettingButton: true,
32 26  
... ... @@ -48,8 +42,13 @@ const setting: ProjectConfig = {
48 42 // 是否显示logo
49 43 showLogo: true,
50 44  
  45 + // 是否显示页脚
  46 + showFooter: true,
  47 +
51 48 // 头部配置
52 49 headerSetting: {
  50 + // header bg color
  51 + bgColor: '#ffffff',
53 52 fixed: true,
54 53 // 是否显示顶部
55 54 show: true,
... ... @@ -69,6 +68,10 @@ const setting: ProjectConfig = {
69 68  
70 69 // 菜单配置
71 70 menuSetting: {
  71 + // sidebar menu bg color
  72 + bgColor: '#273352',
  73 +
  74 + fixed: true,
72 75 // 菜单折叠
73 76 collapsed: false,
74 77 // 折叠菜单时候是否显示菜单名
... ... @@ -107,8 +110,7 @@ const setting: ProjectConfig = {
107 110 show: true,
108 111 // 开启快速操作
109 112 showQuick: true,
110   - // 显示icon
111   - showIcon: false,
  113 +
112 114 // 标签页缓存最大数量
113 115 max: 12,
114 116 },
... ...
src/settings/siteSetting.ts
... ... @@ -2,3 +2,5 @@
2 2 export const GITHUB_URL = 'https://github.com/anncwb/vue-vben-admin';
3 3 // vue-vben-admin-next-doc
4 4 export const DOC_URL = 'https://vvbin.cn/doc-next/';
  5 +// site url
  6 +export const SITE_URL = 'https://vvbin.cn/next/';
... ...
src/setup/App.ts
... ... @@ -53,7 +53,12 @@ export function initAppConfigStore() {
53 53 if (!projCfg) {
54 54 projCfg = projectSetting;
55 55 }
56   - const { colorWeak, grayMode, headerBgColor, menuBgColor } = projCfg;
  56 + const {
  57 + colorWeak,
  58 + grayMode,
  59 + headerSetting: { bgColor: headerBgColor },
  60 + menuSetting: { bgColor },
  61 + } = projCfg;
57 62 try {
58 63 // if (
59 64 // themeColor !== primaryColor &&
... ... @@ -63,7 +68,7 @@ export function initAppConfigStore() {
63 68 // updateTheme(themeColor);
64 69 // }
65 70 headerBgColor && updateHeaderBgColor(headerBgColor);
66   - menuBgColor && updateSidebarBgColor(menuBgColor);
  71 + bgColor && updateSidebarBgColor(bgColor);
67 72 grayMode && updateGrayMode(grayMode);
68 73 colorWeak && updateColorWeak(colorWeak);
69 74 } catch (error) {
... ...
src/types/config.d.ts
... ... @@ -4,6 +4,8 @@ import { ContentEnum, PermissionModeEnum, ThemeEnum, RouterTransitionEnum } from
4 4 import type { LocaleType } from '/@/locales/types';
5 5  
6 6 export interface MenuSetting {
  7 + bgColor: string;
  8 + fixed: boolean;
7 9 collapsed: boolean;
8 10 collapsedShowTitle: boolean;
9 11 hasDrag: boolean;
... ... @@ -26,13 +28,13 @@ export interface MultiTabsSetting {
26 28 show: boolean;
27 29 // 开启快速操作
28 30 showQuick: boolean;
29   - // 显示icon
30   - showIcon: boolean;
  31 +
31 32 // 缓存最大数量
32 33 max: number;
33 34 }
34 35  
35 36 export interface HeaderSetting {
  37 + bgColor: string;
36 38 fixed: boolean;
37 39 show: boolean;
38 40 theme: ThemeEnum;
... ... @@ -59,10 +61,7 @@ export interface LocaleSetting {
59 61  
60 62 export interface ProjectConfig {
61 63 locale: LocaleSetting;
62   - // header背景色
63   - headerBgColor: string;
64   - // 左侧菜单背景色
65   - menuBgColor: string;
  64 +
66 65 // 是否显示配置按钮
67 66 showSettingButton: boolean;
68 67 // 权限模式
... ... @@ -79,6 +78,7 @@ export interface ProjectConfig {
79 78 contentMode: ContentEnum;
80 79 // 是否显示logo
81 80 showLogo: boolean;
  81 + showFooter: boolean;
82 82 headerSetting: HeaderSetting;
83 83 // 菜单类型
84 84 // menuType: MenuTypeEnum;
... ...
src/types/global.d.ts
... ... @@ -55,3 +55,11 @@ declare type TargetContext = &#39;_self&#39; | &#39;_blank&#39;;
55 55 declare type TimeoutHandle = ReturnType<typeof setTimeout>;
56 56  
57 57 declare type IntervalHandle = ReturnType<typeof setInterval>;
  58 +
  59 +declare interface ComponentElRef<T extends HTMLElement = HTMLDivElement> {
  60 + $el: T;
  61 +}
  62 +
  63 +declare type ComponentRef<T extends HTMLElement = HTMLDivElement> = ComponentElRef<T> | null;
  64 +
  65 +declare type ElRef<T extends HTMLElement = HTMLDivElement> = Nullable<T>;
... ...
yarn.lock
... ... @@ -1737,21 +1737,6 @@
1737 1737 vscode-languageserver-textdocument "^1.0.1"
1738 1738 vscode-uri "^2.1.2"
1739 1739  
1740   -"@vueuse/core@^4.0.0-rc.3":
1741   - version "4.0.0-rc.3"
1742   - resolved "https://registry.npmjs.org/@vueuse/core/-/core-4.0.0-rc.3.tgz#5381ca657e10df596cd7027fc5c96b2d4b3a090c"
1743   - integrity sha512-dQ/FZgo0z7kBFOvDWxuzaUrmuO8X1AlQk17e3PU1TVtG2Uu+mCvjPNbuvI2fjhTjl5rzPJawwoU2WZFj+nlFvw==
1744   - dependencies:
1745   - "@vueuse/shared" "4.0.0-rc.3"
1746   - vue-demi latest
1747   -
1748   -"@vueuse/shared@4.0.0-rc.3":
1749   - version "4.0.0-rc.3"
1750   - resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-4.0.0-rc.3.tgz#42fb56fed3779f3b8a17a82c16a364bad20d01b7"
1751   - integrity sha512-VY0x/XxpeTMHp/0FDiv1cgUUxkJGQl7liiM2AjR/J7+Ys/2Y2dijD5cAKViq9FGUPQQsOcLptMvMvUsDMoN4DA==
1752   - dependencies:
1753   - vue-demi latest
1754   -
1755 1740 JSONStream@^1.0.4:
1756 1741 version "1.3.5"
1757 1742 resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0"
... ... @@ -8177,11 +8162,6 @@ vscode-uri@^2.1.2:
8177 8162 resolved "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.2.tgz#c8d40de93eb57af31f3c715dd650e2ca2c096f1c"
8178 8163 integrity sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==
8179 8164  
8180   -vue-demi@latest:
8181   - version "0.4.3"
8182   - resolved "https://registry.npmjs.org/vue-demi/-/vue-demi-0.4.3.tgz#6aaa9b52f02c32b4f9d4d11f02a1ae71031453c3"
8183   - integrity sha512-1DzLcZgHC9ZyFEYR4qZ83TdS1u9DglG8XVesBXqtbbmqFuO7sb8KG36kMfZCszieAweRDwAAVSAzjmEMG0+WwA==
8184   -
8185 8165 vue-eslint-parser@^7.1.1:
8186 8166 version "7.1.1"
8187 8167 resolved "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.1.1.tgz#c43c1c715ff50778b9a7e9a4e16921185f3425d3"
... ...