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