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