Commit 0e7c57bd5ecafd8283bcc950b24bb63b59b70e5a
1 parent
144ab577
feat(tabs): added tab folding
Showing
30 changed files
with
271 additions
and
177 deletions
CHANGELOG.zh_CN.md
mock/_createProductionServer.ts
... | ... | @@ -2,10 +2,18 @@ import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer'; |
2 | 2 | import userMock from './sys/user'; |
3 | 3 | import menuMock from './sys/menu'; |
4 | 4 | import tableDemoMock from './demo/table-demo'; |
5 | +import accountDemoMock from './demo/account'; | |
6 | +import selectDemoMock from './demo/select-demo'; | |
5 | 7 | |
6 | 8 | /** |
7 | 9 | * Used in a production environment. Need to manually import all modules |
8 | 10 | */ |
9 | 11 | export function setupProdMockServer() { |
10 | - createProdMockServer([...userMock, ...menuMock, ...tableDemoMock]); | |
12 | + createProdMockServer([ | |
13 | + ...userMock, | |
14 | + ...menuMock, | |
15 | + ...tableDemoMock, | |
16 | + ...accountDemoMock, | |
17 | + ...selectDemoMock, | |
18 | + ]); | |
11 | 19 | } | ... | ... |
src/components/Icon/src/index.vue
src/components/Menu/src/useOpenKeys.ts
... | ... | @@ -16,16 +16,20 @@ export function useOpenKeys( |
16 | 16 | mode: Ref<MenuModeEnum>, |
17 | 17 | accordion: Ref<boolean> |
18 | 18 | ) { |
19 | - const { getCollapsed, getIsMixSidebar, getMixSideFixed } = useMenuSetting(); | |
19 | + const { getCollapsed, getIsMixSidebar } = useMenuSetting(); | |
20 | 20 | |
21 | 21 | async function setOpenKeys(path: string) { |
22 | 22 | if (mode.value === MenuModeEnum.HORIZONTAL) { |
23 | 23 | return; |
24 | 24 | } |
25 | - const native = unref(getIsMixSidebar) && unref(getMixSideFixed); | |
25 | + const native = unref(getIsMixSidebar); | |
26 | 26 | useTimeoutFn( |
27 | 27 | () => { |
28 | 28 | const menuList = toRaw(menus.value); |
29 | + if (menuList?.length === 0) { | |
30 | + menuState.openKeys = []; | |
31 | + return; | |
32 | + } | |
29 | 33 | if (!unref(accordion)) { |
30 | 34 | menuState.openKeys = es6Unique([ |
31 | 35 | ...menuState.openKeys, | ... | ... |
src/components/Modal/src/BasicModal.vue
... | ... | @@ -51,7 +51,6 @@ |
51 | 51 | watchEffect, |
52 | 52 | toRef, |
53 | 53 | getCurrentInstance, |
54 | - nextTick, | |
55 | 54 | } from 'vue'; |
56 | 55 | |
57 | 56 | import Modal from './components/Modal'; |
... | ... | @@ -111,7 +110,6 @@ |
111 | 110 | visible: unref(visibleRef), |
112 | 111 | title: undefined, |
113 | 112 | }; |
114 | - | |
115 | 113 | return { |
116 | 114 | ...opt, |
117 | 115 | wrapClassName: unref(getWrapClassName), | ... | ... |
src/components/Modal/src/components/ModalWrapper.vue
1 | 1 | <template> |
2 | - <ScrollContainer ref="wrapperRef" :style="wrapStyle"> | |
2 | + <ScrollContainer ref="wrapperRef"> | |
3 | 3 | <div ref="spinRef" :style="spinStyle" v-loading="loading" :loading-tip="loadingTip"> |
4 | 4 | <slot /> |
5 | 5 | </div> |
... | ... | @@ -62,19 +62,10 @@ |
62 | 62 | redoModalHeight: setModalHeight, |
63 | 63 | }); |
64 | 64 | |
65 | - const wrapStyle = computed( | |
66 | - (): CSSProperties => { | |
67 | - return { | |
68 | - minHeight: `${props.minHeight}px`, | |
69 | - height: `${unref(realHeightRef)}px`, | |
70 | - // overflow: 'auto', | |
71 | - }; | |
72 | - } | |
73 | - ); | |
74 | - | |
75 | 65 | const spinStyle = computed( |
76 | 66 | (): CSSProperties => { |
77 | 67 | return { |
68 | + minHeight: `${props.minHeight}px`, | |
78 | 69 | // padding 28 |
79 | 70 | height: `${unref(realHeightRef) - 28}px`, |
80 | 71 | }; |
... | ... | @@ -159,7 +150,7 @@ |
159 | 150 | } |
160 | 151 | } |
161 | 152 | |
162 | - return { wrapStyle, wrapperRef, spinRef, spinStyle }; | |
153 | + return { wrapperRef, spinRef, spinStyle }; | |
163 | 154 | }, |
164 | 155 | }); |
165 | 156 | </script> | ... | ... |
src/components/Modal/src/index.less
src/components/Scrollbar/src/index.vue
... | ... | @@ -18,7 +18,8 @@ |
18 | 18 | </template> |
19 | 19 | <script lang="ts"> |
20 | 20 | import { addResizeListener, removeResizeListener } from '/@/utils/event/resizeEvent'; |
21 | - | |
21 | + import componentSetting from '/@/settings/componentSetting'; | |
22 | + const { scrollbar } = componentSetting; | |
22 | 23 | import { toObject } from './util'; |
23 | 24 | import { |
24 | 25 | defineComponent, |
... | ... | @@ -38,7 +39,7 @@ |
38 | 39 | props: { |
39 | 40 | native: { |
40 | 41 | type: Boolean, |
41 | - default: false, | |
42 | + default: scrollbar?.native ?? false, | |
42 | 43 | }, |
43 | 44 | wrapStyle: { |
44 | 45 | type: [String, Array], | ... | ... |
src/components/Table/src/components/settings/ColumnSetting.vue
src/components/Table/src/const.ts
1 | -import type { SorterResult } from './types/table'; | |
1 | +import componentSetting from '/@/settings/componentSetting'; | |
2 | + | |
3 | +const { table } = componentSetting; | |
4 | + | |
5 | +const { pageSizeOptions, defaultPageSize, fetchSetting, defaultSortFn, defaultFilterFn } = table; | |
2 | 6 | |
3 | 7 | export const ROW_KEY = 'key'; |
4 | 8 | |
5 | 9 | // 可选的每页显示条数; |
6 | -export const PAGE_SIZE_OPTIONS = ['10', '50', '80', '100']; | |
10 | +export const PAGE_SIZE_OPTIONS = pageSizeOptions; | |
7 | 11 | |
8 | 12 | // 每页显示条数 |
9 | -export const PAGE_SIZE = ~~PAGE_SIZE_OPTIONS[0]; | |
13 | +export const PAGE_SIZE = defaultPageSize; | |
10 | 14 | |
11 | 15 | // 通用接口字段设置 |
12 | -// 支持 xxx.xxx.xxx格式 | |
13 | -export const FETCH_SETTING = { | |
14 | - // 传给后台的当前页字段名 | |
15 | - pageField: 'page', | |
16 | - // 传给后台的每页显示记录数字段名 | |
17 | - sizeField: 'pageSize', | |
18 | - // 接口返回的表格数据字段名 | |
19 | - listField: 'items', | |
20 | - // 接口返回的表格总数字段名 | |
21 | - totalField: 'total', | |
22 | -}; | |
16 | +export const FETCH_SETTING = fetchSetting; | |
23 | 17 | |
24 | 18 | // 配置通用排序函数 |
25 | -export function DEFAULT_SORT_FN(sortInfo: SorterResult) { | |
26 | - const { field, order } = sortInfo; | |
27 | - return { | |
28 | - // 传给后台的排序字段你 | |
29 | - field, | |
30 | - // 传给后台的排序方式 asc/desc | |
31 | - order, | |
32 | - }; | |
33 | -} | |
34 | - | |
35 | -export function DEFAULT_FILTER_FN(data: Partial<Recordable<string[]>>) { | |
36 | - return data; | |
37 | -} | |
19 | +export const DEFAULT_SORT_FN = defaultSortFn; | |
20 | + | |
21 | +export const DEFAULT_FILTER_FN = defaultFilterFn; | |
38 | 22 | |
39 | 23 | // 表格单元格默认布局 |
40 | 24 | export const DEFAULT_ALIGN = 'center'; | ... | ... |
src/hooks/setting/useMenuSetting.ts
... | ... | @@ -78,7 +78,9 @@ const getIsMixMode = computed(() => { |
78 | 78 | }); |
79 | 79 | |
80 | 80 | const getRealWidth = computed(() => { |
81 | - return unref(getCollapsed) ? unref(getMiniWidthNumber) : unref(getMenuWidth); | |
81 | + return unref(getCollapsed) && !unref(getMixSideFixed) | |
82 | + ? unref(getMiniWidthNumber) | |
83 | + : unref(getMenuWidth); | |
82 | 84 | }); |
83 | 85 | |
84 | 86 | const getMiniWidthNumber = computed(() => { |
... | ... | @@ -94,7 +96,6 @@ const getCalcContentWidth = computed(() => { |
94 | 96 | ? SIDE_BAR_SHOW_TIT_MINI_WIDTH + |
95 | 97 | (unref(getMixSideFixed) && unref(mixSideHasChildren) ? unref(getRealWidth) : 0) |
96 | 98 | : unref(getRealWidth); |
97 | - | |
98 | 99 | return `calc(100% - ${unref(width)}px)`; |
99 | 100 | }); |
100 | 101 | ... | ... |
src/hooks/setting/useMultipleTabSetting.ts
... | ... | @@ -12,6 +12,8 @@ const getShowQuick = computed(() => unref(getMultipleTabSetting).showQuick); |
12 | 12 | |
13 | 13 | const getShowRedo = computed(() => unref(getMultipleTabSetting).showRedo); |
14 | 14 | |
15 | +const getShowFold = computed(() => unref(getMultipleTabSetting).showFold); | |
16 | + | |
15 | 17 | function setMultipleTabSetting(multiTabsSetting: Partial<MultiTabsSetting>) { |
16 | 18 | appStore.commitProjectConfigState({ multiTabsSetting }); |
17 | 19 | } |
... | ... | @@ -24,5 +26,6 @@ export function useMultipleTabSetting() { |
24 | 26 | getShowMultipleTab, |
25 | 27 | getShowQuick, |
26 | 28 | getShowRedo, |
29 | + getShowFold, | |
27 | 30 | }; |
28 | 31 | } | ... | ... |
src/hooks/web/useTabs.ts
1 | 1 | import { tabStore } from '/@/store/modules/tab'; |
2 | 2 | import { appStore } from '/@/store/modules/app'; |
3 | +import type { RouteLocationNormalized } from 'vue-router'; | |
3 | 4 | |
4 | 5 | export function useTabs() { |
5 | 6 | function canIUseFn(): boolean { |
... | ... | @@ -21,5 +22,7 @@ export function useTabs() { |
21 | 22 | closeRight: () => canIUseFn() && tabStore.closeRightTabAction(tabStore.getCurrentTab), |
22 | 23 | closeOther: () => canIUseFn() && tabStore.closeOtherTabAction(tabStore.getCurrentTab), |
23 | 24 | closeCurrent: () => canIUseFn() && tabStore.closeTabAction(tabStore.getCurrentTab), |
25 | + close: (tab?: RouteLocationNormalized) => | |
26 | + canIUseFn() && tabStore.closeTabAction(tab || tabStore.getCurrentTab), | |
24 | 27 | }; |
25 | 28 | } | ... | ... |
src/layouts/default/setting/SettingDrawer.tsx
... | ... | @@ -85,7 +85,7 @@ export default defineComponent({ |
85 | 85 | getShowSearch, |
86 | 86 | } = useHeaderSetting(); |
87 | 87 | |
88 | - const { getShowMultipleTab, getShowQuick, getShowRedo } = useMultipleTabSetting(); | |
88 | + const { getShowMultipleTab, getShowQuick, getShowRedo, getShowFold } = useMultipleTabSetting(); | |
89 | 89 | |
90 | 90 | const getShowMenuRef = computed(() => { |
91 | 91 | return unref(getShowMenu) && !unref(getIsHorizontal); |
... | ... | @@ -105,33 +105,6 @@ export default defineComponent({ |
105 | 105 | }} |
106 | 106 | def={unref(getMenuType)} |
107 | 107 | /> |
108 | - <SwitchItem | |
109 | - title={t('layout.setting.splitMenu')} | |
110 | - event={HandlerEnum.MENU_SPLIT} | |
111 | - def={unref(getSplit)} | |
112 | - disabled={!unref(getShowMenuRef) || unref(getMenuType) !== MenuTypeEnum.MIX} | |
113 | - /> | |
114 | - <SwitchItem | |
115 | - title={t('layout.setting.mixSidebarFixed')} | |
116 | - event={HandlerEnum.MENU_FIXED_MIX_SIDEBAR} | |
117 | - def={unref(getMixSideFixed)} | |
118 | - disabled={!unref(getIsMixSidebar)} | |
119 | - /> | |
120 | - | |
121 | - <SwitchItem | |
122 | - title={t('layout.setting.closeMixSidebarOnChange')} | |
123 | - event={HandlerEnum.MENU_CLOSE_MIX_SIDEBAR_ON_CHANGE} | |
124 | - def={unref(getCloseMixSidebarOnChange)} | |
125 | - disabled={!unref(getIsMixSidebar)} | |
126 | - /> | |
127 | - | |
128 | - <SelectItem | |
129 | - title={t('layout.setting.mixSidebarTrigger')} | |
130 | - event={HandlerEnum.MENU_TRIGGER_MIX_SIDEBAR} | |
131 | - def={unref(getMixSideTrigger)} | |
132 | - options={mixSidebarTriggerOptions} | |
133 | - disabled={!unref(getIsMixSidebar)} | |
134 | - /> | |
135 | 108 | </> |
136 | 109 | ); |
137 | 110 | } |
... | ... | @@ -171,6 +144,32 @@ export default defineComponent({ |
171 | 144 | return ( |
172 | 145 | <> |
173 | 146 | <SwitchItem |
147 | + title={t('layout.setting.splitMenu')} | |
148 | + event={HandlerEnum.MENU_SPLIT} | |
149 | + def={unref(getSplit)} | |
150 | + disabled={!unref(getShowMenuRef) || unref(getMenuType) !== MenuTypeEnum.MIX} | |
151 | + /> | |
152 | + <SwitchItem | |
153 | + title={t('layout.setting.mixSidebarFixed')} | |
154 | + event={HandlerEnum.MENU_FIXED_MIX_SIDEBAR} | |
155 | + def={unref(getMixSideFixed)} | |
156 | + disabled={!unref(getIsMixSidebar)} | |
157 | + /> | |
158 | + | |
159 | + <SwitchItem | |
160 | + title={t('layout.setting.closeMixSidebarOnChange')} | |
161 | + event={HandlerEnum.MENU_CLOSE_MIX_SIDEBAR_ON_CHANGE} | |
162 | + def={unref(getCloseMixSidebarOnChange)} | |
163 | + disabled={!unref(getIsMixSidebar)} | |
164 | + /> | |
165 | + <SwitchItem | |
166 | + title={t('layout.setting.menuCollapse')} | |
167 | + event={HandlerEnum.MENU_COLLAPSED} | |
168 | + def={unref(getCollapsed)} | |
169 | + disabled={!unref(getShowMenuRef)} | |
170 | + /> | |
171 | + | |
172 | + <SwitchItem | |
174 | 173 | title={t('layout.setting.menuDrag')} |
175 | 174 | event={HandlerEnum.MENU_HAS_DRAG} |
176 | 175 | def={unref(getCanDrag)} |
... | ... | @@ -188,17 +187,12 @@ export default defineComponent({ |
188 | 187 | def={unref(getAccordion)} |
189 | 188 | disabled={!unref(getShowMenuRef)} |
190 | 189 | /> |
191 | - <SwitchItem | |
192 | - title={t('layout.setting.menuCollapse')} | |
193 | - event={HandlerEnum.MENU_COLLAPSED} | |
194 | - def={unref(getCollapsed)} | |
195 | - disabled={!unref(getShowMenuRef) || unref(getIsMixSidebar)} | |
196 | - /> | |
190 | + | |
197 | 191 | <SwitchItem |
198 | 192 | title={t('layout.setting.collapseMenuDisplayName')} |
199 | 193 | event={HandlerEnum.MENU_COLLAPSED_SHOW_TITLE} |
200 | 194 | def={unref(getCollapsedShowTitle)} |
201 | - disabled={!unref(getShowMenuRef) || !unref(getCollapsed)} | |
195 | + disabled={!unref(getShowMenuRef) || !unref(getCollapsed) || unref(getIsMixSidebar)} | |
202 | 196 | /> |
203 | 197 | |
204 | 198 | <SwitchItem |
... | ... | @@ -214,6 +208,13 @@ export default defineComponent({ |
214 | 208 | disabled={!unref(getShowMenuRef) || unref(getIsMixSidebar)} |
215 | 209 | /> |
216 | 210 | <SelectItem |
211 | + title={t('layout.setting.mixSidebarTrigger')} | |
212 | + event={HandlerEnum.MENU_TRIGGER_MIX_SIDEBAR} | |
213 | + def={unref(getMixSideTrigger)} | |
214 | + options={mixSidebarTriggerOptions} | |
215 | + disabled={!unref(getIsMixSidebar)} | |
216 | + /> | |
217 | + <SelectItem | |
217 | 218 | title={t('layout.setting.topMenuLayout')} |
218 | 219 | event={HandlerEnum.MENU_TOP_ALIGN} |
219 | 220 | def={unref(getTopMenuAlign)} |
... | ... | @@ -299,6 +300,12 @@ export default defineComponent({ |
299 | 300 | def={unref(getShowQuick)} |
300 | 301 | disabled={!unref(getShowMultipleTab)} |
301 | 302 | /> |
303 | + <SwitchItem | |
304 | + title={t('layout.setting.tabsFoldBtn')} | |
305 | + event={HandlerEnum.TABS_SHOW_FOLD} | |
306 | + def={unref(getShowFold)} | |
307 | + disabled={!unref(getShowMultipleTab)} | |
308 | + /> | |
302 | 309 | |
303 | 310 | <SwitchItem |
304 | 311 | title={t('layout.setting.sidebar')} | ... | ... |
src/layouts/default/setting/enum.ts
src/layouts/default/setting/handler.ts
... | ... | @@ -71,7 +71,7 @@ export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConf |
71 | 71 | return { menuSetting: { mixSideTrigger: value } }; |
72 | 72 | |
73 | 73 | case HandlerEnum.MENU_FIXED_MIX_SIDEBAR: |
74 | - return { menuSetting: { mixSideTrigger: value } }; | |
74 | + return { menuSetting: { mixSideFixed: value } }; | |
75 | 75 | |
76 | 76 | // ============transition================== |
77 | 77 | case HandlerEnum.OPEN_PAGE_LOADING: |
... | ... | @@ -123,9 +123,13 @@ export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConf |
123 | 123 | |
124 | 124 | case HandlerEnum.TABS_SHOW: |
125 | 125 | return { multiTabsSetting: { show: value } }; |
126 | + | |
126 | 127 | case HandlerEnum.TABS_SHOW_REDO: |
127 | 128 | return { multiTabsSetting: { showRedo: value } }; |
128 | 129 | |
130 | + case HandlerEnum.TABS_SHOW_FOLD: | |
131 | + return { multiTabsSetting: { showFold: value } }; | |
132 | + | |
129 | 133 | // ============header================== |
130 | 134 | case HandlerEnum.HEADER_THEME: |
131 | 135 | updateHeaderBgColor(value); | ... | ... |
src/layouts/default/sider/MixSider.vue
... | ... | @@ -3,11 +3,13 @@ |
3 | 3 | |
4 | 4 | <div |
5 | 5 | v-click-outside="handleClickOutside" |
6 | + :style="getWrapStyle" | |
6 | 7 | :class="[ |
7 | 8 | prefixCls, |
8 | 9 | getMenuTheme, |
9 | 10 | { |
10 | 11 | open: openMenu, |
12 | + mini: getCollapsed, | |
11 | 13 | }, |
12 | 14 | ]" |
13 | 15 | v-bind="getMenuEvents" |
... | ... | @@ -29,7 +31,7 @@ |
29 | 31 | <MenuTag :item="item" :showTitle="false" :isHorizontal="false" /> |
30 | 32 | <Icon |
31 | 33 | :class="`${prefixCls}-module__icon`" |
32 | - :size="22" | |
34 | + :size="getCollapsed ? 16 : 20" | |
33 | 35 | :icon="item.meta && item.meta.icon" |
34 | 36 | /> |
35 | 37 | <p :class="`${prefixCls}-module__name`">{{ t(item.name) }}</p> |
... | ... | @@ -50,12 +52,10 @@ |
50 | 52 | <span class="text"> {{ title }}</span> |
51 | 53 | <Icon |
52 | 54 | :size="16" |
53 | - v-if="getMixSideFixed" | |
54 | - icon="ri:pushpin-2-fill" | |
55 | + :icon="getMixSideFixed ? 'ri:pushpin-2-fill' : 'ri:pushpin-2-line'" | |
55 | 56 | class="pushpin" |
56 | 57 | @click="handleFixedMenu" |
57 | 58 | /> |
58 | - <Icon :size="16" v-else icon="ri:pushpin-2-line" class="pushpin" @click="handleFixedMenu" /> | |
59 | 59 | </div> |
60 | 60 | <ScrollContainer :class="`${prefixCls}-menu-list__content`"> |
61 | 61 | <BasicMenu |
... | ... | @@ -92,7 +92,7 @@ |
92 | 92 | import { useDragLine } from './useLayoutSider'; |
93 | 93 | import { useGlobSetting } from '/@/hooks/setting'; |
94 | 94 | |
95 | - import { SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum'; | |
95 | + import { SIDE_BAR_SHOW_TIT_MINI_WIDTH, SIDE_BAR_MINI_WIDTH } from '/@/enums/appEnum'; | |
96 | 96 | |
97 | 97 | import clickOutside from '/@/directives/clickOutside'; |
98 | 98 | |
... | ... | @@ -130,6 +130,8 @@ |
130 | 130 | getMixSideFixed, |
131 | 131 | mixSideHasChildren, |
132 | 132 | setMenuSetting, |
133 | + getIsMixSidebar, | |
134 | + getCollapsed, | |
133 | 135 | } = useMenuSetting(); |
134 | 136 | |
135 | 137 | const { title } = useGlobSetting(); |
... | ... | @@ -140,6 +142,7 @@ |
140 | 142 | (): CSSProperties => { |
141 | 143 | return { |
142 | 144 | width: unref(openMenu) ? `${unref(getMenuWidth)}px` : 0, |
145 | + left: `${unref(getMixSideWidth)}px`, | |
143 | 146 | }; |
144 | 147 | } |
145 | 148 | ); |
... | ... | @@ -153,32 +156,33 @@ |
153 | 156 | return isFixed; |
154 | 157 | }); |
155 | 158 | |
159 | + const getMixSideWidth = computed(() => { | |
160 | + return unref(getCollapsed) ? SIDE_BAR_MINI_WIDTH : SIDE_BAR_SHOW_TIT_MINI_WIDTH; | |
161 | + }); | |
162 | + | |
156 | 163 | const getDomStyle = computed( |
157 | 164 | (): CSSProperties => { |
158 | 165 | const fixedWidth = unref(getIsFixed) ? unref(getRealWidth) : 0; |
159 | - const width = `${SIDE_BAR_SHOW_TIT_MINI_WIDTH + fixedWidth}px`; | |
160 | - return { | |
161 | - width, | |
162 | - maxWidth: width, | |
163 | - minWidth: width, | |
164 | - flex: `0 0 ${width}`, | |
165 | - }; | |
166 | + const width = `${unref(getMixSideWidth) + fixedWidth}px`; | |
167 | + return getWrapCommonStyle(width); | |
168 | + } | |
169 | + ); | |
170 | + | |
171 | + const getWrapStyle = computed( | |
172 | + (): CSSProperties => { | |
173 | + const width = `${unref(getMixSideWidth)}px`; | |
174 | + return getWrapCommonStyle(width); | |
166 | 175 | } |
167 | 176 | ); |
168 | 177 | |
169 | 178 | const getMenuEvents = computed(() => { |
170 | - // return unref(getMixSideTrigger) === 'hover' | |
171 | - // ? { | |
172 | - // onMouseleave: () => { | |
173 | - // closeMenu(); | |
174 | - // }, | |
175 | - // } | |
176 | - // : {}; | |
177 | - return { | |
178 | - onMouseleave: () => { | |
179 | - closeMenu(); | |
180 | - }, | |
181 | - }; | |
179 | + return !unref(getMixSideFixed) | |
180 | + ? { | |
181 | + onMouseleave: () => { | |
182 | + closeMenu(); | |
183 | + }, | |
184 | + } | |
185 | + : {}; | |
182 | 186 | }); |
183 | 187 | |
184 | 188 | const getShowDragBar = computed(() => unref(getCanDrag)); |
... | ... | @@ -195,6 +199,16 @@ |
195 | 199 | } |
196 | 200 | }); |
197 | 201 | |
202 | + function getWrapCommonStyle(width: string): CSSProperties { | |
203 | + return { | |
204 | + width, | |
205 | + maxWidth: width, | |
206 | + minWidth: width, | |
207 | + flex: `0 0 ${width}`, | |
208 | + }; | |
209 | + } | |
210 | + | |
211 | + // Process module menu click | |
198 | 212 | async function hanldeModuleClick(path: string, hover = false) { |
199 | 213 | const children = await getChildrenMenus(path); |
200 | 214 | |
... | ... | @@ -223,20 +237,24 @@ |
223 | 237 | chilrenMenus.value = children; |
224 | 238 | } |
225 | 239 | |
240 | + // Set the currently active menu and submenu | |
226 | 241 | async function setActive(setChildren = false) { |
227 | 242 | const path = currentRoute.value?.path; |
228 | 243 | if (!path) return; |
229 | 244 | const parentPath = await getCurrentParentPath(path); |
230 | 245 | activePath.value = parentPath; |
231 | 246 | // hanldeModuleClick(parentPath); |
232 | - if (unref(getMixSideFixed)) { | |
247 | + if (unref(getIsMixSidebar)) { | |
233 | 248 | const activeMenu = unref(menuModules).find((item) => item.path === unref(activePath)); |
234 | 249 | const p = activeMenu?.path; |
235 | 250 | if (p) { |
236 | 251 | const children = await getChildrenMenus(p); |
237 | 252 | if (setChildren) { |
238 | 253 | chilrenMenus.value = children; |
239 | - openMenu.value = children.length > 0; | |
254 | + | |
255 | + if (unref(getMixSideFixed)) { | |
256 | + openMenu.value = children.length > 0; | |
257 | + } | |
240 | 258 | } |
241 | 259 | if (children.length === 0) { |
242 | 260 | chilrenMenus.value = []; |
... | ... | @@ -271,6 +289,7 @@ |
271 | 289 | }); |
272 | 290 | } |
273 | 291 | |
292 | + // Close menu | |
274 | 293 | function closeMenu() { |
275 | 294 | if (!unref(getIsFixed)) { |
276 | 295 | openMenu.value = false; |
... | ... | @@ -298,6 +317,8 @@ |
298 | 317 | getDomStyle, |
299 | 318 | handleFixedMenu, |
300 | 319 | getMixSideFixed, |
320 | + getWrapStyle, | |
321 | + getCollapsed, | |
301 | 322 | }; |
302 | 323 | }, |
303 | 324 | }); |
... | ... | @@ -312,14 +333,10 @@ |
312 | 333 | top: 0; |
313 | 334 | left: 0; |
314 | 335 | z-index: @layout-mix-sider-fixed-z-index; |
315 | - width: @width; | |
316 | 336 | height: 100%; |
317 | - max-width: @width; | |
318 | - min-width: @width; | |
319 | 337 | overflow: hidden; |
320 | 338 | background: @sider-dark-bg-color; |
321 | - transition: all 0.3s ease 0s; | |
322 | - flex: 0 0 @width; | |
339 | + transition: all 0.2s ease 0s; | |
323 | 340 | .@{tag-prefix-cls} { |
324 | 341 | position: absolute; |
325 | 342 | top: 6px; |
... | ... | @@ -327,13 +344,9 @@ |
327 | 344 | } |
328 | 345 | |
329 | 346 | &-dom { |
330 | - width: @width; | |
331 | 347 | height: 100%; |
332 | - max-width: @width; | |
333 | - min-width: @width; | |
334 | 348 | overflow: hidden; |
335 | 349 | transition: all 0.2s ease 0s; |
336 | - flex: 0 0 @width; | |
337 | 350 | } |
338 | 351 | |
339 | 352 | &-logo { |
... | ... | @@ -354,7 +367,7 @@ |
354 | 367 | } |
355 | 368 | |
356 | 369 | &.open { |
357 | - > .scroll-container { | |
370 | + > .scrollbar { | |
358 | 371 | border-right: 1px solid rgb(238, 238, 238); |
359 | 372 | } |
360 | 373 | } |
... | ... | @@ -390,7 +403,7 @@ |
390 | 403 | border-bottom: 1px solid @border-color; |
391 | 404 | } |
392 | 405 | |
393 | - > .scroll-container { | |
406 | + > .scrollbar { | |
394 | 407 | border-right: 1px solid @border-color; |
395 | 408 | } |
396 | 409 | } |
... | ... | @@ -409,6 +422,16 @@ |
409 | 422 | height: calc(100% - @header-height) !important; |
410 | 423 | } |
411 | 424 | |
425 | + &.mini &-module { | |
426 | + &__name { | |
427 | + display: none; | |
428 | + } | |
429 | + | |
430 | + &__icon { | |
431 | + margin-bottom: 0; | |
432 | + } | |
433 | + } | |
434 | + | |
412 | 435 | &-module { |
413 | 436 | position: relative; |
414 | 437 | padding-top: 1px; |
... | ... | @@ -456,7 +479,6 @@ |
456 | 479 | &-menu-list { |
457 | 480 | position: fixed; |
458 | 481 | top: 0; |
459 | - left: 80px; | |
460 | 482 | width: 0; |
461 | 483 | width: 200px; |
462 | 484 | height: calc(100%); | ... | ... |
src/layouts/default/tabs/components/FoldButton.vue
0 → 100644
1 | +<template> | |
2 | + <span :class="`${prefixCls}__extra-fold`" @click="handleFold"> | |
3 | + <Icon :icon="getIcon" /> | |
4 | + </span> | |
5 | +</template> | |
6 | +<script lang="ts"> | |
7 | + import { defineComponent, unref, computed } from 'vue'; | |
8 | + import { RedoOutlined } from '@ant-design/icons-vue'; | |
9 | + import { useDesign } from '/@/hooks/web/useDesign'; | |
10 | + import { Tooltip } from 'ant-design-vue'; | |
11 | + import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting'; | |
12 | + import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; | |
13 | + | |
14 | + import Icon from '/@/components/Icon'; | |
15 | + | |
16 | + export default defineComponent({ | |
17 | + name: 'FoldButton', | |
18 | + components: { RedoOutlined, Tooltip, Icon }, | |
19 | + | |
20 | + setup() { | |
21 | + const { prefixCls } = useDesign('multiple-tabs-content'); | |
22 | + const { getShowMenu, setMenuSetting } = useMenuSetting(); | |
23 | + const { getShowHeader, setHeaderSetting } = useHeaderSetting(); | |
24 | + | |
25 | + const getIsUnFold = computed(() => { | |
26 | + return !unref(getShowMenu) && !unref(getShowHeader); | |
27 | + }); | |
28 | + | |
29 | + const getIcon = computed(() => { | |
30 | + return unref(getIsUnFold) ? 'codicon:screen-normal' : 'codicon:screen-full'; | |
31 | + }); | |
32 | + | |
33 | + function handleFold() { | |
34 | + const isScale = !unref(getShowMenu) && !unref(getShowHeader); | |
35 | + setMenuSetting({ | |
36 | + show: isScale, | |
37 | + hidden: !isScale, | |
38 | + }); | |
39 | + setHeaderSetting({ | |
40 | + show: isScale, | |
41 | + }); | |
42 | + } | |
43 | + | |
44 | + return { prefixCls, getIcon, handleFold }; | |
45 | + }, | |
46 | + }); | |
47 | +</script> | ... | ... |
src/layouts/default/tabs/index.less
src/layouts/default/tabs/index.vue
... | ... | @@ -21,6 +21,7 @@ |
21 | 21 | <template #tabBarExtraContent v-if="getShowRedo || getShowQuick"> |
22 | 22 | <TabRedo v-if="getShowRedo" /> |
23 | 23 | <QuickButton v-if="getShowQuick" /> |
24 | + <FoldButton v-if="getShowFold" /> | |
24 | 25 | </template> |
25 | 26 | </Tabs> |
26 | 27 | </div> |
... | ... | @@ -51,6 +52,7 @@ |
51 | 52 | components: { |
52 | 53 | QuickButton: createAsyncComponent(() => import('./components/QuickButton.vue')), |
53 | 54 | TabRedo: createAsyncComponent(() => import('./components/TabRedo.vue')), |
55 | + FoldButton: createAsyncComponent(() => import('./components/FoldButton.vue')), | |
54 | 56 | Tabs, |
55 | 57 | TabPane: Tabs.TabPane, |
56 | 58 | TabContent, |
... | ... | @@ -62,7 +64,7 @@ |
62 | 64 | useTabsDrag(affixTextList); |
63 | 65 | const { prefixCls } = useDesign('multiple-tabs'); |
64 | 66 | const go = useGo(); |
65 | - const { getShowQuick, getShowRedo } = useMultipleTabSetting(); | |
67 | + const { getShowQuick, getShowRedo, getShowFold } = useMultipleTabSetting(); | |
66 | 68 | |
67 | 69 | const getTabsState = computed(() => { |
68 | 70 | return tabStore.getTabsState.filter((item) => !item.meta?.hideTab); |
... | ... | @@ -125,6 +127,7 @@ |
125 | 127 | getTabsState, |
126 | 128 | getShowQuick, |
127 | 129 | getShowRedo, |
130 | + getShowFold, | |
128 | 131 | }; |
129 | 132 | }, |
130 | 133 | }); | ... | ... |
src/layouts/default/tabs/useTabDropdown.ts
... | ... | @@ -8,8 +8,6 @@ import router from '/@/router'; |
8 | 8 | import { RouteLocationNormalized } from 'vue-router'; |
9 | 9 | import { useTabs } from '/@/hooks/web/useTabs'; |
10 | 10 | import { useI18n } from '/@/hooks/web/useI18n'; |
11 | -import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting'; | |
12 | -import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; | |
13 | 11 | |
14 | 12 | const { t } = useI18n(); |
15 | 13 | |
... | ... | @@ -21,9 +19,6 @@ export function useTabDropdown(tabContentProps: TabContentProps) { |
21 | 19 | |
22 | 20 | const { currentRoute } = router; |
23 | 21 | |
24 | - const { getShowMenu, setMenuSetting } = useMenuSetting(); | |
25 | - const { getShowHeader, setHeaderSetting } = useHeaderSetting(); | |
26 | - | |
27 | 22 | const isTabs = computed(() => tabContentProps.type === TabContentEnum.TAB_TYPE); |
28 | 23 | |
29 | 24 | const getCurrentTab = computed( |
... | ... | @@ -32,10 +27,6 @@ export function useTabDropdown(tabContentProps: TabContentProps) { |
32 | 27 | } |
33 | 28 | ); |
34 | 29 | |
35 | - const getIsScale = computed(() => { | |
36 | - return !unref(getShowMenu) && !unref(getShowHeader); | |
37 | - }); | |
38 | - | |
39 | 30 | /** |
40 | 31 | * @description: drop-down list |
41 | 32 | */ |
... | ... | @@ -98,16 +89,6 @@ export function useTabDropdown(tabContentProps: TabContentProps) { |
98 | 89 | }, |
99 | 90 | ]; |
100 | 91 | |
101 | - if (!unref(isTabs)) { | |
102 | - const isScale = unref(getIsScale); | |
103 | - dropMenuList.unshift({ | |
104 | - icon: isScale ? 'codicon:screen-normal' : 'codicon:screen-full', | |
105 | - event: MenuEventEnum.SCALE, | |
106 | - text: isScale ? t('layout.multipleTab.putAway') : t('layout.multipleTab.unfold'), | |
107 | - disabled: false, | |
108 | - }); | |
109 | - } | |
110 | - | |
111 | 92 | return dropMenuList; |
112 | 93 | }); |
113 | 94 | |
... | ... | @@ -125,20 +106,9 @@ export function useTabDropdown(tabContentProps: TabContentProps) { |
125 | 106 | }; |
126 | 107 | } |
127 | 108 | |
128 | - function scaleScreen() { | |
129 | - const isScale = !unref(getShowMenu) && !unref(getShowHeader); | |
130 | - setMenuSetting({ | |
131 | - show: isScale, | |
132 | - hidden: !isScale, | |
133 | - }); | |
134 | - setHeaderSetting({ | |
135 | - show: isScale, | |
136 | - }); | |
137 | - } | |
138 | - | |
139 | 109 | // Handle right click event |
140 | 110 | function handleMenuEvent(menu: DropMenu): void { |
141 | - const { refreshPage, closeAll, closeCurrent, closeLeft, closeOther, closeRight } = useTabs(); | |
111 | + const { refreshPage, closeAll, close, closeLeft, closeOther, closeRight } = useTabs(); | |
142 | 112 | const { event } = menu; |
143 | 113 | switch (event) { |
144 | 114 | case MenuEventEnum.SCALE: |
... | ... | @@ -150,7 +120,7 @@ export function useTabDropdown(tabContentProps: TabContentProps) { |
150 | 120 | break; |
151 | 121 | // Close current |
152 | 122 | case MenuEventEnum.CLOSE_CURRENT: |
153 | - closeCurrent(); | |
123 | + close(tabContentProps.tabItem); | |
154 | 124 | break; |
155 | 125 | // Close left |
156 | 126 | case MenuEventEnum.CLOSE_LEFT: | ... | ... |
src/locales/lang/en/layout/multipleTab.ts
src/locales/lang/en/layout/setting.ts
src/locales/lang/zh_CN/layout/multipleTab.ts
1 | 1 | export default { |
2 | - redo: '刷新当前', | |
3 | - close: '关闭当前', | |
4 | - closeLeft: '关闭左侧', | |
5 | - closeRight: '关闭右侧', | |
6 | - closeOther: '关闭其他', | |
7 | - closeAll: '关闭全部', | |
8 | - putAway: '收起', | |
9 | - unfold: '展开', | |
2 | + redo: '重新加载', | |
3 | + close: '关闭标签页', | |
4 | + closeLeft: '关闭左侧标签页', | |
5 | + closeRight: '关闭右侧标签页', | |
6 | + closeOther: '关闭其它标签页', | |
7 | + closeAll: '关闭全部标签页', | |
10 | 8 | tooltipRedo: '刷新', |
11 | 9 | }; | ... | ... |
src/locales/lang/zh_CN/layout/setting.ts
src/settings/componentSetting.ts
0 → 100644
1 | +// Used to configure the general configuration of some components without modifying the components | |
2 | + | |
3 | +import type { SorterResult } from '../components/Table'; | |
4 | + | |
5 | +export default { | |
6 | + // basic-table setting | |
7 | + table: { | |
8 | + // Form interface request general configuration | |
9 | + // support xxx.xxx.xxx | |
10 | + fetchSetting: { | |
11 | + // The field name of the current page passed to the background | |
12 | + pageField: 'page', | |
13 | + // The number field name of each page displayed in the background | |
14 | + sizeField: 'pageSize', | |
15 | + // Field name of the form data returned by the interface | |
16 | + listField: 'items', | |
17 | + // Total number of tables returned by the interface field name | |
18 | + totalField: 'total', | |
19 | + }, | |
20 | + // Number of pages that can be selected | |
21 | + pageSizeOptions: ['10', '50', '80', '100'], | |
22 | + // Default display quantity on one page | |
23 | + defaultPageSize: 10, | |
24 | + // Custom general sort function | |
25 | + defaultSortFn: (sortInfo: SorterResult) => { | |
26 | + const { field, order } = sortInfo; | |
27 | + return { | |
28 | + // The sort field passed to the backend you | |
29 | + field, | |
30 | + // Sorting method passed to the background asc/desc | |
31 | + order, | |
32 | + }; | |
33 | + }, | |
34 | + // Custom general filter function | |
35 | + defaultFilterFn: (data: Partial<Recordable<string[]>>) => { | |
36 | + return data; | |
37 | + }, | |
38 | + }, | |
39 | + // scrollbar setting | |
40 | + scrollbar: { | |
41 | + // Whether to use native scroll bar | |
42 | + // After opening, the menu, modal, drawer will change the pop-up scroll bar to native | |
43 | + native: false, | |
44 | + }, | |
45 | +}; | ... | ... |
src/settings/encryptionSetting.ts
... | ... | @@ -5,8 +5,8 @@ export const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7; |
5 | 5 | |
6 | 6 | // aes encryption key |
7 | 7 | export const cacheCipher = { |
8 | - key: '_12345678901234@', | |
9 | - iv: '@12345678901234_', | |
8 | + key: '_11111000001111@', | |
9 | + iv: '@11111000001111_', | |
10 | 10 | }; |
11 | 11 | |
12 | 12 | // Whether the system cache is encrypted using aes | ... | ... |
src/settings/projectSetting.ts
src/types/config.d.ts
src/views/demo/page/desc/high/index.vue
1 | 1 | <template> |
2 | 2 | <PageWrapper title="单号:234231029431" contentBackgrond> |
3 | 3 | <template #extra> |
4 | - <a-button key="3"> 操作一 </a-button> | |
5 | - <a-button key="2"> 操作二 </a-button> | |
6 | - <a-button key="1" type="primary"> 主操作 </a-button> | |
4 | + <a-button> 操作一 </a-button> | |
5 | + <a-button> 操作二 </a-button> | |
6 | + <a-button type="primary"> 主操作 </a-button> | |
7 | 7 | </template> |
8 | 8 | |
9 | 9 | <template #footer> | ... | ... |