Commit ba068ba1df797627e88dcf61af3ea13d0c2929ab

Authored by vben
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,7 +23,7 @@ module.exports = {
23 '@typescript-eslint/no-empty-function': 'off', 23 '@typescript-eslint/no-empty-function': 'off',
24 'vue/custom-event-name-casing': 'off', 24 'vue/custom-event-name-casing': 'off',
25 'no-use-before-define': 'off', 25 'no-use-before-define': 'off',
26 - // 'no-use-before-define': [ 26 + // 'no-setting-before-define': [
27 // 'error', 27 // 'error',
28 // { 28 // {
29 // functions: false, 29 // functions: false,
@@ -31,7 +31,7 @@ module.exports = { @@ -31,7 +31,7 @@ module.exports = {
31 // }, 31 // },
32 // ], 32 // ],
33 '@typescript-eslint/no-use-before-define': 'off', 33 '@typescript-eslint/no-use-before-define': 'off',
34 - // '@typescript-eslint/no-use-before-define': [ 34 + // '@typescript-eslint/no-setting-before-define': [
35 // 'error', 35 // 'error',
36 // { 36 // {
37 // functions: false, 37 // functions: false,
src/components/Application/index.ts
1 import AppLocalPicker from './src/AppLocalPicker.vue'; 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 import { withInstall } from '../util'; 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,29 +5,44 @@
5 :selectedKeys="selectedKeys" 5 :selectedKeys="selectedKeys"
6 @menuEvent="handleMenuEvent" 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 </Dropdown> 12 </Dropdown>
10 </template> 13 </template>
11 <script lang="ts"> 14 <script lang="ts">
12 - import { defineComponent, ref, watchEffect, unref } from 'vue'; 15 + import { defineComponent, ref, watchEffect, unref, computed } from 'vue';
13 16
14 import { Dropdown, DropMenu } from '/@/components/Dropdown'; 17 import { Dropdown, DropMenu } from '/@/components/Dropdown';
15 import { GlobalOutlined } from '@ant-design/icons-vue'; 18 import { GlobalOutlined } from '@ant-design/icons-vue';
16 19
17 import { useLocale } from '/@/hooks/web/useLocale'; 20 import { useLocale } from '/@/hooks/web/useLocale';
18 - import { useLocaleSetting } from '/@/settings/use/useLocaleSetting'; 21 + import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting';
19 22
20 import { LocaleType } from '/@/locales/types'; 23 import { LocaleType } from '/@/locales/types';
21 24
22 export default defineComponent({ 25 export default defineComponent({
23 name: 'AppLocalPicker', 26 name: 'AppLocalPicker',
24 components: { GlobalOutlined, Dropdown }, 27 components: { GlobalOutlined, Dropdown },
  28 + props: {
  29 + showText: {
  30 + type: Boolean,
  31 + default: true,
  32 + },
  33 + },
25 setup() { 34 setup() {
26 const { localeList } = useLocaleSetting(); 35 const { localeList } = useLocaleSetting();
27 const selectedKeys = ref<string[]>([]); 36 const selectedKeys = ref<string[]>([]);
28 37
29 const { changeLocale, getLang } = useLocale(); 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 watchEffect(() => { 46 watchEffect(() => {
32 selectedKeys.value = [unref(getLang)]; 47 selectedKeys.value = [unref(getLang)];
33 }); 48 });
@@ -41,13 +56,19 @@ @@ -41,13 +56,19 @@
41 toggleLocale(menu.event as string); 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 </script> 62 </script>
48 63
49 <style lang="less" scoped> 64 <style lang="less" scoped>
50 - .app-locale { 65 + .app-local-picker {
  66 + display: flex;
  67 + align-items: center;
51 cursor: pointer; 68 cursor: pointer;
  69 +
  70 + &__icon {
  71 + margin-right: 4px;
  72 + }
52 } 73 }
53 </style> 74 </style>
src/layouts/logo/index.vue renamed to src/components/Application/src/AppLogo.vue
1 <template> 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 <img src="/@/assets/images/logo.png" /> 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 </div> 5 </div>
6 </template> 6 </template>
7 <script lang="ts"> 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 import { useGo } from '/@/hooks/web/usePage'; 12 import { useGo } from '/@/hooks/web/usePage';
13 13
14 import { PageEnum } from '/@/enums/pageEnum'; 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 export default defineComponent({ 16 export default defineComponent({
21 - name: 'Logo', 17 + name: 'AppLogo',
22 props: { 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 theme: { 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 const globSetting = useGlobSetting(); 27 const globSetting = useGlobSetting();
34 const go = useGo(); 28 const go = useGo();
35 29
36 - function handleGoHome() { 30 + function handleGoHome(): void {
37 go(PageEnum.BASE_HOME); 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 return { 34 return {
66 handleGoHome, 35 handleGoHome,
67 globSetting, 36 globSetting,
68 - show: showRef,  
69 - wrapStyle,  
70 }; 37 };
71 }, 38 },
72 }); 39 });
73 </script> 40 </script>
74 <style lang="less" scoped> 41 <style lang="less" scoped>
75 - @import (reference) '../../design/index.less'; 42 + @import (reference) '../../../design/index.less';
76 43
77 .app-logo { 44 .app-logo {
78 display: flex; 45 display: flex;
79 align-items: center; 46 align-items: center;
80 padding-left: 16px; 47 padding-left: 16px;
81 cursor: pointer; 48 cursor: pointer;
82 - // justify-content: center; 49 +
83 &.light { 50 &.light {
84 border-bottom: 1px solid @border-color-base; 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 font-size: 18px; 63 font-size: 18px;
89 font-weight: 700; 64 font-weight: 700;
90 opacity: 0; 65 opacity: 0;
91 transition: all 0.5s; 66 transition: all 0.5s;
92 .respond-to(medium,{ 67 .respond-to(medium,{
93 opacity: 1; 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 </style> 72 </style>
src/components/Application/src/AppFooterToolbar.vue renamed to src/components/Application/src/AppPageFooter.vue
1 <template> 1 <template>
2 - <div class="app-footer" :style="{ width: getWidth }"> 2 + <div class="app-footer" :style="{ width: getCalcContentWidth }">
3 <div class="app-footer__left"> 3 <div class="app-footer__left">
4 <slot name="left" /> 4 <slot name="left" />
5 </div> 5 </div>
@@ -9,30 +9,16 @@ @@ -9,30 +9,16 @@
9 </div> 9 </div>
10 </template> 10 </template>
11 <script lang="ts"> 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 export default defineComponent({ 16 export default defineComponent({
20 name: 'AppFooterToolbar', 17 name: 'AppFooterToolbar',
21 setup() { 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 </script> 24 </script>
@@ -51,7 +37,7 @@ @@ -51,7 +37,7 @@
51 border-top: 1px solid #f0f0f0; 37 border-top: 1px solid #f0f0f0;
52 box-shadow: 0 -6px 16px -8px rgba(0, 0, 0, 0.08), 0 -9px 28px 0 rgba(0, 0, 0, 0.05), 38 box-shadow: 0 -6px 16px -8px rgba(0, 0, 0, 0.08), 0 -9px 28px 0 rgba(0, 0, 0, 0.05),
53 0 -12px 48px 16px rgba(0, 0, 0, 0.03); 39 0 -12px 48px 16px rgba(0, 0, 0, 0.03);
54 - transition: width 0.3s; 40 + transition: width 0.4s;
55 41
56 &__left { 42 &__left {
57 flex: 1 1; 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 <script lang="ts"> 4 <script lang="ts">
6 import type { PropType } from 'vue'; 5 import type { PropType } from 'vue';
7 - import { defineComponent, computed, unref } from 'vue'; 6 + import { defineComponent, unref } from 'vue';
8 7
9 import { PermissionModeEnum } from '/@/enums/appEnum'; 8 import { PermissionModeEnum } from '/@/enums/appEnum';
10 import { RoleEnum } from '/@/enums/roleEnum'; 9 import { RoleEnum } from '/@/enums/roleEnum';
  10 + import { useRootSetting } from '/@/hooks/setting/useRootSetting';
11 11
12 import { usePermission } from '/@/hooks/web/usePermission'; 12 import { usePermission } from '/@/hooks/web/usePermission';
13 - import { appStore } from '/@/store/modules/app';  
14 13
15 import { getSlot } from '/@/utils/helper/tsxHelper'; 14 import { getSlot } from '/@/utils/helper/tsxHelper';
16 15
@@ -29,9 +28,8 @@ @@ -29,9 +28,8 @@
29 }, 28 },
30 }, 29 },
31 setup(props, { slots }) { 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 * Render role button 35 * Render role button
@@ -41,7 +39,6 @@ @@ -41,7 +39,6 @@
41 if (!value) { 39 if (!value) {
42 return getSlot(slots); 40 return getSlot(slots);
43 } 41 }
44 - const { hasPermission } = usePermission();  
45 return hasPermission(value) ? getSlot(slots) : null; 42 return hasPermission(value) ? getSlot(slots) : null;
46 } 43 }
47 44
@@ -52,12 +49,11 @@ @@ -52,12 +49,11 @@
52 if (!value) { 49 if (!value) {
53 return getSlot(slots); 50 return getSlot(slots);
54 } 51 }
55 - const { hasPermission } = usePermission();  
56 return hasPermission(value) ? getSlot(slots) : null; 52 return hasPermission(value) ? getSlot(slots) : null;
57 } 53 }
58 54
59 return () => { 55 return () => {
60 - const mode = unref(getModeRef); 56 + const mode = unref(getPermissionMode);
61 // Role-based value control 57 // Role-based value control
62 if (mode === PermissionModeEnum.ROLE) { 58 if (mode === PermissionModeEnum.ROLE) {
63 return renderRoleAuth(); 59 return renderRoleAuth();
src/components/Description/src/index.tsx
@@ -33,7 +33,7 @@ export default defineComponent({ @@ -33,7 +33,7 @@ export default defineComponent({
33 }); 33 });
34 34
35 /** 35 /**
36 - * @description: Whether to use title 36 + * @description: Whether to setting title
37 */ 37 */
38 const useWrapper = computed(() => { 38 const useWrapper = computed(() => {
39 return !!unref(getMergeProps).title; 39 return !!unref(getMergeProps).title;
src/components/Dropdown/src/Dropdown.tsx
@@ -15,7 +15,7 @@ export default defineComponent({ @@ -15,7 +15,7 @@ export default defineComponent({
15 const getMenuList = computed(() => props.dropMenuList); 15 const getMenuList = computed(() => props.dropMenuList);
16 16
17 function handleClickMenu({ key }: any) { 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 emit('menuEvent', menu); 19 emit('menuEvent', menu);
20 } 20 }
21 21
src/components/Excel/src/ImportExcel.vue
@@ -104,7 +104,7 @@ @@ -104,7 +104,7 @@
104 */ 104 */
105 function handleInputClick(e: Event) { 105 function handleInputClick(e: Event) {
106 const files = e && (e.target as HTMLInputElement).files; 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 if (!rawFile) return; 108 if (!rawFile) return;
109 upload(rawFile); 109 upload(rawFile);
110 } 110 }
src/components/Form/src/componentMap.ts
@@ -2,7 +2,7 @@ import { Component } from &#39;vue&#39;; @@ -2,7 +2,7 @@ import { Component } from &#39;vue&#39;;
2 import type { ComponentType } from './types/index'; 2 import type { ComponentType } from './types/index';
3 3
4 /** 4 /**
5 - * Component list, register here to use it in the form 5 + * Component list, register here to setting it in the form
6 */ 6 */
7 import { 7 import {
8 Input, 8 Input,
src/components/Form/src/hooks/useLabelWidth.ts
@@ -31,7 +31,7 @@ export function useItemLabelWidth(schemaItemRef: Ref&lt;FormSchema&gt;, propsRef: Ref&lt; @@ -31,7 +31,7 @@ export function useItemLabelWidth(schemaItemRef: Ref&lt;FormSchema&gt;, propsRef: Ref&lt;
31 wrapperCol: globWrapperCol, 31 wrapperCol: globWrapperCol,
32 } = unref(propsRef) as any; 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 if ((!globalLabelWidth && !labelWidth && !globalLabelCol) || disabledLabelWidth) { 35 if ((!globalLabelWidth && !labelWidth && !globalLabelCol) || disabledLabelWidth) {
36 return { labelCol, wrapperCol }; 36 return { labelCol, wrapperCol };
37 } 37 }
src/components/Form/src/types/formItem.ts
@@ -68,7 +68,7 @@ export interface FormItem { @@ -68,7 +68,7 @@ export interface FormItem {
68 */ 68 */
69 labelAlign?: 'left' | 'right'; 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 name?: NamePath; 73 name?: NamePath;
74 /** 74 /**
@@ -76,7 +76,7 @@ export interface FormItem { @@ -76,7 +76,7 @@ export interface FormItem {
76 */ 76 */
77 rules?: object | object[]; 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 * If the conditions for automatic association are not met, you can manually associate them. See the notes below. 80 * If the conditions for automatic association are not met, you can manually associate them. See the notes below.
81 */ 81 */
82 autoLink?: boolean; 82 autoLink?: boolean;
src/components/Menu/index.ts
1 -export { default as BasicMenu } from './src/BasicMenu'; 1 +import BasicMenu from './src/BasicMenu';
  2 +import { withInstall } from '../util';
  3 +
  4 +export default withInstall(BasicMenu);
  5 +export { BasicMenu };
src/components/Menu/src/BasicMenu.tsx
  1 +import './index.less';
  2 +
1 import type { MenuState } from './types'; 3 import type { MenuState } from './types';
2 import type { Menu as MenuType } from '/@/router/types'; 4 import type { Menu as MenuType } from '/@/router/types';
3 5
@@ -9,11 +11,10 @@ import MenuContent from &#39;./MenuContent&#39;; @@ -9,11 +11,10 @@ import MenuContent from &#39;./MenuContent&#39;;
9 import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum'; 11 import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
10 import { ThemeEnum } from '/@/enums/appEnum'; 12 import { ThemeEnum } from '/@/enums/appEnum';
11 13
12 -import { menuStore } from '/@/store/modules/menu';  
13 import { appStore } from '/@/store/modules/app'; 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 import { useRouter } from 'vue-router'; 18 import { useRouter } from 'vue-router';
18 19
19 import { isFunction } from '/@/utils/is'; 20 import { isFunction } from '/@/utils/is';
@@ -23,7 +24,7 @@ import { menuHasChildren } from &#39;./helper&#39;; @@ -23,7 +24,7 @@ import { menuHasChildren } from &#39;./helper&#39;;
23 import { getCurrentParentPath } from '/@/router/menus'; 24 import { getCurrentParentPath } from '/@/router/menus';
24 25
25 import { basicProps } from './props'; 26 import { basicProps } from './props';
26 -import './index.less'; 27 +import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
27 export default defineComponent({ 28 export default defineComponent({
28 name: 'BasicMenu', 29 name: 'BasicMenu',
29 props: basicProps, 30 props: basicProps,
@@ -39,6 +40,8 @@ export default defineComponent({ @@ -39,6 +40,8 @@ export default defineComponent({
39 selectedKeys: [], 40 selectedKeys: [],
40 collapsedOpenKeys: [], 41 collapsedOpenKeys: [],
41 }); 42 });
  43 +
  44 + const { getCollapsed } = useMenuSetting();
42 const { currentRoute } = useRouter(); 45 const { currentRoute } = useRouter();
43 46
44 const { items, flatItems, isAppMenu, mode, accordion } = toRefs(props); 47 const { items, flatItems, isAppMenu, mode, accordion } = toRefs(props);
@@ -61,7 +64,7 @@ export default defineComponent({ @@ -61,7 +64,7 @@ export default defineComponent({
61 64
62 const getOpenKeys = computed(() => { 65 const getOpenKeys = computed(() => {
63 if (props.isAppMenu) { 66 if (props.isAppMenu) {
64 - return menuStore.getCollapsedState ? menuState.collapsedOpenKeys : menuState.openKeys; 67 + return unref(getCollapsed) ? menuState.collapsedOpenKeys : menuState.openKeys;
65 } 68 }
66 return menuState.openKeys; 69 return menuState.openKeys;
67 }); 70 });
@@ -95,20 +98,20 @@ export default defineComponent({ @@ -95,20 +98,20 @@ export default defineComponent({
95 cls.push('basic-menu__sidebar-hor'); 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 cls.push('basic-menu__second'); 102 cls.push('basic-menu__second');
100 } 103 }
101 return cls; 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 watch( 109 watch(
107 () => currentRoute.value.name, 110 () => currentRoute.value.name,
108 (name: string) => { 111 (name: string) => {
109 if (name === 'Redirect') return; 112 if (name === 'Redirect') return;
110 handleMenuChange(); 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,7 +183,7 @@ export default defineComponent({
180 <MenuContent 183 <MenuContent
181 item={menu} 184 item={menu}
182 level={index} 185 level={index}
183 - isTop={props.isTop} 186 + isHorizontal={props.isHorizontal}
184 showTitle={unref(showTitle)} 187 showTitle={unref(showTitle)}
185 searchValue={menuState.searchValue} 188 searchValue={menuState.searchValue}
186 />, 189 />,
@@ -196,7 +199,7 @@ export default defineComponent({ @@ -196,7 +199,7 @@ export default defineComponent({
196 showTitle={unref(showTitle)} 199 showTitle={unref(showTitle)}
197 item={menu} 200 item={menu}
198 level={index} 201 level={index}
199 - isTop={props.isTop} 202 + isHorizontal={props.isHorizontal}
200 searchValue={menuState.searchValue} 203 searchValue={menuState.searchValue}
201 />, 204 />,
202 ], 205 ],
@@ -214,7 +217,7 @@ export default defineComponent({ @@ -214,7 +217,7 @@ export default defineComponent({
214 const inlineCollapsedObj = isInline 217 const inlineCollapsedObj = isInline
215 ? props.isAppMenu 218 ? props.isAppMenu
216 ? { 219 ? {
217 - inlineCollapsed: menuStore.getCollapsedState, 220 + inlineCollapsed: unref(getCollapsed),
218 } 221 }
219 : { inlineCollapsed: props.inlineCollapsed } 222 : { inlineCollapsed: props.inlineCollapsed }
220 : {}; 223 : {};
@@ -246,7 +249,6 @@ export default defineComponent({ @@ -246,7 +249,6 @@ export default defineComponent({
246 }); 249 });
247 250
248 return () => { 251 return () => {
249 - const { getCollapsedState } = menuStore;  
250 const { mode } = props; 252 const { mode } = props;
251 return mode === MenuModeEnum.HORIZONTAL ? ( 253 return mode === MenuModeEnum.HORIZONTAL ? (
252 renderMenu() 254 renderMenu()
@@ -258,7 +260,7 @@ export default defineComponent({ @@ -258,7 +260,7 @@ export default defineComponent({
258 theme={props.theme as ThemeEnum} 260 theme={props.theme as ThemeEnum}
259 onChange={handleInputChange} 261 onChange={handleInputChange}
260 onClick={handleInputClick} 262 onClick={handleInputClick}
261 - collapsed={getCollapsedState} 263 + collapsed={unref(getCollapsed)}
262 /> 264 />
263 <section style={unref(getMenuWrapStyle)} class="basic-menu__content"> 265 <section style={unref(getMenuWrapStyle)} class="basic-menu__content">
264 {renderMenu()} 266 {renderMenu()}
src/components/Menu/src/MenuContent.tsx
@@ -26,7 +26,7 @@ export default defineComponent({ @@ -26,7 +26,7 @@ export default defineComponent({
26 type: Number as PropType<number>, 26 type: Number as PropType<number>,
27 default: 0, 27 default: 0,
28 }, 28 },
29 - isTop: { 29 + isHorizontal: {
30 type: Boolean as PropType<boolean>, 30 type: Boolean as PropType<boolean>,
31 default: true, 31 default: true,
32 }, 32 },
@@ -40,8 +40,8 @@ export default defineComponent({ @@ -40,8 +40,8 @@ export default defineComponent({
40 } 40 }
41 41
42 function renderTag() { 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 const { tag } = item; 46 const { tag } = item;
47 if (!tag) return null; 47 if (!tag) return null;
src/components/Menu/src/useOpenKeys.ts renamed to src/components/Menu/src/hooks/useOpenKeys.ts
1 import { MenuModeEnum } from '/@/enums/menuEnum'; 1 import { MenuModeEnum } from '/@/enums/menuEnum';
2 import type { Menu as MenuType } from '/@/router/types'; 2 import type { Menu as MenuType } from '/@/router/types';
3 -import type { MenuState } from './types'; 3 +import type { MenuState } from '../types';
4 import type { Ref } from 'vue'; 4 import type { Ref } from 'vue';
5 5
6 import { unref } from 'vue'; 6 import { unref } from 'vue';
7 -import { menuStore } from '/@/store/modules/menu';  
8 import { getAllParentPath } from '/@/utils/helper/menuHelper'; 7 import { getAllParentPath } from '/@/utils/helper/menuHelper';
9 import { es6Unique } from '/@/utils'; 8 import { es6Unique } from '/@/utils';
  9 +import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
10 10
11 export function useOpenKeys( 11 export function useOpenKeys(
12 menuState: MenuState, 12 menuState: MenuState,
@@ -16,6 +16,7 @@ export function useOpenKeys( @@ -16,6 +16,7 @@ export function useOpenKeys(
16 mode: Ref<MenuModeEnum>, 16 mode: Ref<MenuModeEnum>,
17 accordion: Ref<boolean> 17 accordion: Ref<boolean>
18 ) { 18 ) {
  19 + const { getCollapsed } = useMenuSetting();
19 /** 20 /**
20 * @description:设置展开 21 * @description:设置展开
21 */ 22 */
@@ -49,7 +50,7 @@ export function useOpenKeys( @@ -49,7 +50,7 @@ export function useOpenKeys(
49 rootSubMenuKeys.push(path); 50 rootSubMenuKeys.push(path);
50 } 51 }
51 } 52 }
52 - if (!menuStore.getCollapsedState || !unref(isAppMenu)) { 53 + if (!unref(getCollapsed) || !unref(isAppMenu)) {
53 const latestOpenKey = openKeys.find((key) => menuState.openKeys.indexOf(key) === -1); 54 const latestOpenKey = openKeys.find((key) => menuState.openKeys.indexOf(key) === -1);
54 if (rootSubMenuKeys.indexOf(latestOpenKey as string) === -1) { 55 if (rootSubMenuKeys.indexOf(latestOpenKey as string) === -1) {
55 menuState.openKeys = openKeys; 56 menuState.openKeys = openKeys;
src/components/Menu/src/useSearchInput.ts renamed to src/components/Menu/src/hooks/useSearchInput.ts
1 import type { Menu as MenuType } from '/@/router/types'; 1 import type { Menu as MenuType } from '/@/router/types';
2 -import type { MenuState } from './types'; 2 +import type { MenuState } from '../types';
3 import type { Ref } from 'vue'; 3 import type { Ref } from 'vue';
4 4
5 import { isString } from '/@/utils/is'; 5 import { isString } from '/@/utils/is';
src/components/Menu/src/props.ts
@@ -8,6 +8,10 @@ export const basicProps = { @@ -8,6 +8,10 @@ export const basicProps = {
8 type: Array as PropType<Menu[]>, 8 type: Array as PropType<Menu[]>,
9 default: () => [], 9 default: () => [],
10 }, 10 },
  11 + flatItems: {
  12 + type: Array as PropType<Menu[]>,
  13 + default: () => [],
  14 + },
11 appendClass: { 15 appendClass: {
12 type: Boolean as PropType<boolean>, 16 type: Boolean as PropType<boolean>,
13 default: false, 17 default: false,
@@ -16,10 +20,6 @@ export const basicProps = { @@ -16,10 +20,6 @@ export const basicProps = {
16 type: Boolean as PropType<boolean>, 20 type: Boolean as PropType<boolean>,
17 default: false, 21 default: false,
18 }, 22 },
19 - flatItems: {  
20 - type: Array as PropType<Menu[]>,  
21 - default: () => [],  
22 - },  
23 // 是否显示搜索框 23 // 是否显示搜索框
24 search: { 24 search: {
25 type: Boolean as PropType<boolean>, 25 type: Boolean as PropType<boolean>,
@@ -55,7 +55,7 @@ export const basicProps = { @@ -55,7 +55,7 @@ export const basicProps = {
55 type: Boolean as PropType<boolean>, 55 type: Boolean as PropType<boolean>,
56 default: true, 56 default: true,
57 }, 57 },
58 - isTop: { 58 + isHorizontal: {
59 type: Boolean as PropType<boolean>, 59 type: Boolean as PropType<boolean>,
60 default: false, 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,7 +35,7 @@ export const basicProps = Object.assign({}, modalProps, {
35 }, 35 },
36 // Warm reminder message 36 // Warm reminder message
37 helpMessage: [String, Array] as PropType<string | string[]>, 37 helpMessage: [String, Array] as PropType<string | string[]>,
38 - // Whether to use wrapper 38 + // Whether to setting wrapper
39 useWrapper: { 39 useWrapper: {
40 type: Boolean as PropType<boolean>, 40 type: Boolean as PropType<boolean>,
41 default: true, 41 default: true,
src/components/Table/src/types/column.ts
@@ -190,7 +190,7 @@ export interface ColumnProps&lt;T&gt; { @@ -190,7 +190,7 @@ export interface ColumnProps&lt;T&gt; {
190 onFilterDropdownVisibleChange?: (visible: boolean) => void; 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 * such as slots: { filterIcon: 'XXX'} 194 * such as slots: { filterIcon: 'XXX'}
195 * @type object 195 * @type object
196 */ 196 */
src/components/Table/src/types/pagination.ts
@@ -86,7 +86,7 @@ export interface PaginationProps { @@ -86,7 +86,7 @@ export interface PaginationProps {
86 size?: string; 86 size?: string;
87 87
88 /** 88 /**
89 - * whether to use simple mode 89 + * whether to setting simple mode
90 * @type boolean 90 * @type boolean
91 */ 91 */
92 simple?: boolean; 92 simple?: boolean;
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 // Detail plugins list see https://www.tinymce.com/docs/plugins/ 2 // Detail plugins list see https://www.tinymce.com/docs/plugins/
3 // Custom builds see https://www.tinymce.com/download/custom-builds/ 3 // Custom builds see https://www.tinymce.com/download/custom-builds/
4 // colorpicker/contextmenu/textcolor plugin is now built in to the core editor, please remove it from your editor configuration 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,6 +31,7 @@ import {
31 PageHeader, 31 PageHeader,
32 Result, 32 Result,
33 Empty, 33 Empty,
  34 + Avatar,
34 } from 'ant-design-vue'; 35 } from 'ant-design-vue';
35 import { getApp } from '/@/setup/App'; 36 import { getApp } from '/@/setup/App';
36 37
@@ -76,5 +77,6 @@ export function registerGlobComp() { @@ -76,5 +77,6 @@ export function registerGlobComp() {
76 .use(PageHeader) 77 .use(PageHeader)
77 .use(Result) 78 .use(Result)
78 .use(Empty) 79 .use(Empty)
  80 + .use(Avatar)
79 .use(Tabs); 81 .use(Tabs);
80 } 82 }
src/components/types.ts 0 → 100644
  1 +import { defineComponent } from 'vue';
  2 +
  3 +export type Component = ReturnType<typeof defineComponent>;
src/design/var/index.less
@@ -16,4 +16,4 @@ @@ -16,4 +16,4 @@
16 @page-loading-z-index: 10000; 16 @page-loading-z-index: 10000;
17 17
18 // left-menu 18 // left-menu
19 -@app-menu-item-height: 44px; 19 +@app-menu-item-height: 42px;
src/enums/pageEnum.ts
@@ -5,4 +5,6 @@ export enum PageEnum { @@ -5,4 +5,6 @@ export enum PageEnum {
5 BASE_HOME = '/dashboard', 5 BASE_HOME = '/dashboard',
6 // error page path 6 // error page path
7 ERROR_PAGE = '/exception', 7 ERROR_PAGE = '/exception',
  8 + // error log page path
  9 + ERROR_LOG_PAGE = '/exception/error-log',
8 } 10 }
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 import type { LocaleSetting } from '/@/types/config'; 1 import type { LocaleSetting } from '/@/types/config';
2 2
3 -import { computed } from 'vue'; 3 +import { computed, unref } from 'vue';
4 import { appStore } from '/@/store/modules/app'; 4 import { appStore } from '/@/store/modules/app';
5 5
6 import getProjectSetting from '/@/settings/projectSetting'; 6 import getProjectSetting from '/@/settings/projectSetting';
@@ -8,24 +8,16 @@ import { localeList } from &#39;/@/locales&#39;; @@ -8,24 +8,16 @@ import { localeList } from &#39;/@/locales&#39;;
8 8
9 export function useLocaleSetting() { 9 export function useLocaleSetting() {
10 // Get locale configuration 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 // get current language 13 // get current language
16 - const getLang = computed(() => {  
17 - return getLocale.value.lang;  
18 - }); 14 + const getLang = computed(() => unref(getLocale).lang);
19 15
20 // get Available Locales 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 // get Fallback Locales 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 // Set locale configuration 22 // Set locale configuration
31 function setLocale(locale: Partial<LocaleSetting>): void { 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 &#39;vue&#39;; @@ -7,7 +7,7 @@ import { unref, ref } from &#39;vue&#39;;
7 7
8 import { getI18n } from '/@/setup/i18n'; 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 import moment from 'moment'; 12 import moment from 'moment';
13 13
@@ -67,7 +67,7 @@ export function useLocale() { @@ -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 export function useExternalI18n() { 72 export function useExternalI18n() {
73 return getI18n().global; 73 return getI18n().global;
src/hooks/web/usePage.ts
@@ -20,6 +20,7 @@ function handleError(e: Error) { @@ -20,6 +20,7 @@ function handleError(e: Error) {
20 export function useGo() { 20 export function useGo() {
21 const { push, replace } = useRouter(); 21 const { push, replace } = useRouter();
22 function go(opt: PageEnum | RouteLocationRawEx | string = PageEnum.BASE_HOME, isReplace = false) { 22 function go(opt: PageEnum | RouteLocationRawEx | string = PageEnum.BASE_HOME, isReplace = false) {
  23 + if (!opt) return;
23 if (isString(opt)) { 24 if (isString(opt)) {
24 isReplace ? replace(opt).catch(handleError) : push(opt).catch(handleError); 25 isReplace ? replace(opt).catch(handleError) : push(opt).catch(handleError);
25 } else { 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 import { 4 import {
2 DoubleRightOutlined, 5 DoubleRightOutlined,
3 DoubleLeftOutlined, 6 DoubleLeftOutlined,
4 MenuUnfoldOutlined, 7 MenuUnfoldOutlined,
5 MenuFoldOutlined, 8 MenuFoldOutlined,
6 } from '@ant-design/icons-vue'; 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 export default defineComponent({ 13 export default defineComponent({
13 name: 'LayoutTrigger', 14 name: 'LayoutTrigger',
14 props: { 15 props: {
15 sider: { 16 sider: {
16 - type: Boolean, 17 + type: Boolean as PropType<boolean>,
17 default: true, 18 default: true,
18 }, 19 },
19 theme: { 20 theme: {
20 - type: String, 21 + type: String as PropType<string>,
21 }, 22 },
22 }, 23 },
23 setup(props) { 24 setup(props) {
24 - function toggleMenu() {  
25 - menuStore.commitCollapsedState(!menuStore.getCollapsedState);  
26 - } 25 + const { toggleCollapsed, getCollapsed } = useMenuSetting();
27 26
28 return () => { 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 return ( 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 </span> 37 </span>
40 ); 38 );
41 }; 39 };
src/layouts/default/header/LayoutBreadcrumb.tsx
@@ -4,14 +4,17 @@ import type { PropType } from &#39;vue&#39;; @@ -4,14 +4,17 @@ import type { PropType } from &#39;vue&#39;;
4 4
5 import { defineComponent, TransitionGroup, unref, watch, ref } from 'vue'; 5 import { defineComponent, TransitionGroup, unref, watch, ref } from 'vue';
6 import Breadcrumb from '/@/components/Breadcrumb/Breadcrumb.vue'; 6 import Breadcrumb from '/@/components/Breadcrumb/Breadcrumb.vue';
  7 +import Icon from '/@/components/Icon';
7 import BreadcrumbItem from '/@/components/Breadcrumb/BreadcrumbItem.vue'; 8 import BreadcrumbItem from '/@/components/Breadcrumb/BreadcrumbItem.vue';
  9 +
8 import { useRouter } from 'vue-router'; 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 import { compile } from 'path-to-regexp'; 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 export default defineComponent({ 19 export default defineComponent({
17 name: 'BasicBreadcrumb', 20 name: 'BasicBreadcrumb',
@@ -40,7 +43,6 @@ export default defineComponent({ @@ -40,7 +43,6 @@ export default defineComponent({
40 const matchedList = matched.filter((item) => item.meta && item.meta.title).slice(1); 43 const matchedList = matched.filter((item) => item.meta && item.meta.title).slice(1);
41 const firstItem = matchedList[0]; 44 const firstItem = matchedList[0];
42 const ret = getHomeRoute(firstItem); 45 const ret = getHomeRoute(firstItem);
43 -  
44 if (!isBoolean(ret)) { 46 if (!isBoolean(ret)) {
45 matchedList.unshift(ret); 47 matchedList.unshift(ret);
46 } 48 }
@@ -74,42 +76,51 @@ export default defineComponent({ @@ -74,42 +76,51 @@ export default defineComponent({
74 return push(pathCompile(path)); 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 return () => ( 121 return () => (
78 <Breadcrumb class={['layout-breadcrumb', unref(itemList).length === 0 ? 'hidden' : '']}> 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 </Breadcrumb> 124 </Breadcrumb>
114 ); 125 );
115 }, 126 },
src/layouts/default/header/LayoutHeader.tsx
  1 +import './index.less';
  2 +
1 import { defineComponent, unref, computed, ref } from 'vue'; 3 import { defineComponent, unref, computed, ref } from 'vue';
2 4
3 import { Layout, Tooltip, Badge } from 'ant-design-vue'; 5 import { Layout, Tooltip, Badge } from 'ant-design-vue';
4 -import Logo from '/@/layouts/logo/index.vue'; 6 +import { AppLogo } from '/@/components/Application';
5 import UserDropdown from './UserDropdown'; 7 import UserDropdown from './UserDropdown';
6 import LayoutMenu from '/@/layouts/default/menu/LayoutMenu'; 8 import LayoutMenu from '/@/layouts/default/menu/LayoutMenu';
7 import LayoutBreadcrumb from './LayoutBreadcrumb'; 9 import LayoutBreadcrumb from './LayoutBreadcrumb';
@@ -12,50 +14,57 @@ import { @@ -12,50 +14,57 @@ import {
12 RedoOutlined, 14 RedoOutlined,
13 FullscreenExitOutlined, 15 FullscreenExitOutlined,
14 FullscreenOutlined, 16 FullscreenOutlined,
15 - GithubFilled,  
16 LockOutlined, 17 LockOutlined,
17 BugOutlined, 18 BugOutlined,
18 } from '@ant-design/icons-vue'; 19 } from '@ant-design/icons-vue';
  20 +import { useModal } from '/@/components/Modal';
19 21
20 import { useFullscreen } from '/@/hooks/web/useFullScreen'; 22 import { useFullscreen } from '/@/hooks/web/useFullScreen';
21 import { useTabs } from '/@/hooks/web/useTabs'; 23 import { useTabs } from '/@/hooks/web/useTabs';
22 import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn'; 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 import { useRouter } from 'vue-router'; 29 import { useRouter } from 'vue-router';
24 -import { useModal } from '/@/components/Modal';  
25 30
26 -import { appStore } from '/@/store/modules/app';  
27 import { errorStore } from '/@/store/modules/error'; 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 export default defineComponent({ 37 export default defineComponent({
34 - name: 'DefaultLayoutHeader', 38 + name: 'LayoutHeader',
35 setup() { 39 setup() {
36 - const widthRef = ref(200);  
37 let logoEl: Element | null; 40 let logoEl: Element | null;
38 41
  42 + const widthRef = ref(200);
  43 +
39 const { refreshPage } = useTabs(); 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 const { push } = useRouter(); 61 const { push } = useRouter();
41 const [register, { openModal }] = useModal(); 62 const [register, { openModal }] = useModal();
42 const { toggleFullscreen, isFullscreenRef } = useFullscreen(); 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 useWindowSizeFn( 65 useWindowSizeFn(
57 () => { 66 () => {
58 - if (!unref(showTopMenu)) return; 67 + if (!unref(getShowTopMenu)) return;
59 let width = 0; 68 let width = 0;
60 if (!logoEl) { 69 if (!logoEl) {
61 logoEl = document.querySelector('.layout-header__logo'); 70 logoEl = document.querySelector('.layout-header__logo');
@@ -69,24 +78,23 @@ export default defineComponent({ @@ -69,24 +78,23 @@ export default defineComponent({
69 { immediate: true } 78 { immediate: true }
70 ); 79 );
71 80
72 - function goToGithub() {  
73 - window.open(GITHUB_URL, '__blank');  
74 - }  
75 -  
76 const headerClass = computed(() => { 81 const headerClass = computed(() => {
77 - const theme = unref(getProjectConfigRef).headerSetting.theme; 82 + const theme = unref(getTheme);
78 return theme ? `layout-header__header--${theme}` : ''; 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 function handleToErrorList() { 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,162 +104,129 @@ export default defineComponent({
96 openModal(true); 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 return ( 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 </Layout.Header> 230 </Layout.Header>
256 ); 231 );
257 }; 232 };
src/layouts/default/header/LockActionItem.less
1 .lock-modal { 1 .lock-modal {
2 &__entry { 2 &__entry {
3 position: relative; 3 position: relative;
4 - // width: 500px;  
5 height: 240px; 4 height: 240px;
6 padding: 130px 30px 60px 30px; 5 padding: 130px 30px 60px 30px;
7 background: #fff; 6 background: #fff;
src/layouts/default/header/LockActionItem.tsx
1 -// 组件相关 1 +import './LockActionItem.less';
  2 +
2 import { defineComponent } from 'vue'; 3 import { defineComponent } from 'vue';
3 import { BasicModal, useModalInner } from '/@/components/Modal/index'; 4 import { BasicModal, useModalInner } from '/@/components/Modal/index';
4 -  
5 -// hook 5 +import Button from '/@/components/Button/index.vue';
6 import { BasicForm, useForm } from '/@/components/Form/index'; 6 import { BasicForm, useForm } from '/@/components/Form/index';
7 7
8 import headerImg from '/@/assets/images/header.jpg'; 8 import headerImg from '/@/assets/images/header.jpg';
9 9
10 import { appStore } from '/@/store/modules/app'; 10 import { appStore } from '/@/store/modules/app';
11 import { userStore } from '/@/store/modules/user'; 11 import { userStore } from '/@/store/modules/user';
12 -import Button from '/@/components/Button/index.vue';  
13 -import './LockActionItem.less'; 12 +
14 const prefixCls = 'lock-modal'; 13 const prefixCls = 'lock-modal';
15 export default defineComponent({ 14 export default defineComponent({
16 name: 'LockModal', 15 name: 'LockModal',
17 setup(_, { attrs }) { 16 setup(_, { attrs }) {
18 - const [register, { setModalProps }] = useModalInner();  
19 - // 样式前缀 17 + const [register, { closeModal }] = useModalInner();
  18 +
20 const [registerForm, { validateFields, resetFields }] = useForm({ 19 const [registerForm, { validateFields, resetFields }] = useForm({
21 - // 隐藏按钮  
22 showActionButtonGroup: false, 20 showActionButtonGroup: false,
23 - // 表单项  
24 schemas: [ 21 schemas: [
25 { 22 {
26 field: 'password', 23 field: 'password',
27 - label: '', 24 + label: '锁屏密码',
28 component: 'InputPassword', 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 async function lock(valid = true) { 31 async function lock(valid = true) {
40 let password: string | undefined = ''; 32 let password: string | undefined = '';
41 33
@@ -46,9 +38,7 @@ export default defineComponent({ @@ -46,9 +38,7 @@ export default defineComponent({
46 const values = (await validateFields()) as any; 38 const values = (await validateFields()) as any;
47 password = values.password; 39 password = values.password;
48 } 40 }
49 - setModalProps({  
50 - visible: false,  
51 - }); 41 + closeModal();
52 42
53 appStore.commitLockInfoState({ 43 appStore.commitLockInfoState({
54 isLock: true, 44 isLock: true,
@@ -57,7 +47,7 @@ export default defineComponent({ @@ -57,7 +47,7 @@ export default defineComponent({
57 await resetFields(); 47 await resetFields();
58 } catch (error) {} 48 } catch (error) {}
59 } 49 }
60 - // 账号密码登录 50 +
61 return () => ( 51 return () => (
62 <BasicModal footer={null} title="锁定屏幕" {...attrs} class={prefixCls} onRegister={register}> 52 <BasicModal footer={null} title="锁定屏幕" {...attrs} class={prefixCls} onRegister={register}>
63 {() => ( 53 {() => (
@@ -66,7 +56,9 @@ export default defineComponent({ @@ -66,7 +56,9 @@ export default defineComponent({
66 <img src={headerImg} class={`${prefixCls}__header-img`} /> 56 <img src={headerImg} class={`${prefixCls}__header-img`} />
67 <p class={`${prefixCls}__header-name`}>{userStore.getUserInfoState.realName}</p> 57 <p class={`${prefixCls}__header-name`}>{userStore.getUserInfoState.realName}</p>
68 </div> 58 </div>
69 - <BasicForm onRegister={registerForm} /> 59 +
  60 + <BasicForm onRegister={registerForm} layout="vertical" />
  61 +
70 <div class={`${prefixCls}__footer`}> 62 <div class={`${prefixCls}__footer`}>
71 <Button type="primary" block class="mt-2" onClick={lock}> 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 &#39;/@/components/Icon/index&#39;; @@ -11,15 +11,23 @@ import Icon from &#39;/@/components/Icon/index&#39;;
11 import { userStore } from '/@/store/modules/user'; 11 import { userStore } from '/@/store/modules/user';
12 12
13 import { DOC_URL } from '/@/settings/siteSetting'; 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 const prefixCls = 'user-dropdown'; 25 const prefixCls = 'user-dropdown';
  26 +
17 export default defineComponent({ 27 export default defineComponent({
18 name: 'UserDropdown', 28 name: 'UserDropdown',
19 setup() { 29 setup() {
20 - const getProjectConfigRef = computed(() => {  
21 - return appStore.getProjectConfig;  
22 - }); 30 + const { getShowDoc } = useHeaderSetting();
23 31
24 const getUserInfo = computed(() => { 32 const getUserInfo = computed(() => {
25 const { realName = '', desc } = userStore.getUserInfoState || {}; 33 const { realName = '', desc } = userStore.getUserInfoState || {};
@@ -33,7 +41,7 @@ export default defineComponent({ @@ -33,7 +41,7 @@ export default defineComponent({
33 41
34 // open doc 42 // open doc
35 function openDoc() { 43 function openDoc() {
36 - window.open(DOC_URL, '__blank'); 44 + openWindow(DOC_URL);
37 } 45 }
38 46
39 function handleMenuClick(e: any) { 47 function handleMenuClick(e: any) {
@@ -44,7 +52,7 @@ export default defineComponent({ @@ -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 return ( 56 return (
49 <Menu.Item key={key}> 57 <Menu.Item key={key}>
50 {() => ( 58 {() => (
@@ -57,37 +65,43 @@ export default defineComponent({ @@ -57,37 +65,43 @@ export default defineComponent({
57 ); 65 );
58 } 66 }
59 67
60 - return () => { 68 + function renderSlotsDefault() {
61 const { realName } = unref(getUserInfo); 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 return ( 100 return (
66 <Dropdown placement="bottomLeft"> 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 </Dropdown> 106 </Dropdown>
93 ); 107 );
src/layouts/default/header/notice/NoticeActionItem.vue
1 <template> 1 <template>
2 <div class="layout-header__action-item notify-action"> 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 <Badge :count="count" dot :numberStyle="numberStyle"> 4 <Badge :count="count" dot :numberStyle="numberStyle">
5 <BellOutlined class="layout-header__action-icon" /> 5 <BellOutlined class="layout-header__action-icon" />
6 </Badge> 6 </Badge>
@@ -31,6 +31,7 @@ @@ -31,6 +31,7 @@
31 components: { Popover, BellOutlined, Tabs, TabPane: Tabs.TabPane, Badge, NoticeList }, 31 components: { Popover, BellOutlined, Tabs, TabPane: Tabs.TabPane, Badge, NoticeList },
32 setup() { 32 setup() {
33 let count = 0; 33 let count = 0;
  34 +
34 for (let i = 0; i < tabListData.length; i++) { 35 for (let i = 0; i < tabListData.length; i++) {
35 count += tabListData[i].list.length; 36 count += tabListData[i].list.length;
36 } 37 }
@@ -44,6 +45,10 @@ @@ -44,6 +45,10 @@
44 }); 45 });
45 </script> 46 </script>
46 <style lang="less"> 47 <style lang="less">
  48 + .layout-header__notify-action {
  49 + max-width: 360px;
  50 + }
  51 +
47 .notify-action { 52 .notify-action {
48 padding-top: 2px; 53 padding-top: 2px;
49 54
@@ -56,7 +61,6 @@ @@ -56,7 +61,6 @@
56 61
57 .ant-badge-multiple-words { 62 .ant-badge-multiple-words {
58 padding: 0 4px; 63 padding: 0 4px;
59 - // transform: translate(26%, -40%);  
60 } 64 }
61 65
62 svg { 66 svg {
src/layouts/default/header/notice/NoticeList.vue
1 <template> 1 <template>
2 - <List class="list"> 2 + <a-list class="list">
3 <template v-for="item in list" :key="item.id"> 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 <template #title> 6 <template #title>
7 <div class="title"> 7 <div class="title">
8 {{ item.title }} 8 {{ item.title }}
9 <div class="extra" v-if="item.extra"> 9 <div class="extra" v-if="item.extra">
10 - <Tag class="tag" :color="item.color"> 10 + <a-tag class="tag" :color="item.color">
11 {{ item.extra }} 11 {{ item.extra }}
12 - </Tag> 12 + </a-tag>
13 </div> 13 </div>
14 </div> 14 </div>
15 </template> 15 </template>
  16 +
16 <template #avatar> 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 <span v-else> {{ item.avatar }}</span> 19 <span v-else> {{ item.avatar }}</span>
19 </template> 20 </template>
  21 +
20 <template #description> 22 <template #description>
21 <div> 23 <div>
22 <div class="description">{{ item.description }}</div> 24 <div class="description">{{ item.description }}</div>
23 <div class="datetime">{{ item.datetime }}</div> 25 <div class="datetime">{{ item.datetime }}</div>
24 </div> 26 </div>
25 </template> 27 </template>
26 - </ListItemMeta>  
27 - </ListItem> 28 + </a-list-item-meta>
  29 + </a-list-item>
28 </template> 30 </template>
29 - </List> 31 + </a-list>
30 </template> 32 </template>
31 <script lang="ts"> 33 <script lang="ts">
32 import { defineComponent, PropType } from 'vue'; 34 import { defineComponent, PropType } from 'vue';
33 - import { List, Avatar, Tag } from 'ant-design-vue';  
34 import { ListItem } from './data'; 35 import { ListItem } from './data';
35 36
36 export default defineComponent({ 37 export default defineComponent({
@@ -40,19 +41,6 @@ @@ -40,19 +41,6 @@
40 default: () => [], 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 </script> 45 </script>
58 <style lang="less" scoped> 46 <style lang="less" scoped>
@@ -61,7 +49,7 @@ @@ -61,7 +49,7 @@
61 display: none; 49 display: none;
62 } 50 }
63 51
64 - &__item { 52 + &-item {
65 padding: 6px; 53 padding: 6px;
66 overflow: hidden; 54 overflow: hidden;
67 cursor: pointer; 55 cursor: pointer;
src/layouts/default/header/notice/data.ts
@@ -56,14 +56,6 @@ export const tabListData: TabItem[] = [ @@ -56,14 +56,6 @@ export const tabListData: TabItem[] = [
56 datetime: '2017-08-07', 56 datetime: '2017-08-07',
57 type: '1', 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,47 +36,4 @@
36 margin: 0 auto; 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 &#39;./header/LayoutHeader&#39;; @@ -4,7 +4,7 @@ import LayoutHeader from &#39;./header/LayoutHeader&#39;;
4 4
5 import { appStore } from '/@/store/modules/app'; 5 import { appStore } from '/@/store/modules/app';
6 import LayoutContent from './LayoutContent'; 6 import LayoutContent from './LayoutContent';
7 -import LayoutSideBar from './LayoutSideBar'; 7 +import LayoutSideBar from './sider/LayoutSideBar';
8 import SettingBtn from './setting/index.vue'; 8 import SettingBtn from './setting/index.vue';
9 import MultipleTabs from './multitabs/index'; 9 import MultipleTabs from './multitabs/index';
10 10
@@ -36,7 +36,7 @@ export default defineComponent({ @@ -36,7 +36,7 @@ export default defineComponent({
36 return show; 36 return show;
37 }); 37 });
38 38
39 - const isShowMixHeaderRef = computed(() => { 39 + const showMixHeaderRef = computed(() => {
40 const { 40 const {
41 menuSetting: { type }, 41 menuSetting: { type },
42 } = unref(getProjectConfigRef); 42 } = unref(getProjectConfigRef);
@@ -57,11 +57,11 @@ export default defineComponent({ @@ -57,11 +57,11 @@ export default defineComponent({
57 }); 57 });
58 58
59 const showFullHeaderRef = computed(() => { 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 const showInsetHeaderRef = computed(() => { 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 const fixedHeaderClsRef = computed(() => { 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 import type { Menu } from '/@/router/types'; 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 import { BasicMenu } from '/@/components/Menu/index'; 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 export default defineComponent({ 19 export default defineComponent({
28 name: 'DefaultLayoutMenu', 20 name: 'DefaultLayoutMenu',
29 props: { 21 props: {
@@ -43,7 +35,7 @@ export default defineComponent({ @@ -43,7 +35,7 @@ export default defineComponent({
43 type: Boolean as PropType<boolean>, 35 type: Boolean as PropType<boolean>,
44 default: true, 36 default: true,
45 }, 37 },
46 - isTop: { 38 + isHorizontal: {
47 type: Boolean as PropType<boolean>, 39 type: Boolean as PropType<boolean>,
48 default: false, 40 default: false,
49 }, 41 },
@@ -53,190 +45,99 @@ export default defineComponent({ @@ -53,190 +45,99 @@ export default defineComponent({
53 }, 45 },
54 }, 46 },
55 setup(props) { 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 function handleMenuClick(menu: Menu) { 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 async function beforeMenuClickFn(menu: Menu) { 95 async function beforeMenuClickFn(menu: Menu) {
167 const { meta: { externalLink } = {} } = menu; 96 const { meta: { externalLink } = {} } = menu;
168 97
169 if (externalLink) { 98 if (externalLink) {
170 - window.open(externalLink, '_blank'); 99 + openWindow(externalLink);
171 return false; 100 return false;
172 } 101 }
173 -  
174 return true; 102 return true;
175 } 103 }
176 104
177 function handleClickSearchInput() { 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 return ( 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 return () => { 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 return ( 121 return (
214 <BasicMenu 122 <BasicMenu
215 - beforeClickFn={beforeMenuClickFn}  
216 - onMenuClick={handleMenuClick}  
217 - type={menuType}  
218 - mode={props.menuMode || mode}  
219 class="layout-menu" 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 items={unref(menusRef)} 133 items={unref(menusRef)}
225 flatItems={unref(flatMenusRef)} 134 flatItems={unref(flatMenusRef)}
  135 + accordion={unref(getAccordion)}
  136 + onMenuClick={handleMenuClick}
226 onClickSearchInput={handleClickSearchInput} 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 </BasicMenu> 142 </BasicMenu>
242 ); 143 );
src/layouts/default/menu/index.less
@@ -9,17 +9,5 @@ @@ -9,17 +9,5 @@
9 width: @logo-width; 9 width: @logo-width;
10 height: @logo-width; 10 height: @logo-width;
11 } 11 }
12 -  
13 - &.light {  
14 - .logo-title {  
15 - color: @text-color-base;  
16 - }  
17 - }  
18 -  
19 - &.dark {  
20 - .logo-title {  
21 - color: @white;  
22 - }  
23 - }  
24 } 12 }
25 } 13 }
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,7 +33,7 @@ export default defineComponent({
33 return tabStore.getTabsState; 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 watch( 37 watch(
38 () => tabStore.getLastChangeRouteState, 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 &#39;vue-router&#39;; @@ -5,12 +5,29 @@ import { useRouter } from &#39;vue-router&#39;;
5 import router from '/@/router'; 5 import router from '/@/router';
6 6
7 import { tabStore } from '/@/store/modules/tab'; 7 import { tabStore } from '/@/store/modules/tab';
8 -import { appStore } from '/@/store/modules/app';  
9 8
10 import { unique } from '/@/utils'; 9 import { unique } from '/@/utils';
11 10
  11 +import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
  12 +
12 export function useFrameKeepAlive() { 13 export function useFrameKeepAlive() {
13 const { currentRoute } = useRouter(); 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 function getAllFramePages(routes: AppRouteRecordRaw[]): AppRouteRecordRaw[] { 32 function getAllFramePages(routes: AppRouteRecordRaw[]): AppRouteRecordRaw[] {
16 let res: AppRouteRecordRaw[] = []; 33 let res: AppRouteRecordRaw[] = [];
@@ -30,26 +47,9 @@ export function useFrameKeepAlive() { @@ -30,26 +47,9 @@ export function useFrameKeepAlive() {
30 function showIframe(item: AppRouteRecordRaw) { 47 function showIframe(item: AppRouteRecordRaw) {
31 return item.path === unref(currentRoute).path; 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 function hasRenderFrame(path: string) { 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 return { hasRenderFrame, getFramePages, showIframe, getAllFramePages }; 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 import { RouterView, RouteLocation } from 'vue-router'; 4 import { RouterView, RouteLocation } from 'vue-router';
3 5
4 import FrameLayout from '/@/layouts/iframe/index.vue'; 6 import FrameLayout from '/@/layouts/iframe/index.vue';
5 7
6 import { useTransition } from './useTransition'; 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 import { tabStore } from '/@/store/modules/tab'; 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 export default defineComponent({ 20 export default defineComponent({
13 name: 'PageLayout', 21 name: 'PageLayout',
14 setup() { 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 return ( 40 return (
41 <div> 41 <div>
42 <RouterView> 42 <RouterView>
43 {{ 43 {{
44 - default: ({ Component, route }: { Component: any; route: RouteLocation }) => { 44 + default: ({ Component, route }: DefaultContext) => {
45 // No longer show animations that are already in the tab 45 // No longer show animations that are already in the tab
46 const cacheTabs = unref(getCacheTabsRef); 46 const cacheTabs = unref(getCacheTabsRef);
47 const isInCache = cacheTabs.includes(route.name as string); 47 const isInCache = cacheTabs.includes(route.name as string);
48 const name = isInCache && route.meta.inTab ? 'fade' : null; 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 </KeepAlive> 55 </KeepAlive>
54 ) : ( 56 ) : (
55 - <Component key={route.fullPath} /> 57 + renderComp()
56 ); 58 );
57 - return openRouterTransition ? ( 59 +
  60 + return unref(getOpenRouterTransition) ? (
58 <Transition 61 <Transition
59 - {...on}  
60 - name={name || route.meta.transitionName || routerTransition} 62 + {...transitionEvent}
  63 + name={name || route.meta.transitionName || unref(getRouterTransition)}
61 mode="out-in" 64 mode="out-in"
62 appear={true} 65 appear={true}
63 > 66 >
64 - {() => Content} 67 + {() => PageContent}
65 </Transition> 68 </Transition>
66 ) : ( 69 ) : (
67 - Content 70 + PageContent
68 ); 71 );
69 }, 72 },
70 }} 73 }}
71 </RouterView> 74 </RouterView>
72 - {projectSetting.canEmbedIFramePage && <FrameLayout />} 75 + {unref(getCanEmbedIFramePage) && <FrameLayout />}
73 </div> 76 </div>
74 ); 77 );
75 }; 78 };
src/layouts/page/useTransition.ts
  1 +import { useRootSetting } from '/@/hooks/setting/useRootSetting';
1 import { appStore } from '/@/store/modules/app'; 2 import { appStore } from '/@/store/modules/app';
2 import { tryOnUnmounted } from '/@/utils/helper/vueHelper'; 3 import { tryOnUnmounted } from '/@/utils/helper/vueHelper';
  4 +
3 export function useTransition() { 5 export function useTransition() {
4 function handleAfterEnter() { 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 // Close loading after the route switching animation ends 9 // Close loading after the route switching animation ends
9 appStore.setPageLoadingAction(false); 10 appStore.setPageLoadingAction(false);
10 } 11 }
@@ -15,9 +16,6 @@ export function useTransition() { @@ -15,9 +16,6 @@ export function useTransition() {
15 }); 16 });
16 17
17 return { 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,7 +49,7 @@ if (isDevMode()) {
49 window.__APP__ = app; 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 if (isProdMode() && isUseMock()) { 53 if (isProdMode() && isUseMock()) {
54 setupProdMockServer(); 54 setupProdMockServer();
55 } 55 }
src/router/guard/index.ts
@@ -6,7 +6,7 @@ import { createProgressGuard } from &#39;./progressGuard&#39;; @@ -6,7 +6,7 @@ import { createProgressGuard } from &#39;./progressGuard&#39;;
6 import { createPermissionGuard } from './permissionGuard'; 6 import { createPermissionGuard } from './permissionGuard';
7 import { createPageLoadingGuard } from './pageLoadingGuard'; 7 import { createPageLoadingGuard } from './pageLoadingGuard';
8 8
9 -import { useGlobSetting, useProjectSetting } from '/@/settings/use'; 9 +import { useGlobSetting, useProjectSetting } from '/@/hooks/setting';
10 10
11 import { getIsOpenTab, setCurrentTo } from '/@/utils/helper/routeHelper'; 11 import { getIsOpenTab, setCurrentTo } from '/@/utils/helper/routeHelper';
12 import { setTitle } from '/@/utils/browser'; 12 import { setTitle } from '/@/utils/browser';
src/router/menus/index.ts
@@ -7,6 +7,7 @@ import { filter } from &#39;/@/utils/helper/treeHelper&#39;; @@ -7,6 +7,7 @@ import { filter } from &#39;/@/utils/helper/treeHelper&#39;;
7 import router from '/@/router'; 7 import router from '/@/router';
8 import { PermissionModeEnum } from '/@/enums/appEnum'; 8 import { PermissionModeEnum } from '/@/enums/appEnum';
9 import { pathToRegexp } from 'path-to-regexp'; 9 import { pathToRegexp } from 'path-to-regexp';
  10 +
10 import modules from 'globby!/@/router/menus/modules/**/*.@(ts)'; 11 import modules from 'globby!/@/router/menus/modules/**/*.@(ts)';
11 12
12 const menuModules: MenuModule[] = []; 13 const menuModules: MenuModule[] = [];
@@ -44,7 +45,6 @@ async function getAsyncMenus() { @@ -44,7 +45,6 @@ async function getAsyncMenus() {
44 // 获取深层扁平化菜单 45 // 获取深层扁平化菜单
45 export const getFlatMenus = async () => { 46 export const getFlatMenus = async () => {
46 const menus = await getAsyncMenus(); 47 const menus = await getAsyncMenus();
47 -  
48 return flatMenus(menus); 48 return flatMenus(menus);
49 }; 49 };
50 50
src/settings/projectSetting.ts
@@ -9,7 +9,7 @@ import { isProdMode } from &#39;/@/utils/env&#39;; @@ -9,7 +9,7 @@ import { isProdMode } from &#39;/@/utils/env&#39;;
9 const setting: ProjectConfig = { 9 const setting: ProjectConfig = {
10 // locale setting 10 // locale setting
11 locale: { 11 locale: {
12 - // Locales 12 + // Locale
13 lang: 'zh_CN', 13 lang: 'zh_CN',
14 // Default locale 14 // Default locale
15 fallback: 'zh_CN', 15 fallback: 'zh_CN',
@@ -29,17 +29,22 @@ const setting: ProjectConfig = { @@ -29,17 +29,22 @@ const setting: ProjectConfig = {
29 29
30 // Whether to show the configuration button 30 // Whether to show the configuration button
31 showSettingButton: true, 31 showSettingButton: true,
  32 +
32 // 权限模式 33 // 权限模式
33 permissionMode: PermissionModeEnum.ROLE, 34 permissionMode: PermissionModeEnum.ROLE,
  35 +
34 // 网站灰色模式,用于可能悼念的日期开启 36 // 网站灰色模式,用于可能悼念的日期开启
35 grayMode: false, 37 grayMode: false,
  38 +
36 // 色弱模式 39 // 色弱模式
37 colorWeak: false, 40 colorWeak: false,
38 41
39 // 是否取消菜单,顶部,多标签页显示, 用于可能内嵌在别的系统内 42 // 是否取消菜单,顶部,多标签页显示, 用于可能内嵌在别的系统内
40 fullContent: false, 43 fullContent: false,
  44 +
41 // content mode 45 // content mode
42 contentMode: ContentEnum.FULL, 46 contentMode: ContentEnum.FULL,
  47 +
43 // 是否显示logo 48 // 是否显示logo
44 showLogo: true, 49 showLogo: true,
45 50
@@ -58,11 +63,10 @@ const setting: ProjectConfig = { @@ -58,11 +63,10 @@ const setting: ProjectConfig = {
58 showFullScreen: true, 63 showFullScreen: true,
59 // 显示文档按钮 64 // 显示文档按钮
60 showDoc: true, 65 showDoc: true,
61 - // 是否显示github  
62 - showGithub: true,  
63 // 显示消息中心按钮 66 // 显示消息中心按钮
64 showNotice: true, 67 showNotice: true,
65 }, 68 },
  69 +
66 // 菜单配置 70 // 菜单配置
67 menuSetting: { 71 menuSetting: {
68 // 菜单折叠 72 // 菜单折叠
@@ -108,13 +112,16 @@ const setting: ProjectConfig = { @@ -108,13 +112,16 @@ const setting: ProjectConfig = {
108 // 标签页缓存最大数量 112 // 标签页缓存最大数量
109 max: 12, 113 max: 12,
110 }, 114 },
  115 +
111 // 是否开启KeepAlive缓存 开发时候最好关闭,不然每次都需要清除缓存 116 // 是否开启KeepAlive缓存 开发时候最好关闭,不然每次都需要清除缓存
112 openKeepAlive: true, 117 openKeepAlive: true,
113 118
114 // 自动锁屏时间,为0不锁屏。 单位分钟 默认0 119 // 自动锁屏时间,为0不锁屏。 单位分钟 默认0
115 lockTime: 0, 120 lockTime: 0,
  121 +
116 // 显示面包屑 122 // 显示面包屑
117 showBreadCrumb: true, 123 showBreadCrumb: true,
  124 +
118 // 显示面包屑图标 125 // 显示面包屑图标
119 showBreadCrumbIcon: false, 126 showBreadCrumbIcon: false,
120 127
src/setup/error-handle/index.ts
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 */ 3 */
4 4
5 import { errorStore, ErrorInfo } from '/@/store/modules/error'; 5 import { errorStore, ErrorInfo } from '/@/store/modules/error';
6 -import { useProjectSetting } from '/@/settings/use'; 6 +import { useProjectSetting } from '/@/hooks/setting';
7 import { ErrorTypeEnum } from '/@/enums/exceptionEnum'; 7 import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
8 import { App } from 'vue'; 8 import { App } from 'vue';
9 9
src/setup/i18n/index.ts
@@ -4,7 +4,7 @@ import type { I18n, I18nOptions } from &#39;vue-i18n&#39;; @@ -4,7 +4,7 @@ import type { I18n, I18nOptions } from &#39;vue-i18n&#39;;
4 import { createI18n } from 'vue-i18n'; 4 import { createI18n } from 'vue-i18n';
5 import localeMessages from '/@/locales'; 5 import localeMessages from '/@/locales';
6 import { useLocale } from '/@/hooks/web/useLocale'; 6 import { useLocale } from '/@/hooks/web/useLocale';
7 -import { useLocaleSetting } from '/@/settings/use/useLocaleSetting'; 7 +import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting';
8 8
9 const { setupLocale } = useLocale(); 9 const { setupLocale } = useLocale();
10 10
src/store/modules/error.ts
@@ -4,7 +4,7 @@ import { VuexModule, getModule, Module, Mutation, Action } from &#39;vuex-module-dec @@ -4,7 +4,7 @@ import { VuexModule, getModule, Module, Mutation, Action } from &#39;vuex-module-dec
4 4
5 import { formatToDateTime } from '/@/utils/dateUtil'; 5 import { formatToDateTime } from '/@/utils/dateUtil';
6 import { ErrorTypeEnum } from '/@/enums/exceptionEnum'; 6 import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
7 -import { useProjectSetting } from '/@/settings/use'; 7 +import { useProjectSetting } from '/@/hooks/setting';
8 8
9 export interface ErrorInfo { 9 export interface ErrorInfo {
10 type: ErrorTypeEnum; 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,11 +97,6 @@ class Permission extends VuexModule {
97 if (!roles) return true; 97 if (!roles) return true;
98 return roleList.some((role) => roles.includes(role)); 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 } else if (permissionMode === PermissionModeEnum.BACK) { 101 } else if (permissionMode === PermissionModeEnum.BACK) {
107 const messageKey = 'loadMenu'; 102 const messageKey = 'loadMenu';
src/types/config.d.ts
@@ -44,7 +44,6 @@ export interface HeaderSetting { @@ -44,7 +44,6 @@ export interface HeaderSetting {
44 useLockPage: boolean; 44 useLockPage: boolean;
45 // 显示文档按钮 45 // 显示文档按钮
46 showDoc: boolean; 46 showDoc: boolean;
47 - showGithub: boolean;  
48 // 显示消息中心按钮 47 // 显示消息中心按钮
49 showNotice: boolean; 48 showNotice: boolean;
50 } 49 }
src/utils/file/download.ts
  1 +import { openWindow } from '..';
1 import { dataURLtoBlob, urlToBase64 } from './base64Conver'; 2 import { dataURLtoBlob, urlToBase64 } from './base64Conver';
2 3
3 /** 4 /**
@@ -93,6 +94,6 @@ export function downloadByUrl({ @@ -93,6 +94,6 @@ export function downloadByUrl({
93 url += '?download'; 94 url += '?download';
94 } 95 }
95 96
96 - window.open(url, target); 97 + openWindow(url, { target });
97 return true; 98 return true;
98 } 99 }
src/utils/helper/envHelper.ts
1 import { getEnv } from '/@/utils/env'; 1 import { getEnv } from '/@/utils/env';
2 -import { useGlobSetting } from '/@/settings/use'; 2 +import { useGlobSetting } from '/@/hooks/setting';
3 import pkg from '../../../package.json'; 3 import pkg from '../../../package.json';
4 const globSetting = useGlobSetting(); 4 const globSetting = useGlobSetting();
5 5
src/utils/http/axios/index.ts
@@ -10,7 +10,7 @@ import { AxiosTransform } from &#39;./axiosTransform&#39;; @@ -10,7 +10,7 @@ import { AxiosTransform } from &#39;./axiosTransform&#39;;
10 10
11 import { checkStatus } from './checkStatus'; 11 import { checkStatus } from './checkStatus';
12 12
13 -import { useGlobSetting } from '/@/settings/use'; 13 +import { useGlobSetting } from '/@/hooks/setting';
14 import { useMessage } from '/@/hooks/web/useMessage'; 14 import { useMessage } from '/@/hooks/web/useMessage';
15 15
16 import { RequestEnum, ResultEnum, ContentTypeEnum } from '/@/enums/httpEnum'; 16 import { RequestEnum, ResultEnum, ContentTypeEnum } from '/@/enums/httpEnum';
src/utils/index.ts
@@ -11,6 +11,7 @@ export function getPopupContainer(node?: HTMLElement): HTMLElement { @@ -11,6 +11,7 @@ export function getPopupContainer(node?: HTMLElement): HTMLElement {
11 } 11 }
12 return document.body; 12 return document.body;
13 } 13 }
  14 +
14 /** 15 /**
15 * Add the object as a parameter to the URL 16 * Add the object as a parameter to the URL
16 * @param baseUrl url 17 * @param baseUrl url
@@ -64,3 +65,16 @@ export function unique&lt;T = any&gt;(arr: T[], key: string): T[] { @@ -64,3 +65,16 @@ export function unique&lt;T = any&gt;(arr: T[], key: string): T[] {
64 export function es6Unique<T>(arr: T[]): T[] { 65 export function es6Unique<T>(arr: T[]): T[] {
65 return Array.from(new Set(arr)); 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,6 +45,8 @@
45 45
46 import Icon from '/@/components/Icon/index'; 46 import Icon from '/@/components/Icon/index';
47 47
  48 + import { openWindow } from '/@/utils';
  49 +
48 export default defineComponent({ 50 export default defineComponent({
49 components: { 51 components: {
50 CollapseContainer, 52 CollapseContainer,
@@ -61,7 +63,7 @@ @@ -61,7 +63,7 @@
61 setup() { 63 setup() {
62 return { 64 return {
63 toIconify: () => { 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,23 +16,23 @@
16 </a-card> 16 </a-card>
17 </div> 17 </div>
18 18
19 - <AppFooterToolbar> 19 + <AppPageFooter>
20 <template #right> 20 <template #right>
21 <a-button type="primary" @click="submitAll">提交</a-button> 21 <a-button type="primary" @click="submitAll">提交</a-button>
22 </template> 22 </template>
23 - </AppFooterToolbar> 23 + </AppPageFooter>
24 </div> 24 </div>
25 </template> 25 </template>
26 <script lang="ts"> 26 <script lang="ts">
27 import { BasicForm, useForm } from '/@/components/Form'; 27 import { BasicForm, useForm } from '/@/components/Form';
28 import { defineComponent, ref } from 'vue'; 28 import { defineComponent, ref } from 'vue';
29 import PersonTable from './PersonTable.vue'; 29 import PersonTable from './PersonTable.vue';
30 - import { AppFooterToolbar } from '/@/components/Application'; 30 + import { AppPageFooter } from '/@/components/Application';
31 31
32 import { schemas, taskSchemas } from './data'; 32 import { schemas, taskSchemas } from './data';
33 33
34 export default defineComponent({ 34 export default defineComponent({
35 - components: { BasicForm, PersonTable, AppFooterToolbar }, 35 + components: { BasicForm, PersonTable, AppPageFooter },
36 setup() { 36 setup() {
37 const tableRef = ref<{ getDataSource: () => any } | null>(null); 37 const tableRef = ref<{ getDataSource: () => any } | null>(null);
38 38
src/views/sys/login/Login.vue
@@ -72,7 +72,7 @@ @@ -72,7 +72,7 @@
72 72
73 // import { appStore } from '/@/store/modules/app'; 73 // import { appStore } from '/@/store/modules/app';
74 import { useMessage } from '/@/hooks/web/useMessage'; 74 import { useMessage } from '/@/hooks/web/useMessage';
75 - import { useGlobSetting } from '/@/settings/use'; 75 + import { useGlobSetting } from '/@/hooks/setting';
76 import logo from '/@/assets/images/logo.png'; 76 import logo from '/@/assets/images/logo.png';
77 77
78 export default defineComponent({ 78 export default defineComponent({
vite.config.ts
@@ -30,7 +30,7 @@ function pathResolve(dir: string) { @@ -30,7 +30,7 @@ function pathResolve(dir: string) {
30 30
31 const viteConfig: UserConfig = { 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 * `index.html` does not exist (e.g. serving vite assets from a different host) 34 * `index.html` does not exist (e.g. serving vite assets from a different host)
35 * @default 'index.html' 35 * @default 'index.html'
36 */ 36 */
@@ -51,7 +51,7 @@ const viteConfig: UserConfig = { @@ -51,7 +51,7 @@ const viteConfig: UserConfig = {
51 */ 51 */
52 open: false, 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 * Available options are 'terser' or 'esbuild'. 55 * Available options are 'terser' or 'esbuild'.
56 * @default 'terser' 56 * @default 'terser'
57 */ 57 */
@@ -112,7 +112,7 @@ const viteConfig: UserConfig = { @@ -112,7 +112,7 @@ const viteConfig: UserConfig = {
112 }, 112 },
113 define: { 113 define: {
114 __VERSION__: pkg.version, 114 __VERSION__: pkg.version,
115 - // use vue-i18-next 115 + // setting vue-i18-next
116 // Suppress warning 116 // Suppress warning
117 __VUE_I18N_LEGACY_API__: false, 117 __VUE_I18N_LEGACY_API__: false,
118 __VUE_I18N_FULL_INSTALL__: false, 118 __VUE_I18N_FULL_INSTALL__: false,