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 ## Wip 1 ## Wip
2 2
  3 +### ✨ Refactor
  4 +
  5 +- 重构整体 layout。更改代码实现方式。代码更精简
  6 +
3 ### ✨ Features 7 ### ✨ Features
4 8
5 - 缓存可以配置是否加密 9 - 缓存可以配置是否加密
@@ -7,6 +11,7 @@ @@ -7,6 +11,7 @@
7 ### 🎫 Chores 11 ### 🎫 Chores
8 12
9 - 移除 messageSetting 配置 13 - 移除 messageSetting 配置
  14 +- 暂时删除 `@vueuse/core`.等稳定后在集成。目前不太稳定。
10 15
11 ## 2.0.0-rc.11 (2020-11-18) 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,15 +154,17 @@ const globTransform = function (config: SharedConfig): Transform {
154 154
155 const groups: Array<string>[] = []; 155 const groups: Array<string>[] = [];
156 const replaceFiles = files.map((f, i) => { 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 if (isLocale) { 160 if (isLocale) {
162 const globrexRes = globrex(globPath, { extended: true, globstar: true }); 161 const globrexRes = globrex(globPath, { extended: true, globstar: true });
163 162
164 // Get segments for files like an en/system ch/modules for: 163 // Get segments for files like an en/system ch/modules for:
165 // ['en', 'system'] ['ch', 'modules'] 164 // ['en', 'system'] ['ch', 'modules']
  165 +
  166 + // TODO The window system and mac system path are inconsistent?
  167 + const fileNameWithAlias = filePath.replace(/^(\/src\/)/, '/@/');
166 const matchedGroups = globrexRes.regex.exec(fileNameWithAlias); 168 const matchedGroups = globrexRes.regex.exec(fileNameWithAlias);
167 169
168 if (matchedGroups && matchedGroups.length) { 170 if (matchedGroups && matchedGroups.length) {
package.json
@@ -22,7 +22,6 @@ @@ -22,7 +22,6 @@
22 }, 22 },
23 "dependencies": { 23 "dependencies": {
24 "@iconify/iconify": "^2.0.0-rc.2", 24 "@iconify/iconify": "^2.0.0-rc.2",
25 - "@vueuse/core": "^4.0.0-rc.3",  
26 "ant-design-vue": "2.0.0-beta.15", 25 "ant-design-vue": "2.0.0-beta.15",
27 "apexcharts": "3.22.0", 26 "apexcharts": "3.22.0",
28 "axios": "^0.21.0", 27 "axios": "^0.21.0",
src/components/Application/src/AppLogo.vue
1 <template> 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 <img src="/@/assets/images/logo.png" /> 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 </div> 9 </div>
6 </template> 10 </template>
7 <script lang="ts"> 11 <script lang="ts">
@@ -10,6 +14,7 @@ @@ -10,6 +14,7 @@
10 14
11 import { useGlobSetting } from '/@/hooks/setting'; 15 import { useGlobSetting } from '/@/hooks/setting';
12 import { useGo } from '/@/hooks/web/usePage'; 16 import { useGo } from '/@/hooks/web/usePage';
  17 + import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
13 18
14 import { PageEnum } from '/@/enums/pageEnum'; 19 import { PageEnum } from '/@/enums/pageEnum';
15 20
@@ -22,8 +27,13 @@ @@ -22,8 +27,13 @@
22 theme: { 27 theme: {
23 type: String as PropType<string>, 28 type: String as PropType<string>,
24 }, 29 },
  30 + showTitle: {
  31 + type: Boolean,
  32 + default: true,
  33 + },
25 }, 34 },
26 setup() { 35 setup() {
  36 + const { getCollapsedShowTitle } = useMenuSetting();
27 const globSetting = useGlobSetting(); 37 const globSetting = useGlobSetting();
28 const go = useGo(); 38 const go = useGo();
29 39
@@ -34,6 +44,7 @@ @@ -34,6 +44,7 @@
34 return { 44 return {
35 handleGoHome, 45 handleGoHome,
36 globSetting, 46 globSetting,
  47 + getCollapsedShowTitle,
37 }; 48 };
38 }, 49 },
39 }); 50 });
@@ -44,9 +55,13 @@ @@ -44,9 +55,13 @@
44 .app-logo { 55 .app-logo {
45 display: flex; 56 display: flex;
46 align-items: center; 57 align-items: center;
47 - padding-left: 16px; 58 + padding-left: 10px;
48 cursor: pointer; 59 cursor: pointer;
49 60
  61 + &.collapsed-show-title {
  62 + padding-left: 20px;
  63 + }
  64 +
50 &.light { 65 &.light {
51 border-bottom: 1px solid @border-color-base; 66 border-bottom: 1px solid @border-color-base;
52 } 67 }
@@ -64,6 +79,7 @@ @@ -64,6 +79,7 @@
64 font-weight: 700; 79 font-weight: 700;
65 opacity: 0; 80 opacity: 0;
66 transition: all 0.5s; 81 transition: all 0.5s;
  82 +
67 .respond-to(medium,{ 83 .respond-to(medium,{
68 opacity: 1; 84 opacity: 1;
69 }); 85 });
src/components/Drawer/src/BasicDrawer.tsx
@@ -49,7 +49,7 @@ export default defineComponent({ @@ -49,7 +49,7 @@ export default defineComponent({
49 ? `${opt.wrapClassName} ${prefixCls}__detail` 49 ? `${opt.wrapClassName} ${prefixCls}__detail`
50 : `${prefixCls}__detail`; 50 : `${prefixCls}__detail`;
51 if (!opt.getContainer) { 51 if (!opt.getContainer) {
52 - opt.getContainer = `.default-layout__main`; 52 + opt.getContainer = '.layout-content';
53 } 53 }
54 } 54 }
55 return opt; 55 return opt;
src/components/Menu/src/BasicMenu.tsx
@@ -80,7 +80,7 @@ export default defineComponent({ @@ -80,7 +80,7 @@ export default defineComponent({
80 offset += 46; 80 offset += 46;
81 } 81 }
82 return { 82 return {
83 - height: `calc(100% - ${offset - 12}px)`, 83 + height: `calc(100% - ${offset}px)`,
84 position: 'relative', 84 position: 'relative',
85 overflowY: 'auto', 85 overflowY: 'auto',
86 }; 86 };
src/components/Modal/src/BasicModal.tsx
@@ -219,11 +219,7 @@ export default defineComponent({ @@ -219,11 +219,7 @@ export default defineComponent({
219 emit('register', modalMethods, uuid); 219 emit('register', modalMethods, uuid);
220 220
221 return () => ( 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 footer: () => renderFooter(), 224 footer: () => renderFooter(),
229 closeIcon: () => renderClose(), 225 closeIcon: () => renderClose(),
src/components/Modal/src/useModal.ts
@@ -33,6 +33,7 @@ export function useModal(): UseModalReturnType { @@ -33,6 +33,7 @@ export function useModal(): UseModalReturnType {
33 33
34 modalRef.value = modalMethod; 34 modalRef.value = modalMethod;
35 } 35 }
  36 +
36 const getInstance = () => { 37 const getInstance = () => {
37 const instance = unref(modalRef); 38 const instance = unref(modalRef);
38 if (!instance) { 39 if (!instance) {
@@ -50,6 +51,7 @@ export function useModal(): UseModalReturnType { @@ -50,6 +51,7 @@ export function useModal(): UseModalReturnType {
50 getInstance().setModalProps({ 51 getInstance().setModalProps({
51 visible: visible, 52 visible: visible,
52 }); 53 });
  54 +
53 if (data) { 55 if (data) {
54 dataTransferRef[unref(uidRef)] = openOnSet 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,7 +43,6 @@ export function useTableScroll(refProps: ComputedRef&lt;BasicTableProps&gt;, tableElRe
43 const tableEl: Element = table.$el; 43 const tableEl: Element = table.$el;
44 if (!tableEl) return; 44 if (!tableEl) return;
45 const headEl = tableEl.querySelector('.ant-table-thead '); 45 const headEl = tableEl.querySelector('.ant-table-thead ');
46 - // const layoutMain: Element | null = document.querySelector('.default-layout__main ');  
47 if (!headEl) return; 46 if (!headEl) return;
48 47
49 // 表格距离底部高度 48 // 表格距离底部高度
src/design/index.less
@@ -35,7 +35,7 @@ html, @@ -35,7 +35,7 @@ html,
35 body { 35 body {
36 width: 100%; 36 width: 100%;
37 height: 100%; 37 height: 100%;
38 - overflow: hidden; 38 + overflow-x: hidden;
39 39
40 &.color-weak { 40 &.color-weak {
41 filter: invert(80%); 41 filter: invert(80%);
@@ -160,9 +160,7 @@ object { @@ -160,9 +160,7 @@ object {
160 vertical-align: baseline !important; 160 vertical-align: baseline !important;
161 } 161 }
162 162
163 -#app,  
164 -#app > div,  
165 -.ant-layout { 163 +#app {
166 width: 100%; 164 width: 100%;
167 height: 100%; 165 height: 100%;
168 } 166 }
@@ -170,8 +168,8 @@ object { @@ -170,8 +168,8 @@ object {
170 .ant-layout { 168 .ant-layout {
171 background: #f0f2f5; 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,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,20 +7,50 @@ import { appStore } from &#39;/@/store/modules/app&#39;;
7 import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting'; 7 import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
8 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; 8 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
9 import { useRootSetting } from '/@/hooks/setting/useRootSetting'; 9 import { useRootSetting } from '/@/hooks/setting/useRootSetting';
  10 +import { useFullContent } from '/@/hooks/web/useFullContent';
10 11
11 import { MenuModeEnum } from '/@/enums/menuEnum'; 12 import { MenuModeEnum } from '/@/enums/menuEnum';
12 13
13 export function useHeaderSetting() { 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 const { getShowBreadCrumb, getShowLogo } = useRootSetting(); 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 // Get header configuration 42 // Get header configuration
19 const getHeaderSetting = computed(() => appStore.getProjectConfig.headerSetting); 43 const getHeaderSetting = computed(() => appStore.getProjectConfig.headerSetting);
20 44
21 const getShowDoc = computed(() => unref(getHeaderSetting).showDoc); 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 const getShowRedo = computed(() => unref(getHeaderSetting).showRedo && unref(getShowMultipleTab)); 55 const getShowRedo = computed(() => unref(getHeaderSetting).showRedo && unref(getShowMultipleTab));
26 56
@@ -30,9 +60,11 @@ export function useHeaderSetting() { @@ -30,9 +60,11 @@ export function useHeaderSetting() {
30 60
31 const getShowNotice = computed(() => unref(getHeaderSetting).showNotice); 61 const getShowNotice = computed(() => unref(getHeaderSetting).showNotice);
32 62
  63 + const getUnFixedAndFull = computed(() => !unref(getFixed) && !unref(getShowFullHeaderRef));
  64 +
33 const getShowBread = computed(() => { 65 const getShowBread = computed(() => {
34 return ( 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,7 +87,7 @@ export function useHeaderSetting() {
55 getHeaderSetting, 87 getHeaderSetting,
56 88
57 getShowDoc, 89 getShowDoc,
58 - getTheme, 90 + getHeaderTheme,
59 getShowRedo, 91 getShowRedo,
60 getUseLockPage, 92 getUseLockPage,
61 getShowFullScreen, 93 getShowFullScreen,
@@ -63,5 +95,12 @@ export function useHeaderSetting() { @@ -63,5 +95,12 @@ export function useHeaderSetting() {
63 getShowBread, 95 getShowBread,
64 getShowContent, 96 getShowContent,
65 getShowHeaderLogo, 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,24 +11,28 @@ export function useMenuSetting() {
11 // Get menu configuration 11 // Get menu configuration
12 const getMenuSetting = computed(() => appStore.getProjectConfig.menuSetting); 12 const getMenuSetting = computed(() => appStore.getProjectConfig.menuSetting);
13 13
14 - const getMiniWidth = computed(() => unref(getMenuSetting).menuWidth);  
15 -  
16 const getCollapsed = computed(() => unref(getMenuSetting).collapsed); 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 const getMenuWidth = computed(() => unref(getMenuSetting).menuWidth); 26 const getMenuWidth = computed(() => unref(getMenuSetting).menuWidth);
25 27
26 const getTrigger = computed(() => unref(getMenuSetting).trigger); 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 const getSplit = computed(() => unref(getMenuSetting).split); 32 const getSplit = computed(() => unref(getMenuSetting).split);
31 33
  34 + const getMenuBgColor = computed(() => unref(getMenuSetting).bgColor);
  35 +
32 const getHasDrag = computed(() => unref(getMenuSetting).hasDrag); 36 const getHasDrag = computed(() => unref(getMenuSetting).hasDrag);
33 37
34 const getAccordion = computed(() => unref(getMenuSetting).accordion); 38 const getAccordion = computed(() => unref(getMenuSetting).accordion);
@@ -39,17 +43,19 @@ export function useMenuSetting() { @@ -39,17 +43,19 @@ export function useMenuSetting() {
39 43
40 const getTopMenuAlign = computed(() => unref(getMenuSetting).topMenuAlign); 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 const getShowTopMenu = computed(() => { 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 const getShowHeaderTrigger = computed(() => { 54 const getShowHeaderTrigger = computed(() => {
49 if ( 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 return false; 60 return false;
55 } 61 }
@@ -60,12 +66,16 @@ export function useMenuSetting() { @@ -60,12 +66,16 @@ export function useMenuSetting() {
60 const getShowSearch = computed(() => { 66 const getShowSearch = computed(() => {
61 return ( 67 return (
62 unref(getMenuSetting).showSearch && 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 const getIsHorizontal = computed(() => { 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 const getMiniWidthNumber = computed(() => { 81 const getMiniWidthNumber = computed(() => {
@@ -74,8 +84,8 @@ export function useMenuSetting() { @@ -74,8 +84,8 @@ export function useMenuSetting() {
74 }); 84 });
75 85
76 const getCalcContentWidth = computed(() => { 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 // Set menu configuration 91 // Set menu configuration
@@ -94,18 +104,19 @@ export function useMenuSetting() { @@ -94,18 +104,19 @@ export function useMenuSetting() {
94 104
95 toggleCollapsed, 105 toggleCollapsed,
96 106
  107 + getMenuFixed,
97 getMenuSetting, 108 getMenuSetting,
98 - getMiniWidth,  
99 - getType,  
100 - getMode,  
101 - getShow, 109 + getRealWidth,
  110 + getMenuType,
  111 + getMenuMode,
  112 + getShowMenu,
102 getCollapsed, 113 getCollapsed,
103 getMiniWidthNumber, 114 getMiniWidthNumber,
104 getCalcContentWidth, 115 getCalcContentWidth,
105 getMenuWidth, 116 getMenuWidth,
106 getTrigger, 117 getTrigger,
107 getSplit, 118 getSplit,
108 - getTheme, 119 + getMenuTheme,
109 getHasDrag, 120 getHasDrag,
110 getIsHorizontal, 121 getIsHorizontal,
111 getShowSearch, 122 getShowSearch,
@@ -116,5 +127,8 @@ export function useMenuSetting() { @@ -116,5 +127,8 @@ export function useMenuSetting() {
116 getShowTopMenu, 127 getShowTopMenu,
117 getShowHeaderTrigger, 128 getShowHeaderTrigger,
118 getTopMenuAlign, 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,7 +9,9 @@ export function useMultipleTabSetting() {
9 9
10 const getMax = computed(() => unref(getMultipleTabSetting).max); 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 function setMultipleTabSetting(multiTabsSetting: Partial<MultiTabsSetting>) { 16 function setMultipleTabSetting(multiTabsSetting: Partial<MultiTabsSetting>) {
15 appStore.commitProjectConfigState({ multiTabsSetting }); 17 appStore.commitProjectConfigState({ multiTabsSetting });
@@ -20,6 +22,7 @@ export function useMultipleTabSetting() { @@ -20,6 +22,7 @@ export function useMultipleTabSetting() {
20 22
21 getMultipleTabSetting, 23 getMultipleTabSetting,
22 getMax, 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,6 +3,7 @@ import type { ProjectConfig } from &#39;/@/types/config&#39;;
3 import { computed, unref } from 'vue'; 3 import { computed, unref } from 'vue';
4 4
5 import { appStore } from '/@/store/modules/app'; 5 import { appStore } from '/@/store/modules/app';
  6 +import { ContentEnum } from '/@/enums/appEnum';
6 7
7 type RootSetting = Omit< 8 type RootSetting = Omit<
8 ProjectConfig, 9 ProjectConfig,
@@ -13,6 +14,8 @@ export function useRootSetting() { @@ -13,6 +14,8 @@ export function useRootSetting() {
13 14
14 const getOpenPageLoading = computed(() => unref(getRootSetting).openPageLoading); 15 const getOpenPageLoading = computed(() => unref(getRootSetting).openPageLoading);
15 16
  17 + const getPageLoading = computed(() => appStore.getPageLoading);
  18 +
16 const getOpenRouterTransition = computed(() => unref(getRootSetting).openRouterTransition); 19 const getOpenRouterTransition = computed(() => unref(getRootSetting).openRouterTransition);
17 20
18 const getOpenKeepAlive = computed(() => unref(getRootSetting).openKeepAlive); 21 const getOpenKeepAlive = computed(() => unref(getRootSetting).openKeepAlive);
@@ -25,12 +28,30 @@ export function useRootSetting() { @@ -25,12 +28,30 @@ export function useRootSetting() {
25 28
26 const getShowLogo = computed(() => unref(getRootSetting).showLogo); 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 const getUseErrorHandle = computed(() => unref(getRootSetting).useErrorHandle); 37 const getUseErrorHandle = computed(() => unref(getRootSetting).useErrorHandle);
29 38
  39 + const getShowFooter = computed(() => unref(getRootSetting).showFooter);
  40 +
30 const getShowBreadCrumb = computed(() => unref(getRootSetting).showBreadCrumb); 41 const getShowBreadCrumb = computed(() => unref(getRootSetting).showBreadCrumb);
31 42
32 const getShowBreadCrumbIcon = computed(() => unref(getRootSetting).showBreadCrumbIcon); 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 function setRootSetting(setting: RootSetting) { 55 function setRootSetting(setting: RootSetting) {
35 appStore.commitProjectConfigState(setting); 56 appStore.commitProjectConfigState(setting);
36 } 57 }
@@ -38,7 +59,12 @@ export function useRootSetting() { @@ -38,7 +59,12 @@ export function useRootSetting() {
38 return { 59 return {
39 setRootSetting, 60 setRootSetting,
40 61
  62 + getFullContent,
  63 + getColorWeak,
  64 + getGrayMode,
41 getRootSetting, 65 getRootSetting,
  66 + getLayoutContentMode,
  67 + getPageLoading,
42 getOpenPageLoading, 68 getOpenPageLoading,
43 getOpenRouterTransition, 69 getOpenRouterTransition,
44 getOpenKeepAlive, 70 getOpenKeepAlive,
@@ -49,5 +75,9 @@ export function useRootSetting() { @@ -49,5 +75,9 @@ export function useRootSetting() {
49 getUseErrorHandle, 75 getUseErrorHandle,
50 getShowBreadCrumb, 76 getShowBreadCrumb,
51 getShowBreadCrumbIcon, 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 import { defineComponent, unref } from 'vue'; 3 import { defineComponent, unref } from 'vue';
4 import { 4 import {
@@ -10,6 +10,22 @@ import { @@ -10,6 +10,22 @@ import {
10 10
11 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; 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 export default defineComponent({ 29 export default defineComponent({
14 name: 'LayoutTrigger', 30 name: 'LayoutTrigger',
15 props: { 31 props: {
@@ -22,20 +38,8 @@ export default defineComponent({ @@ -22,20 +38,8 @@ export default defineComponent({
22 }, 38 },
23 }, 39 },
24 setup(props) { 40 setup(props) {
25 - const { toggleCollapsed, getCollapsed } = useMenuSetting();  
26 -  
27 return () => { 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 import './index.less'; 1 import './index.less';
2 2
  3 +import type { FunctionalComponent } from 'vue';
  4 +
3 import { defineComponent, unref, computed, ref, nextTick } from 'vue'; 5 import { defineComponent, unref, computed, ref, nextTick } from 'vue';
4 6
5 import { Layout, Tooltip, Badge } from 'ant-design-vue'; 7 import { Layout, Tooltip, Badge } from 'ant-design-vue';
6 import { AppLogo } from '/@/components/Application'; 8 import { AppLogo } from '/@/components/Application';
7 import UserDropdown from './UserDropdown'; 9 import UserDropdown from './UserDropdown';
8 -import LayoutMenu from '/@/layouts/default/menu/LayoutMenu'; 10 +import LayoutMenu from '../menu';
9 import LayoutBreadcrumb from './LayoutBreadcrumb'; 11 import LayoutBreadcrumb from './LayoutBreadcrumb';
10 -import LockAction from './LockActionItem'; 12 +import LockAction from '../lock/LockAction';
11 import LayoutTrigger from '../LayoutTrigger'; 13 import LayoutTrigger from '../LayoutTrigger';
12 import NoticeAction from './notice/NoticeActionItem.vue'; 14 import NoticeAction from './notice/NoticeActionItem.vue';
13 import { 15 import {
@@ -34,9 +36,30 @@ import { PageEnum } from &#39;/@/enums/pageEnum&#39;; @@ -34,9 +36,30 @@ import { PageEnum } from &#39;/@/enums/pageEnum&#39;;
34 import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum'; 36 import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
35 import { Component } from '/@/components/types'; 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 export default defineComponent({ 54 export default defineComponent({
38 name: 'LayoutHeader', 55 name: 'LayoutHeader',
39 - setup() { 56 + props: {
  57 + fixed: {
  58 + type: Boolean,
  59 + default: false,
  60 + },
  61 + },
  62 + setup(props) {
40 let logoEl: Element | null; 63 let logoEl: Element | null;
41 64
42 const logoWidthRef = ref(200); 65 const logoWidthRef = ref(200);
@@ -48,7 +71,7 @@ export default defineComponent({ @@ -48,7 +71,7 @@ export default defineComponent({
48 const { getUseErrorHandle, getShowBreadCrumbIcon } = useRootSetting(); 71 const { getUseErrorHandle, getShowBreadCrumbIcon } = useRootSetting();
49 72
50 const { 73 const {
51 - getTheme, 74 + getHeaderTheme,
52 getShowRedo, 75 getShowRedo,
53 getUseLockPage, 76 getUseLockPage,
54 getShowFullScreen, 77 getShowFullScreen,
@@ -69,8 +92,7 @@ export default defineComponent({ @@ -69,8 +92,7 @@ export default defineComponent({
69 let width = 0; 92 let width = 0;
70 if (!logoEl) { 93 if (!logoEl) {
71 logoEl = logoRef.value.$el; 94 logoEl = logoRef.value.$el;
72 - }  
73 - if (logoEl) { 95 + } else {
74 width += logoEl.clientWidth; 96 width += logoEl.clientWidth;
75 } 97 }
76 logoWidthRef.value = width + 80; 98 logoWidthRef.value = width + 80;
@@ -81,7 +103,7 @@ export default defineComponent({ @@ -81,7 +103,7 @@ export default defineComponent({
81 ); 103 );
82 104
83 const headerClass = computed(() => { 105 const headerClass = computed(() => {
84 - const theme = unref(getTheme); 106 + const theme = unref(getHeaderTheme);
85 return theme ? `layout-header__header--${theme}` : ''; 107 return theme ? `layout-header__header--${theme}` : '';
86 }); 108 });
87 109
@@ -99,9 +121,6 @@ export default defineComponent({ @@ -99,9 +121,6 @@ export default defineComponent({
99 }); 121 });
100 } 122 }
101 123
102 - /**  
103 - * @description: 锁定屏幕  
104 - */  
105 function handleLockPage() { 124 function handleLockPage() {
106 openModal(true); 125 openModal(true);
107 } 126 }
@@ -111,13 +130,13 @@ export default defineComponent({ @@ -111,13 +130,13 @@ export default defineComponent({
111 return ( 130 return (
112 <div class="layout-header__content "> 131 <div class="layout-header__content ">
113 {unref(getShowHeaderLogo) && ( 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 {unref(getShowContent) && ( 136 {unref(getShowContent) && (
118 <div class="layout-header__left"> 137 <div class="layout-header__left">
119 {unref(getShowHeaderTrigger) && ( 138 {unref(getShowHeaderTrigger) && (
120 - <LayoutTrigger theme={unref(getTheme)} sider={false} /> 139 + <LayoutTrigger theme={unref(getHeaderTheme)} sider={false} />
121 )} 140 )}
122 {unref(getShowBread) && <LayoutBreadcrumb showIcon={unref(getShowBreadCrumbIcon)} />} 141 {unref(getShowBread) && <LayoutBreadcrumb showIcon={unref(getShowBreadCrumbIcon)} />}
123 </div> 142 </div>
@@ -128,7 +147,7 @@ export default defineComponent({ @@ -128,7 +147,7 @@ export default defineComponent({
128 <LayoutMenu 147 <LayoutMenu
129 isHorizontal={true} 148 isHorizontal={true}
130 class={`justify-${unref(getTopMenuAlign)}`} 149 class={`justify-${unref(getTopMenuAlign)}`}
131 - theme={unref(getTheme)} 150 + theme={unref(getHeaderTheme)}
132 splitType={unref(getSplitType)} 151 splitType={unref(getSplitType)}
133 menuMode={unref(getMenuMode)} 152 menuMode={unref(getMenuMode)}
134 showSearch={false} 153 showSearch={false}
@@ -151,64 +170,47 @@ export default defineComponent({ @@ -151,64 +170,47 @@ export default defineComponent({
151 return ( 170 return (
152 <div class={`layout-header__action`}> 171 <div class={`layout-header__action`}>
153 {unref(getUseErrorHandle) && ( 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 {unref(getUseLockPage) && ( 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 {unref(getShowNotice) && ( 193 {unref(getShowNotice) && (
181 - <Tooltip>  
182 - {{  
183 - title: () => '消息通知',  
184 - default: () => <NoticeAction />,  
185 - }}  
186 - </Tooltip> 194 + <TooltipItem title="消息通知">{() => <NoticeAction />}</TooltipItem>
187 )} 195 )}
188 196
189 {unref(getShowRedo) && ( 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 {unref(getShowFullScreen) && ( 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 <UserDropdown class={`layout-header__user-dropdown`} /> 215 <UserDropdown class={`layout-header__user-dropdown`} />
214 </div> 216 </div>
@@ -227,7 +229,9 @@ export default defineComponent({ @@ -227,7 +229,9 @@ export default defineComponent({
227 229
228 return () => { 230 return () => {
229 return ( 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 {() => renderHeaderDefault()} 235 {() => renderHeaderDefault()}
232 </Layout.Header> 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 +15,31 @@ import { DOC_URL } from &#39;/@/settings/siteSetting&#39;;
15 import { openWindow } from '/@/utils'; 15 import { openWindow } from '/@/utils';
16 16
17 import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting'; 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 icon: string; 22 icon: string;
21 text: string; 23 text: string;
22 - key: string; 24 + key: MenuEvent;
23 } 25 }
24 26
25 const prefixCls = 'user-dropdown'; 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 export default defineComponent({ 43 export default defineComponent({
28 name: 'UserDropdown', 44 name: 'UserDropdown',
29 setup() { 45 setup() {
@@ -44,27 +60,17 @@ export default defineComponent({ @@ -44,27 +60,17 @@ export default defineComponent({
44 openWindow(DOC_URL); 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 function renderSlotsDefault() { 74 function renderSlotsDefault() {
69 const { realName } = unref(getUserInfo); 75 const { realName } = unref(getUserInfo);
70 return ( 76 return (
@@ -83,13 +89,9 @@ export default defineComponent({ @@ -83,13 +89,9 @@ export default defineComponent({
83 <Menu onClick={handleMenuClick}> 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 {showDoc && <Divider />} 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 </Menu> 97 </Menu>
src/layouts/default/header/index.less
@@ -10,13 +10,21 @@ @@ -10,13 +10,21 @@
10 align-items: center; 10 align-items: center;
11 justify-content: space-between; 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 &__left { 21 &__left {
14 display: flex; 22 display: flex;
15 // flex-grow: 1; 23 // flex-grow: 1;
16 align-items: center; 24 align-items: center;
17 25
18 .layout-trigger { 26 .layout-trigger {
19 - padding: 4px 10px 0 16px; 27 + padding: 1px 10px 0 16px;
20 cursor: pointer; 28 cursor: pointer;
21 29
22 .anticon { 30 .anticon {
@@ -150,6 +158,7 @@ @@ -150,6 +158,7 @@
150 } 158 }
151 159
152 &__inner, 160 &__inner,
  161 + &__inner.is-link,
153 &__separator { 162 &__separator {
154 color: @white; 163 color: @white;
155 } 164 }
src/layouts/default/index.less
1 @import (reference) '../../design/index.less'; 1 @import (reference) '../../design/index.less';
2 2
3 .default-layout { 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 import { Layout, BackTop } from 'ant-design-vue'; 4 import { Layout, BackTop } from 'ant-design-vue';
3 import LayoutHeader from './header/LayoutHeader'; 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 import SettingBtn from './setting/index.vue'; 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 import { useFullContent } from '/@/hooks/web/useFullContent'; 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 import { registerGlobComp } from '/@/components/registerGlobComp'; 23 import { registerGlobComp } from '/@/components/registerGlobComp';
16 24
17 -import './index.less';  
18 export default defineComponent({ 25 export default defineComponent({
19 name: 'DefaultLayout', 26 name: 'DefaultLayout',
20 setup() { 27 setup() {
  28 + const { currentRoute } = useRouter();
  29 + const headerRef = ref<ComponentRef>(null);
  30 +
  31 + createLayoutContext({ fullHeaderRef: headerRef });
  32 +
21 // ! Only register global components here 33 // ! Only register global components here
22 // ! Can reduce the size of the first screen code 34 // ! Can reduce the size of the first screen code
23 // default layout It is loaded after login. So it won’t be packaged to the first screen 35 // default layout It is loaded after login. So it won’t be packaged to the first screen
24 registerGlobComp(); 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 const showSideBarRef = computed(() => { 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 return () => { 71 return () => {
99 - const { useOpenBackTop, showSettingButton } = unref(getProjectConfigRef);  
100 return ( 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 <Layout> 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 </Layout> 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 import { defineComponent } from 'vue'; 3 import { defineComponent } from 'vue';
4 import { BasicModal, useModalInner } from '/@/components/Modal/index'; 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,7 +17,7 @@ import { useSplitMenu } from &#39;./useLayoutMenu&#39;;
17 import { openWindow } from '/@/utils'; 17 import { openWindow } from '/@/utils';
18 18
19 export default defineComponent({ 19 export default defineComponent({
20 - name: 'DefaultLayoutMenu', 20 + name: 'LayoutMenu',
21 props: { 21 props: {
22 theme: { 22 theme: {
23 type: String as PropType<string>, 23 type: String as PropType<string>,
@@ -50,12 +50,12 @@ export default defineComponent({ @@ -50,12 +50,12 @@ export default defineComponent({
50 const { 50 const {
51 setMenuSetting, 51 setMenuSetting,
52 getShowSearch, 52 getShowSearch,
53 - getMode,  
54 - getType, 53 + getMenuMode,
  54 + getMenuType,
55 getCollapsedShowTitle, 55 getCollapsedShowTitle,
56 getCollapsedShowSearch, 56 getCollapsedShowSearch,
57 getIsSidebarType, 57 getIsSidebarType,
58 - getTheme, 58 + getMenuTheme,
59 getCollapsed, 59 getCollapsed,
60 getAccordion, 60 getAccordion,
61 } = useMenuSetting(); 61 } = useMenuSetting();
@@ -66,9 +66,9 @@ export default defineComponent({ @@ -66,9 +66,9 @@ export default defineComponent({
66 66
67 const showLogo = computed(() => unref(getShowLogo) && unref(getIsSidebarType)); 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 const appendClass = computed(() => props.splitType === MenuSplitTyeEnum.TOP); 73 const appendClass = computed(() => props.splitType === MenuSplitTyeEnum.TOP);
74 74
@@ -111,8 +111,8 @@ export default defineComponent({ @@ -111,8 +111,8 @@ export default defineComponent({
111 return ( 111 return (
112 <AppLogo 112 <AppLogo
113 showTitle={!unref(getCollapsed)} 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,10 +124,10 @@ export default defineComponent({
124 beforeClickFn={beforeMenuClickFn} 124 beforeClickFn={beforeMenuClickFn}
125 isHorizontal={props.isHorizontal} 125 isHorizontal={props.isHorizontal}
126 appendClass={unref(appendClass)} 126 appendClass={unref(appendClass)}
127 - type={unref(getType)}  
128 - mode={unref(getMenuMode)} 127 + type={unref(getMenuType)}
  128 + mode={unref(getComputedMenuMode)}
129 collapsedShowTitle={unref(getCollapsedShowTitle)} 129 collapsedShowTitle={unref(getCollapsedShowTitle)}
130 - theme={unref(getMenuTheme)} 130 + theme={unref(getComputedMenuTheme)}
131 showLogo={unref(showLogo)} 131 showLogo={unref(showLogo)}
132 search={unref(showSearch)} 132 search={unref(showSearch)}
133 items={unref(menusRef)} 133 items={unref(menusRef)}
src/layouts/default/multitabs/TabContent.tsx
@@ -6,7 +6,6 @@ import { TabItem, tabStore } from &#39;/@/store/modules/tab&#39;; @@ -6,7 +6,6 @@ import { TabItem, tabStore } from &#39;/@/store/modules/tab&#39;;
6 import { getScaleAction, TabContentProps } from './tab.data'; 6 import { getScaleAction, TabContentProps } from './tab.data';
7 7
8 import { Dropdown } from '/@/components/Dropdown/index'; 8 import { Dropdown } from '/@/components/Dropdown/index';
9 -import Icon from '/@/components/Icon/index';  
10 import { RightOutlined } from '@ant-design/icons-vue'; 9 import { RightOutlined } from '@ant-design/icons-vue';
11 import { appStore } from '/@/store/modules/app'; 10 import { appStore } from '/@/store/modules/app';
12 11
@@ -57,18 +56,11 @@ export default defineComponent({ @@ -57,18 +56,11 @@ export default defineComponent({
57 /** 56 /**
58 * @description: 渲染图标 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 function renderTabContent() { 60 function renderTabContent() {
68 const { tabItem: { meta } = {} } = props; 61 const { tabItem: { meta } = {} } = props;
69 return ( 62 return (
70 <div class={`multiple-tabs-content__content `} onContextmenu={handleContextMenu}> 63 <div class={`multiple-tabs-content__content `} onContextmenu={handleContextMenu}>
71 - {renderIcon()}  
72 <span class="ml-1">{meta && meta.title}</span> 64 <span class="ml-1">{meta && meta.title}</span>
73 </div> 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 import { BasicDrawer } from '/@/components/Drawer/index'; 6 import { BasicDrawer } from '/@/components/Drawer/index';
3 import { Divider, Switch, Tooltip, InputNumber, Select } from 'ant-design-vue'; 7 import { Divider, Switch, Tooltip, InputNumber, Select } from 'ant-design-vue';
4 import Button from '/@/components/Button/index.vue'; 8 import Button from '/@/components/Button/index.vue';
5 -import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';  
6 import { CopyOutlined, RedoOutlined, CheckOutlined } from '@ant-design/icons-vue'; 9 import { CopyOutlined, RedoOutlined, CheckOutlined } from '@ant-design/icons-vue';
  10 +
  11 +import { MenuTypeEnum } from '/@/enums/menuEnum';
7 import { appStore } from '/@/store/modules/app'; 12 import { appStore } from '/@/store/modules/app';
8 -import { ProjectConfig } from '/@/types/config';  
9 13
10 import { useMessage } from '/@/hooks/web/useMessage'; 14 import { useMessage } from '/@/hooks/web/useMessage';
11 import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard'; 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 import { updateColorWeak, updateGrayMode } from '/@/setup/theme'; 21 import { updateColorWeak, updateGrayMode } from '/@/setup/theme';
  22 +
19 import { baseHandler } from './handler'; 23 import { baseHandler } from './handler';
  24 +
20 import { 25 import {
21 HandlerEnum, 26 HandlerEnum,
22 contentModeOptions, 27 contentModeOptions,
23 topMenuAlignOptions, 28 topMenuAlignOptions,
24 menuTriggerOptions, 29 menuTriggerOptions,
25 routerTransitionOptions, 30 routerTransitionOptions,
26 -} from './const'; 31 + menuTypeList,
  32 +} from './enum';
  33 +
27 import { HEADER_PRESET_BG_COLOR_LIST, SIDE_BAR_BG_COLOR_LIST } from '/@/settings/colorSetting'; 34 import { HEADER_PRESET_BG_COLOR_LIST, SIDE_BAR_BG_COLOR_LIST } from '/@/settings/colorSetting';
28 35
29 interface SwitchOptions { 36 interface SwitchOptions {
@@ -40,215 +47,280 @@ interface SelectConfig { @@ -40,215 +47,280 @@ interface SelectConfig {
40 handler?: Fn; 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 export default defineComponent({ 175 export default defineComponent({
49 name: 'SettingDrawer', 176 name: 'SettingDrawer',
50 setup(_, { attrs }) { 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 const getShowMenuRef = computed(() => { 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 * @description: 257 * @description:
180 */ 258 */
181 function renderFeatures() { 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 return [ 260 return [
197 renderSwitchItem('侧边菜单拖拽', { 261 renderSwitchItem('侧边菜单拖拽', {
198 handler: (e) => { 262 handler: (e) => {
199 baseHandler(HandlerEnum.MENU_HAS_DRAG, e); 263 baseHandler(HandlerEnum.MENU_HAS_DRAG, e);
200 }, 264 },
201 - def: hasDrag, 265 + def: unref(getHasDrag),
202 disabled: !unref(getShowMenuRef), 266 disabled: !unref(getShowMenuRef),
203 }), 267 }),
204 renderSwitchItem('侧边菜单搜索', { 268 renderSwitchItem('侧边菜单搜索', {
205 handler: (e) => { 269 handler: (e) => {
206 baseHandler(HandlerEnum.MENU_SHOW_SEARCH, e); 270 baseHandler(HandlerEnum.MENU_SHOW_SEARCH, e);
207 }, 271 },
208 - def: showSearch, 272 + def: unref(getShowSearch),
209 disabled: !unref(getShowMenuRef), 273 disabled: !unref(getShowMenuRef),
210 }), 274 }),
211 renderSwitchItem('侧边菜单手风琴模式', { 275 renderSwitchItem('侧边菜单手风琴模式', {
212 handler: (e) => { 276 handler: (e) => {
213 baseHandler(HandlerEnum.MENU_ACCORDION, e); 277 baseHandler(HandlerEnum.MENU_ACCORDION, e);
214 }, 278 },
215 - def: accordion, 279 + def: unref(getAccordion),
216 disabled: !unref(getShowMenuRef), 280 disabled: !unref(getShowMenuRef),
217 }), 281 }),
218 renderSwitchItem('折叠菜单', { 282 renderSwitchItem('折叠菜单', {
219 handler: (e) => { 283 handler: (e) => {
220 baseHandler(HandlerEnum.MENU_COLLAPSED, e); 284 baseHandler(HandlerEnum.MENU_COLLAPSED, e);
221 }, 285 },
222 - def: collapsed, 286 + def: unref(getCollapsed),
223 disabled: !unref(getShowMenuRef), 287 disabled: !unref(getShowMenuRef),
224 }), 288 }),
225 renderSwitchItem('折叠菜单显示名称', { 289 renderSwitchItem('折叠菜单显示名称', {
226 handler: (e) => { 290 handler: (e) => {
227 baseHandler(HandlerEnum.MENU_COLLAPSED_SHOW_TITLE, e); 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 renderSwitchItem('固定header', { 296 renderSwitchItem('固定header', {
233 handler: (e) => { 297 handler: (e) => {
234 baseHandler(HandlerEnum.HEADER_FIXED, e); 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 renderSelectItem('顶部菜单布局', { 310 renderSelectItem('顶部菜单布局', {
240 handler: (e) => { 311 handler: (e) => {
241 baseHandler(HandlerEnum.MENU_TOP_ALIGN, e); 312 baseHandler(HandlerEnum.MENU_TOP_ALIGN, e);
242 }, 313 },
243 - def: topMenuAlign, 314 + def: unref(getTopMenuAlign),
244 options: topMenuAlignOptions, 315 options: topMenuAlignOptions,
245 - disabled: !unref(getShowHeaderRef), 316 + disabled: !unref(getShowHeader) || (!unref(getIsTopMenu) && !unref(getSplit)),
246 }), 317 }),
247 renderSelectItem('菜单折叠按钮', { 318 renderSelectItem('菜单折叠按钮', {
248 handler: (e) => { 319 handler: (e) => {
249 baseHandler(HandlerEnum.MENU_TRIGGER, e); 320 baseHandler(HandlerEnum.MENU_TRIGGER, e);
250 }, 321 },
251 - def: trigger, 322 + disabled: !unref(getShowMenuRef),
  323 + def: unref(getTrigger),
252 options: menuTriggerOptions, 324 options: menuTriggerOptions,
253 }), 325 }),
254 326
@@ -256,7 +328,7 @@ export default defineComponent({ @@ -256,7 +328,7 @@ export default defineComponent({
256 handler: (e) => { 328 handler: (e) => {
257 baseHandler(HandlerEnum.CONTENT_MODE, e); 329 baseHandler(HandlerEnum.CONTENT_MODE, e);
258 }, 330 },
259 - def: contentMode, 331 + def: unref(getContentMode),
260 options: contentModeOptions, 332 options: contentModeOptions,
261 }), 333 }),
262 <div class={`setting-drawer__cell-item`}> 334 <div class={`setting-drawer__cell-item`}>
@@ -286,7 +358,7 @@ export default defineComponent({ @@ -286,7 +358,7 @@ export default defineComponent({
286 min={100} 358 min={100}
287 step={10} 359 step={10}
288 disabled={!unref(getShowMenuRef)} 360 disabled={!unref(getShowMenuRef)}
289 - defaultValue={menuWidth} 361 + defaultValue={unref(getMenuWidth)}
290 formatter={(value: string) => `${parseInt(value)}px`} 362 formatter={(value: string) => `${parseInt(value)}px`}
291 onChange={(e: any) => { 363 onChange={(e: any) => {
292 baseHandler(HandlerEnum.MENU_WIDTH, e); 364 baseHandler(HandlerEnum.MENU_WIDTH, e);
@@ -295,121 +367,112 @@ export default defineComponent({ @@ -295,121 +367,112 @@ export default defineComponent({
295 </div>, 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 function renderContent() { 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 return [ 372 return [
339 renderSwitchItem('面包屑', { 373 renderSwitchItem('面包屑', {
340 handler: (e) => { 374 handler: (e) => {
341 baseHandler(HandlerEnum.SHOW_BREADCRUMB, e); 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 renderSwitchItem('面包屑图标', { 380 renderSwitchItem('面包屑图标', {
347 handler: (e) => { 381 handler: (e) => {
348 baseHandler(HandlerEnum.SHOW_BREADCRUMB_ICON, e); 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 renderSwitchItem('标签页', { 387 renderSwitchItem('标签页', {
354 handler: (e) => { 388 handler: (e) => {
355 baseHandler(HandlerEnum.TABS_SHOW, e); 389 baseHandler(HandlerEnum.TABS_SHOW, e);
356 }, 390 },
357 - def: showMultiple, 391 + def: unref(getShowMultipleTab),
358 }), 392 }),
359 renderSwitchItem('标签页快捷按钮', { 393 renderSwitchItem('标签页快捷按钮', {
360 handler: (e) => { 394 handler: (e) => {
361 baseHandler(HandlerEnum.TABS_SHOW_QUICK, e); 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 renderSwitchItem('左侧菜单', { 401 renderSwitchItem('左侧菜单', {
374 handler: (e) => { 402 handler: (e) => {
375 baseHandler(HandlerEnum.MENU_SHOW_SIDEBAR, e); 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 renderSwitchItem('顶栏', { 408 renderSwitchItem('顶栏', {
381 handler: (e) => { 409 handler: (e) => {
382 baseHandler(HandlerEnum.HEADER_SHOW, e); 410 baseHandler(HandlerEnum.HEADER_SHOW, e);
383 }, 411 },
384 - def: showHeader, 412 + def: unref(getShowHeader),
385 }), 413 }),
386 renderSwitchItem('Logo', { 414 renderSwitchItem('Logo', {
387 handler: (e) => { 415 handler: (e) => {
388 baseHandler(HandlerEnum.SHOW_LOGO, e); 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 renderSwitchItem('全屏内容', { 426 renderSwitchItem('全屏内容', {
393 handler: (e) => { 427 handler: (e) => {
394 baseHandler(HandlerEnum.FULL_CONTENT, e); 428 baseHandler(HandlerEnum.FULL_CONTENT, e);
395 }, 429 },
396 - def: fullContent, 430 + def: unref(getFullContent),
397 }), 431 }),
398 renderSwitchItem('灰色模式', { 432 renderSwitchItem('灰色模式', {
399 handler: (e) => { 433 handler: (e) => {
400 baseHandler(HandlerEnum.GRAY_MODE, e); 434 baseHandler(HandlerEnum.GRAY_MODE, e);
401 }, 435 },
402 - def: grayMode, 436 + def: unref(getGrayMode),
403 }), 437 }),
404 renderSwitchItem('色弱模式', { 438 renderSwitchItem('色弱模式', {
405 handler: (e) => { 439 handler: (e) => {
406 baseHandler(HandlerEnum.COLOR_WEAK, e); 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 function renderSelectItem(text: string, config?: SelectConfig) { 476 function renderSelectItem(text: string, config?: SelectConfig) {
414 const { handler, def, disabled = false, options } = config || {}; 477 const { handler, def, disabled = false, options } = config || {};
415 const opt = def ? { value: def, defaultValue: def } : {}; 478 const opt = def ? { value: def, defaultValue: def } : {};
@@ -449,50 +512,6 @@ export default defineComponent({ @@ -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 return () => ( 515 return () => (
497 <BasicDrawer {...attrs} title="项目配置" width={300} wrapClassName="setting-drawer"> 516 <BasicDrawer {...attrs} title="项目配置" width={300} wrapClassName="setting-drawer">
498 {{ 517 {{
@@ -500,9 +519,7 @@ export default defineComponent({ @@ -500,9 +519,7 @@ export default defineComponent({
500 <> 519 <>
501 <Divider>{() => '导航栏模式'}</Divider> 520 <Divider>{() => '导航栏模式'}</Divider>
502 {renderSidebar()} 521 {renderSidebar()}
503 -  
504 {renderTheme()} 522 {renderTheme()}
505 -  
506 <Divider>{() => '界面功能'}</Divider> 523 <Divider>{() => '界面功能'}</Divider>
507 {renderFeatures()} 524 {renderFeatures()}
508 <Divider>{() => '界面显示'}</Divider> 525 <Divider>{() => '界面显示'}</Divider>
@@ -510,32 +527,7 @@ export default defineComponent({ @@ -510,32 +527,7 @@ export default defineComponent({
510 <Divider>{() => '切换动画'}</Divider> 527 <Divider>{() => '切换动画'}</Divider>
511 {renderTransition()} 528 {renderTransition()}
512 <Divider /> 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 import { ContentEnum, RouterTransitionEnum, ThemeEnum } from '/@/enums/appEnum'; 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 export enum HandlerEnum { 8 export enum HandlerEnum {
5 CHANGE_LAYOUT, 9 CHANGE_LAYOUT,
@@ -15,6 +19,7 @@ export enum HandlerEnum { @@ -15,6 +19,7 @@ export enum HandlerEnum {
15 MENU_THEME, 19 MENU_THEME,
16 MENU_SPLIT, 20 MENU_SPLIT,
17 MENU_SHOW_SEARCH, 21 MENU_SHOW_SEARCH,
  22 + MENU_FIXED,
18 23
19 // header 24 // header
20 HEADER_SHOW, 25 HEADER_SHOW,
@@ -23,7 +28,6 @@ export enum HandlerEnum { @@ -23,7 +28,6 @@ export enum HandlerEnum {
23 28
24 TABS_SHOW_QUICK, 29 TABS_SHOW_QUICK,
25 TABS_SHOW, 30 TABS_SHOW,
26 - TABS_SHOW_ICON,  
27 31
28 OPEN_PAGE_LOADING, 32 OPEN_PAGE_LOADING,
29 OPEN_ROUTE_TRANSITION, 33 OPEN_ROUTE_TRANSITION,
@@ -36,6 +40,7 @@ export enum HandlerEnum { @@ -36,6 +40,7 @@ export enum HandlerEnum {
36 GRAY_MODE, 40 GRAY_MODE,
37 COLOR_WEAK, 41 COLOR_WEAK,
38 SHOW_LOGO, 42 SHOW_LOGO,
  43 + SHOW_FOOTER,
39 } 44 }
40 45
41 export const themeOptions = [ 46 export const themeOptions = [
@@ -102,3 +107,25 @@ export const routerTransitionOptions = [ @@ -102,3 +107,25 @@ export const routerTransitionOptions = [
102 value: item, 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 import { 2 import {
4 updateColorWeak, 3 updateColorWeak,
5 updateGrayMode, 4 updateGrayMode,
@@ -19,12 +18,7 @@ export function handler(event: HandlerEnum, value: any): DeepPartial&lt;ProjectConf @@ -19,12 +18,7 @@ export function handler(event: HandlerEnum, value: any): DeepPartial&lt;ProjectConf
19 case HandlerEnum.CHANGE_LAYOUT: 18 case HandlerEnum.CHANGE_LAYOUT:
20 const { mode, type, split } = value; 19 const { mode, type, split } = value;
21 const splitOpt = split === undefined ? { split } : {}; 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 return { 22 return {
29 menuSetting: { 23 menuSetting: {
30 mode, 24 mode,
@@ -33,159 +27,103 @@ export function handler(event: HandlerEnum, value: any): DeepPartial&lt;ProjectConf @@ -33,159 +27,103 @@ export function handler(event: HandlerEnum, value: any): DeepPartial&lt;ProjectConf
33 show: true, 27 show: true,
34 ...splitOpt, 28 ...splitOpt,
35 }, 29 },
36 - // headerSetting,  
37 }; 30 };
38 31
39 case HandlerEnum.MENU_HAS_DRAG: 32 case HandlerEnum.MENU_HAS_DRAG:
40 - return {  
41 - menuSetting: {  
42 - hasDrag: value,  
43 - },  
44 - }; 33 + return { menuSetting: { hasDrag: value } };
45 34
46 case HandlerEnum.MENU_ACCORDION: 35 case HandlerEnum.MENU_ACCORDION:
47 - return {  
48 - menuSetting: {  
49 - accordion: value,  
50 - },  
51 - }; 36 + return { menuSetting: { accordion: value } };
  37 +
52 case HandlerEnum.MENU_TRIGGER: 38 case HandlerEnum.MENU_TRIGGER:
53 - return {  
54 - menuSetting: {  
55 - trigger: value,  
56 - },  
57 - }; 39 + return { menuSetting: { trigger: value } };
  40 +
58 case HandlerEnum.MENU_TOP_ALIGN: 41 case HandlerEnum.MENU_TOP_ALIGN:
59 - return {  
60 - menuSetting: {  
61 - topMenuAlign: value,  
62 - },  
63 - }; 42 + return { menuSetting: { topMenuAlign: value } };
  43 +
64 case HandlerEnum.MENU_COLLAPSED: 44 case HandlerEnum.MENU_COLLAPSED:
65 - return {  
66 - menuSetting: {  
67 - collapsed: value,  
68 - },  
69 - }; 45 + return { menuSetting: { collapsed: value } };
  46 +
70 case HandlerEnum.MENU_WIDTH: 47 case HandlerEnum.MENU_WIDTH:
71 - return {  
72 - menuSetting: {  
73 - menuWidth: value,  
74 - },  
75 - }; 48 + return { menuSetting: { menuWidth: value } };
  49 +
76 case HandlerEnum.MENU_COLLAPSED_SHOW_TITLE: 50 case HandlerEnum.MENU_COLLAPSED_SHOW_TITLE:
77 - return {  
78 - menuSetting: {  
79 - collapsedShowTitle: value,  
80 - },  
81 - }; 51 + return { menuSetting: { collapsedShowTitle: value } };
  52 +
82 case HandlerEnum.MENU_SHOW_SIDEBAR: 53 case HandlerEnum.MENU_SHOW_SIDEBAR:
83 - return {  
84 - menuSetting: {  
85 - show: value,  
86 - },  
87 - }; 54 + return { menuSetting: { show: value } };
  55 +
88 case HandlerEnum.MENU_THEME: 56 case HandlerEnum.MENU_THEME:
89 updateSidebarBgColor(value); 57 updateSidebarBgColor(value);
90 - return {  
91 - menuBgColor: value,  
92 - // menuSetting: {  
93 - // theme: value,  
94 - // },  
95 - }; 58 + return { menuSetting: { bgColor: value } };
  59 +
96 case HandlerEnum.MENU_SPLIT: 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 case HandlerEnum.MENU_SHOW_SEARCH: 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 case HandlerEnum.OPEN_PAGE_LOADING: 71 case HandlerEnum.OPEN_PAGE_LOADING:
109 - return {  
110 - openPageLoading: value,  
111 - }; 72 + appStore.commitPageLoadingState(false);
  73 + return { openPageLoading: value };
  74 +
112 case HandlerEnum.OPEN_ROUTE_TRANSITION: 75 case HandlerEnum.OPEN_ROUTE_TRANSITION:
113 - return {  
114 - openRouterTransition: value,  
115 - }; 76 + return { openRouterTransition: value };
  77 +
116 case HandlerEnum.ROUTER_TRANSITION: 78 case HandlerEnum.ROUTER_TRANSITION:
117 - return {  
118 - routerTransition: value,  
119 - }; 79 + return { routerTransition: value };
  80 +
120 case HandlerEnum.LOCK_TIME: 81 case HandlerEnum.LOCK_TIME:
121 - return {  
122 - lockTime: value,  
123 - }; 82 + return { lockTime: value };
  83 +
124 case HandlerEnum.FULL_CONTENT: 84 case HandlerEnum.FULL_CONTENT:
125 - return {  
126 - fullContent: value,  
127 - }; 85 + return { fullContent: value };
  86 +
128 case HandlerEnum.CONTENT_MODE: 87 case HandlerEnum.CONTENT_MODE:
129 - return {  
130 - contentMode: value,  
131 - }; 88 + return { contentMode: value };
  89 +
132 case HandlerEnum.SHOW_BREADCRUMB: 90 case HandlerEnum.SHOW_BREADCRUMB:
133 - return {  
134 - showBreadCrumb: value,  
135 - }; 91 + return { showBreadCrumb: value };
  92 +
136 case HandlerEnum.SHOW_BREADCRUMB_ICON: 93 case HandlerEnum.SHOW_BREADCRUMB_ICON:
137 - return {  
138 - showBreadCrumbIcon: value,  
139 - }; 94 + return { showBreadCrumbIcon: value };
  95 +
140 case HandlerEnum.GRAY_MODE: 96 case HandlerEnum.GRAY_MODE:
141 updateGrayMode(value); 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 case HandlerEnum.COLOR_WEAK: 103 case HandlerEnum.COLOR_WEAK:
146 updateColorWeak(value); 104 updateColorWeak(value);
147 - return {  
148 - colorWeak: value,  
149 - }; 105 + return { colorWeak: value };
  106 +
150 case HandlerEnum.SHOW_LOGO: 107 case HandlerEnum.SHOW_LOGO:
151 - return {  
152 - showLogo: value,  
153 - }; 108 + return { showLogo: value };
  109 +
  110 + // ============tabs==================
154 case HandlerEnum.TABS_SHOW_QUICK: 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 case HandlerEnum.TABS_SHOW: 114 case HandlerEnum.TABS_SHOW:
167 - return {  
168 - multiTabsSetting: {  
169 - show: value,  
170 - },  
171 - }; 115 + return { multiTabsSetting: { show: value } };
  116 +
  117 + // ============header==================
172 case HandlerEnum.HEADER_THEME: 118 case HandlerEnum.HEADER_THEME:
173 updateHeaderBgColor(value); 119 updateHeaderBgColor(value);
174 - return {  
175 - headerBgColor: value,  
176 - }; 120 + return { headerSetting: { bgColor: value } };
  121 +
177 case HandlerEnum.HEADER_FIXED: 122 case HandlerEnum.HEADER_FIXED:
178 - return {  
179 - headerSetting: {  
180 - fixed: value,  
181 - },  
182 - }; 123 + return { headerSetting: { fixed: value } };
  124 +
183 case HandlerEnum.HEADER_SHOW: 125 case HandlerEnum.HEADER_SHOW:
184 - return {  
185 - headerSetting: {  
186 - show: value,  
187 - },  
188 - }; 126 + return { headerSetting: { show: value } };
189 default: 127 default:
190 return {}; 128 return {};
191 } 129 }
src/layouts/default/setting/index.vue
@@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
10 import SettingDrawer from './SettingDrawer'; 10 import SettingDrawer from './SettingDrawer';
11 11
12 import { useDrawer } from '/@/components/Drawer'; 12 import { useDrawer } from '/@/components/Drawer';
13 - // 13 +
14 export default defineComponent({ 14 export default defineComponent({
15 name: 'SettingBtn', 15 name: 'SettingBtn',
16 components: { SettingOutlined, SettingDrawer }, 16 components: { SettingOutlined, SettingDrawer },
src/layouts/default/sider/index.less
1 @import (reference) '../../../design/index.less'; 1 @import (reference) '../../../design/index.less';
2 2
3 .layout-sidebar { 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 &.ant-layout-sider-dark { 13 &.ant-layout-sider-dark {
7 background: @sider-dark-bg-color; 14 background: @sider-dark-bg-color;
@@ -9,6 +16,7 @@ @@ -9,6 +16,7 @@
9 16
10 &:not(.ant-layout-sider-dark) { 17 &:not(.ant-layout-sider-dark) {
11 border-right: 1px solid @border-color-light; 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 .ant-layout-sider-zero-width-trigger { 22 .ant-layout-sider-zero-width-trigger {
src/layouts/default/sider/LayoutSideBar.tsx renamed to src/layouts/default/sider/index.tsx
1 import './index.less'; 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 import { Layout } from 'ant-design-vue'; 5 import { Layout } from 'ant-design-vue';
6 -import LayoutMenu from '/@/layouts/default/menu/LayoutMenu'; 6 +import LayoutMenu from '../menu';
7 7
8 import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum'; 8 import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
9 9
10 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; 10 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
  11 +import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
11 import { useTrigger, useDragLine, useSiderEvent } from './useLayoutSider'; 12 import { useTrigger, useDragLine, useSiderEvent } from './useLayoutSider';
  13 +import { useLayoutContext } from '../useLayoutContext';
12 14
13 export default defineComponent({ 15 export default defineComponent({
14 name: 'LayoutSideBar', 16 name: 'LayoutSideBar',
15 setup() { 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 const { getTriggerAttr, getTriggerSlot } = useTrigger(); 36 const { getTriggerAttr, getTriggerSlot } = useTrigger();
22 37
@@ -37,11 +52,62 @@ export default defineComponent({ @@ -37,11 +52,62 @@ export default defineComponent({
37 return unref(getSplit) ? MenuSplitTyeEnum.LEFT : MenuSplitTyeEnum.NONE; 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 function renderDefault() { 106 function renderDefault() {
41 return ( 107 return (
42 <> 108 <>
43 <LayoutMenu 109 <LayoutMenu
44 - theme={unref(getTheme)} 110 + theme={unref(getMenuTheme)}
45 menuMode={unref(getMode)} 111 menuMode={unref(getMode)}
46 splitType={unref(getSplitType)} 112 splitType={unref(getSplitType)}
47 /> 113 />
@@ -52,25 +118,32 @@ export default defineComponent({ @@ -52,25 +118,32 @@ export default defineComponent({
52 118
53 return () => { 119 return () => {
54 return ( 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,7 +16,7 @@ export function useSiderEvent() {
16 const brokenRef = ref(false); 16 const brokenRef = ref(false);
17 const collapseRef = ref(true); 17 const collapseRef = ref(true);
18 18
19 - const { setMenuSetting, getCollapsed, getMiniWidthNumber, getShow } = useMenuSetting(); 19 + const { setMenuSetting, getCollapsed, getMiniWidthNumber, getShowMenu } = useMenuSetting();
20 20
21 const getCollapsedWidth = computed(() => { 21 const getCollapsedWidth = computed(() => {
22 return unref(brokenRef) ? 0 : unref(getMiniWidthNumber); 22 return unref(brokenRef) ? 0 : unref(getMiniWidthNumber);
@@ -38,7 +38,7 @@ export function useSiderEvent() { @@ -38,7 +38,7 @@ export function useSiderEvent() {
38 38
39 function onSiderClick(e: ChangeEvent) { 39 function onSiderClick(e: ChangeEvent) {
40 if (!e || !e.target || e.target.className !== 'basic-menu__content') return; 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 setMenuSetting({ collapsed: false }); 42 setMenuSetting({ collapsed: false });
43 } 43 }
44 return { getCollapsedWidth, onCollapseChange, onBreakpointChange, onSiderClick }; 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,7 +12,7 @@ import { useMultipleTabSetting } from &#39;/@/hooks/setting/useMultipleTabSetting&#39;;
12 12
13 export function useFrameKeepAlive() { 13 export function useFrameKeepAlive() {
14 const { currentRoute } = useRouter(); 14 const { currentRoute } = useRouter();
15 - const { getShow } = useMultipleTabSetting(); 15 + const { getShowMultipleTab } = useMultipleTabSetting();
16 16
17 const getFramePages = computed(() => { 17 const getFramePages = computed(() => {
18 const ret = 18 const ret =
@@ -49,7 +49,7 @@ export function useFrameKeepAlive() { @@ -49,7 +49,7 @@ export function useFrameKeepAlive() {
49 } 49 }
50 50
51 function hasRenderFrame(path: string) { 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 return { hasRenderFrame, getFramePages, showIframe, getAllFramePages }; 54 return { hasRenderFrame, getFramePages, showIframe, getAllFramePages };
55 } 55 }
src/layouts/page/index.tsx
@@ -20,7 +20,7 @@ interface DefaultContext { @@ -20,7 +20,7 @@ interface DefaultContext {
20 export default defineComponent({ 20 export default defineComponent({
21 name: 'PageLayout', 21 name: 'PageLayout',
22 setup() { 22 setup() {
23 - const { getShow } = useMenuSetting(); 23 + const { getShowMenu } = useMenuSetting();
24 const { 24 const {
25 getOpenKeepAlive, 25 getOpenKeepAlive,
26 getRouterTransition, 26 getRouterTransition,
@@ -32,7 +32,7 @@ export default defineComponent({ @@ -32,7 +32,7 @@ export default defineComponent({
32 32
33 const transitionEvent = useTransition(); 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 const getCacheTabsRef = computed(() => tabStore.getKeepAliveTabsState as string[]); 37 const getCacheTabsRef = computed(() => tabStore.getKeepAliveTabsState as string[]);
38 38
src/settings/projectSetting.ts
@@ -21,12 +21,6 @@ const setting: ProjectConfig = { @@ -21,12 +21,6 @@ const setting: ProjectConfig = {
21 // TODO 主题色 21 // TODO 主题色
22 themeColor: primaryColor, 22 themeColor: primaryColor,
23 23
24 - // header bg color  
25 - headerBgColor: '#ffffff',  
26 -  
27 - // sidebar menu bg color  
28 - menuBgColor: '#273352',  
29 -  
30 // Whether to show the configuration button 24 // Whether to show the configuration button
31 showSettingButton: true, 25 showSettingButton: true,
32 26
@@ -48,8 +42,13 @@ const setting: ProjectConfig = { @@ -48,8 +42,13 @@ const setting: ProjectConfig = {
48 // 是否显示logo 42 // 是否显示logo
49 showLogo: true, 43 showLogo: true,
50 44
  45 + // 是否显示页脚
  46 + showFooter: true,
  47 +
51 // 头部配置 48 // 头部配置
52 headerSetting: { 49 headerSetting: {
  50 + // header bg color
  51 + bgColor: '#ffffff',
53 fixed: true, 52 fixed: true,
54 // 是否显示顶部 53 // 是否显示顶部
55 show: true, 54 show: true,
@@ -69,6 +68,10 @@ const setting: ProjectConfig = { @@ -69,6 +68,10 @@ const setting: ProjectConfig = {
69 68
70 // 菜单配置 69 // 菜单配置
71 menuSetting: { 70 menuSetting: {
  71 + // sidebar menu bg color
  72 + bgColor: '#273352',
  73 +
  74 + fixed: true,
72 // 菜单折叠 75 // 菜单折叠
73 collapsed: false, 76 collapsed: false,
74 // 折叠菜单时候是否显示菜单名 77 // 折叠菜单时候是否显示菜单名
@@ -107,8 +110,7 @@ const setting: ProjectConfig = { @@ -107,8 +110,7 @@ const setting: ProjectConfig = {
107 show: true, 110 show: true,
108 // 开启快速操作 111 // 开启快速操作
109 showQuick: true, 112 showQuick: true,
110 - // 显示icon  
111 - showIcon: false, 113 +
112 // 标签页缓存最大数量 114 // 标签页缓存最大数量
113 max: 12, 115 max: 12,
114 }, 116 },
src/settings/siteSetting.ts
@@ -2,3 +2,5 @@ @@ -2,3 +2,5 @@
2 export const GITHUB_URL = 'https://github.com/anncwb/vue-vben-admin'; 2 export const GITHUB_URL = 'https://github.com/anncwb/vue-vben-admin';
3 // vue-vben-admin-next-doc 3 // vue-vben-admin-next-doc
4 export const DOC_URL = 'https://vvbin.cn/doc-next/'; 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,7 +53,12 @@ export function initAppConfigStore() {
53 if (!projCfg) { 53 if (!projCfg) {
54 projCfg = projectSetting; 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 try { 62 try {
58 // if ( 63 // if (
59 // themeColor !== primaryColor && 64 // themeColor !== primaryColor &&
@@ -63,7 +68,7 @@ export function initAppConfigStore() { @@ -63,7 +68,7 @@ export function initAppConfigStore() {
63 // updateTheme(themeColor); 68 // updateTheme(themeColor);
64 // } 69 // }
65 headerBgColor && updateHeaderBgColor(headerBgColor); 70 headerBgColor && updateHeaderBgColor(headerBgColor);
66 - menuBgColor && updateSidebarBgColor(menuBgColor); 71 + bgColor && updateSidebarBgColor(bgColor);
67 grayMode && updateGrayMode(grayMode); 72 grayMode && updateGrayMode(grayMode);
68 colorWeak && updateColorWeak(colorWeak); 73 colorWeak && updateColorWeak(colorWeak);
69 } catch (error) { 74 } catch (error) {
src/types/config.d.ts
@@ -4,6 +4,8 @@ import { ContentEnum, PermissionModeEnum, ThemeEnum, RouterTransitionEnum } from @@ -4,6 +4,8 @@ import { ContentEnum, PermissionModeEnum, ThemeEnum, RouterTransitionEnum } from
4 import type { LocaleType } from '/@/locales/types'; 4 import type { LocaleType } from '/@/locales/types';
5 5
6 export interface MenuSetting { 6 export interface MenuSetting {
  7 + bgColor: string;
  8 + fixed: boolean;
7 collapsed: boolean; 9 collapsed: boolean;
8 collapsedShowTitle: boolean; 10 collapsedShowTitle: boolean;
9 hasDrag: boolean; 11 hasDrag: boolean;
@@ -26,13 +28,13 @@ export interface MultiTabsSetting { @@ -26,13 +28,13 @@ export interface MultiTabsSetting {
26 show: boolean; 28 show: boolean;
27 // 开启快速操作 29 // 开启快速操作
28 showQuick: boolean; 30 showQuick: boolean;
29 - // 显示icon  
30 - showIcon: boolean; 31 +
31 // 缓存最大数量 32 // 缓存最大数量
32 max: number; 33 max: number;
33 } 34 }
34 35
35 export interface HeaderSetting { 36 export interface HeaderSetting {
  37 + bgColor: string;
36 fixed: boolean; 38 fixed: boolean;
37 show: boolean; 39 show: boolean;
38 theme: ThemeEnum; 40 theme: ThemeEnum;
@@ -59,10 +61,7 @@ export interface LocaleSetting { @@ -59,10 +61,7 @@ export interface LocaleSetting {
59 61
60 export interface ProjectConfig { 62 export interface ProjectConfig {
61 locale: LocaleSetting; 63 locale: LocaleSetting;
62 - // header背景色  
63 - headerBgColor: string;  
64 - // 左侧菜单背景色  
65 - menuBgColor: string; 64 +
66 // 是否显示配置按钮 65 // 是否显示配置按钮
67 showSettingButton: boolean; 66 showSettingButton: boolean;
68 // 权限模式 67 // 权限模式
@@ -79,6 +78,7 @@ export interface ProjectConfig { @@ -79,6 +78,7 @@ export interface ProjectConfig {
79 contentMode: ContentEnum; 78 contentMode: ContentEnum;
80 // 是否显示logo 79 // 是否显示logo
81 showLogo: boolean; 80 showLogo: boolean;
  81 + showFooter: boolean;
82 headerSetting: HeaderSetting; 82 headerSetting: HeaderSetting;
83 // 菜单类型 83 // 菜单类型
84 // menuType: MenuTypeEnum; 84 // menuType: MenuTypeEnum;
src/types/global.d.ts
@@ -55,3 +55,11 @@ declare type TargetContext = &#39;_self&#39; | &#39;_blank&#39;; @@ -55,3 +55,11 @@ declare type TargetContext = &#39;_self&#39; | &#39;_blank&#39;;
55 declare type TimeoutHandle = ReturnType<typeof setTimeout>; 55 declare type TimeoutHandle = ReturnType<typeof setTimeout>;
56 56
57 declare type IntervalHandle = ReturnType<typeof setInterval>; 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,21 +1737,6 @@
1737 vscode-languageserver-textdocument "^1.0.1" 1737 vscode-languageserver-textdocument "^1.0.1"
1738 vscode-uri "^2.1.2" 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 JSONStream@^1.0.4: 1740 JSONStream@^1.0.4:
1756 version "1.3.5" 1741 version "1.3.5"
1757 resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" 1742 resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0"
@@ -8177,11 +8162,6 @@ vscode-uri@^2.1.2: @@ -8177,11 +8162,6 @@ vscode-uri@^2.1.2:
8177 resolved "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.2.tgz#c8d40de93eb57af31f3c715dd650e2ca2c096f1c" 8162 resolved "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.2.tgz#c8d40de93eb57af31f3c715dd650e2ca2c096f1c"
8178 integrity sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A== 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 vue-eslint-parser@^7.1.1: 8165 vue-eslint-parser@^7.1.1:
8186 version "7.1.1" 8166 version "7.1.1"
8187 resolved "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.1.1.tgz#c43c1c715ff50778b9a7e9a4e16921185f3425d3" 8167 resolved "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.1.1.tgz#c43c1c715ff50778b9a7e9a4e16921185f3425d3"