Commit ba068ba1df797627e88dcf61af3ea13d0c2929ab
1 parent
234c1d1f
wip: refactor layout
Showing
77 changed files
with
1294 additions
and
1097 deletions
.eslintrc.js
... | ... | @@ -23,7 +23,7 @@ module.exports = { |
23 | 23 | '@typescript-eslint/no-empty-function': 'off', |
24 | 24 | 'vue/custom-event-name-casing': 'off', |
25 | 25 | 'no-use-before-define': 'off', |
26 | - // 'no-use-before-define': [ | |
26 | + // 'no-setting-before-define': [ | |
27 | 27 | // 'error', |
28 | 28 | // { |
29 | 29 | // functions: false, |
... | ... | @@ -31,7 +31,7 @@ module.exports = { |
31 | 31 | // }, |
32 | 32 | // ], |
33 | 33 | '@typescript-eslint/no-use-before-define': 'off', |
34 | - // '@typescript-eslint/no-use-before-define': [ | |
34 | + // '@typescript-eslint/no-setting-before-define': [ | |
35 | 35 | // 'error', |
36 | 36 | // { |
37 | 37 | // functions: false, | ... | ... |
src/components/Application/index.ts
1 | 1 | import AppLocalPicker from './src/AppLocalPicker.vue'; |
2 | -import AppFooterToolbar from './src/AppFooterToolbar.vue'; | |
2 | +import AppPageFooter from './src/AppPageFooter.vue'; | |
3 | +import AppLogo from './src/AppLogo.vue'; | |
3 | 4 | import { withInstall } from '../util'; |
4 | 5 | |
5 | -export { AppLocalPicker, AppFooterToolbar }; | |
6 | +export { AppLocalPicker, AppPageFooter, AppLogo }; | |
6 | 7 | |
7 | -export default withInstall(AppLocalPicker, AppFooterToolbar); | |
8 | +export default withInstall(AppLocalPicker, AppPageFooter, AppLogo); | ... | ... |
src/components/Application/src/AppLocalPicker.vue
... | ... | @@ -5,29 +5,44 @@ |
5 | 5 | :selectedKeys="selectedKeys" |
6 | 6 | @menuEvent="handleMenuEvent" |
7 | 7 | > |
8 | - <GlobalOutlined class="app-locale" /> | |
8 | + <span class="app-local-picker"> | |
9 | + <GlobalOutlined class="app-local-picker__icon" /> | |
10 | + <span v-if="showText">{{ getLangText }}</span> | |
11 | + </span> | |
9 | 12 | </Dropdown> |
10 | 13 | </template> |
11 | 14 | <script lang="ts"> |
12 | - import { defineComponent, ref, watchEffect, unref } from 'vue'; | |
15 | + import { defineComponent, ref, watchEffect, unref, computed } from 'vue'; | |
13 | 16 | |
14 | 17 | import { Dropdown, DropMenu } from '/@/components/Dropdown'; |
15 | 18 | import { GlobalOutlined } from '@ant-design/icons-vue'; |
16 | 19 | |
17 | 20 | import { useLocale } from '/@/hooks/web/useLocale'; |
18 | - import { useLocaleSetting } from '/@/settings/use/useLocaleSetting'; | |
21 | + import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting'; | |
19 | 22 | |
20 | 23 | import { LocaleType } from '/@/locales/types'; |
21 | 24 | |
22 | 25 | export default defineComponent({ |
23 | 26 | name: 'AppLocalPicker', |
24 | 27 | components: { GlobalOutlined, Dropdown }, |
28 | + props: { | |
29 | + showText: { | |
30 | + type: Boolean, | |
31 | + default: true, | |
32 | + }, | |
33 | + }, | |
25 | 34 | setup() { |
26 | 35 | const { localeList } = useLocaleSetting(); |
27 | 36 | const selectedKeys = ref<string[]>([]); |
28 | 37 | |
29 | 38 | const { changeLocale, getLang } = useLocale(); |
30 | 39 | |
40 | + const getLangText = computed(() => { | |
41 | + const key = selectedKeys.value[0]; | |
42 | + if (!key) return ''; | |
43 | + return localeList.find((item) => item.event === key)?.text; | |
44 | + }); | |
45 | + | |
31 | 46 | watchEffect(() => { |
32 | 47 | selectedKeys.value = [unref(getLang)]; |
33 | 48 | }); |
... | ... | @@ -41,13 +56,19 @@ |
41 | 56 | toggleLocale(menu.event as string); |
42 | 57 | } |
43 | 58 | |
44 | - return { localeList, handleMenuEvent, selectedKeys }; | |
59 | + return { localeList, handleMenuEvent, selectedKeys, getLangText }; | |
45 | 60 | }, |
46 | 61 | }); |
47 | 62 | </script> |
48 | 63 | |
49 | 64 | <style lang="less" scoped> |
50 | - .app-locale { | |
65 | + .app-local-picker { | |
66 | + display: flex; | |
67 | + align-items: center; | |
51 | 68 | cursor: pointer; |
69 | + | |
70 | + &__icon { | |
71 | + margin-right: 4px; | |
72 | + } | |
52 | 73 | } |
53 | 74 | </style> | ... | ... |
src/layouts/logo/index.vue renamed to src/components/Application/src/AppLogo.vue
1 | 1 | <template> |
2 | - <div class="app-logo anticon" :class="theme" @click="handleGoHome" :style="wrapStyle"> | |
2 | + <div class="app-logo anticon" :class="theme" @click="handleGoHome"> | |
3 | 3 | <img src="/@/assets/images/logo.png" /> |
4 | - <div v-if="show" class="logo-title ml-2 ellipsis">{{ globSetting.title }}</div> | |
4 | + <div class="app-logo__title ml-2 ellipsis">{{ globSetting.title }}</div> | |
5 | 5 | </div> |
6 | 6 | </template> |
7 | 7 | <script lang="ts"> |
8 | - import { computed, defineComponent, PropType, ref, watch } from 'vue'; | |
9 | - // hooks | |
10 | - import { useGlobSetting } from '/@/settings/use'; | |
11 | - import { useTimeoutFn } from '/@/hooks/core/useTimeout'; | |
8 | + import type { PropType } from 'vue'; | |
9 | + import { defineComponent } from 'vue'; | |
10 | + | |
11 | + import { useGlobSetting } from '/@/hooks/setting'; | |
12 | 12 | import { useGo } from '/@/hooks/web/usePage'; |
13 | 13 | |
14 | 14 | import { PageEnum } from '/@/enums/pageEnum'; |
15 | - import { MenuTypeEnum } from '/@/enums/menuEnum'; | |
16 | - | |
17 | - import { menuStore } from '/@/store/modules/menu'; | |
18 | - import { appStore } from '/@/store/modules/app'; | |
19 | 15 | |
20 | 16 | export default defineComponent({ |
21 | - name: 'Logo', | |
17 | + name: 'AppLogo', | |
22 | 18 | props: { |
23 | - showTitle: { | |
24 | - type: Boolean as PropType<boolean>, | |
25 | - default: true, | |
26 | - }, | |
19 | + /** | |
20 | + * The theme of the current parent component | |
21 | + */ | |
27 | 22 | theme: { |
28 | - type: String, | |
23 | + type: String as PropType<string>, | |
29 | 24 | }, |
30 | 25 | }, |
31 | - setup(props) { | |
32 | - const showRef = ref<boolean>(!!props.showTitle); | |
26 | + setup() { | |
33 | 27 | const globSetting = useGlobSetting(); |
34 | 28 | const go = useGo(); |
35 | 29 | |
36 | - function handleGoHome() { | |
30 | + function handleGoHome(): void { | |
37 | 31 | go(PageEnum.BASE_HOME); |
38 | 32 | } |
39 | 33 | |
40 | - watch( | |
41 | - () => props.showTitle, | |
42 | - (show: boolean) => { | |
43 | - if (show) { | |
44 | - useTimeoutFn(() => { | |
45 | - showRef.value = show; | |
46 | - }, 280); | |
47 | - } else { | |
48 | - showRef.value = show; | |
49 | - } | |
50 | - } | |
51 | - ); | |
52 | - | |
53 | - const wrapStyle = computed(() => { | |
54 | - const { getCollapsedState } = menuStore; | |
55 | - const { | |
56 | - menuSetting: { menuWidth, type }, | |
57 | - } = appStore.getProjectConfig; | |
58 | - const miniWidth = { minWidth: `${menuWidth}px` }; | |
59 | - if (type !== MenuTypeEnum.SIDEBAR) { | |
60 | - return miniWidth; | |
61 | - } | |
62 | - return getCollapsedState ? {} : miniWidth; | |
63 | - }); | |
64 | - | |
65 | 34 | return { |
66 | 35 | handleGoHome, |
67 | 36 | globSetting, |
68 | - show: showRef, | |
69 | - wrapStyle, | |
70 | 37 | }; |
71 | 38 | }, |
72 | 39 | }); |
73 | 40 | </script> |
74 | 41 | <style lang="less" scoped> |
75 | - @import (reference) '../../design/index.less'; | |
42 | + @import (reference) '../../../design/index.less'; | |
76 | 43 | |
77 | 44 | .app-logo { |
78 | 45 | display: flex; |
79 | 46 | align-items: center; |
80 | 47 | padding-left: 16px; |
81 | 48 | cursor: pointer; |
82 | - // justify-content: center; | |
49 | + | |
83 | 50 | &.light { |
84 | 51 | border-bottom: 1px solid @border-color-base; |
85 | 52 | } |
86 | 53 | |
87 | - .logo-title { | |
54 | + &.light &__title { | |
55 | + color: @primary-color; | |
56 | + } | |
57 | + | |
58 | + &.dark &__title { | |
59 | + color: @white; | |
60 | + } | |
61 | + | |
62 | + &__title { | |
88 | 63 | font-size: 18px; |
89 | 64 | font-weight: 700; |
90 | 65 | opacity: 0; |
91 | 66 | transition: all 0.5s; |
92 | 67 | .respond-to(medium,{ |
93 | 68 | opacity: 1; |
94 | - }); | |
95 | - } | |
96 | - | |
97 | - // &.dark .logo-title { | |
98 | - // font-weight: 400; | |
99 | - // } | |
100 | - | |
101 | - &.light .logo-title { | |
102 | - color: @primary-color; | |
69 | + }); | |
103 | 70 | } |
104 | 71 | } |
105 | 72 | </style> | ... | ... |
src/components/Application/src/AppFooterToolbar.vue renamed to src/components/Application/src/AppPageFooter.vue
1 | 1 | <template> |
2 | - <div class="app-footer" :style="{ width: getWidth }"> | |
2 | + <div class="app-footer" :style="{ width: getCalcContentWidth }"> | |
3 | 3 | <div class="app-footer__left"> |
4 | 4 | <slot name="left" /> |
5 | 5 | </div> |
... | ... | @@ -9,30 +9,16 @@ |
9 | 9 | </div> |
10 | 10 | </template> |
11 | 11 | <script lang="ts"> |
12 | - import { defineComponent, computed, unref } from 'vue'; | |
12 | + import { defineComponent } from 'vue'; | |
13 | 13 | |
14 | - import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum'; | |
15 | - | |
16 | - import { appStore } from '/@/store/modules/app'; | |
17 | - import { menuStore } from '/@/store/modules/menu'; | |
14 | + import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; | |
18 | 15 | |
19 | 16 | export default defineComponent({ |
20 | 17 | name: 'AppFooterToolbar', |
21 | 18 | setup() { |
22 | - const getMiniWidth = computed(() => { | |
23 | - const { | |
24 | - menuSetting: { collapsedShowTitle }, | |
25 | - } = appStore.getProjectConfig; | |
26 | - return collapsedShowTitle ? SIDE_BAR_SHOW_TIT_MINI_WIDTH : SIDE_BAR_MINI_WIDTH; | |
27 | - }); | |
28 | - | |
29 | - const getWidth = computed(() => { | |
30 | - const { getCollapsedState, getMenuWidthState } = menuStore; | |
31 | - const width = getCollapsedState ? unref(getMiniWidth) : getMenuWidthState; | |
32 | - return `calc(100% - ${width}px)`; | |
33 | - }); | |
19 | + const { getCalcContentWidth } = useMenuSetting(); | |
34 | 20 | |
35 | - return { getWidth }; | |
21 | + return { getCalcContentWidth }; | |
36 | 22 | }, |
37 | 23 | }); |
38 | 24 | </script> |
... | ... | @@ -51,7 +37,7 @@ |
51 | 37 | border-top: 1px solid #f0f0f0; |
52 | 38 | box-shadow: 0 -6px 16px -8px rgba(0, 0, 0, 0.08), 0 -9px 28px 0 rgba(0, 0, 0, 0.05), |
53 | 39 | 0 -12px 48px 16px rgba(0, 0, 0, 0.03); |
54 | - transition: width 0.3s; | |
40 | + transition: width 0.4s; | |
55 | 41 | |
56 | 42 | &__left { |
57 | 43 | flex: 1 1; | ... | ... |
src/components/Authority/src/index.vue
1 | 1 | <!-- |
2 | - * @Author: Vben | |
3 | - * @Description:Access control component for fine-grained access control. | |
2 | + Access control component for fine-grained access control. | |
4 | 3 | --> |
5 | 4 | <script lang="ts"> |
6 | 5 | import type { PropType } from 'vue'; |
7 | - import { defineComponent, computed, unref } from 'vue'; | |
6 | + import { defineComponent, unref } from 'vue'; | |
8 | 7 | |
9 | 8 | import { PermissionModeEnum } from '/@/enums/appEnum'; |
10 | 9 | import { RoleEnum } from '/@/enums/roleEnum'; |
10 | + import { useRootSetting } from '/@/hooks/setting/useRootSetting'; | |
11 | 11 | |
12 | 12 | import { usePermission } from '/@/hooks/web/usePermission'; |
13 | - import { appStore } from '/@/store/modules/app'; | |
14 | 13 | |
15 | 14 | import { getSlot } from '/@/utils/helper/tsxHelper'; |
16 | 15 | |
... | ... | @@ -29,9 +28,8 @@ |
29 | 28 | }, |
30 | 29 | }, |
31 | 30 | setup(props, { slots }) { |
32 | - const getModeRef = computed(() => { | |
33 | - return appStore.getProjectConfig.permissionMode; | |
34 | - }); | |
31 | + const { getPermissionMode } = useRootSetting(); | |
32 | + const { hasPermission } = usePermission(); | |
35 | 33 | |
36 | 34 | /** |
37 | 35 | * Render role button |
... | ... | @@ -41,7 +39,6 @@ |
41 | 39 | if (!value) { |
42 | 40 | return getSlot(slots); |
43 | 41 | } |
44 | - const { hasPermission } = usePermission(); | |
45 | 42 | return hasPermission(value) ? getSlot(slots) : null; |
46 | 43 | } |
47 | 44 | |
... | ... | @@ -52,12 +49,11 @@ |
52 | 49 | if (!value) { |
53 | 50 | return getSlot(slots); |
54 | 51 | } |
55 | - const { hasPermission } = usePermission(); | |
56 | 52 | return hasPermission(value) ? getSlot(slots) : null; |
57 | 53 | } |
58 | 54 | |
59 | 55 | return () => { |
60 | - const mode = unref(getModeRef); | |
56 | + const mode = unref(getPermissionMode); | |
61 | 57 | // Role-based value control |
62 | 58 | if (mode === PermissionModeEnum.ROLE) { |
63 | 59 | return renderRoleAuth(); | ... | ... |
src/components/Description/src/index.tsx
src/components/Dropdown/src/Dropdown.tsx
... | ... | @@ -15,7 +15,7 @@ export default defineComponent({ |
15 | 15 | const getMenuList = computed(() => props.dropMenuList); |
16 | 16 | |
17 | 17 | function handleClickMenu({ key }: any) { |
18 | - const menu = unref(getMenuList).find((item) => item.event === key); | |
18 | + const menu = unref(getMenuList).find((item) => `${item.event}` === `${key}`); | |
19 | 19 | emit('menuEvent', menu); |
20 | 20 | } |
21 | 21 | ... | ... |
src/components/Excel/src/ImportExcel.vue
... | ... | @@ -104,7 +104,7 @@ |
104 | 104 | */ |
105 | 105 | function handleInputClick(e: Event) { |
106 | 106 | const files = e && (e.target as HTMLInputElement).files; |
107 | - const rawFile = files && files[0]; // only use files[0] | |
107 | + const rawFile = files && files[0]; // only setting files[0] | |
108 | 108 | if (!rawFile) return; |
109 | 109 | upload(rawFile); |
110 | 110 | } | ... | ... |
src/components/Form/src/componentMap.ts
src/components/Form/src/hooks/useLabelWidth.ts
... | ... | @@ -31,7 +31,7 @@ export function useItemLabelWidth(schemaItemRef: Ref<FormSchema>, propsRef: Ref< |
31 | 31 | wrapperCol: globWrapperCol, |
32 | 32 | } = unref(propsRef) as any; |
33 | 33 | |
34 | - // If labelWidth is set globally, all items use | |
34 | + // If labelWidth is set globally, all items setting | |
35 | 35 | if ((!globalLabelWidth && !labelWidth && !globalLabelCol) || disabledLabelWidth) { |
36 | 36 | return { labelCol, wrapperCol }; |
37 | 37 | } | ... | ... |
src/components/Form/src/types/formItem.ts
... | ... | @@ -68,7 +68,7 @@ export interface FormItem { |
68 | 68 | */ |
69 | 69 | labelAlign?: 'left' | 'right'; |
70 | 70 | /** |
71 | - * a key of model. In the use of validate and resetFields method, the attribute is required | |
71 | + * a key of model. In the setting of validate and resetFields method, the attribute is required | |
72 | 72 | */ |
73 | 73 | name?: NamePath; |
74 | 74 | /** |
... | ... | @@ -76,7 +76,7 @@ export interface FormItem { |
76 | 76 | */ |
77 | 77 | rules?: object | object[]; |
78 | 78 | /** |
79 | - * Whether to automatically associate form fields. In most cases, you can use automatic association. | |
79 | + * Whether to automatically associate form fields. In most cases, you can setting automatic association. | |
80 | 80 | * If the conditions for automatic association are not met, you can manually associate them. See the notes below. |
81 | 81 | */ |
82 | 82 | autoLink?: boolean; | ... | ... |
src/components/Menu/index.ts
src/components/Menu/src/BasicMenu.tsx
1 | +import './index.less'; | |
2 | + | |
1 | 3 | import type { MenuState } from './types'; |
2 | 4 | import type { Menu as MenuType } from '/@/router/types'; |
3 | 5 | |
... | ... | @@ -9,11 +11,10 @@ import MenuContent from './MenuContent'; |
9 | 11 | import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum'; |
10 | 12 | import { ThemeEnum } from '/@/enums/appEnum'; |
11 | 13 | |
12 | -import { menuStore } from '/@/store/modules/menu'; | |
13 | 14 | import { appStore } from '/@/store/modules/app'; |
14 | 15 | |
15 | -import { useSearchInput } from './useSearchInput'; | |
16 | -import { useOpenKeys } from './useOpenKeys'; | |
16 | +import { useSearchInput } from './hooks/useSearchInput'; | |
17 | +import { useOpenKeys } from './hooks/useOpenKeys'; | |
17 | 18 | import { useRouter } from 'vue-router'; |
18 | 19 | |
19 | 20 | import { isFunction } from '/@/utils/is'; |
... | ... | @@ -23,7 +24,7 @@ import { menuHasChildren } from './helper'; |
23 | 24 | import { getCurrentParentPath } from '/@/router/menus'; |
24 | 25 | |
25 | 26 | import { basicProps } from './props'; |
26 | -import './index.less'; | |
27 | +import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; | |
27 | 28 | export default defineComponent({ |
28 | 29 | name: 'BasicMenu', |
29 | 30 | props: basicProps, |
... | ... | @@ -39,6 +40,8 @@ export default defineComponent({ |
39 | 40 | selectedKeys: [], |
40 | 41 | collapsedOpenKeys: [], |
41 | 42 | }); |
43 | + | |
44 | + const { getCollapsed } = useMenuSetting(); | |
42 | 45 | const { currentRoute } = useRouter(); |
43 | 46 | |
44 | 47 | const { items, flatItems, isAppMenu, mode, accordion } = toRefs(props); |
... | ... | @@ -61,7 +64,7 @@ export default defineComponent({ |
61 | 64 | |
62 | 65 | const getOpenKeys = computed(() => { |
63 | 66 | if (props.isAppMenu) { |
64 | - return menuStore.getCollapsedState ? menuState.collapsedOpenKeys : menuState.openKeys; | |
67 | + return unref(getCollapsed) ? menuState.collapsedOpenKeys : menuState.openKeys; | |
65 | 68 | } |
66 | 69 | return menuState.openKeys; |
67 | 70 | }); |
... | ... | @@ -95,20 +98,20 @@ export default defineComponent({ |
95 | 98 | cls.push('basic-menu__sidebar-hor'); |
96 | 99 | } |
97 | 100 | |
98 | - if (!props.isTop && props.isAppMenu && appStore.getProjectConfig.menuSetting.split) { | |
101 | + if (!props.isHorizontal && props.isAppMenu && appStore.getProjectConfig.menuSetting.split) { | |
99 | 102 | cls.push('basic-menu__second'); |
100 | 103 | } |
101 | 104 | return cls; |
102 | 105 | }); |
103 | 106 | |
104 | - const showTitle = computed(() => props.collapsedShowTitle && menuStore.getCollapsedState); | |
107 | + const showTitle = computed(() => props.collapsedShowTitle && unref(getCollapsed)); | |
105 | 108 | |
106 | 109 | watch( |
107 | 110 | () => currentRoute.value.name, |
108 | 111 | (name: string) => { |
109 | 112 | if (name === 'Redirect') return; |
110 | 113 | handleMenuChange(); |
111 | - props.isTop && appStore.getProjectConfig.menuSetting.split && getParentPath(); | |
114 | + props.isHorizontal && appStore.getProjectConfig.menuSetting.split && getParentPath(); | |
112 | 115 | } |
113 | 116 | ); |
114 | 117 | |
... | ... | @@ -180,7 +183,7 @@ export default defineComponent({ |
180 | 183 | <MenuContent |
181 | 184 | item={menu} |
182 | 185 | level={index} |
183 | - isTop={props.isTop} | |
186 | + isHorizontal={props.isHorizontal} | |
184 | 187 | showTitle={unref(showTitle)} |
185 | 188 | searchValue={menuState.searchValue} |
186 | 189 | />, |
... | ... | @@ -196,7 +199,7 @@ export default defineComponent({ |
196 | 199 | showTitle={unref(showTitle)} |
197 | 200 | item={menu} |
198 | 201 | level={index} |
199 | - isTop={props.isTop} | |
202 | + isHorizontal={props.isHorizontal} | |
200 | 203 | searchValue={menuState.searchValue} |
201 | 204 | />, |
202 | 205 | ], |
... | ... | @@ -214,7 +217,7 @@ export default defineComponent({ |
214 | 217 | const inlineCollapsedObj = isInline |
215 | 218 | ? props.isAppMenu |
216 | 219 | ? { |
217 | - inlineCollapsed: menuStore.getCollapsedState, | |
220 | + inlineCollapsed: unref(getCollapsed), | |
218 | 221 | } |
219 | 222 | : { inlineCollapsed: props.inlineCollapsed } |
220 | 223 | : {}; |
... | ... | @@ -246,7 +249,6 @@ export default defineComponent({ |
246 | 249 | }); |
247 | 250 | |
248 | 251 | return () => { |
249 | - const { getCollapsedState } = menuStore; | |
250 | 252 | const { mode } = props; |
251 | 253 | return mode === MenuModeEnum.HORIZONTAL ? ( |
252 | 254 | renderMenu() |
... | ... | @@ -258,7 +260,7 @@ export default defineComponent({ |
258 | 260 | theme={props.theme as ThemeEnum} |
259 | 261 | onChange={handleInputChange} |
260 | 262 | onClick={handleInputClick} |
261 | - collapsed={getCollapsedState} | |
263 | + collapsed={unref(getCollapsed)} | |
262 | 264 | /> |
263 | 265 | <section style={unref(getMenuWrapStyle)} class="basic-menu__content"> |
264 | 266 | {renderMenu()} | ... | ... |
src/components/Menu/src/MenuContent.tsx
... | ... | @@ -26,7 +26,7 @@ export default defineComponent({ |
26 | 26 | type: Number as PropType<number>, |
27 | 27 | default: 0, |
28 | 28 | }, |
29 | - isTop: { | |
29 | + isHorizontal: { | |
30 | 30 | type: Boolean as PropType<boolean>, |
31 | 31 | default: true, |
32 | 32 | }, |
... | ... | @@ -40,8 +40,8 @@ export default defineComponent({ |
40 | 40 | } |
41 | 41 | |
42 | 42 | function renderTag() { |
43 | - const { item, showTitle, isTop } = props; | |
44 | - if (!item || showTitle || isTop) return null; | |
43 | + const { item, showTitle, isHorizontal } = props; | |
44 | + if (!item || showTitle || isHorizontal) return null; | |
45 | 45 | |
46 | 46 | const { tag } = item; |
47 | 47 | if (!tag) return null; | ... | ... |
src/components/Menu/src/useOpenKeys.ts renamed to src/components/Menu/src/hooks/useOpenKeys.ts
1 | 1 | import { MenuModeEnum } from '/@/enums/menuEnum'; |
2 | 2 | import type { Menu as MenuType } from '/@/router/types'; |
3 | -import type { MenuState } from './types'; | |
3 | +import type { MenuState } from '../types'; | |
4 | 4 | import type { Ref } from 'vue'; |
5 | 5 | |
6 | 6 | import { unref } from 'vue'; |
7 | -import { menuStore } from '/@/store/modules/menu'; | |
8 | 7 | import { getAllParentPath } from '/@/utils/helper/menuHelper'; |
9 | 8 | import { es6Unique } from '/@/utils'; |
9 | +import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; | |
10 | 10 | |
11 | 11 | export function useOpenKeys( |
12 | 12 | menuState: MenuState, |
... | ... | @@ -16,6 +16,7 @@ export function useOpenKeys( |
16 | 16 | mode: Ref<MenuModeEnum>, |
17 | 17 | accordion: Ref<boolean> |
18 | 18 | ) { |
19 | + const { getCollapsed } = useMenuSetting(); | |
19 | 20 | /** |
20 | 21 | * @description:设置展开 |
21 | 22 | */ |
... | ... | @@ -49,7 +50,7 @@ export function useOpenKeys( |
49 | 50 | rootSubMenuKeys.push(path); |
50 | 51 | } |
51 | 52 | } |
52 | - if (!menuStore.getCollapsedState || !unref(isAppMenu)) { | |
53 | + if (!unref(getCollapsed) || !unref(isAppMenu)) { | |
53 | 54 | const latestOpenKey = openKeys.find((key) => menuState.openKeys.indexOf(key) === -1); |
54 | 55 | if (rootSubMenuKeys.indexOf(latestOpenKey as string) === -1) { |
55 | 56 | menuState.openKeys = openKeys; | ... | ... |
src/components/Menu/src/useSearchInput.ts renamed to src/components/Menu/src/hooks/useSearchInput.ts
src/components/Menu/src/props.ts
... | ... | @@ -8,6 +8,10 @@ export const basicProps = { |
8 | 8 | type: Array as PropType<Menu[]>, |
9 | 9 | default: () => [], |
10 | 10 | }, |
11 | + flatItems: { | |
12 | + type: Array as PropType<Menu[]>, | |
13 | + default: () => [], | |
14 | + }, | |
11 | 15 | appendClass: { |
12 | 16 | type: Boolean as PropType<boolean>, |
13 | 17 | default: false, |
... | ... | @@ -16,10 +20,6 @@ export const basicProps = { |
16 | 20 | type: Boolean as PropType<boolean>, |
17 | 21 | default: false, |
18 | 22 | }, |
19 | - flatItems: { | |
20 | - type: Array as PropType<Menu[]>, | |
21 | - default: () => [], | |
22 | - }, | |
23 | 23 | // 是否显示搜索框 |
24 | 24 | search: { |
25 | 25 | type: Boolean as PropType<boolean>, |
... | ... | @@ -55,7 +55,7 @@ export const basicProps = { |
55 | 55 | type: Boolean as PropType<boolean>, |
56 | 56 | default: true, |
57 | 57 | }, |
58 | - isTop: { | |
58 | + isHorizontal: { | |
59 | 59 | type: Boolean as PropType<boolean>, |
60 | 60 | default: false, |
61 | 61 | }, | ... | ... |
src/components/Menu/src/types.d.ts renamed to src/components/Menu/src/types.ts
src/components/Modal/src/props.ts
... | ... | @@ -35,7 +35,7 @@ export const basicProps = Object.assign({}, modalProps, { |
35 | 35 | }, |
36 | 36 | // Warm reminder message |
37 | 37 | helpMessage: [String, Array] as PropType<string | string[]>, |
38 | - // Whether to use wrapper | |
38 | + // Whether to setting wrapper | |
39 | 39 | useWrapper: { |
40 | 40 | type: Boolean as PropType<boolean>, |
41 | 41 | default: true, | ... | ... |
src/components/Table/src/types/column.ts
... | ... | @@ -190,7 +190,7 @@ export interface ColumnProps<T> { |
190 | 190 | onFilterDropdownVisibleChange?: (visible: boolean) => void; |
191 | 191 | |
192 | 192 | /** |
193 | - * When using columns, you can use this property to configure the properties that support the slot, | |
193 | + * When using columns, you can setting this property to configure the properties that support the slot, | |
194 | 194 | * such as slots: { filterIcon: 'XXX'} |
195 | 195 | * @type object |
196 | 196 | */ | ... | ... |
src/components/Table/src/types/pagination.ts
src/components/Tinymce/src/plugins.ts
1 | -// Any plugins you want to use has to be imported | |
1 | +// Any plugins you want to setting has to be imported | |
2 | 2 | // Detail plugins list see https://www.tinymce.com/docs/plugins/ |
3 | 3 | // Custom builds see https://www.tinymce.com/download/custom-builds/ |
4 | 4 | // colorpicker/contextmenu/textcolor plugin is now built in to the core editor, please remove it from your editor configuration | ... | ... |
src/components/registerGlobComp.ts
... | ... | @@ -31,6 +31,7 @@ import { |
31 | 31 | PageHeader, |
32 | 32 | Result, |
33 | 33 | Empty, |
34 | + Avatar, | |
34 | 35 | } from 'ant-design-vue'; |
35 | 36 | import { getApp } from '/@/setup/App'; |
36 | 37 | |
... | ... | @@ -76,5 +77,6 @@ export function registerGlobComp() { |
76 | 77 | .use(PageHeader) |
77 | 78 | .use(Result) |
78 | 79 | .use(Empty) |
80 | + .use(Avatar) | |
79 | 81 | .use(Tabs); |
80 | 82 | } | ... | ... |
src/components/types.ts
0 → 100644
src/design/var/index.less
src/enums/pageEnum.ts
src/hooks/core/useDebouncedRef.ts
0 → 100644
1 | +import { customRef } from 'vue'; | |
2 | + | |
3 | +export function useDebouncedRef<T = any>(value: T, delay = 100) { | |
4 | + let timeout: TimeoutHandle; | |
5 | + return customRef((track, trigger) => { | |
6 | + return { | |
7 | + get() { | |
8 | + track(); | |
9 | + return value; | |
10 | + }, | |
11 | + set(newValue: T) { | |
12 | + clearTimeout(timeout); | |
13 | + timeout = setTimeout(() => { | |
14 | + value = newValue; | |
15 | + trigger(); | |
16 | + }, delay); | |
17 | + }, | |
18 | + }; | |
19 | + }); | |
20 | +} | ... | ... |
src/settings/use/index.ts renamed to src/hooks/setting/index.ts
src/hooks/setting/useHeaderSetting.ts
0 → 100644
1 | +import type { HeaderSetting } from '/@/types/config'; | |
2 | + | |
3 | +import { computed, unref } from 'vue'; | |
4 | + | |
5 | +import { appStore } from '/@/store/modules/app'; | |
6 | + | |
7 | +import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting'; | |
8 | +import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; | |
9 | +import { useRootSetting } from '/@/hooks/setting/useRootSetting'; | |
10 | + | |
11 | +import { MenuModeEnum } from '/@/enums/menuEnum'; | |
12 | + | |
13 | +export function useHeaderSetting() { | |
14 | + const { getShow: getShowMultipleTab } = useMultipleTabSetting(); | |
15 | + const { getMode, getSplit, getShowHeaderTrigger, getIsSidebarType } = useMenuSetting(); | |
16 | + const { getShowBreadCrumb, getShowLogo } = useRootSetting(); | |
17 | + | |
18 | + // Get header configuration | |
19 | + const getHeaderSetting = computed(() => appStore.getProjectConfig.headerSetting); | |
20 | + | |
21 | + const getShowDoc = computed(() => unref(getHeaderSetting).showDoc); | |
22 | + | |
23 | + const getTheme = computed(() => unref(getHeaderSetting).theme); | |
24 | + | |
25 | + const getShowRedo = computed(() => unref(getHeaderSetting).showRedo && unref(getShowMultipleTab)); | |
26 | + | |
27 | + const getUseLockPage = computed(() => unref(getHeaderSetting).useLockPage); | |
28 | + | |
29 | + const getShowFullScreen = computed(() => unref(getHeaderSetting).showFullScreen); | |
30 | + | |
31 | + const getShowNotice = computed(() => unref(getHeaderSetting).showNotice); | |
32 | + | |
33 | + const getShowBread = computed(() => { | |
34 | + return ( | |
35 | + unref(getMode) !== MenuModeEnum.HORIZONTAL && unref(getShowBreadCrumb) && !unref(getSplit) | |
36 | + ); | |
37 | + }); | |
38 | + | |
39 | + const getShowHeaderLogo = computed(() => { | |
40 | + return unref(getShowLogo) && !unref(getIsSidebarType); | |
41 | + }); | |
42 | + | |
43 | + const getShowContent = computed(() => { | |
44 | + return unref(getShowBread) || unref(getShowHeaderTrigger); | |
45 | + }); | |
46 | + | |
47 | + // Set header configuration | |
48 | + function setHeaderSetting(headerSetting: Partial<HeaderSetting>): void { | |
49 | + appStore.commitProjectConfigState({ headerSetting }); | |
50 | + } | |
51 | + | |
52 | + return { | |
53 | + setHeaderSetting, | |
54 | + | |
55 | + getHeaderSetting, | |
56 | + | |
57 | + getShowDoc, | |
58 | + getTheme, | |
59 | + getShowRedo, | |
60 | + getUseLockPage, | |
61 | + getShowFullScreen, | |
62 | + getShowNotice, | |
63 | + getShowBread, | |
64 | + getShowContent, | |
65 | + getShowHeaderLogo, | |
66 | + }; | |
67 | +} | ... | ... |
src/settings/use/useLocaleSetting.ts renamed to src/hooks/setting/useLocaleSetting.ts
1 | 1 | import type { LocaleSetting } from '/@/types/config'; |
2 | 2 | |
3 | -import { computed } from 'vue'; | |
3 | +import { computed, unref } from 'vue'; | |
4 | 4 | import { appStore } from '/@/store/modules/app'; |
5 | 5 | |
6 | 6 | import getProjectSetting from '/@/settings/projectSetting'; |
... | ... | @@ -8,24 +8,16 @@ import { localeList } from '/@/locales'; |
8 | 8 | |
9 | 9 | export function useLocaleSetting() { |
10 | 10 | // Get locale configuration |
11 | - const getLocale = computed(() => { | |
12 | - return appStore.getProjectConfig.locale || getProjectSetting.locale; | |
13 | - }); | |
11 | + const getLocale = computed(() => appStore.getProjectConfig.locale || getProjectSetting.locale); | |
14 | 12 | |
15 | 13 | // get current language |
16 | - const getLang = computed(() => { | |
17 | - return getLocale.value.lang; | |
18 | - }); | |
14 | + const getLang = computed(() => unref(getLocale).lang); | |
19 | 15 | |
20 | 16 | // get Available Locales |
21 | - const getAvailableLocales = computed((): string[] => { | |
22 | - return getLocale.value.availableLocales; | |
23 | - }); | |
17 | + const getAvailableLocales = computed((): string[] => unref(getLocale).availableLocales); | |
24 | 18 | |
25 | 19 | // get Fallback Locales |
26 | - const getFallbackLocale = computed((): string => { | |
27 | - return getLocale.value.fallback; | |
28 | - }); | |
20 | + const getFallbackLocale = computed((): string => unref(getLocale).fallback); | |
29 | 21 | |
30 | 22 | // Set locale configuration |
31 | 23 | function setLocale(locale: Partial<LocaleSetting>): void { | ... | ... |
src/hooks/setting/useMenuSetting.ts
0 → 100644
1 | +import type { MenuSetting } from '/@/types/config'; | |
2 | + | |
3 | +import { computed, unref } from 'vue'; | |
4 | + | |
5 | +import { appStore } from '/@/store/modules/app'; | |
6 | + | |
7 | +import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum'; | |
8 | +import { MenuModeEnum, MenuTypeEnum, TriggerEnum } from '/@/enums/menuEnum'; | |
9 | + | |
10 | +export function useMenuSetting() { | |
11 | + // Get menu configuration | |
12 | + const getMenuSetting = computed(() => appStore.getProjectConfig.menuSetting); | |
13 | + | |
14 | + const getMiniWidth = computed(() => unref(getMenuSetting).menuWidth); | |
15 | + | |
16 | + const getCollapsed = computed(() => unref(getMenuSetting).collapsed); | |
17 | + | |
18 | + const getType = computed(() => unref(getMenuSetting).type); | |
19 | + | |
20 | + const getMode = computed(() => unref(getMenuSetting).mode); | |
21 | + | |
22 | + const getShow = computed(() => unref(getMenuSetting).show); | |
23 | + | |
24 | + const getMenuWidth = computed(() => unref(getMenuSetting).menuWidth); | |
25 | + | |
26 | + const getTrigger = computed(() => unref(getMenuSetting).trigger); | |
27 | + | |
28 | + const getTheme = computed(() => unref(getMenuSetting).theme); | |
29 | + | |
30 | + const getSplit = computed(() => unref(getMenuSetting).split); | |
31 | + | |
32 | + const getHasDrag = computed(() => unref(getMenuSetting).hasDrag); | |
33 | + | |
34 | + const getAccordion = computed(() => unref(getMenuSetting).accordion); | |
35 | + | |
36 | + const getCollapsedShowTitle = computed(() => unref(getMenuSetting).collapsedShowTitle); | |
37 | + | |
38 | + const getCollapsedShowSearch = computed(() => unref(getMenuSetting).collapsedShowSearch); | |
39 | + | |
40 | + const getTopMenuAlign = computed(() => unref(getMenuSetting).topMenuAlign); | |
41 | + | |
42 | + const getIsSidebarType = computed(() => unref(getType) === MenuTypeEnum.SIDEBAR); | |
43 | + | |
44 | + const getShowTopMenu = computed(() => { | |
45 | + return unref(getMode) === MenuModeEnum.HORIZONTAL || unref(getSplit); | |
46 | + }); | |
47 | + | |
48 | + const getShowHeaderTrigger = computed(() => { | |
49 | + if ( | |
50 | + unref(getType) === MenuTypeEnum.TOP_MENU || | |
51 | + !unref(getShow) || | |
52 | + !unref(getMenuSetting).hidden | |
53 | + ) { | |
54 | + return false; | |
55 | + } | |
56 | + | |
57 | + return unref(getTrigger) === TriggerEnum.HEADER; | |
58 | + }); | |
59 | + | |
60 | + const getShowSearch = computed(() => { | |
61 | + return ( | |
62 | + unref(getMenuSetting).showSearch && | |
63 | + !(unref(getType) === MenuTypeEnum.MIX && unref(getMode) === MenuModeEnum.HORIZONTAL) | |
64 | + ); | |
65 | + }); | |
66 | + | |
67 | + const getIsHorizontal = computed(() => { | |
68 | + return unref(getMode) === MenuModeEnum.HORIZONTAL; | |
69 | + }); | |
70 | + | |
71 | + const getMiniWidthNumber = computed(() => { | |
72 | + const { collapsedShowTitle } = unref(getMenuSetting); | |
73 | + return collapsedShowTitle ? SIDE_BAR_SHOW_TIT_MINI_WIDTH : SIDE_BAR_MINI_WIDTH; | |
74 | + }); | |
75 | + | |
76 | + const getCalcContentWidth = computed(() => { | |
77 | + const width = unref(getCollapsed) ? unref(getMiniWidthNumber) : unref(getMiniWidth); | |
78 | + return `calc(100% - ${width}px)`; | |
79 | + }); | |
80 | + | |
81 | + // Set menu configuration | |
82 | + function setMenuSetting(menuSetting: Partial<MenuSetting>): void { | |
83 | + appStore.commitProjectConfigState({ menuSetting }); | |
84 | + } | |
85 | + | |
86 | + function toggleCollapsed() { | |
87 | + setMenuSetting({ | |
88 | + collapsed: !unref(getCollapsed), | |
89 | + }); | |
90 | + } | |
91 | + | |
92 | + return { | |
93 | + setMenuSetting, | |
94 | + | |
95 | + toggleCollapsed, | |
96 | + | |
97 | + getMenuSetting, | |
98 | + getMiniWidth, | |
99 | + getType, | |
100 | + getMode, | |
101 | + getShow, | |
102 | + getCollapsed, | |
103 | + getMiniWidthNumber, | |
104 | + getCalcContentWidth, | |
105 | + getMenuWidth, | |
106 | + getTrigger, | |
107 | + getSplit, | |
108 | + getTheme, | |
109 | + getHasDrag, | |
110 | + getIsHorizontal, | |
111 | + getShowSearch, | |
112 | + getCollapsedShowTitle, | |
113 | + getCollapsedShowSearch, | |
114 | + getIsSidebarType, | |
115 | + getAccordion, | |
116 | + getShowTopMenu, | |
117 | + getShowHeaderTrigger, | |
118 | + getTopMenuAlign, | |
119 | + }; | |
120 | +} | ... | ... |
src/hooks/setting/useMultipleTabSetting.ts
0 → 100644
1 | +import type { MultiTabsSetting } from '/@/types/config'; | |
2 | + | |
3 | +import { computed, unref } from 'vue'; | |
4 | + | |
5 | +import { appStore } from '/@/store/modules/app'; | |
6 | + | |
7 | +export function useMultipleTabSetting() { | |
8 | + const getMultipleTabSetting = computed(() => appStore.getProjectConfig.multiTabsSetting); | |
9 | + | |
10 | + const getMax = computed(() => unref(getMultipleTabSetting).max); | |
11 | + | |
12 | + const getShow = computed(() => unref(getMultipleTabSetting).show); | |
13 | + | |
14 | + function setMultipleTabSetting(multiTabsSetting: Partial<MultiTabsSetting>) { | |
15 | + appStore.commitProjectConfigState({ multiTabsSetting }); | |
16 | + } | |
17 | + | |
18 | + return { | |
19 | + setMultipleTabSetting, | |
20 | + | |
21 | + getMultipleTabSetting, | |
22 | + getMax, | |
23 | + getShow, | |
24 | + }; | |
25 | +} | ... | ... |
src/hooks/setting/useRootSetting.ts
0 → 100644
1 | +import type { ProjectConfig } from '/@/types/config'; | |
2 | + | |
3 | +import { computed, unref } from 'vue'; | |
4 | + | |
5 | +import { appStore } from '/@/store/modules/app'; | |
6 | + | |
7 | +type RootSetting = Omit< | |
8 | + ProjectConfig, | |
9 | + 'locale' | 'headerSetting' | 'menuSetting' | 'multiTabsSetting' | |
10 | +>; | |
11 | +export function useRootSetting() { | |
12 | + const getRootSetting = computed((): RootSetting => appStore.getProjectConfig); | |
13 | + | |
14 | + const getOpenPageLoading = computed(() => unref(getRootSetting).openPageLoading); | |
15 | + | |
16 | + const getOpenRouterTransition = computed(() => unref(getRootSetting).openRouterTransition); | |
17 | + | |
18 | + const getOpenKeepAlive = computed(() => unref(getRootSetting).openKeepAlive); | |
19 | + | |
20 | + const getRouterTransition = computed(() => unref(getRootSetting).routerTransition); | |
21 | + | |
22 | + const getCanEmbedIFramePage = computed(() => unref(getRootSetting).canEmbedIFramePage); | |
23 | + | |
24 | + const getPermissionMode = computed(() => unref(getRootSetting).permissionMode); | |
25 | + | |
26 | + const getShowLogo = computed(() => unref(getRootSetting).showLogo); | |
27 | + | |
28 | + const getUseErrorHandle = computed(() => unref(getRootSetting).useErrorHandle); | |
29 | + | |
30 | + const getShowBreadCrumb = computed(() => unref(getRootSetting).showBreadCrumb); | |
31 | + | |
32 | + const getShowBreadCrumbIcon = computed(() => unref(getRootSetting).showBreadCrumbIcon); | |
33 | + | |
34 | + function setRootSetting(setting: RootSetting) { | |
35 | + appStore.commitProjectConfigState(setting); | |
36 | + } | |
37 | + | |
38 | + return { | |
39 | + setRootSetting, | |
40 | + | |
41 | + getRootSetting, | |
42 | + getOpenPageLoading, | |
43 | + getOpenRouterTransition, | |
44 | + getOpenKeepAlive, | |
45 | + getRouterTransition, | |
46 | + getCanEmbedIFramePage, | |
47 | + getPermissionMode, | |
48 | + getShowLogo, | |
49 | + getUseErrorHandle, | |
50 | + getShowBreadCrumb, | |
51 | + getShowBreadCrumbIcon, | |
52 | + }; | |
53 | +} | ... | ... |
src/hooks/web/useLocale.ts
... | ... | @@ -7,7 +7,7 @@ import { unref, ref } from 'vue'; |
7 | 7 | |
8 | 8 | import { getI18n } from '/@/setup/i18n'; |
9 | 9 | |
10 | -import { useLocaleSetting } from '/@/settings/use/useLocaleSetting'; | |
10 | +import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting'; | |
11 | 11 | |
12 | 12 | import moment from 'moment'; |
13 | 13 | |
... | ... | @@ -67,7 +67,7 @@ export function useLocale() { |
67 | 67 | } |
68 | 68 | |
69 | 69 | /** |
70 | - * For non-setup use | |
70 | + * For non-setup setting | |
71 | 71 | */ |
72 | 72 | export function useExternalI18n() { |
73 | 73 | return getI18n().global; | ... | ... |
src/hooks/web/usePage.ts
... | ... | @@ -20,6 +20,7 @@ function handleError(e: Error) { |
20 | 20 | export function useGo() { |
21 | 21 | const { push, replace } = useRouter(); |
22 | 22 | function go(opt: PageEnum | RouteLocationRawEx | string = PageEnum.BASE_HOME, isReplace = false) { |
23 | + if (!opt) return; | |
23 | 24 | if (isString(opt)) { |
24 | 25 | isReplace ? replace(opt).catch(handleError) : push(opt).catch(handleError); |
25 | 26 | } else { | ... | ... |
src/hooks/web/useSideBar.ts deleted
100644 → 0
1 | -import { computed } from 'vue'; | |
2 | -import { appStore } from '/@/store/modules/app'; | |
3 | - | |
4 | -export function useSideBar() { | |
5 | - const currentCollapsedRef = computed(() => { | |
6 | - return appStore.getProjectConfig.menuSetting.collapsed; | |
7 | - }); | |
8 | - const changeCollapsed = (collapsed: boolean) => { | |
9 | - appStore.commitProjectConfigState({ | |
10 | - menuSetting: { | |
11 | - collapsed: collapsed, | |
12 | - }, | |
13 | - }); | |
14 | - }; | |
15 | - return { | |
16 | - openSider: changeCollapsed(false), | |
17 | - closeSider: changeCollapsed(true), | |
18 | - currentCollapsedRef, | |
19 | - }; | |
20 | -} |
src/layouts/default/LayoutSideBar.tsx deleted
100644 → 0
1 | -import { computed, defineComponent, nextTick, onMounted, ref, unref } from 'vue'; | |
2 | - | |
3 | -import { Layout } from 'ant-design-vue'; | |
4 | -import LayoutTrigger from './LayoutTrigger'; | |
5 | -import LayoutMenu from '/@/layouts/default/menu/LayoutMenu'; | |
6 | - | |
7 | -import { menuStore } from '/@/store/modules/menu'; | |
8 | -import { appStore } from '/@/store/modules/app'; | |
9 | - | |
10 | -import { MenuModeEnum, MenuSplitTyeEnum, TriggerEnum } from '/@/enums/menuEnum'; | |
11 | -import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum'; | |
12 | - | |
13 | -import { useDebounce } from '/@/hooks/core/useDebounce'; | |
14 | - | |
15 | -export default defineComponent({ | |
16 | - name: 'DefaultLayoutSideBar', | |
17 | - setup() { | |
18 | - const initRef = ref(false); | |
19 | - const brokenRef = ref(false); | |
20 | - const collapseRef = ref(true); | |
21 | - const dragBarRef = ref<Nullable<HTMLDivElement>>(null); | |
22 | - const sideRef = ref<any>(null); | |
23 | - | |
24 | - const getProjectConfigRef = computed(() => { | |
25 | - return appStore.getProjectConfig; | |
26 | - }); | |
27 | - | |
28 | - const getMiniWidth = computed(() => { | |
29 | - const { | |
30 | - menuSetting: { collapsedShowTitle }, | |
31 | - } = unref(getProjectConfigRef); | |
32 | - return collapsedShowTitle ? SIDE_BAR_SHOW_TIT_MINI_WIDTH : SIDE_BAR_MINI_WIDTH; | |
33 | - }); | |
34 | - | |
35 | - function onCollapseChange(val: boolean) { | |
36 | - if (initRef.value) { | |
37 | - collapseRef.value = val; | |
38 | - menuStore.commitCollapsedState(val); | |
39 | - } else { | |
40 | - const collapsed = appStore.getProjectConfig.menuSetting.collapsed; | |
41 | - !collapsed && menuStore.commitCollapsedState(val); | |
42 | - } | |
43 | - initRef.value = true; | |
44 | - } | |
45 | - | |
46 | - // Menu area drag and drop-mouse movement | |
47 | - function handleMouseMove(ele: any, wrap: any, clientX: number) { | |
48 | - document.onmousemove = function (innerE) { | |
49 | - let iT = ele.left + ((innerE || event).clientX - clientX); | |
50 | - innerE = innerE || window.event; | |
51 | - // let tarnameb = innerE.target || innerE.srcElement; | |
52 | - const maxT = 600; | |
53 | - const minT = unref(getMiniWidth); | |
54 | - iT < 0 && (iT = 0); | |
55 | - iT > maxT && (iT = maxT); | |
56 | - iT < minT && (iT = minT); | |
57 | - ele.style.left = wrap.style.width = iT + 'px'; | |
58 | - return false; | |
59 | - }; | |
60 | - } | |
61 | - | |
62 | - // 菜单区域拖拽 - 鼠标松开 | |
63 | - function removeMouseup(ele: any) { | |
64 | - const wrap = unref(sideRef).$el; | |
65 | - document.onmouseup = function () { | |
66 | - document.onmousemove = null; | |
67 | - document.onmouseup = null; | |
68 | - const width = parseInt(wrap.style.width); | |
69 | - menuStore.commitDragStartState(false); | |
70 | - if (!menuStore.getCollapsedState) { | |
71 | - if (width > unref(getMiniWidth) + 20) { | |
72 | - setMenuWidth(width); | |
73 | - } else { | |
74 | - menuStore.commitCollapsedState(true); | |
75 | - } | |
76 | - } else { | |
77 | - if (width > unref(getMiniWidth)) { | |
78 | - setMenuWidth(width); | |
79 | - menuStore.commitCollapsedState(false); | |
80 | - } | |
81 | - } | |
82 | - | |
83 | - ele.releaseCapture && ele.releaseCapture(); | |
84 | - }; | |
85 | - } | |
86 | - | |
87 | - function setMenuWidth(width: number) { | |
88 | - appStore.commitProjectConfigState({ | |
89 | - menuSetting: { | |
90 | - menuWidth: width, | |
91 | - }, | |
92 | - }); | |
93 | - } | |
94 | - | |
95 | - function changeWrapWidth() { | |
96 | - const ele = unref(dragBarRef) as any; | |
97 | - const side = unref(sideRef); | |
98 | - | |
99 | - const wrap = (side || {}).$el; | |
100 | - ele && | |
101 | - (ele.onmousedown = (e: any) => { | |
102 | - menuStore.commitDragStartState(true); | |
103 | - wrap.style.transition = 'unset'; | |
104 | - const clientX = (e || event).clientX; | |
105 | - ele.left = ele.offsetLeft; | |
106 | - handleMouseMove(ele, wrap, clientX); | |
107 | - removeMouseup(ele); | |
108 | - ele.setCapture && ele.setCapture(); | |
109 | - return false; | |
110 | - }); | |
111 | - } | |
112 | - function handleBreakpoint(broken: boolean) { | |
113 | - brokenRef.value = broken; | |
114 | - } | |
115 | - | |
116 | - const getDragBarStyle = computed(() => { | |
117 | - if (menuStore.getCollapsedState) { | |
118 | - return { left: `${unref(getMiniWidth)}px` }; | |
119 | - } | |
120 | - return {}; | |
121 | - }); | |
122 | - | |
123 | - const getCollapsedWidth = computed(() => { | |
124 | - return unref(brokenRef) ? 0 : unref(getMiniWidth); | |
125 | - }); | |
126 | - | |
127 | - const showTrigger = computed(() => { | |
128 | - const { | |
129 | - menuSetting: { trigger }, | |
130 | - } = unref(getProjectConfigRef); | |
131 | - return trigger !== TriggerEnum.NONE && trigger === TriggerEnum.FOOTER; | |
132 | - }); | |
133 | - | |
134 | - onMounted(() => { | |
135 | - nextTick(() => { | |
136 | - const [exec] = useDebounce(changeWrapWidth, 20); | |
137 | - exec(); | |
138 | - }); | |
139 | - }); | |
140 | - | |
141 | - function handleSiderClick(e: ChangeEvent) { | |
142 | - if (!e || !e.target || e.target.className !== 'basic-menu__content') return; | |
143 | - | |
144 | - const { collapsed, show } = appStore.getProjectConfig.menuSetting; | |
145 | - if (!collapsed || !show) return; | |
146 | - appStore.commitProjectConfigState({ | |
147 | - menuSetting: { | |
148 | - collapsed: false, | |
149 | - }, | |
150 | - }); | |
151 | - } | |
152 | - | |
153 | - function renderDragLine() { | |
154 | - const { menuSetting: { hasDrag = true } = {} } = unref(getProjectConfigRef); | |
155 | - return ( | |
156 | - <div | |
157 | - class={[`layout-sidebar__dargbar`, !hasDrag ? 'hide' : '']} | |
158 | - style={unref(getDragBarStyle)} | |
159 | - ref={dragBarRef} | |
160 | - /> | |
161 | - ); | |
162 | - } | |
163 | - | |
164 | - return () => { | |
165 | - const { | |
166 | - menuSetting: { theme, split: splitMenu }, | |
167 | - } = unref(getProjectConfigRef); | |
168 | - const { getCollapsedState, getMenuWidthState } = menuStore; | |
169 | - | |
170 | - const triggerDom = unref(showTrigger) | |
171 | - ? { | |
172 | - trigger: () => <LayoutTrigger />, | |
173 | - } | |
174 | - : {}; | |
175 | - | |
176 | - const triggerAttr = unref(showTrigger) | |
177 | - ? {} | |
178 | - : { | |
179 | - trigger: null, | |
180 | - }; | |
181 | - | |
182 | - return ( | |
183 | - <Layout.Sider | |
184 | - onClick={handleSiderClick} | |
185 | - onCollapse={onCollapseChange} | |
186 | - breakpoint="md" | |
187 | - width={getMenuWidthState} | |
188 | - collapsed={getCollapsedState} | |
189 | - collapsible | |
190 | - collapsedWidth={unref(getCollapsedWidth)} | |
191 | - theme={theme} | |
192 | - class="layout-sidebar" | |
193 | - ref={sideRef} | |
194 | - onBreakpoint={handleBreakpoint} | |
195 | - {...triggerAttr} | |
196 | - > | |
197 | - {{ | |
198 | - ...triggerDom, | |
199 | - default: () => ( | |
200 | - <> | |
201 | - <LayoutMenu | |
202 | - theme={theme} | |
203 | - menuMode={splitMenu ? MenuModeEnum.INLINE : null} | |
204 | - splitType={splitMenu ? MenuSplitTyeEnum.LEFT : MenuSplitTyeEnum.NONE} | |
205 | - /> | |
206 | - {renderDragLine()} | |
207 | - </> | |
208 | - ), | |
209 | - }} | |
210 | - </Layout.Sider> | |
211 | - ); | |
212 | - }; | |
213 | - }, | |
214 | -}); |
src/layouts/default/LayoutTrigger.tsx
1 | +import type { PropType } from 'vue'; | |
2 | + | |
3 | +import { defineComponent, unref } from 'vue'; | |
1 | 4 | import { |
2 | 5 | DoubleRightOutlined, |
3 | 6 | DoubleLeftOutlined, |
4 | 7 | MenuUnfoldOutlined, |
5 | 8 | MenuFoldOutlined, |
6 | 9 | } from '@ant-design/icons-vue'; |
7 | -import { defineComponent } from 'vue'; | |
8 | 10 | |
9 | -// store | |
10 | -import { menuStore } from '/@/store/modules/menu'; | |
11 | +import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; | |
11 | 12 | |
12 | 13 | export default defineComponent({ |
13 | 14 | name: 'LayoutTrigger', |
14 | 15 | props: { |
15 | 16 | sider: { |
16 | - type: Boolean, | |
17 | + type: Boolean as PropType<boolean>, | |
17 | 18 | default: true, |
18 | 19 | }, |
19 | 20 | theme: { |
20 | - type: String, | |
21 | + type: String as PropType<string>, | |
21 | 22 | }, |
22 | 23 | }, |
23 | 24 | setup(props) { |
24 | - function toggleMenu() { | |
25 | - menuStore.commitCollapsedState(!menuStore.getCollapsedState); | |
26 | - } | |
25 | + const { toggleCollapsed, getCollapsed } = useMenuSetting(); | |
27 | 26 | |
28 | 27 | return () => { |
29 | - const siderTrigger = menuStore.getCollapsedState ? ( | |
30 | - <DoubleRightOutlined /> | |
31 | - ) : ( | |
32 | - <DoubleLeftOutlined /> | |
33 | - ); | |
34 | - if (props.sider) return siderTrigger; | |
28 | + const siderTrigger = unref(getCollapsed) ? <DoubleRightOutlined /> : <DoubleLeftOutlined />; | |
29 | + | |
30 | + if (props.sider) { | |
31 | + return siderTrigger; | |
32 | + } | |
35 | 33 | |
36 | 34 | return ( |
37 | - <span class={['layout-trigger', props.theme]} onClick={toggleMenu}> | |
38 | - {menuStore.getCollapsedState ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />} | |
35 | + <span class={['layout-trigger', props.theme]} onClick={toggleCollapsed}> | |
36 | + {unref(getCollapsed) ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />} | |
39 | 37 | </span> |
40 | 38 | ); |
41 | 39 | }; | ... | ... |
src/layouts/default/header/LayoutBreadcrumb.tsx
... | ... | @@ -4,14 +4,17 @@ import type { PropType } from 'vue'; |
4 | 4 | |
5 | 5 | import { defineComponent, TransitionGroup, unref, watch, ref } from 'vue'; |
6 | 6 | import Breadcrumb from '/@/components/Breadcrumb/Breadcrumb.vue'; |
7 | +import Icon from '/@/components/Icon'; | |
7 | 8 | import BreadcrumbItem from '/@/components/Breadcrumb/BreadcrumbItem.vue'; |
9 | + | |
8 | 10 | import { useRouter } from 'vue-router'; |
9 | -import router from '/@/router'; | |
10 | -import { PageEnum } from '/@/enums/pageEnum'; | |
11 | -import { isBoolean } from '/@/utils/is'; | |
12 | 11 | |
12 | +import { isBoolean } from '/@/utils/is'; | |
13 | 13 | import { compile } from 'path-to-regexp'; |
14 | -import Icon from '/@/components/Icon'; | |
14 | + | |
15 | +import router from '/@/router'; | |
16 | + | |
17 | +import { PageEnum } from '/@/enums/pageEnum'; | |
15 | 18 | |
16 | 19 | export default defineComponent({ |
17 | 20 | name: 'BasicBreadcrumb', |
... | ... | @@ -40,7 +43,6 @@ export default defineComponent({ |
40 | 43 | const matchedList = matched.filter((item) => item.meta && item.meta.title).slice(1); |
41 | 44 | const firstItem = matchedList[0]; |
42 | 45 | const ret = getHomeRoute(firstItem); |
43 | - | |
44 | 46 | if (!isBoolean(ret)) { |
45 | 47 | matchedList.unshift(ret); |
46 | 48 | } |
... | ... | @@ -74,42 +76,51 @@ export default defineComponent({ |
74 | 76 | return push(pathCompile(path)); |
75 | 77 | } |
76 | 78 | |
79 | + function renderItemContent(item: AppRouteRecordRaw) { | |
80 | + return ( | |
81 | + <> | |
82 | + {props.showIcon && item.meta.icon && item.meta.icon.trim() !== '' && ( | |
83 | + <Icon | |
84 | + icon={item.meta.icon} | |
85 | + class="icon mr-1 " | |
86 | + style={{ | |
87 | + marginBottom: '2px', | |
88 | + }} | |
89 | + /> | |
90 | + )} | |
91 | + {item.meta.title} | |
92 | + </> | |
93 | + ); | |
94 | + } | |
95 | + | |
96 | + function renderBreadcrumbItemList() { | |
97 | + return unref(itemList).map((item) => { | |
98 | + const isLink = | |
99 | + (!!item.redirect && !item.meta.disabledRedirect) || | |
100 | + !item.children || | |
101 | + item.children.length === 0; | |
102 | + | |
103 | + return ( | |
104 | + <BreadcrumbItem | |
105 | + key={item.path} | |
106 | + isLink={isLink} | |
107 | + onClick={handleItemClick.bind(null, item)} | |
108 | + > | |
109 | + {() => renderItemContent(item as AppRouteRecordRaw)} | |
110 | + </BreadcrumbItem> | |
111 | + ); | |
112 | + }); | |
113 | + } | |
114 | + | |
115 | + function renderBreadcrumbDefault() { | |
116 | + return ( | |
117 | + <TransitionGroup name="breadcrumb">{() => renderBreadcrumbItemList()}</TransitionGroup> | |
118 | + ); | |
119 | + } | |
120 | + | |
77 | 121 | return () => ( |
78 | 122 | <Breadcrumb class={['layout-breadcrumb', unref(itemList).length === 0 ? 'hidden' : '']}> |
79 | - {() => ( | |
80 | - <TransitionGroup name="breadcrumb"> | |
81 | - {() => { | |
82 | - return unref(itemList).map((item) => { | |
83 | - const isLink = | |
84 | - (!!item.redirect && !item.meta.disabledRedirect) || | |
85 | - !item.children || | |
86 | - item.children.length === 0; | |
87 | - return ( | |
88 | - <BreadcrumbItem | |
89 | - key={item.path} | |
90 | - isLink={isLink} | |
91 | - onClick={handleItemClick.bind(null, item)} | |
92 | - > | |
93 | - {() => ( | |
94 | - <> | |
95 | - {props.showIcon && item.meta.icon && item.meta.icon.trim() !== '' && ( | |
96 | - <Icon | |
97 | - icon={item.meta.icon} | |
98 | - class="icon mr-1 " | |
99 | - style={{ | |
100 | - marginBottom: '2px', | |
101 | - }} | |
102 | - /> | |
103 | - )} | |
104 | - {item.meta.title} | |
105 | - </> | |
106 | - )} | |
107 | - </BreadcrumbItem> | |
108 | - ); | |
109 | - }); | |
110 | - }} | |
111 | - </TransitionGroup> | |
112 | - )} | |
123 | + {() => renderBreadcrumbDefault()} | |
113 | 124 | </Breadcrumb> |
114 | 125 | ); |
115 | 126 | }, | ... | ... |
src/layouts/default/header/LayoutHeader.tsx
1 | +import './index.less'; | |
2 | + | |
1 | 3 | import { defineComponent, unref, computed, ref } from 'vue'; |
2 | 4 | |
3 | 5 | import { Layout, Tooltip, Badge } from 'ant-design-vue'; |
4 | -import Logo from '/@/layouts/logo/index.vue'; | |
6 | +import { AppLogo } from '/@/components/Application'; | |
5 | 7 | import UserDropdown from './UserDropdown'; |
6 | 8 | import LayoutMenu from '/@/layouts/default/menu/LayoutMenu'; |
7 | 9 | import LayoutBreadcrumb from './LayoutBreadcrumb'; |
... | ... | @@ -12,50 +14,57 @@ import { |
12 | 14 | RedoOutlined, |
13 | 15 | FullscreenExitOutlined, |
14 | 16 | FullscreenOutlined, |
15 | - GithubFilled, | |
16 | 17 | LockOutlined, |
17 | 18 | BugOutlined, |
18 | 19 | } from '@ant-design/icons-vue'; |
20 | +import { useModal } from '/@/components/Modal'; | |
19 | 21 | |
20 | 22 | import { useFullscreen } from '/@/hooks/web/useFullScreen'; |
21 | 23 | import { useTabs } from '/@/hooks/web/useTabs'; |
22 | 24 | import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn'; |
25 | +import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting'; | |
26 | +import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; | |
27 | +import { useRootSetting } from '/@/hooks/setting/useRootSetting'; | |
28 | + | |
23 | 29 | import { useRouter } from 'vue-router'; |
24 | -import { useModal } from '/@/components/Modal'; | |
25 | 30 | |
26 | -import { appStore } from '/@/store/modules/app'; | |
27 | 31 | import { errorStore } from '/@/store/modules/error'; |
28 | 32 | |
29 | -import { MenuModeEnum, MenuSplitTyeEnum, MenuTypeEnum, TriggerEnum } from '/@/enums/menuEnum'; | |
30 | -import { GITHUB_URL } from '/@/settings/siteSetting'; | |
33 | +import { PageEnum } from '/@/enums/pageEnum'; | |
34 | +import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum'; | |
35 | +import { Component } from '/@/components/types'; | |
31 | 36 | |
32 | -import './index.less'; | |
33 | 37 | export default defineComponent({ |
34 | - name: 'DefaultLayoutHeader', | |
38 | + name: 'LayoutHeader', | |
35 | 39 | setup() { |
36 | - const widthRef = ref(200); | |
37 | 40 | let logoEl: Element | null; |
38 | 41 | |
42 | + const widthRef = ref(200); | |
43 | + | |
39 | 44 | const { refreshPage } = useTabs(); |
45 | + | |
46 | + const { getShowTopMenu, getShowHeaderTrigger, getSplit, getTopMenuAlign } = useMenuSetting(); | |
47 | + | |
48 | + const { getUseErrorHandle, getShowBreadCrumbIcon } = useRootSetting(); | |
49 | + | |
50 | + const { | |
51 | + getTheme, | |
52 | + getShowRedo, | |
53 | + getUseLockPage, | |
54 | + getShowFullScreen, | |
55 | + getShowNotice, | |
56 | + getShowContent, | |
57 | + getShowBread, | |
58 | + getShowHeaderLogo, | |
59 | + } = useHeaderSetting(); | |
60 | + | |
40 | 61 | const { push } = useRouter(); |
41 | 62 | const [register, { openModal }] = useModal(); |
42 | 63 | const { toggleFullscreen, isFullscreenRef } = useFullscreen(); |
43 | 64 | |
44 | - const getProjectConfigRef = computed(() => { | |
45 | - return appStore.getProjectConfig; | |
46 | - }); | |
47 | - | |
48 | - const showTopMenu = computed(() => { | |
49 | - const getProjectConfig = unref(getProjectConfigRef); | |
50 | - const { | |
51 | - menuSetting: { mode, split: splitMenu }, | |
52 | - } = getProjectConfig; | |
53 | - return mode === MenuModeEnum.HORIZONTAL || splitMenu; | |
54 | - }); | |
55 | - | |
56 | 65 | useWindowSizeFn( |
57 | 66 | () => { |
58 | - if (!unref(showTopMenu)) return; | |
67 | + if (!unref(getShowTopMenu)) return; | |
59 | 68 | let width = 0; |
60 | 69 | if (!logoEl) { |
61 | 70 | logoEl = document.querySelector('.layout-header__logo'); |
... | ... | @@ -69,24 +78,23 @@ export default defineComponent({ |
69 | 78 | { immediate: true } |
70 | 79 | ); |
71 | 80 | |
72 | - function goToGithub() { | |
73 | - window.open(GITHUB_URL, '__blank'); | |
74 | - } | |
75 | - | |
76 | 81 | const headerClass = computed(() => { |
77 | - const theme = unref(getProjectConfigRef).headerSetting.theme; | |
82 | + const theme = unref(getTheme); | |
78 | 83 | return theme ? `layout-header__header--${theme}` : ''; |
79 | 84 | }); |
80 | 85 | |
81 | - const showHeaderTrigger = computed(() => { | |
82 | - const { show, trigger, hidden, type } = unref(getProjectConfigRef).menuSetting; | |
83 | - if (type === MenuTypeEnum.TOP_MENU || !show || !hidden) return false; | |
84 | - return trigger === TriggerEnum.HEADER; | |
86 | + const getSplitType = computed(() => { | |
87 | + return unref(getSplit) ? MenuSplitTyeEnum.TOP : MenuSplitTyeEnum.NONE; | |
88 | + }); | |
89 | + | |
90 | + const getMenuMode = computed(() => { | |
91 | + return unref(getSplit) ? MenuModeEnum.HORIZONTAL : null; | |
85 | 92 | }); |
86 | 93 | |
87 | 94 | function handleToErrorList() { |
88 | - errorStore.commitErrorListCountState(0); | |
89 | - push('/exception/error-log'); | |
95 | + push(PageEnum.ERROR_LOG_PAGE).then(() => { | |
96 | + errorStore.commitErrorListCountState(0); | |
97 | + }); | |
90 | 98 | } |
91 | 99 | |
92 | 100 | /** |
... | ... | @@ -96,162 +104,129 @@ export default defineComponent({ |
96 | 104 | openModal(true); |
97 | 105 | } |
98 | 106 | |
99 | - return () => { | |
100 | - const getProjectConfig = unref(getProjectConfigRef); | |
101 | - const { | |
102 | - useErrorHandle, | |
103 | - showLogo, | |
104 | - multiTabsSetting: { show: showTab }, | |
105 | - headerSetting: { | |
106 | - theme: headerTheme, | |
107 | - useLockPage, | |
108 | - showRedo, | |
109 | - showGithub, | |
110 | - showFullScreen, | |
111 | - showNotice, | |
112 | - }, | |
113 | - menuSetting: { mode, type: menuType, split: splitMenu, topMenuAlign }, | |
114 | - showBreadCrumb, | |
115 | - showBreadCrumbIcon, | |
116 | - } = getProjectConfig; | |
107 | + function renderHeaderContent() { | |
108 | + const width = unref(widthRef); | |
109 | + return ( | |
110 | + <div class="layout-header__content "> | |
111 | + {unref(getShowHeaderLogo) && ( | |
112 | + <AppLogo class={`layout-header__logo`} theme={unref(getTheme)} /> | |
113 | + )} | |
117 | 114 | |
118 | - const isSidebarType = menuType === MenuTypeEnum.SIDEBAR; | |
115 | + {unref(getShowContent) && ( | |
116 | + <div class="layout-header__left"> | |
117 | + {unref(getShowHeaderTrigger) && ( | |
118 | + <LayoutTrigger theme={unref(getTheme)} sider={false} /> | |
119 | + )} | |
120 | + {unref(getShowBread) && <LayoutBreadcrumb showIcon={unref(getShowBreadCrumbIcon)} />} | |
121 | + </div> | |
122 | + )} | |
119 | 123 | |
120 | - const width = unref(widthRef); | |
124 | + {unref(getShowTopMenu) && ( | |
125 | + <div class={[`layout-header__menu `]} style={{ width: `calc(100% - ${width}px)` }}> | |
126 | + <LayoutMenu | |
127 | + isHorizontal={true} | |
128 | + class={`justify-${unref(getTopMenuAlign)}`} | |
129 | + theme={unref(getTheme)} | |
130 | + splitType={unref(getSplitType)} | |
131 | + menuMode={unref(getMenuMode)} | |
132 | + showSearch={false} | |
133 | + /> | |
134 | + </div> | |
135 | + )} | |
136 | + </div> | |
137 | + ); | |
138 | + } | |
121 | 139 | |
122 | - const showLeft = | |
123 | - (mode !== MenuModeEnum.HORIZONTAL && showBreadCrumb && !splitMenu) || | |
124 | - unref(showHeaderTrigger); | |
140 | + function renderActionDefault(Comp: Component | any, event: Fn) { | |
125 | 141 | return ( |
126 | - <Layout.Header class={['layout-header', 'flex p-0 px-4 ', unref(headerClass)]}> | |
127 | - {() => ( | |
128 | - <> | |
129 | - <div class="layout-header__content "> | |
130 | - {showLogo && !isSidebarType && ( | |
131 | - <Logo class={`layout-header__logo`} theme={headerTheme} /> | |
132 | - )} | |
133 | - | |
134 | - {showLeft && ( | |
135 | - <div class="layout-header__left"> | |
136 | - {unref(showHeaderTrigger) && ( | |
137 | - <LayoutTrigger theme={headerTheme} sider={false} /> | |
138 | - )} | |
139 | - {mode !== MenuModeEnum.HORIZONTAL && showBreadCrumb && !splitMenu && ( | |
140 | - <LayoutBreadcrumb showIcon={showBreadCrumbIcon} /> | |
141 | - )} | |
142 | - </div> | |
143 | - )} | |
142 | + <div class={`layout-header__action-item`} onClick={event}> | |
143 | + <Comp class={`layout-header__action-icon`} /> | |
144 | + </div> | |
145 | + ); | |
146 | + } | |
144 | 147 | |
145 | - {unref(showTopMenu) && ( | |
146 | - <div | |
147 | - class={[`layout-header__menu `]} | |
148 | - style={{ width: `calc(100% - ${unref(width)}px)` }} | |
148 | + function renderAction() { | |
149 | + return ( | |
150 | + <div class={`layout-header__action`}> | |
151 | + {unref(getUseErrorHandle) && ( | |
152 | + <Tooltip> | |
153 | + {{ | |
154 | + title: () => '错误日志', | |
155 | + default: () => ( | |
156 | + <Badge | |
157 | + count={errorStore.getErrorListCountState} | |
158 | + offset={[0, 10]} | |
159 | + dot | |
160 | + overflowCount={99} | |
149 | 161 | > |
150 | - <LayoutMenu | |
151 | - isTop={true} | |
152 | - class={`justify-${topMenuAlign}`} | |
153 | - theme={headerTheme} | |
154 | - splitType={splitMenu ? MenuSplitTyeEnum.TOP : MenuSplitTyeEnum.NONE} | |
155 | - menuMode={splitMenu ? MenuModeEnum.HORIZONTAL : null} | |
156 | - showSearch={false} | |
157 | - /> | |
158 | - </div> | |
159 | - )} | |
160 | - </div> | |
162 | + {() => renderActionDefault(BugOutlined, handleToErrorList)} | |
163 | + </Badge> | |
164 | + ), | |
165 | + }} | |
166 | + </Tooltip> | |
167 | + )} | |
161 | 168 | |
162 | - <div class={`layout-header__action`}> | |
163 | - {useErrorHandle && ( | |
164 | - <Tooltip> | |
165 | - {{ | |
166 | - title: () => '错误日志', | |
167 | - default: () => ( | |
168 | - <Badge | |
169 | - count={errorStore.getErrorListCountState} | |
170 | - offset={[0, 10]} | |
171 | - dot | |
172 | - overflowCount={99} | |
173 | - > | |
174 | - {() => ( | |
175 | - <div class={`layout-header__action-item`} onClick={handleToErrorList}> | |
176 | - <BugOutlined class={`layout-header__action-icon`} /> | |
177 | - </div> | |
178 | - )} | |
179 | - </Badge> | |
180 | - ), | |
181 | - }} | |
182 | - </Tooltip> | |
183 | - )} | |
169 | + {unref(getUseLockPage) && ( | |
170 | + <Tooltip> | |
171 | + {{ | |
172 | + title: () => '锁定屏幕', | |
173 | + default: () => renderActionDefault(LockOutlined, handleLockPage), | |
174 | + }} | |
175 | + </Tooltip> | |
176 | + )} | |
177 | + | |
178 | + {unref(getShowNotice) && ( | |
179 | + <Tooltip> | |
180 | + {{ | |
181 | + title: () => '消息通知', | |
182 | + default: () => <NoticeAction />, | |
183 | + }} | |
184 | + </Tooltip> | |
185 | + )} | |
184 | 186 | |
185 | - {showGithub && ( | |
186 | - <Tooltip> | |
187 | - {{ | |
188 | - title: () => 'github', | |
189 | - default: () => ( | |
190 | - <div class={`layout-header__action-item`} onClick={goToGithub}> | |
191 | - <GithubFilled class={`layout-header__action-icon`} /> | |
192 | - </div> | |
193 | - ), | |
194 | - }} | |
195 | - </Tooltip> | |
196 | - )} | |
197 | - {useLockPage && ( | |
198 | - <Tooltip> | |
199 | - {{ | |
200 | - title: () => '锁定屏幕', | |
201 | - default: () => ( | |
202 | - <div class={`layout-header__action-item`} onClick={handleLockPage}> | |
203 | - <LockOutlined class={`layout-header__action-icon`} /> | |
204 | - </div> | |
205 | - ), | |
206 | - }} | |
207 | - </Tooltip> | |
208 | - )} | |
209 | - {showNotice && ( | |
210 | - <div> | |
211 | - <Tooltip> | |
212 | - {{ | |
213 | - title: () => '消息通知', | |
214 | - default: () => <NoticeAction />, | |
215 | - }} | |
216 | - </Tooltip> | |
217 | - </div> | |
218 | - )} | |
219 | - {showRedo && showTab && ( | |
220 | - <Tooltip> | |
221 | - {{ | |
222 | - title: () => '刷新', | |
223 | - default: () => ( | |
224 | - <div class={`layout-header__action-item`} onClick={refreshPage}> | |
225 | - <RedoOutlined class={`layout-header__action-icon`} /> | |
226 | - </div> | |
227 | - ), | |
228 | - }} | |
229 | - </Tooltip> | |
230 | - )} | |
231 | - {showFullScreen && ( | |
232 | - <Tooltip> | |
233 | - {{ | |
234 | - title: () => (unref(isFullscreenRef) ? '退出全屏' : '全屏'), | |
235 | - default: () => { | |
236 | - const Icon: any = !unref(isFullscreenRef) ? ( | |
237 | - <FullscreenOutlined /> | |
238 | - ) : ( | |
239 | - <FullscreenExitOutlined /> | |
240 | - ); | |
241 | - return ( | |
242 | - <div class={`layout-header__action-item`} onClick={toggleFullscreen}> | |
243 | - <Icon class={`layout-header__action-icon`} /> | |
244 | - </div> | |
245 | - ); | |
246 | - }, | |
247 | - }} | |
248 | - </Tooltip> | |
249 | - )} | |
250 | - <UserDropdown class={`layout-header__user-dropdown`} /> | |
251 | - </div> | |
252 | - <LockAction onRegister={register} /> | |
253 | - </> | |
187 | + {unref(getShowRedo) && ( | |
188 | + <Tooltip> | |
189 | + {{ | |
190 | + title: () => '刷新', | |
191 | + default: () => renderActionDefault(RedoOutlined, refreshPage), | |
192 | + }} | |
193 | + </Tooltip> | |
254 | 194 | )} |
195 | + | |
196 | + {unref(getShowFullScreen) && ( | |
197 | + <Tooltip> | |
198 | + {{ | |
199 | + title: () => (unref(isFullscreenRef) ? '退出全屏' : '全屏'), | |
200 | + default: () => { | |
201 | + const Icon = !unref(isFullscreenRef) ? ( | |
202 | + <FullscreenOutlined /> | |
203 | + ) : ( | |
204 | + <FullscreenExitOutlined /> | |
205 | + ); | |
206 | + return renderActionDefault(Icon, toggleFullscreen); | |
207 | + }, | |
208 | + }} | |
209 | + </Tooltip> | |
210 | + )} | |
211 | + <UserDropdown class={`layout-header__user-dropdown`} /> | |
212 | + </div> | |
213 | + ); | |
214 | + } | |
215 | + | |
216 | + function renderHeaderDefault() { | |
217 | + return ( | |
218 | + <> | |
219 | + {renderHeaderContent()} | |
220 | + {renderAction()} | |
221 | + <LockAction onRegister={register} /> | |
222 | + </> | |
223 | + ); | |
224 | + } | |
225 | + | |
226 | + return () => { | |
227 | + return ( | |
228 | + <Layout.Header class={['layout-header', 'flex p-0 px-4 ', unref(headerClass)]}> | |
229 | + {() => renderHeaderDefault()} | |
255 | 230 | </Layout.Header> |
256 | 231 | ); |
257 | 232 | }; | ... | ... |
src/layouts/default/header/LockActionItem.less
src/layouts/default/header/LockActionItem.tsx
1 | -// 组件相关 | |
1 | +import './LockActionItem.less'; | |
2 | + | |
2 | 3 | import { defineComponent } from 'vue'; |
3 | 4 | import { BasicModal, useModalInner } from '/@/components/Modal/index'; |
4 | - | |
5 | -// hook | |
5 | +import Button from '/@/components/Button/index.vue'; | |
6 | 6 | import { BasicForm, useForm } from '/@/components/Form/index'; |
7 | 7 | |
8 | 8 | import headerImg from '/@/assets/images/header.jpg'; |
9 | 9 | |
10 | 10 | import { appStore } from '/@/store/modules/app'; |
11 | 11 | import { userStore } from '/@/store/modules/user'; |
12 | -import Button from '/@/components/Button/index.vue'; | |
13 | -import './LockActionItem.less'; | |
12 | + | |
14 | 13 | const prefixCls = 'lock-modal'; |
15 | 14 | export default defineComponent({ |
16 | 15 | name: 'LockModal', |
17 | 16 | setup(_, { attrs }) { |
18 | - const [register, { setModalProps }] = useModalInner(); | |
19 | - // 样式前缀 | |
17 | + const [register, { closeModal }] = useModalInner(); | |
18 | + | |
20 | 19 | const [registerForm, { validateFields, resetFields }] = useForm({ |
21 | - // 隐藏按钮 | |
22 | 20 | showActionButtonGroup: false, |
23 | - // 表单项 | |
24 | 21 | schemas: [ |
25 | 22 | { |
26 | 23 | field: 'password', |
27 | - label: '', | |
24 | + label: '锁屏密码', | |
28 | 25 | component: 'InputPassword', |
29 | - componentProps: { | |
30 | - placeholder: '请输入锁屏密码', | |
31 | - }, | |
32 | - rules: [{ required: true }], | |
26 | + required: true, | |
33 | 27 | }, |
34 | 28 | ], |
35 | 29 | }); |
36 | - /** | |
37 | - * @description: lock | |
38 | - */ | |
30 | + | |
39 | 31 | async function lock(valid = true) { |
40 | 32 | let password: string | undefined = ''; |
41 | 33 | |
... | ... | @@ -46,9 +38,7 @@ export default defineComponent({ |
46 | 38 | const values = (await validateFields()) as any; |
47 | 39 | password = values.password; |
48 | 40 | } |
49 | - setModalProps({ | |
50 | - visible: false, | |
51 | - }); | |
41 | + closeModal(); | |
52 | 42 | |
53 | 43 | appStore.commitLockInfoState({ |
54 | 44 | isLock: true, |
... | ... | @@ -57,7 +47,7 @@ export default defineComponent({ |
57 | 47 | await resetFields(); |
58 | 48 | } catch (error) {} |
59 | 49 | } |
60 | - // 账号密码登录 | |
50 | + | |
61 | 51 | return () => ( |
62 | 52 | <BasicModal footer={null} title="锁定屏幕" {...attrs} class={prefixCls} onRegister={register}> |
63 | 53 | {() => ( |
... | ... | @@ -66,7 +56,9 @@ export default defineComponent({ |
66 | 56 | <img src={headerImg} class={`${prefixCls}__header-img`} /> |
67 | 57 | <p class={`${prefixCls}__header-name`}>{userStore.getUserInfoState.realName}</p> |
68 | 58 | </div> |
69 | - <BasicForm onRegister={registerForm} /> | |
59 | + | |
60 | + <BasicForm onRegister={registerForm} layout="vertical" /> | |
61 | + | |
70 | 62 | <div class={`${prefixCls}__footer`}> |
71 | 63 | <Button type="primary" block class="mt-2" onClick={lock}> |
72 | 64 | {() => '锁屏'} | ... | ... |
src/layouts/default/header/UserDropdown.tsx
... | ... | @@ -11,15 +11,23 @@ import Icon from '/@/components/Icon/index'; |
11 | 11 | import { userStore } from '/@/store/modules/user'; |
12 | 12 | |
13 | 13 | import { DOC_URL } from '/@/settings/siteSetting'; |
14 | -import { appStore } from '/@/store/modules/app'; | |
14 | + | |
15 | +import { openWindow } from '/@/utils'; | |
16 | + | |
17 | +import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting'; | |
18 | + | |
19 | +interface RenderItemParams { | |
20 | + icon: string; | |
21 | + text: string; | |
22 | + key: string; | |
23 | +} | |
15 | 24 | |
16 | 25 | const prefixCls = 'user-dropdown'; |
26 | + | |
17 | 27 | export default defineComponent({ |
18 | 28 | name: 'UserDropdown', |
19 | 29 | setup() { |
20 | - const getProjectConfigRef = computed(() => { | |
21 | - return appStore.getProjectConfig; | |
22 | - }); | |
30 | + const { getShowDoc } = useHeaderSetting(); | |
23 | 31 | |
24 | 32 | const getUserInfo = computed(() => { |
25 | 33 | const { realName = '', desc } = userStore.getUserInfoState || {}; |
... | ... | @@ -33,7 +41,7 @@ export default defineComponent({ |
33 | 41 | |
34 | 42 | // open doc |
35 | 43 | function openDoc() { |
36 | - window.open(DOC_URL, '__blank'); | |
44 | + openWindow(DOC_URL); | |
37 | 45 | } |
38 | 46 | |
39 | 47 | function handleMenuClick(e: any) { |
... | ... | @@ -44,7 +52,7 @@ export default defineComponent({ |
44 | 52 | } |
45 | 53 | } |
46 | 54 | |
47 | - function renderItem({ icon, text, key }: { icon: string; text: string; key: string }) { | |
55 | + function renderItem({ icon, text, key }: RenderItemParams) { | |
48 | 56 | return ( |
49 | 57 | <Menu.Item key={key}> |
50 | 58 | {() => ( |
... | ... | @@ -57,37 +65,43 @@ export default defineComponent({ |
57 | 65 | ); |
58 | 66 | } |
59 | 67 | |
60 | - return () => { | |
68 | + function renderSlotsDefault() { | |
61 | 69 | const { realName } = unref(getUserInfo); |
62 | - const { | |
63 | - headerSetting: { showDoc }, | |
64 | - } = unref(getProjectConfigRef); | |
70 | + return ( | |
71 | + <section class={prefixCls}> | |
72 | + <img class={`${prefixCls}__header`} src={headerImg} /> | |
73 | + <section class={`${prefixCls}__info`}> | |
74 | + <section class={`${prefixCls}__name`}>{realName}</section> | |
75 | + </section> | |
76 | + </section> | |
77 | + ); | |
78 | + } | |
79 | + | |
80 | + function renderSlotOverlay() { | |
81 | + const showDoc = unref(getShowDoc); | |
82 | + return ( | |
83 | + <Menu onClick={handleMenuClick}> | |
84 | + {() => ( | |
85 | + <> | |
86 | + {showDoc && renderItem({ key: 'doc', text: '文档', icon: 'gg:loadbar-doc' })} | |
87 | + {showDoc && <Divider />} | |
88 | + {renderItem({ | |
89 | + key: 'loginOut', | |
90 | + text: '退出系统', | |
91 | + icon: 'ant-design:poweroff-outlined', | |
92 | + })} | |
93 | + </> | |
94 | + )} | |
95 | + </Menu> | |
96 | + ); | |
97 | + } | |
98 | + | |
99 | + return () => { | |
65 | 100 | return ( |
66 | 101 | <Dropdown placement="bottomLeft"> |
67 | 102 | {{ |
68 | - default: () => ( | |
69 | - <section class={prefixCls}> | |
70 | - <img class={`${prefixCls}__header`} src={headerImg} /> | |
71 | - <section class={`${prefixCls}__info`}> | |
72 | - <section class={`${prefixCls}__name`}>{realName}</section> | |
73 | - </section> | |
74 | - </section> | |
75 | - ), | |
76 | - overlay: () => ( | |
77 | - <Menu slot="overlay" onClick={handleMenuClick}> | |
78 | - {() => ( | |
79 | - <> | |
80 | - {showDoc && renderItem({ key: 'doc', text: '文档', icon: 'gg:loadbar-doc' })} | |
81 | - {showDoc && <Divider />} | |
82 | - {renderItem({ | |
83 | - key: 'loginOut', | |
84 | - text: '退出系统', | |
85 | - icon: 'ant-design:poweroff-outlined', | |
86 | - })} | |
87 | - </> | |
88 | - )} | |
89 | - </Menu> | |
90 | - ), | |
103 | + default: () => renderSlotsDefault(), | |
104 | + overlay: () => renderSlotOverlay(), | |
91 | 105 | }} |
92 | 106 | </Dropdown> |
93 | 107 | ); | ... | ... |
src/layouts/default/header/notice/NoticeActionItem.vue
1 | 1 | <template> |
2 | 2 | <div class="layout-header__action-item notify-action"> |
3 | - <Popover title="" trigger="click"> | |
3 | + <Popover title="" trigger="click" overlayClassName="layout-header__notify-action"> | |
4 | 4 | <Badge :count="count" dot :numberStyle="numberStyle"> |
5 | 5 | <BellOutlined class="layout-header__action-icon" /> |
6 | 6 | </Badge> |
... | ... | @@ -31,6 +31,7 @@ |
31 | 31 | components: { Popover, BellOutlined, Tabs, TabPane: Tabs.TabPane, Badge, NoticeList }, |
32 | 32 | setup() { |
33 | 33 | let count = 0; |
34 | + | |
34 | 35 | for (let i = 0; i < tabListData.length; i++) { |
35 | 36 | count += tabListData[i].list.length; |
36 | 37 | } |
... | ... | @@ -44,6 +45,10 @@ |
44 | 45 | }); |
45 | 46 | </script> |
46 | 47 | <style lang="less"> |
48 | + .layout-header__notify-action { | |
49 | + max-width: 360px; | |
50 | + } | |
51 | + | |
47 | 52 | .notify-action { |
48 | 53 | padding-top: 2px; |
49 | 54 | |
... | ... | @@ -56,7 +61,6 @@ |
56 | 61 | |
57 | 62 | .ant-badge-multiple-words { |
58 | 63 | padding: 0 4px; |
59 | - // transform: translate(26%, -40%); | |
60 | 64 | } |
61 | 65 | |
62 | 66 | svg { | ... | ... |
src/layouts/default/header/notice/NoticeList.vue
1 | 1 | <template> |
2 | - <List class="list"> | |
2 | + <a-list class="list"> | |
3 | 3 | <template v-for="item in list" :key="item.id"> |
4 | - <ListItem class="list__item"> | |
5 | - <ListItemMeta> | |
4 | + <a-list-item class="list-item"> | |
5 | + <a-list-item-meta> | |
6 | 6 | <template #title> |
7 | 7 | <div class="title"> |
8 | 8 | {{ item.title }} |
9 | 9 | <div class="extra" v-if="item.extra"> |
10 | - <Tag class="tag" :color="item.color"> | |
10 | + <a-tag class="tag" :color="item.color"> | |
11 | 11 | {{ item.extra }} |
12 | - </Tag> | |
12 | + </a-tag> | |
13 | 13 | </div> |
14 | 14 | </div> |
15 | 15 | </template> |
16 | + | |
16 | 17 | <template #avatar> |
17 | - <Avatar v-if="item.avatar" class="avatar" :src="item.avatar" /> | |
18 | + <a-avatar v-if="item.avatar" class="avatar" :src="item.avatar" /> | |
18 | 19 | <span v-else> {{ item.avatar }}</span> |
19 | 20 | </template> |
21 | + | |
20 | 22 | <template #description> |
21 | 23 | <div> |
22 | 24 | <div class="description">{{ item.description }}</div> |
23 | 25 | <div class="datetime">{{ item.datetime }}</div> |
24 | 26 | </div> |
25 | 27 | </template> |
26 | - </ListItemMeta> | |
27 | - </ListItem> | |
28 | + </a-list-item-meta> | |
29 | + </a-list-item> | |
28 | 30 | </template> |
29 | - </List> | |
31 | + </a-list> | |
30 | 32 | </template> |
31 | 33 | <script lang="ts"> |
32 | 34 | import { defineComponent, PropType } from 'vue'; |
33 | - import { List, Avatar, Tag } from 'ant-design-vue'; | |
34 | 35 | import { ListItem } from './data'; |
35 | 36 | |
36 | 37 | export default defineComponent({ |
... | ... | @@ -40,19 +41,6 @@ |
40 | 41 | default: () => [], |
41 | 42 | }, |
42 | 43 | }, |
43 | - components: { | |
44 | - List, | |
45 | - ListItem: List.Item, | |
46 | - ListItemMeta: List.Item.Meta, | |
47 | - Avatar, | |
48 | - Tag, | |
49 | - }, | |
50 | - setup(props) { | |
51 | - const { list = [] } = props; | |
52 | - return { | |
53 | - list, | |
54 | - }; | |
55 | - }, | |
56 | 44 | }); |
57 | 45 | </script> |
58 | 46 | <style lang="less" scoped> |
... | ... | @@ -61,7 +49,7 @@ |
61 | 49 | display: none; |
62 | 50 | } |
63 | 51 | |
64 | - &__item { | |
52 | + &-item { | |
65 | 53 | padding: 6px; |
66 | 54 | overflow: hidden; |
67 | 55 | cursor: pointer; | ... | ... |
src/layouts/default/header/notice/data.ts
... | ... | @@ -56,14 +56,6 @@ export const tabListData: TabItem[] = [ |
56 | 56 | datetime: '2017-08-07', |
57 | 57 | type: '1', |
58 | 58 | }, |
59 | - // { | |
60 | - // id: '000000005', | |
61 | - // avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png', | |
62 | - // title: '内容不要超过两行字,超出时自动截断', | |
63 | - // description: '', | |
64 | - // datetime: '2017-08-07', | |
65 | - // type: '1', | |
66 | - // }, | |
67 | 59 | ], |
68 | 60 | }, |
69 | 61 | { | ... | ... |
src/layouts/default/index.less
... | ... | @@ -36,47 +36,4 @@ |
36 | 36 | margin: 0 auto; |
37 | 37 | } |
38 | 38 | } |
39 | - | |
40 | - .layout-sidebar { | |
41 | - background-size: 100% 100%; | |
42 | - | |
43 | - &.ant-layout-sider-dark { | |
44 | - background: @sider-dark-bg-color; | |
45 | - } | |
46 | - | |
47 | - &:not(.ant-layout-sider-dark) { | |
48 | - border-right: 1px solid @border-color-light; | |
49 | - } | |
50 | - | |
51 | - .ant-layout-sider-zero-width-trigger { | |
52 | - top: 40%; | |
53 | - z-index: 10; | |
54 | - } | |
55 | - | |
56 | - &__dargbar { | |
57 | - position: absolute; | |
58 | - top: 0; | |
59 | - right: -2px; | |
60 | - z-index: @side-drag-z-index; | |
61 | - width: 2px; | |
62 | - height: 100%; | |
63 | - cursor: col-resize; | |
64 | - border-top: none; | |
65 | - border-bottom: none; | |
66 | - | |
67 | - &.hide { | |
68 | - display: none; | |
69 | - } | |
70 | - | |
71 | - &:hover { | |
72 | - background: @primary-color; | |
73 | - box-shadow: 0 0 4px 0 rgba(28, 36, 56, 0.15); | |
74 | - } | |
75 | - } | |
76 | - } | |
77 | -} | |
78 | - | |
79 | -.ant-layout-sider-trigger { | |
80 | - height: 36px; | |
81 | - line-height: 36px; | |
82 | 39 | } | ... | ... |
src/layouts/default/index.tsx
... | ... | @@ -4,7 +4,7 @@ import LayoutHeader from './header/LayoutHeader'; |
4 | 4 | |
5 | 5 | import { appStore } from '/@/store/modules/app'; |
6 | 6 | import LayoutContent from './LayoutContent'; |
7 | -import LayoutSideBar from './LayoutSideBar'; | |
7 | +import LayoutSideBar from './sider/LayoutSideBar'; | |
8 | 8 | import SettingBtn from './setting/index.vue'; |
9 | 9 | import MultipleTabs from './multitabs/index'; |
10 | 10 | |
... | ... | @@ -36,7 +36,7 @@ export default defineComponent({ |
36 | 36 | return show; |
37 | 37 | }); |
38 | 38 | |
39 | - const isShowMixHeaderRef = computed(() => { | |
39 | + const showMixHeaderRef = computed(() => { | |
40 | 40 | const { |
41 | 41 | menuSetting: { type }, |
42 | 42 | } = unref(getProjectConfigRef); |
... | ... | @@ -57,11 +57,11 @@ export default defineComponent({ |
57 | 57 | }); |
58 | 58 | |
59 | 59 | const showFullHeaderRef = computed(() => { |
60 | - return !unref(getFullContent) && unref(isShowMixHeaderRef) && unref(showHeaderRef); | |
60 | + return !unref(getFullContent) && unref(showMixHeaderRef) && unref(showHeaderRef); | |
61 | 61 | }); |
62 | 62 | |
63 | 63 | const showInsetHeaderRef = computed(() => { |
64 | - return !unref(getFullContent) && !unref(isShowMixHeaderRef) && unref(showHeaderRef); | |
64 | + return !unref(getFullContent) && !unref(showMixHeaderRef) && unref(showHeaderRef); | |
65 | 65 | }); |
66 | 66 | |
67 | 67 | const fixedHeaderClsRef = computed(() => { | ... | ... |
src/layouts/default/menu/LayoutMenu.tsx
1 | -import type { PropType } from 'vue'; | |
1 | +import './index.less'; | |
2 | + | |
3 | +import { PropType, toRef } from 'vue'; | |
2 | 4 | import type { Menu } from '/@/router/types'; |
3 | 5 | |
4 | -import { computed, defineComponent, unref, ref, onMounted, watch } from 'vue'; | |
6 | +import { computed, defineComponent, unref } from 'vue'; | |
5 | 7 | import { BasicMenu } from '/@/components/Menu/index'; |
6 | -import Logo from '/@/layouts/logo/index.vue'; | |
7 | - | |
8 | -import { MenuModeEnum, MenuSplitTyeEnum, MenuTypeEnum } from '/@/enums/menuEnum'; | |
9 | - | |
10 | -// store | |
11 | -import { appStore } from '/@/store/modules/app'; | |
12 | -import { menuStore } from '/@/store/modules/menu'; | |
13 | - | |
14 | -import { | |
15 | - getMenus, | |
16 | - getFlatMenus, | |
17 | - getShallowMenus, | |
18 | - getChildrenMenus, | |
19 | - getFlatChildrenMenus, | |
20 | - getCurrentParentPath, | |
21 | -} from '/@/router/menus/index'; | |
22 | -import { useRouter } from 'vue-router'; | |
23 | -import { useThrottle } from '/@/hooks/core/useThrottle'; | |
24 | -import { permissionStore } from '/@/store/modules/permission'; | |
8 | +import { AppLogo } from '/@/components/Application'; | |
9 | + | |
10 | +import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum'; | |
11 | + | |
12 | +import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; | |
13 | +import { useRootSetting } from '/@/hooks/setting/useRootSetting'; | |
14 | + | |
15 | +import { useGo } from '/@/hooks/web/usePage'; | |
16 | +import { useSplitMenu } from './useLayoutMenu'; | |
17 | +import { openWindow } from '/@/utils'; | |
25 | 18 | |
26 | -import './index.less'; | |
27 | 19 | export default defineComponent({ |
28 | 20 | name: 'DefaultLayoutMenu', |
29 | 21 | props: { |
... | ... | @@ -43,7 +35,7 @@ export default defineComponent({ |
43 | 35 | type: Boolean as PropType<boolean>, |
44 | 36 | default: true, |
45 | 37 | }, |
46 | - isTop: { | |
38 | + isHorizontal: { | |
47 | 39 | type: Boolean as PropType<boolean>, |
48 | 40 | default: false, |
49 | 41 | }, |
... | ... | @@ -53,190 +45,99 @@ export default defineComponent({ |
53 | 45 | }, |
54 | 46 | }, |
55 | 47 | setup(props) { |
56 | - // Menu array | |
57 | - const menusRef = ref<Menu[]>([]); | |
58 | - // flat menu array | |
59 | - const flatMenusRef = ref<Menu[]>([]); | |
60 | - const { currentRoute, push } = useRouter(); | |
61 | - | |
62 | - // get app config | |
63 | - const getProjectConfigRef = computed(() => { | |
64 | - return appStore.getProjectConfig; | |
65 | - }); | |
48 | + const go = useGo(); | |
66 | 49 | |
67 | - // get is Horizontal | |
68 | - const getIsHorizontalRef = computed(() => { | |
69 | - return unref(getProjectConfigRef).menuSetting.mode === MenuModeEnum.HORIZONTAL; | |
70 | - }); | |
50 | + const { | |
51 | + setMenuSetting, | |
52 | + getShowSearch, | |
53 | + getMode, | |
54 | + getType, | |
55 | + getCollapsedShowTitle, | |
56 | + getCollapsedShowSearch, | |
57 | + getIsSidebarType, | |
58 | + getTheme, | |
59 | + getCollapsed, | |
60 | + getAccordion, | |
61 | + } = useMenuSetting(); | |
71 | 62 | |
72 | - const [throttleHandleSplitLeftMenu] = useThrottle(handleSplitLeftMenu, 50); | |
73 | - | |
74 | - // Route change split menu | |
75 | - watch( | |
76 | - [() => unref(currentRoute).path, () => props.splitType], | |
77 | - async ([path, splitType]: [string, MenuSplitTyeEnum]) => { | |
78 | - if (splitType !== MenuSplitTyeEnum.LEFT && !unref(getIsHorizontalRef)) return; | |
79 | - const parentPath = await getCurrentParentPath(path); | |
80 | - parentPath && throttleHandleSplitLeftMenu(parentPath); | |
81 | - }, | |
82 | - { | |
83 | - immediate: true, | |
84 | - } | |
85 | - ); | |
63 | + const { getShowLogo } = useRootSetting(); | |
86 | 64 | |
87 | - // Menu changes | |
88 | - watch( | |
89 | - [() => permissionStore.getLastBuildMenuTimeState, () => permissionStore.getBackMenuListState], | |
90 | - () => { | |
91 | - genMenus(); | |
92 | - } | |
93 | - ); | |
94 | - | |
95 | - // split Menu changes | |
96 | - watch([() => appStore.getProjectConfig.menuSetting.split], () => { | |
97 | - if (props.splitType !== MenuSplitTyeEnum.LEFT && !unref(getIsHorizontalRef)) return; | |
98 | - genMenus(); | |
99 | - }); | |
65 | + const { flatMenusRef, menusRef } = useSplitMenu(toRef(props, 'splitType')); | |
100 | 66 | |
101 | - // Handle left menu split | |
102 | - async function handleSplitLeftMenu(parentPath: string) { | |
103 | - const isSplitMenu = unref(getProjectConfigRef).menuSetting.split; | |
104 | - if (!isSplitMenu) return; | |
105 | - const { splitType } = props; | |
106 | - // spilt mode left | |
107 | - if (splitType === MenuSplitTyeEnum.LEFT) { | |
108 | - const children = await getChildrenMenus(parentPath); | |
109 | - if (!children) { | |
110 | - appStore.commitProjectConfigState({ | |
111 | - menuSetting: { | |
112 | - hidden: false, | |
113 | - }, | |
114 | - }); | |
115 | - flatMenusRef.value = []; | |
116 | - menusRef.value = []; | |
117 | - return; | |
118 | - } | |
119 | - const flatChildren = await getFlatChildrenMenus(children); | |
120 | - appStore.commitProjectConfigState({ | |
121 | - menuSetting: { | |
122 | - hidden: true, | |
123 | - }, | |
124 | - }); | |
125 | - flatMenusRef.value = flatChildren; | |
126 | - menusRef.value = children; | |
127 | - } | |
128 | - } | |
67 | + const showLogo = computed(() => unref(getShowLogo) && unref(getIsSidebarType)); | |
129 | 68 | |
130 | - // get menus | |
131 | - async function genMenus() { | |
132 | - const isSplitMenu = unref(getProjectConfigRef).menuSetting.split; | |
69 | + const getMenuMode = computed(() => props.menuMode || unref(getMode)); | |
133 | 70 | |
134 | - // normal mode | |
135 | - const { splitType } = props; | |
136 | - if (splitType === MenuSplitTyeEnum.NONE || !isSplitMenu) { | |
137 | - flatMenusRef.value = await getFlatMenus(); | |
138 | - menusRef.value = await getMenus(); | |
139 | - return; | |
140 | - } | |
71 | + const getMenuTheme = computed(() => props.theme || unref(getTheme)); | |
141 | 72 | |
142 | - // split-top | |
143 | - if (splitType === MenuSplitTyeEnum.TOP) { | |
144 | - const parentPath = await getCurrentParentPath(unref(currentRoute).path); | |
145 | - menuStore.commitCurrentTopSplitMenuPathState(parentPath); | |
146 | - const shallowMenus = await getShallowMenus(); | |
73 | + const appendClass = computed(() => props.splitType === MenuSplitTyeEnum.TOP); | |
147 | 74 | |
148 | - flatMenusRef.value = shallowMenus; | |
149 | - menusRef.value = shallowMenus; | |
150 | - return; | |
151 | - } | |
152 | - } | |
75 | + const showSearch = computed(() => { | |
76 | + return ( | |
77 | + unref(getShowSearch) && | |
78 | + props.showSearch && | |
79 | + (unref(getCollapsedShowSearch) ? true : !unref(getCollapsed)) | |
80 | + ); | |
81 | + }); | |
153 | 82 | |
83 | + /** | |
84 | + * click menu | |
85 | + * @param menu | |
86 | + */ | |
154 | 87 | function handleMenuClick(menu: Menu) { |
155 | - const { path } = menu; | |
156 | - if (path) { | |
157 | - push(path); | |
158 | - const { splitType } = props; | |
159 | - // split mode top | |
160 | - if (splitType === MenuSplitTyeEnum.TOP) { | |
161 | - menuStore.commitCurrentTopSplitMenuPathState(path); | |
162 | - } | |
163 | - } | |
88 | + go(menu.path); | |
164 | 89 | } |
165 | 90 | |
91 | + /** | |
92 | + * before click menu | |
93 | + * @param menu | |
94 | + */ | |
166 | 95 | async function beforeMenuClickFn(menu: Menu) { |
167 | 96 | const { meta: { externalLink } = {} } = menu; |
168 | 97 | |
169 | 98 | if (externalLink) { |
170 | - window.open(externalLink, '_blank'); | |
99 | + openWindow(externalLink); | |
171 | 100 | return false; |
172 | 101 | } |
173 | - | |
174 | 102 | return true; |
175 | 103 | } |
176 | 104 | |
177 | 105 | function handleClickSearchInput() { |
178 | - if (menuStore.getCollapsedState) { | |
179 | - menuStore.commitCollapsedState(false); | |
180 | - } | |
106 | + unref(getCollapsed) && setMenuSetting({ collapsed: false }); | |
181 | 107 | } |
182 | 108 | |
183 | - const showSearchRef = computed(() => { | |
184 | - const { showSearch, type, mode } = unref(getProjectConfigRef).menuSetting; | |
109 | + function renderHeader() { | |
110 | + if (!unref(showLogo)) return null; | |
185 | 111 | return ( |
186 | - showSearch && | |
187 | - props.showSearch && | |
188 | - !(type === MenuTypeEnum.MIX && mode === MenuModeEnum.HORIZONTAL) | |
112 | + <AppLogo | |
113 | + showTitle={!unref(getCollapsed)} | |
114 | + class={[`layout-menu__logo`, unref(getMenuTheme)]} | |
115 | + theme={unref(getMenuTheme)} | |
116 | + /> | |
189 | 117 | ); |
190 | - }); | |
191 | - | |
192 | - onMounted(() => { | |
193 | - genMenus(); | |
194 | - }); | |
118 | + } | |
195 | 119 | |
196 | 120 | return () => { |
197 | - const { | |
198 | - showLogo, | |
199 | - menuSetting: { | |
200 | - type: menuType, | |
201 | - mode, | |
202 | - theme, | |
203 | - collapsed, | |
204 | - collapsedShowTitle, | |
205 | - collapsedShowSearch, | |
206 | - accordion, | |
207 | - }, | |
208 | - } = unref(getProjectConfigRef); | |
209 | - | |
210 | - const isSidebarType = menuType === MenuTypeEnum.SIDEBAR; | |
211 | - const isShowLogo = showLogo && isSidebarType; | |
212 | - const themeData = props.theme || theme; | |
213 | 121 | return ( |
214 | 122 | <BasicMenu |
215 | - beforeClickFn={beforeMenuClickFn} | |
216 | - onMenuClick={handleMenuClick} | |
217 | - type={menuType} | |
218 | - mode={props.menuMode || mode} | |
219 | 123 | class="layout-menu" |
220 | - collapsedShowTitle={collapsedShowTitle} | |
221 | - theme={themeData} | |
222 | - showLogo={isShowLogo} | |
223 | - search={unref(showSearchRef) && (collapsedShowSearch ? true : !collapsed)} | |
124 | + beforeClickFn={beforeMenuClickFn} | |
125 | + isHorizontal={props.isHorizontal} | |
126 | + appendClass={unref(appendClass)} | |
127 | + type={unref(getType)} | |
128 | + mode={unref(getMenuMode)} | |
129 | + collapsedShowTitle={unref(getCollapsedShowTitle)} | |
130 | + theme={unref(getMenuTheme)} | |
131 | + showLogo={unref(showLogo)} | |
132 | + search={unref(showSearch)} | |
224 | 133 | items={unref(menusRef)} |
225 | 134 | flatItems={unref(flatMenusRef)} |
135 | + accordion={unref(getAccordion)} | |
136 | + onMenuClick={handleMenuClick} | |
226 | 137 | onClickSearchInput={handleClickSearchInput} |
227 | - appendClass={props.splitType === MenuSplitTyeEnum.TOP} | |
228 | - isTop={props.isTop} | |
229 | - accordion={accordion} | |
230 | 138 | > |
231 | 139 | {{ |
232 | - header: () => | |
233 | - isShowLogo && ( | |
234 | - <Logo | |
235 | - showTitle={!collapsed} | |
236 | - class={[`layout-menu__logo`, themeData]} | |
237 | - theme={themeData} | |
238 | - /> | |
239 | - ), | |
140 | + header: () => renderHeader(), | |
240 | 141 | }} |
241 | 142 | </BasicMenu> |
242 | 143 | ); | ... | ... |
src/layouts/default/menu/index.less
src/layouts/default/menu/useLayoutMenu.ts
0 → 100644
1 | +import type { Menu } from '/@/router/types'; | |
2 | +import type { Ref } from 'vue'; | |
3 | + | |
4 | +import { watch, unref, ref, computed } from 'vue'; | |
5 | +import { useRouter } from 'vue-router'; | |
6 | + | |
7 | +import { MenuSplitTyeEnum } from '/@/enums/menuEnum'; | |
8 | +import { useThrottle } from '/@/hooks/core/useThrottle'; | |
9 | +import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; | |
10 | + | |
11 | +import { | |
12 | + getChildrenMenus, | |
13 | + getCurrentParentPath, | |
14 | + getFlatChildrenMenus, | |
15 | + getFlatMenus, | |
16 | + getMenus, | |
17 | + getShallowMenus, | |
18 | +} from '/@/router/menus'; | |
19 | +import { permissionStore } from '/@/store/modules/permission'; | |
20 | + | |
21 | +export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) { | |
22 | + // Menu array | |
23 | + const menusRef = ref<Menu[]>([]); | |
24 | + // flat menu array | |
25 | + const flatMenusRef = ref<Menu[]>([]); | |
26 | + | |
27 | + const { currentRoute } = useRouter(); | |
28 | + | |
29 | + const { setMenuSetting, getIsHorizontal, getSplit } = useMenuSetting(); | |
30 | + | |
31 | + const [throttleHandleSplitLeftMenu] = useThrottle(handleSplitLeftMenu, 50); | |
32 | + | |
33 | + const splitNotLeft = computed( | |
34 | + () => unref(splitType) !== MenuSplitTyeEnum.LEFT && !unref(getIsHorizontal) | |
35 | + ); | |
36 | + | |
37 | + const splitLeft = computed(() => !unref(getSplit) || unref(splitType) !== MenuSplitTyeEnum.LEFT); | |
38 | + | |
39 | + const spiltTop = computed(() => unref(splitType) === MenuSplitTyeEnum.TOP); | |
40 | + | |
41 | + const normalType = computed(() => { | |
42 | + return unref(splitType) === MenuSplitTyeEnum.NONE || !unref(getSplit); | |
43 | + }); | |
44 | + | |
45 | + watch( | |
46 | + [() => unref(currentRoute).path, () => unref(splitType)], | |
47 | + async ([path]: [string, MenuSplitTyeEnum]) => { | |
48 | + if (unref(splitNotLeft)) return; | |
49 | + | |
50 | + const parentPath = await getCurrentParentPath(path); | |
51 | + parentPath && throttleHandleSplitLeftMenu(parentPath); | |
52 | + }, | |
53 | + { | |
54 | + immediate: true, | |
55 | + } | |
56 | + ); | |
57 | + | |
58 | + // Menu changes | |
59 | + watch( | |
60 | + [() => permissionStore.getLastBuildMenuTimeState, () => permissionStore.getBackMenuListState], | |
61 | + () => { | |
62 | + genMenus(); | |
63 | + }, | |
64 | + { | |
65 | + immediate: true, | |
66 | + } | |
67 | + ); | |
68 | + | |
69 | + // split Menu changes | |
70 | + watch([() => getSplit.value], () => { | |
71 | + if (unref(splitNotLeft)) return; | |
72 | + genMenus(); | |
73 | + }); | |
74 | + | |
75 | + // Handle left menu split | |
76 | + async function handleSplitLeftMenu(parentPath: string) { | |
77 | + if (unref(splitLeft)) return; | |
78 | + | |
79 | + // spilt mode left | |
80 | + const children = await getChildrenMenus(parentPath); | |
81 | + if (!children) { | |
82 | + setMenuSetting({ hidden: false }); | |
83 | + flatMenusRef.value = []; | |
84 | + menusRef.value = []; | |
85 | + return; | |
86 | + } | |
87 | + | |
88 | + const flatChildren = await getFlatChildrenMenus(children); | |
89 | + setMenuSetting({ hidden: true }); | |
90 | + flatMenusRef.value = flatChildren; | |
91 | + menusRef.value = children; | |
92 | + } | |
93 | + | |
94 | + // get menus | |
95 | + async function genMenus() { | |
96 | + // normal mode | |
97 | + if (unref(normalType)) { | |
98 | + flatMenusRef.value = await getFlatMenus(); | |
99 | + menusRef.value = await getMenus(); | |
100 | + return; | |
101 | + } | |
102 | + | |
103 | + // split-top | |
104 | + if (unref(spiltTop)) { | |
105 | + const shallowMenus = await getShallowMenus(); | |
106 | + | |
107 | + flatMenusRef.value = shallowMenus; | |
108 | + menusRef.value = shallowMenus; | |
109 | + return; | |
110 | + } | |
111 | + } | |
112 | + return { flatMenusRef, menusRef }; | |
113 | +} | ... | ... |
src/layouts/default/multitabs/index.tsx
... | ... | @@ -33,7 +33,7 @@ export default defineComponent({ |
33 | 33 | return tabStore.getTabsState; |
34 | 34 | }); |
35 | 35 | |
36 | - // If you monitor routing changes, tab switching will be stuck. So use this method | |
36 | + // If you monitor routing changes, tab switching will be stuck. So setting this method | |
37 | 37 | watch( |
38 | 38 | () => tabStore.getLastChangeRouteState, |
39 | 39 | () => { | ... | ... |
src/layouts/default/sider/LayoutSideBar.tsx
0 → 100644
1 | +import './index.less'; | |
2 | + | |
3 | +import { computed, defineComponent, ref, unref } from 'vue'; | |
4 | + | |
5 | +import { Layout } from 'ant-design-vue'; | |
6 | +import LayoutMenu from '/@/layouts/default/menu/LayoutMenu'; | |
7 | + | |
8 | +import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum'; | |
9 | + | |
10 | +import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; | |
11 | +import { useTrigger, useDragLine, useSiderEvent } from './useLayoutSider'; | |
12 | + | |
13 | +export default defineComponent({ | |
14 | + name: 'LayoutSideBar', | |
15 | + setup() { | |
16 | + const dragBarRef = ref<Nullable<HTMLDivElement>>(null); | |
17 | + const sideRef = ref<Nullable<HTMLDivElement>>(null); | |
18 | + | |
19 | + const { getCollapsed, getMenuWidth, getSplit, getTheme } = useMenuSetting(); | |
20 | + | |
21 | + const { getTriggerAttr, getTriggerSlot } = useTrigger(); | |
22 | + | |
23 | + const { renderDragLine } = useDragLine(sideRef, dragBarRef); | |
24 | + | |
25 | + const { | |
26 | + getCollapsedWidth, | |
27 | + onBreakpointChange, | |
28 | + onCollapseChange, | |
29 | + onSiderClick, | |
30 | + } = useSiderEvent(); | |
31 | + | |
32 | + const getMode = computed(() => { | |
33 | + return unref(getSplit) ? MenuModeEnum.INLINE : null; | |
34 | + }); | |
35 | + | |
36 | + const getSplitType = computed(() => { | |
37 | + return unref(getSplit) ? MenuSplitTyeEnum.LEFT : MenuSplitTyeEnum.NONE; | |
38 | + }); | |
39 | + | |
40 | + function renderDefault() { | |
41 | + return ( | |
42 | + <> | |
43 | + <LayoutMenu | |
44 | + theme={unref(getTheme)} | |
45 | + menuMode={unref(getMode)} | |
46 | + splitType={unref(getSplitType)} | |
47 | + /> | |
48 | + {renderDragLine()} | |
49 | + </> | |
50 | + ); | |
51 | + } | |
52 | + | |
53 | + return () => { | |
54 | + 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> | |
74 | + ); | |
75 | + }; | |
76 | + }, | |
77 | +}); | ... | ... |
src/layouts/default/sider/index.less
0 → 100644
1 | +@import (reference) '../../../design/index.less'; | |
2 | + | |
3 | +.layout-sidebar { | |
4 | + background-size: 100% 100%; | |
5 | + | |
6 | + &.ant-layout-sider-dark { | |
7 | + background: @sider-dark-bg-color; | |
8 | + } | |
9 | + | |
10 | + &:not(.ant-layout-sider-dark) { | |
11 | + border-right: 1px solid @border-color-light; | |
12 | + } | |
13 | + | |
14 | + .ant-layout-sider-zero-width-trigger { | |
15 | + top: 40%; | |
16 | + z-index: 10; | |
17 | + } | |
18 | + | |
19 | + &__darg-bar { | |
20 | + position: absolute; | |
21 | + top: 0; | |
22 | + right: -2px; | |
23 | + z-index: @side-drag-z-index; | |
24 | + width: 2px; | |
25 | + height: 100%; | |
26 | + cursor: col-resize; | |
27 | + border-top: none; | |
28 | + border-bottom: none; | |
29 | + | |
30 | + &.hide { | |
31 | + display: none; | |
32 | + } | |
33 | + | |
34 | + &:hover { | |
35 | + background: @primary-color; | |
36 | + box-shadow: 0 0 4px 0 rgba(28, 36, 56, 0.15); | |
37 | + } | |
38 | + } | |
39 | +} | |
40 | + | |
41 | +.ant-layout-sider-trigger { | |
42 | + height: 36px; | |
43 | + line-height: 36px; | |
44 | +} | ... | ... |
src/layouts/default/sider/useLayoutSider.tsx
0 → 100644
1 | +import type { Ref } from 'vue'; | |
2 | + | |
3 | +import { computed, unref, onMounted, nextTick, ref } from 'vue'; | |
4 | +import LayoutTrigger from '/@/layouts/default/LayoutTrigger'; | |
5 | + | |
6 | +import { TriggerEnum } from '/@/enums/menuEnum'; | |
7 | + | |
8 | +import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; | |
9 | +import { useDebounce } from '/@/hooks/core/useDebounce'; | |
10 | + | |
11 | +/** | |
12 | + * Handle related operations of menu events | |
13 | + */ | |
14 | +export function useSiderEvent() { | |
15 | + const initRef = ref(false); | |
16 | + const brokenRef = ref(false); | |
17 | + const collapseRef = ref(true); | |
18 | + | |
19 | + const { setMenuSetting, getCollapsed, getMiniWidthNumber, getShow } = useMenuSetting(); | |
20 | + | |
21 | + const getCollapsedWidth = computed(() => { | |
22 | + return unref(brokenRef) ? 0 : unref(getMiniWidthNumber); | |
23 | + }); | |
24 | + | |
25 | + function onCollapseChange(val: boolean) { | |
26 | + if (initRef.value) { | |
27 | + collapseRef.value = val; | |
28 | + setMenuSetting({ collapsed: val }); | |
29 | + } else { | |
30 | + !unref(getCollapsed) && setMenuSetting({ collapsed: val }); | |
31 | + } | |
32 | + initRef.value = true; | |
33 | + } | |
34 | + | |
35 | + function onBreakpointChange(broken: boolean) { | |
36 | + brokenRef.value = broken; | |
37 | + } | |
38 | + | |
39 | + function onSiderClick(e: ChangeEvent) { | |
40 | + if (!e || !e.target || e.target.className !== 'basic-menu__content') return; | |
41 | + if (!unref(getCollapsed) || !unref(getShow)) return; | |
42 | + setMenuSetting({ collapsed: false }); | |
43 | + } | |
44 | + return { getCollapsedWidth, onCollapseChange, onBreakpointChange, onSiderClick }; | |
45 | +} | |
46 | + | |
47 | +/** | |
48 | + * Handle related operations of menu folding | |
49 | + */ | |
50 | +export function useTrigger() { | |
51 | + const { getTrigger } = useMenuSetting(); | |
52 | + | |
53 | + const showTrigger = computed(() => { | |
54 | + const trigger = unref(getTrigger); | |
55 | + return trigger !== TriggerEnum.NONE && trigger === TriggerEnum.FOOTER; | |
56 | + }); | |
57 | + | |
58 | + const getTriggerAttr = computed(() => { | |
59 | + if (unref(showTrigger)) { | |
60 | + return {}; | |
61 | + } | |
62 | + return { | |
63 | + trigger: null, | |
64 | + }; | |
65 | + }); | |
66 | + | |
67 | + const getTriggerSlot = computed(() => { | |
68 | + if (unref(showTrigger)) { | |
69 | + return { | |
70 | + trigger: () => <LayoutTrigger />, | |
71 | + }; | |
72 | + } | |
73 | + return {}; | |
74 | + }); | |
75 | + | |
76 | + return { getTriggerAttr, getTriggerSlot }; | |
77 | +} | |
78 | + | |
79 | +/** | |
80 | + * Handle menu drag and drop related operations | |
81 | + * @param siderRef | |
82 | + * @param dragBarRef | |
83 | + */ | |
84 | +export function useDragLine(siderRef: Ref<any>, dragBarRef: Ref<any>) { | |
85 | + const { getMiniWidthNumber, getCollapsed, setMenuSetting, getHasDrag } = useMenuSetting(); | |
86 | + | |
87 | + const getDragBarStyle = computed(() => { | |
88 | + if (unref(getCollapsed)) { | |
89 | + return { left: `${unref(getMiniWidthNumber)}px` }; | |
90 | + } | |
91 | + return {}; | |
92 | + }); | |
93 | + | |
94 | + onMounted(() => { | |
95 | + nextTick(() => { | |
96 | + const [exec] = useDebounce(changeWrapWidth, 20); | |
97 | + exec(); | |
98 | + }); | |
99 | + }); | |
100 | + | |
101 | + function renderDragLine() { | |
102 | + return ( | |
103 | + <div | |
104 | + class={[`layout-sidebar__darg-bar`, !unref(getHasDrag) ? 'hide' : '']} | |
105 | + style={unref(getDragBarStyle)} | |
106 | + ref={dragBarRef} | |
107 | + /> | |
108 | + ); | |
109 | + } | |
110 | + | |
111 | + function handleMouseMove(ele: HTMLElement, wrap: HTMLElement, clientX: number) { | |
112 | + document.onmousemove = function (innerE) { | |
113 | + let iT = (ele as any).left + (innerE.clientX - clientX); | |
114 | + innerE = innerE || window.event; | |
115 | + const maxT = 600; | |
116 | + const minT = unref(getMiniWidthNumber); | |
117 | + iT < 0 && (iT = 0); | |
118 | + iT > maxT && (iT = maxT); | |
119 | + iT < minT && (iT = minT); | |
120 | + ele.style.left = wrap.style.width = iT + 'px'; | |
121 | + return false; | |
122 | + }; | |
123 | + } | |
124 | + | |
125 | + // Drag and drop in the menu area-release the mouse | |
126 | + function removeMouseup(ele: any) { | |
127 | + const wrap = unref(siderRef).$el; | |
128 | + document.onmouseup = function () { | |
129 | + document.onmousemove = null; | |
130 | + document.onmouseup = null; | |
131 | + const width = parseInt(wrap.style.width); | |
132 | + const miniWidth = unref(getMiniWidthNumber); | |
133 | + | |
134 | + if (!unref(getCollapsed)) { | |
135 | + width > miniWidth + 20 | |
136 | + ? setMenuSetting({ menuWidth: width }) | |
137 | + : setMenuSetting({ collapsed: true }); | |
138 | + } else { | |
139 | + width > miniWidth && setMenuSetting({ collapsed: false, menuWidth: width }); | |
140 | + } | |
141 | + ele.releaseCapture?.(); | |
142 | + }; | |
143 | + } | |
144 | + | |
145 | + function changeWrapWidth() { | |
146 | + const ele = unref(dragBarRef) as any; | |
147 | + const side = unref(siderRef); | |
148 | + | |
149 | + const wrap = (side || {}).$el; | |
150 | + ele && | |
151 | + (ele.onmousedown = (e: any) => { | |
152 | + wrap.style.transition = 'unset'; | |
153 | + const clientX = e?.clientX; | |
154 | + ele.left = ele.offsetLeft; | |
155 | + handleMouseMove(ele, wrap, clientX); | |
156 | + removeMouseup(ele); | |
157 | + ele.setCapture?.(); | |
158 | + return false; | |
159 | + }); | |
160 | + } | |
161 | + | |
162 | + return { renderDragLine }; | |
163 | +} | ... | ... |
src/layouts/iframe/useFrameKeepAlive.ts
... | ... | @@ -5,12 +5,29 @@ import { useRouter } from 'vue-router'; |
5 | 5 | import router from '/@/router'; |
6 | 6 | |
7 | 7 | import { tabStore } from '/@/store/modules/tab'; |
8 | -import { appStore } from '/@/store/modules/app'; | |
9 | 8 | |
10 | 9 | import { unique } from '/@/utils'; |
11 | 10 | |
11 | +import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting'; | |
12 | + | |
12 | 13 | export function useFrameKeepAlive() { |
13 | 14 | const { currentRoute } = useRouter(); |
15 | + const { getShow } = useMultipleTabSetting(); | |
16 | + | |
17 | + const getFramePages = computed(() => { | |
18 | + const ret = | |
19 | + getAllFramePages((toRaw(router.getRoutes()) as unknown) as AppRouteRecordRaw[]) || []; | |
20 | + return ret; | |
21 | + }); | |
22 | + | |
23 | + const getOpenTabList = computed((): string[] => { | |
24 | + return tabStore.getTabsState.reduce((prev: string[], next) => { | |
25 | + if (next.meta && Reflect.has(next.meta, 'frameSrc')) { | |
26 | + prev.push(next.path!); | |
27 | + } | |
28 | + return prev; | |
29 | + }, []); | |
30 | + }); | |
14 | 31 | |
15 | 32 | function getAllFramePages(routes: AppRouteRecordRaw[]): AppRouteRecordRaw[] { |
16 | 33 | let res: AppRouteRecordRaw[] = []; |
... | ... | @@ -30,26 +47,9 @@ export function useFrameKeepAlive() { |
30 | 47 | function showIframe(item: AppRouteRecordRaw) { |
31 | 48 | return item.path === unref(currentRoute).path; |
32 | 49 | } |
33 | - const getFramePages = computed(() => { | |
34 | - const ret = | |
35 | - getAllFramePages((toRaw(router.getRoutes()) as unknown) as AppRouteRecordRaw[]) || []; | |
36 | - return ret; | |
37 | - }); | |
38 | - | |
39 | - const getOpenTabList = computed((): string[] => { | |
40 | - return tabStore.getTabsState.reduce((prev: string[], next) => { | |
41 | - if (next.meta && Reflect.has(next.meta, 'frameSrc')) { | |
42 | - prev.push(next.path!); | |
43 | - } | |
44 | - return prev; | |
45 | - }, []); | |
46 | - }); | |
47 | 50 | |
48 | 51 | function hasRenderFrame(path: string) { |
49 | - const { | |
50 | - multiTabsSetting: { show }, | |
51 | - } = appStore.getProjectConfig; | |
52 | - return show ? unref(getOpenTabList).includes(path) : true; | |
52 | + return unref(getShow) ? unref(getOpenTabList).includes(path) : true; | |
53 | 53 | } |
54 | 54 | return { hasRenderFrame, getFramePages, showIframe, getAllFramePages }; |
55 | 55 | } | ... | ... |
src/layouts/page/index.tsx
1 | -import { computed, defineComponent, unref, Transition, KeepAlive, toRaw } from 'vue'; | |
1 | +import type { FunctionalComponent } from 'vue'; | |
2 | + | |
3 | +import { computed, defineComponent, unref, Transition, KeepAlive } from 'vue'; | |
2 | 4 | import { RouterView, RouteLocation } from 'vue-router'; |
3 | 5 | |
4 | 6 | import FrameLayout from '/@/layouts/iframe/index.vue'; |
5 | 7 | |
6 | 8 | import { useTransition } from './useTransition'; |
7 | -import { useProjectSetting } from '/@/settings/use'; | |
9 | +import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; | |
10 | +import { useRootSetting } from '/@/hooks/setting/useRootSetting'; | |
11 | +import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting'; | |
8 | 12 | |
9 | 13 | import { tabStore } from '/@/store/modules/tab'; |
10 | -import { appStore } from '/@/store/modules/app'; | |
14 | + | |
15 | +interface DefaultContext { | |
16 | + Component: FunctionalComponent; | |
17 | + route: RouteLocation; | |
18 | +} | |
11 | 19 | |
12 | 20 | export default defineComponent({ |
13 | 21 | name: 'PageLayout', |
14 | 22 | setup() { |
15 | - const getProjectConfigRef = computed(() => appStore.getProjectConfig); | |
16 | - const openCacheRef = computed(() => { | |
17 | - const { | |
18 | - openKeepAlive, | |
19 | - multiTabsSetting: { show }, | |
20 | - } = unref(getProjectConfigRef); | |
21 | - return openKeepAlive && show; | |
22 | - }); | |
23 | - const getCacheTabsRef = computed(() => toRaw(tabStore.getKeepAliveTabsState) as string[]); | |
23 | + const { getShow } = useMenuSetting(); | |
24 | + const { | |
25 | + getOpenKeepAlive, | |
26 | + getRouterTransition, | |
27 | + getOpenRouterTransition, | |
28 | + getCanEmbedIFramePage, | |
29 | + } = useRootSetting(); | |
24 | 30 | |
25 | - const { openPageLoading } = unref(getProjectConfigRef); | |
31 | + const { getMax } = useMultipleTabSetting(); | |
26 | 32 | |
27 | - let on = {}; | |
28 | - if (openPageLoading) { | |
29 | - const { on: transitionOn } = useTransition(); | |
30 | - on = transitionOn; | |
31 | - } | |
32 | - const projectSetting = useProjectSetting(); | |
33 | - return () => { | |
34 | - const { | |
35 | - routerTransition, | |
36 | - openRouterTransition, | |
37 | - multiTabsSetting: { max }, | |
38 | - } = unref(getProjectConfigRef); | |
33 | + const transitionEvent = useTransition(); | |
34 | + | |
35 | + const openCacheRef = computed(() => unref(getOpenKeepAlive) && unref(getShow)); | |
39 | 36 | |
37 | + const getCacheTabsRef = computed(() => tabStore.getKeepAliveTabsState as string[]); | |
38 | + | |
39 | + return () => { | |
40 | 40 | return ( |
41 | 41 | <div> |
42 | 42 | <RouterView> |
43 | 43 | {{ |
44 | - default: ({ Component, route }: { Component: any; route: RouteLocation }) => { | |
44 | + default: ({ Component, route }: DefaultContext) => { | |
45 | 45 | // No longer show animations that are already in the tab |
46 | 46 | const cacheTabs = unref(getCacheTabsRef); |
47 | 47 | const isInCache = cacheTabs.includes(route.name as string); |
48 | 48 | const name = isInCache && route.meta.inTab ? 'fade' : null; |
49 | 49 | |
50 | - const Content = unref(openCacheRef) ? ( | |
51 | - <KeepAlive max={max} include={cacheTabs}> | |
52 | - <Component key={route.fullPath} /> | |
50 | + const renderComp = () => <Component key={route.fullPath} />; | |
51 | + | |
52 | + const PageContent = unref(openCacheRef) ? ( | |
53 | + <KeepAlive max={unref(getMax)} include={cacheTabs}> | |
54 | + {renderComp()} | |
53 | 55 | </KeepAlive> |
54 | 56 | ) : ( |
55 | - <Component key={route.fullPath} /> | |
57 | + renderComp() | |
56 | 58 | ); |
57 | - return openRouterTransition ? ( | |
59 | + | |
60 | + return unref(getOpenRouterTransition) ? ( | |
58 | 61 | <Transition |
59 | - {...on} | |
60 | - name={name || route.meta.transitionName || routerTransition} | |
62 | + {...transitionEvent} | |
63 | + name={name || route.meta.transitionName || unref(getRouterTransition)} | |
61 | 64 | mode="out-in" |
62 | 65 | appear={true} |
63 | 66 | > |
64 | - {() => Content} | |
67 | + {() => PageContent} | |
65 | 68 | </Transition> |
66 | 69 | ) : ( |
67 | - Content | |
70 | + PageContent | |
68 | 71 | ); |
69 | 72 | }, |
70 | 73 | }} |
71 | 74 | </RouterView> |
72 | - {projectSetting.canEmbedIFramePage && <FrameLayout />} | |
75 | + {unref(getCanEmbedIFramePage) && <FrameLayout />} | |
73 | 76 | </div> |
74 | 77 | ); |
75 | 78 | }; | ... | ... |
src/layouts/page/useTransition.ts
1 | +import { useRootSetting } from '/@/hooks/setting/useRootSetting'; | |
1 | 2 | import { appStore } from '/@/store/modules/app'; |
2 | 3 | import { tryOnUnmounted } from '/@/utils/helper/vueHelper'; |
4 | + | |
3 | 5 | export function useTransition() { |
4 | 6 | function handleAfterEnter() { |
5 | - const { openRouterTransition, openPageLoading } = appStore.getProjectConfig; | |
6 | - | |
7 | - if (!openRouterTransition || !openPageLoading) return; | |
7 | + const { getOpenPageLoading, getOpenRouterTransition } = useRootSetting(); | |
8 | + if (!getOpenPageLoading.value || !getOpenRouterTransition.value) return; | |
8 | 9 | // Close loading after the route switching animation ends |
9 | 10 | appStore.setPageLoadingAction(false); |
10 | 11 | } |
... | ... | @@ -15,9 +16,6 @@ export function useTransition() { |
15 | 16 | }); |
16 | 17 | |
17 | 18 | return { |
18 | - handleAfterEnter, | |
19 | - on: { | |
20 | - onAfterEnter: handleAfterEnter, | |
21 | - }, | |
19 | + onAfterEnter: handleAfterEnter, | |
22 | 20 | }; |
23 | 21 | } | ... | ... |
src/main.ts
... | ... | @@ -49,7 +49,7 @@ if (isDevMode()) { |
49 | 49 | window.__APP__ = app; |
50 | 50 | } |
51 | 51 | |
52 | -// If you do not need to use the mock service in the production environment, you can comment the code | |
52 | +// If you do not need to setting the mock service in the production environment, you can comment the code | |
53 | 53 | if (isProdMode() && isUseMock()) { |
54 | 54 | setupProdMockServer(); |
55 | 55 | } | ... | ... |
src/router/guard/index.ts
... | ... | @@ -6,7 +6,7 @@ import { createProgressGuard } from './progressGuard'; |
6 | 6 | import { createPermissionGuard } from './permissionGuard'; |
7 | 7 | import { createPageLoadingGuard } from './pageLoadingGuard'; |
8 | 8 | |
9 | -import { useGlobSetting, useProjectSetting } from '/@/settings/use'; | |
9 | +import { useGlobSetting, useProjectSetting } from '/@/hooks/setting'; | |
10 | 10 | |
11 | 11 | import { getIsOpenTab, setCurrentTo } from '/@/utils/helper/routeHelper'; |
12 | 12 | import { setTitle } from '/@/utils/browser'; | ... | ... |
src/router/menus/index.ts
... | ... | @@ -7,6 +7,7 @@ import { filter } from '/@/utils/helper/treeHelper'; |
7 | 7 | import router from '/@/router'; |
8 | 8 | import { PermissionModeEnum } from '/@/enums/appEnum'; |
9 | 9 | import { pathToRegexp } from 'path-to-regexp'; |
10 | + | |
10 | 11 | import modules from 'globby!/@/router/menus/modules/**/*.@(ts)'; |
11 | 12 | |
12 | 13 | const menuModules: MenuModule[] = []; |
... | ... | @@ -44,7 +45,6 @@ async function getAsyncMenus() { |
44 | 45 | // 获取深层扁平化菜单 |
45 | 46 | export const getFlatMenus = async () => { |
46 | 47 | const menus = await getAsyncMenus(); |
47 | - | |
48 | 48 | return flatMenus(menus); |
49 | 49 | }; |
50 | 50 | ... | ... |
src/settings/projectSetting.ts
... | ... | @@ -9,7 +9,7 @@ import { isProdMode } from '/@/utils/env'; |
9 | 9 | const setting: ProjectConfig = { |
10 | 10 | // locale setting |
11 | 11 | locale: { |
12 | - // Locales | |
12 | + // Locale | |
13 | 13 | lang: 'zh_CN', |
14 | 14 | // Default locale |
15 | 15 | fallback: 'zh_CN', |
... | ... | @@ -29,17 +29,22 @@ const setting: ProjectConfig = { |
29 | 29 | |
30 | 30 | // Whether to show the configuration button |
31 | 31 | showSettingButton: true, |
32 | + | |
32 | 33 | // 权限模式 |
33 | 34 | permissionMode: PermissionModeEnum.ROLE, |
35 | + | |
34 | 36 | // 网站灰色模式,用于可能悼念的日期开启 |
35 | 37 | grayMode: false, |
38 | + | |
36 | 39 | // 色弱模式 |
37 | 40 | colorWeak: false, |
38 | 41 | |
39 | 42 | // 是否取消菜单,顶部,多标签页显示, 用于可能内嵌在别的系统内 |
40 | 43 | fullContent: false, |
44 | + | |
41 | 45 | // content mode |
42 | 46 | contentMode: ContentEnum.FULL, |
47 | + | |
43 | 48 | // 是否显示logo |
44 | 49 | showLogo: true, |
45 | 50 | |
... | ... | @@ -58,11 +63,10 @@ const setting: ProjectConfig = { |
58 | 63 | showFullScreen: true, |
59 | 64 | // 显示文档按钮 |
60 | 65 | showDoc: true, |
61 | - // 是否显示github | |
62 | - showGithub: true, | |
63 | 66 | // 显示消息中心按钮 |
64 | 67 | showNotice: true, |
65 | 68 | }, |
69 | + | |
66 | 70 | // 菜单配置 |
67 | 71 | menuSetting: { |
68 | 72 | // 菜单折叠 |
... | ... | @@ -108,13 +112,16 @@ const setting: ProjectConfig = { |
108 | 112 | // 标签页缓存最大数量 |
109 | 113 | max: 12, |
110 | 114 | }, |
115 | + | |
111 | 116 | // 是否开启KeepAlive缓存 开发时候最好关闭,不然每次都需要清除缓存 |
112 | 117 | openKeepAlive: true, |
113 | 118 | |
114 | 119 | // 自动锁屏时间,为0不锁屏。 单位分钟 默认0 |
115 | 120 | lockTime: 0, |
121 | + | |
116 | 122 | // 显示面包屑 |
117 | 123 | showBreadCrumb: true, |
124 | + | |
118 | 125 | // 显示面包屑图标 |
119 | 126 | showBreadCrumbIcon: false, |
120 | 127 | ... | ... |
src/setup/error-handle/index.ts
... | ... | @@ -3,7 +3,7 @@ |
3 | 3 | */ |
4 | 4 | |
5 | 5 | import { errorStore, ErrorInfo } from '/@/store/modules/error'; |
6 | -import { useProjectSetting } from '/@/settings/use'; | |
6 | +import { useProjectSetting } from '/@/hooks/setting'; | |
7 | 7 | import { ErrorTypeEnum } from '/@/enums/exceptionEnum'; |
8 | 8 | import { App } from 'vue'; |
9 | 9 | ... | ... |
src/setup/i18n/index.ts
... | ... | @@ -4,7 +4,7 @@ import type { I18n, I18nOptions } from 'vue-i18n'; |
4 | 4 | import { createI18n } from 'vue-i18n'; |
5 | 5 | import localeMessages from '/@/locales'; |
6 | 6 | import { useLocale } from '/@/hooks/web/useLocale'; |
7 | -import { useLocaleSetting } from '/@/settings/use/useLocaleSetting'; | |
7 | +import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting'; | |
8 | 8 | |
9 | 9 | const { setupLocale } = useLocale(); |
10 | 10 | ... | ... |
src/store/modules/error.ts
... | ... | @@ -4,7 +4,7 @@ import { VuexModule, getModule, Module, Mutation, Action } from 'vuex-module-dec |
4 | 4 | |
5 | 5 | import { formatToDateTime } from '/@/utils/dateUtil'; |
6 | 6 | import { ErrorTypeEnum } from '/@/enums/exceptionEnum'; |
7 | -import { useProjectSetting } from '/@/settings/use'; | |
7 | +import { useProjectSetting } from '/@/hooks/setting'; | |
8 | 8 | |
9 | 9 | export interface ErrorInfo { |
10 | 10 | type: ErrorTypeEnum; | ... | ... |
src/store/modules/menu.ts deleted
100644 → 0
1 | -import store from '/@/store'; | |
2 | -import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper'; | |
3 | -import { VuexModule, Module, getModule, Mutation } from 'vuex-module-decorators'; | |
4 | - | |
5 | -import { appStore } from '/@/store/modules/app'; | |
6 | - | |
7 | -const NAME = 'menu'; | |
8 | -hotModuleUnregisterModule(NAME); | |
9 | -@Module({ namespaced: true, name: NAME, dynamic: true, store }) | |
10 | -class Menu extends VuexModule { | |
11 | - // 是否开始拖拽 | |
12 | - private dragStartState = false; | |
13 | - | |
14 | - private currentTopSplitMenuPathState = ''; | |
15 | - | |
16 | - /** | |
17 | - * @description: 获取窗口名称 | |
18 | - */ | |
19 | - get getCollapsedState() { | |
20 | - return appStore.getProjectConfig.menuSetting.collapsed; | |
21 | - } | |
22 | - | |
23 | - get getCurrentTopSplitMenuPathState() { | |
24 | - return this.currentTopSplitMenuPathState; | |
25 | - } | |
26 | - | |
27 | - get getDragStartState() { | |
28 | - return this.dragStartState; | |
29 | - } | |
30 | - | |
31 | - get getMenuWidthState() { | |
32 | - return appStore.getProjectConfig.menuSetting.menuWidth; | |
33 | - } | |
34 | - | |
35 | - @Mutation | |
36 | - commitDragStartState(dragStart: boolean): void { | |
37 | - this.dragStartState = dragStart; | |
38 | - } | |
39 | - | |
40 | - @Mutation | |
41 | - commitCurrentTopSplitMenuPathState(path: string): void { | |
42 | - this.currentTopSplitMenuPathState = path; | |
43 | - } | |
44 | - | |
45 | - // 改变菜单展开状态 | |
46 | - @Mutation | |
47 | - commitCollapsedState(collapsed: boolean): void { | |
48 | - // this.collapsedState = collapsed; | |
49 | - appStore.commitProjectConfigState({ | |
50 | - menuSetting: { | |
51 | - collapsed: collapsed, | |
52 | - }, | |
53 | - }); | |
54 | - } | |
55 | - | |
56 | - @Mutation | |
57 | - commitMenuWidthState(menuWidth: number): void { | |
58 | - // this.menuWidthState = menuWidth; | |
59 | - appStore.commitProjectConfigState({ | |
60 | - menuSetting: { | |
61 | - menuWidth: menuWidth, | |
62 | - }, | |
63 | - }); | |
64 | - } | |
65 | -} | |
66 | - | |
67 | -export const menuStore = getModule<Menu>(Menu); |
src/store/modules/permission.ts
... | ... | @@ -97,11 +97,6 @@ class Permission extends VuexModule { |
97 | 97 | if (!roles) return true; |
98 | 98 | return roleList.some((role) => roles.includes(role)); |
99 | 99 | }); |
100 | - // this.commitRoutesState(routes); | |
101 | - // Background permissions | |
102 | - // warn( | |
103 | - // `当前权限模式为:${PermissionModeEnum.ROLE},请将src/store/modules/permission.ts内的后台菜单获取函数注释,如果已注释可以忽略此信息!` | |
104 | - // ); | |
105 | 100 | // 如果确定不需要做后台动态权限,请将下面整个判断注释 |
106 | 101 | } else if (permissionMode === PermissionModeEnum.BACK) { |
107 | 102 | const messageKey = 'loadMenu'; | ... | ... |
src/types/config.d.ts
src/utils/file/download.ts
1 | +import { openWindow } from '..'; | |
1 | 2 | import { dataURLtoBlob, urlToBase64 } from './base64Conver'; |
2 | 3 | |
3 | 4 | /** |
... | ... | @@ -93,6 +94,6 @@ export function downloadByUrl({ |
93 | 94 | url += '?download'; |
94 | 95 | } |
95 | 96 | |
96 | - window.open(url, target); | |
97 | + openWindow(url, { target }); | |
97 | 98 | return true; |
98 | 99 | } | ... | ... |
src/utils/helper/envHelper.ts
src/utils/http/axios/index.ts
... | ... | @@ -10,7 +10,7 @@ import { AxiosTransform } from './axiosTransform'; |
10 | 10 | |
11 | 11 | import { checkStatus } from './checkStatus'; |
12 | 12 | |
13 | -import { useGlobSetting } from '/@/settings/use'; | |
13 | +import { useGlobSetting } from '/@/hooks/setting'; | |
14 | 14 | import { useMessage } from '/@/hooks/web/useMessage'; |
15 | 15 | |
16 | 16 | import { RequestEnum, ResultEnum, ContentTypeEnum } from '/@/enums/httpEnum'; | ... | ... |
src/utils/index.ts
... | ... | @@ -11,6 +11,7 @@ export function getPopupContainer(node?: HTMLElement): HTMLElement { |
11 | 11 | } |
12 | 12 | return document.body; |
13 | 13 | } |
14 | + | |
14 | 15 | /** |
15 | 16 | * Add the object as a parameter to the URL |
16 | 17 | * @param baseUrl url |
... | ... | @@ -64,3 +65,16 @@ export function unique<T = any>(arr: T[], key: string): T[] { |
64 | 65 | export function es6Unique<T>(arr: T[]): T[] { |
65 | 66 | return Array.from(new Set(arr)); |
66 | 67 | } |
68 | + | |
69 | +export function openWindow( | |
70 | + url: string, | |
71 | + opt?: { target?: TargetContext | string; noopener?: boolean; noreferrer?: boolean } | |
72 | +) { | |
73 | + const { target = '__blank', noopener = true, noreferrer = true } = opt || {}; | |
74 | + const feature: string[] = []; | |
75 | + | |
76 | + noopener && feature.push('noopener=yes'); | |
77 | + noreferrer && feature.push('noreferrer=yes'); | |
78 | + | |
79 | + window.open(url, target, feature.join(',')); | |
80 | +} | ... | ... |
src/views/demo/feat/icon/index.vue
... | ... | @@ -45,6 +45,8 @@ |
45 | 45 | |
46 | 46 | import Icon from '/@/components/Icon/index'; |
47 | 47 | |
48 | + import { openWindow } from '/@/utils'; | |
49 | + | |
48 | 50 | export default defineComponent({ |
49 | 51 | components: { |
50 | 52 | CollapseContainer, |
... | ... | @@ -61,7 +63,7 @@ |
61 | 63 | setup() { |
62 | 64 | return { |
63 | 65 | toIconify: () => { |
64 | - window.open('https://iconify.design/', '__blank'); | |
66 | + openWindow('https://iconify.design/'); | |
65 | 67 | }, |
66 | 68 | }; |
67 | 69 | }, | ... | ... |
src/views/demo/page/form/high/index.vue
... | ... | @@ -16,23 +16,23 @@ |
16 | 16 | </a-card> |
17 | 17 | </div> |
18 | 18 | |
19 | - <AppFooterToolbar> | |
19 | + <AppPageFooter> | |
20 | 20 | <template #right> |
21 | 21 | <a-button type="primary" @click="submitAll">提交</a-button> |
22 | 22 | </template> |
23 | - </AppFooterToolbar> | |
23 | + </AppPageFooter> | |
24 | 24 | </div> |
25 | 25 | </template> |
26 | 26 | <script lang="ts"> |
27 | 27 | import { BasicForm, useForm } from '/@/components/Form'; |
28 | 28 | import { defineComponent, ref } from 'vue'; |
29 | 29 | import PersonTable from './PersonTable.vue'; |
30 | - import { AppFooterToolbar } from '/@/components/Application'; | |
30 | + import { AppPageFooter } from '/@/components/Application'; | |
31 | 31 | |
32 | 32 | import { schemas, taskSchemas } from './data'; |
33 | 33 | |
34 | 34 | export default defineComponent({ |
35 | - components: { BasicForm, PersonTable, AppFooterToolbar }, | |
35 | + components: { BasicForm, PersonTable, AppPageFooter }, | |
36 | 36 | setup() { |
37 | 37 | const tableRef = ref<{ getDataSource: () => any } | null>(null); |
38 | 38 | ... | ... |
src/views/sys/login/Login.vue
... | ... | @@ -72,7 +72,7 @@ |
72 | 72 | |
73 | 73 | // import { appStore } from '/@/store/modules/app'; |
74 | 74 | import { useMessage } from '/@/hooks/web/useMessage'; |
75 | - import { useGlobSetting } from '/@/settings/use'; | |
75 | + import { useGlobSetting } from '/@/hooks/setting'; | |
76 | 76 | import logo from '/@/assets/images/logo.png'; |
77 | 77 | |
78 | 78 | export default defineComponent({ | ... | ... |
vite.config.ts
... | ... | @@ -30,7 +30,7 @@ function pathResolve(dir: string) { |
30 | 30 | |
31 | 31 | const viteConfig: UserConfig = { |
32 | 32 | /** |
33 | - * Entry. Use this to specify a js entry file in use cases where an | |
33 | + * Entry. Use this to specify a js entry file in setting cases where an | |
34 | 34 | * `index.html` does not exist (e.g. serving vite assets from a different host) |
35 | 35 | * @default 'index.html' |
36 | 36 | */ |
... | ... | @@ -51,7 +51,7 @@ const viteConfig: UserConfig = { |
51 | 51 | */ |
52 | 52 | open: false, |
53 | 53 | /** |
54 | - * Set to `false` to disable minification, or specify the minifier to use. | |
54 | + * Set to `false` to disable minification, or specify the minifier to setting. | |
55 | 55 | * Available options are 'terser' or 'esbuild'. |
56 | 56 | * @default 'terser' |
57 | 57 | */ |
... | ... | @@ -112,7 +112,7 @@ const viteConfig: UserConfig = { |
112 | 112 | }, |
113 | 113 | define: { |
114 | 114 | __VERSION__: pkg.version, |
115 | - // use vue-i18-next | |
115 | + // setting vue-i18-next | |
116 | 116 | // Suppress warning |
117 | 117 | __VUE_I18N_LEGACY_API__: false, |
118 | 118 | __VUE_I18N_FULL_INSTALL__: false, | ... | ... |