Commit 4f6b65b8a1b7e694718b4aa42aced1e59e90ec9e

Authored by vben
1 parent 58f988a7

feat(trigger): add trigger config

CHANGELOG.zh_CN.md
1 ## Wip 1 ## Wip
2 2
  3 +### ✨ Features
  4 +
  5 +- 菜单 trigger 可以选择位置
  6 +- 增加富文本嵌入表单的示例
  7 +- 表单组件 schema 增加 `required`属性。简化配置
  8 +- openModal 和 openDrawer 第二个参数可以代替`transferModalData`传参到内部
  9 +
3 ### ⚡ Performance Improvements 10 ### ⚡ Performance Improvements
4 11
5 - 菜单性能继续优化,更流畅 12 - 菜单性能继续优化,更流畅
6 - 优化懒加载组件及示例 13 - 优化懒加载组件及示例
  14 +- layout 样式微调
7 15
8 ### 🎫 Chores 16 ### 🎫 Chores
9 17
package.json
@@ -28,6 +28,7 @@ @@ -28,6 +28,7 @@
28 "echarts": "^4.9.0", 28 "echarts": "^4.9.0",
29 "lodash-es": "^4.17.15", 29 "lodash-es": "^4.17.15",
30 "mockjs": "^1.1.0", 30 "mockjs": "^1.1.0",
  31 + "moment": "^2.29.1",
31 "nprogress": "^0.2.0", 32 "nprogress": "^0.2.0",
32 "path-to-regexp": "^6.2.0", 33 "path-to-regexp": "^6.2.0",
33 "qrcode": "^1.4.4", 34 "qrcode": "^1.4.4",
src/App.vue
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 11
12 import zhCN from 'ant-design-vue/es/locale/zh_CN'; 12 import zhCN from 'ant-design-vue/es/locale/zh_CN';
13 import moment from 'moment'; 13 import moment from 'moment';
14 - import 'moment/locale/zh-cn'; 14 + import 'moment/dist/locale/zh-cn';
15 15
16 import { useConfigProvider, useInitAppConfigStore, useListenerNetWork } from './useApp'; 16 import { useConfigProvider, useInitAppConfigStore, useListenerNetWork } from './useApp';
17 import { useLockPage } from '/@/hooks/web/useLockPage'; 17 import { useLockPage } from '/@/hooks/web/useLockPage';
src/components/Menu/src/BasicMenu.tsx
@@ -73,7 +73,7 @@ export default defineComponent({ @@ -73,7 +73,7 @@ export default defineComponent({
73 offset += 46; 73 offset += 46;
74 } 74 }
75 return { 75 return {
76 - height: `calc(100% - ${offset - 10}px)`, 76 + height: `calc(100% - ${offset - 12}px)`,
77 position: 'relative', 77 position: 'relative',
78 overflowY: 'auto', 78 overflowY: 'auto',
79 }; 79 };
src/components/Menu/src/MenuContent.tsx
@@ -32,7 +32,7 @@ export default defineComponent({ @@ -32,7 +32,7 @@ export default defineComponent({
32 * @description: 渲染图标 32 * @description: 渲染图标
33 */ 33 */
34 function renderIcon(icon: string) { 34 function renderIcon(icon: string) {
35 - return icon ? <Icon icon={icon} size={18} class="mr-1 menu-item-icon" /> : null; 35 + return icon ? <Icon icon={icon} size={18} class="menu-item-icon" /> : null;
36 } 36 }
37 37
38 return () => { 38 return () => {
src/components/Menu/src/index.less
@@ -41,6 +41,7 @@ @@ -41,6 +41,7 @@
41 font-size: 12px; 41 font-size: 12px;
42 flex-direction: column; 42 flex-direction: column;
43 align-items: center; 43 align-items: center;
  44 + line-height: 24px;
44 } 45 }
45 46
46 & > li > .ant-menu-submenu-title { 47 & > li > .ant-menu-submenu-title {
@@ -183,9 +184,17 @@ @@ -183,9 +184,17 @@
183 transition: unset; 184 transition: unset;
184 } 185 }
185 186
  187 + &:not(.basic-menu__sidebar-hor).ant-menu-inline-collapsed {
  188 + .basic-menu-item__level1 {
  189 + > div {
  190 + align-items: center;
  191 + }
  192 + }
  193 + }
  194 +
186 &.ant-menu-dark:not(.basic-menu__sidebar-hor):not(.basic-menu__second) { 195 &.ant-menu-dark:not(.basic-menu__sidebar-hor):not(.basic-menu__second) {
187 // Reset menu item row height 196 // Reset menu item row height
188 - .ant-menu-item, 197 + .ant-menu-item:not(.basic-menu-item__level1),
189 .ant-menu-sub.ant-menu-inline > .ant-menu-item, 198 .ant-menu-sub.ant-menu-inline > .ant-menu-item,
190 .ant-menu-sub.ant-menu-inline > .ant-menu-submenu > .ant-menu-submenu-title { 199 .ant-menu-sub.ant-menu-inline > .ant-menu-submenu > .ant-menu-submenu-title {
191 height: @app-menu-item-height; 200 height: @app-menu-item-height;
src/enums/menuEnum.ts
@@ -17,6 +17,16 @@ export enum MenuThemeEnum { @@ -17,6 +17,16 @@ export enum MenuThemeEnum {
17 LIGHT = 'light', 17 LIGHT = 'light',
18 } 18 }
19 19
  20 +// 折叠触发器位置
  21 +export enum TriggerEnum {
  22 + // 不显示
  23 + NONE = 'NONE',
  24 + // 菜单底部
  25 + FOOTER = 'FOOTER',
  26 + // 头部
  27 + HEADER = 'HEADER',
  28 +}
  29 +
20 export type Mode = 'vertical' | 'vertical-right' | 'horizontal' | 'inline'; 30 export type Mode = 'vertical' | 'vertical-right' | 'horizontal' | 'inline';
21 31
22 // menu mode 32 // menu mode
src/layouts/Logo.vue
1 <template> 1 <template>
2 - <div class="app-logo anticon" @click="handleGoHome" :style="wrapStyle"> 2 + <div class="app-logo anticon" :class="theme" @click="handleGoHome" :style="wrapStyle">
3 <img :src="logo" /> 3 <img :src="logo" />
4 <div v-if="show" class="logo-title ml-2 ellipsis">{{ globSetting.title }}</div> 4 <div v-if="show" class="logo-title ml-2 ellipsis">{{ globSetting.title }}</div>
5 </div> 5 </div>
@@ -26,6 +26,9 @@ @@ -26,6 +26,9 @@
26 type: Boolean as PropType<boolean>, 26 type: Boolean as PropType<boolean>,
27 default: true, 27 default: true,
28 }, 28 },
  29 + theme: {
  30 + type: String,
  31 + },
29 }, 32 },
30 setup(props) { 33 setup(props) {
31 const showRef = ref<boolean>(!!props.showTitle); 34 const showRef = ref<boolean>(!!props.showTitle);
@@ -80,6 +83,9 @@ @@ -80,6 +83,9 @@
80 padding-left: 16px; 83 padding-left: 16px;
81 cursor: pointer; 84 cursor: pointer;
82 // justify-content: center; 85 // justify-content: center;
  86 + &.light {
  87 + border-bottom: 1px solid @border-color-base;
  88 + }
83 89
84 .logo-title { 90 .logo-title {
85 font-size: 18px; 91 font-size: 18px;
src/layouts/default/LayoutContent.tsx
1 import { defineComponent } from 'vue'; 1 import { defineComponent } from 'vue';
2 -import { Layout } from 'ant-design-vue'; 2 +// import { Layout } from 'ant-design-vue';
3 // hooks 3 // hooks
4 4
5 import { ContentEnum } from '/@/enums/appEnum'; 5 import { ContentEnum } from '/@/enums/appEnum';
@@ -13,9 +13,9 @@ export default defineComponent({ @@ -13,9 +13,9 @@ export default defineComponent({
13 const { contentMode } = getProjectConfig; 13 const { contentMode } = getProjectConfig;
14 const wrapClass = contentMode === ContentEnum.FULL ? 'full' : 'fixed'; 14 const wrapClass = contentMode === ContentEnum.FULL ? 'full' : 'fixed';
15 return ( 15 return (
16 - <Layout.Content class={`layout-content ${wrapClass} `}>  
17 - {() => <PageLayout />}  
18 - </Layout.Content> 16 + // <Layout.Content class={`layout-content ${wrapClass} `}>
  17 + <PageLayout class={`layout-content ${wrapClass} `} />
  18 + // </Layout.Content>
19 ); 19 );
20 }; 20 };
21 }, 21 },
src/layouts/default/LayoutHeader.tsx
@@ -6,6 +6,7 @@ import UserDropdown from &#39;./UserDropdown&#39;; @@ -6,6 +6,7 @@ import UserDropdown from &#39;./UserDropdown&#39;;
6 import LayoutMenu from './LayoutMenu'; 6 import LayoutMenu from './LayoutMenu';
7 import LayoutBreadcrumb from './LayoutBreadcrumb'; 7 import LayoutBreadcrumb from './LayoutBreadcrumb';
8 import LockAction from './actions/LockActionItem'; 8 import LockAction from './actions/LockActionItem';
  9 +import LayoutTrigger from './LayoutTrigger';
9 import NoticeAction from './actions/notice/NoticeActionItem.vue'; 10 import NoticeAction from './actions/notice/NoticeActionItem.vue';
10 import { 11 import {
11 RedoOutlined, 12 RedoOutlined,
@@ -25,7 +26,7 @@ import { useModal } from &#39;/@/components/Modal/index&#39;; @@ -25,7 +26,7 @@ import { useModal } from &#39;/@/components/Modal/index&#39;;
25 import { appStore } from '/@/store/modules/app'; 26 import { appStore } from '/@/store/modules/app';
26 import { errorStore } from '/@/store/modules/error'; 27 import { errorStore } from '/@/store/modules/error';
27 28
28 -import { MenuModeEnum, MenuSplitTyeEnum, MenuTypeEnum } from '/@/enums/menuEnum'; 29 +import { MenuModeEnum, MenuSplitTyeEnum, MenuTypeEnum, TriggerEnum } from '/@/enums/menuEnum';
29 import { GITHUB_URL } from '/@/settings/siteSetting'; 30 import { GITHUB_URL } from '/@/settings/siteSetting';
30 export default defineComponent({ 31 export default defineComponent({
31 name: 'DefaultLayoutHeader', 32 name: 'DefaultLayoutHeader',
@@ -75,6 +76,13 @@ export default defineComponent({ @@ -75,6 +76,13 @@ export default defineComponent({
75 return theme ? `layout-header__header--${theme}` : ''; 76 return theme ? `layout-header__header--${theme}` : '';
76 }); 77 });
77 78
  79 + const showHeaderTrigger = computed(() => {
  80 + const { show, trigger, hidden } = unref(getProjectConfigRef).menuSetting;
  81 +
  82 + if (!show || !hidden) return false;
  83 + return trigger === TriggerEnum.HEADER;
  84 + });
  85 +
78 function handleToErrorList() { 86 function handleToErrorList() {
79 errorStore.commitErrorListCountState(0); 87 errorStore.commitErrorListCountState(0);
80 push('/exception/error-log'); 88 push('/exception/error-log');
@@ -92,6 +100,7 @@ export default defineComponent({ @@ -92,6 +100,7 @@ export default defineComponent({
92 const { 100 const {
93 useErrorHandle, 101 useErrorHandle,
94 showLogo, 102 showLogo,
  103 + multiTabsSetting: { show: showTab },
95 headerSetting: { 104 headerSetting: {
96 theme: headerTheme, 105 theme: headerTheme,
97 useLockPage, 106 useLockPage,
@@ -114,11 +123,17 @@ export default defineComponent({ @@ -114,11 +123,17 @@ export default defineComponent({
114 {() => ( 123 {() => (
115 <> 124 <>
116 <div class="layout-header__content "> 125 <div class="layout-header__content ">
117 - {showLogo && !isSidebarType && <Logo class={`layout-header__logo`} />}  
118 -  
119 - {mode !== MenuModeEnum.HORIZONTAL && showBreadCrumb && !splitMenu && (  
120 - <LayoutBreadcrumb showIcon={showBreadCrumbIcon} /> 126 + {showLogo && !isSidebarType && (
  127 + <Logo class={`layout-header__logo`} theme={headerTheme} />
121 )} 128 )}
  129 +
  130 + <div class="layout-header__left">
  131 + {unref(showHeaderTrigger) && <LayoutTrigger theme={headerTheme} sider={false} />}
  132 + {mode !== MenuModeEnum.HORIZONTAL && showBreadCrumb && !splitMenu && (
  133 + <LayoutBreadcrumb showIcon={showBreadCrumbIcon} />
  134 + )}
  135 + </div>
  136 +
122 {unref(showTopMenu) && ( 137 {unref(showTopMenu) && (
123 <div 138 <div
124 class={[`layout-header__menu `]} 139 class={[`layout-header__menu `]}
@@ -193,7 +208,7 @@ export default defineComponent({ @@ -193,7 +208,7 @@ export default defineComponent({
193 </Tooltip> 208 </Tooltip>
194 </div> 209 </div>
195 )} 210 )}
196 - {showRedo && ( 211 + {showRedo && showTab && (
197 <Tooltip> 212 <Tooltip>
198 {{ 213 {{
199 title: () => '刷新', 214 title: () => '刷新',
src/layouts/default/LayoutMenu.tsx
@@ -68,9 +68,6 @@ export default defineComponent({ @@ -68,9 +68,6 @@ export default defineComponent({
68 return unref(getProjectConfigRef).menuSetting.mode === MenuModeEnum.HORIZONTAL; 68 return unref(getProjectConfigRef).menuSetting.mode === MenuModeEnum.HORIZONTAL;
69 }); 69 });
70 70
71 - onMounted(() => {  
72 - genMenus();  
73 - });  
74 const [throttleHandleSplitLeftMenu] = useThrottle(handleSplitLeftMenu, 50); 71 const [throttleHandleSplitLeftMenu] = useThrottle(handleSplitLeftMenu, 50);
75 72
76 // watch( 73 // watch(
@@ -90,6 +87,7 @@ export default defineComponent({ @@ -90,6 +87,7 @@ export default defineComponent({
90 immediate: true, 87 immediate: true,
91 } 88 }
92 ); 89 );
  90 +
93 watch( 91 watch(
94 [() => permissionStore.getLastBuildMenuTimeState, permissionStore.getBackMenuListState], 92 [() => permissionStore.getLastBuildMenuTimeState, permissionStore.getBackMenuListState],
95 () => { 93 () => {
@@ -112,7 +110,7 @@ export default defineComponent({ @@ -112,7 +110,7 @@ export default defineComponent({
112 if (!children) { 110 if (!children) {
113 appStore.commitProjectConfigState({ 111 appStore.commitProjectConfigState({
114 menuSetting: { 112 menuSetting: {
115 - show: false, 113 + hidden: false,
116 }, 114 },
117 }); 115 });
118 flatMenusRef.value = []; 116 flatMenusRef.value = [];
@@ -122,7 +120,7 @@ export default defineComponent({ @@ -122,7 +120,7 @@ export default defineComponent({
122 const flatChildren = await getFlatChildrenMenus(children); 120 const flatChildren = await getFlatChildrenMenus(children);
123 appStore.commitProjectConfigState({ 121 appStore.commitProjectConfigState({
124 menuSetting: { 122 menuSetting: {
125 - show: true, 123 + hidden: true,
126 }, 124 },
127 }); 125 });
128 flatMenusRef.value = flatChildren; 126 flatMenusRef.value = flatChildren;
@@ -193,6 +191,10 @@ export default defineComponent({ @@ -193,6 +191,10 @@ export default defineComponent({
193 ); 191 );
194 }); 192 });
195 193
  194 + onMounted(() => {
  195 + genMenus();
  196 + });
  197 +
196 return () => { 198 return () => {
197 const { 199 const {
198 showLogo, 200 showLogo,
@@ -229,7 +231,11 @@ export default defineComponent({ @@ -229,7 +231,11 @@ export default defineComponent({
229 {{ 231 {{
230 header: () => 232 header: () =>
231 isShowLogo && ( 233 isShowLogo && (
232 - <Logo showTitle={!collapsed} class={[`layout-menu__logo`, themeData]} /> 234 + <Logo
  235 + showTitle={!collapsed}
  236 + class={[`layout-menu__logo`, themeData]}
  237 + theme={themeData}
  238 + />
233 ), 239 ),
234 }} 240 }}
235 </BasicMenu> 241 </BasicMenu>
src/layouts/default/LayoutSideBar.tsx
1 import { computed, defineComponent, nextTick, onMounted, ref, unref } from 'vue'; 1 import { computed, defineComponent, nextTick, onMounted, ref, unref } from 'vue';
2 2
3 import { Layout } from 'ant-design-vue'; 3 import { Layout } from 'ant-design-vue';
4 -import SideBarTrigger from './SideBarTrigger'; 4 +import LayoutTrigger from './LayoutTrigger';
5 import { menuStore } from '/@/store/modules/menu'; 5 import { menuStore } from '/@/store/modules/menu';
6 6
7 // import darkMiniIMg from '/@/assets/images/sidebar/dark-mini.png'; 7 // import darkMiniIMg from '/@/assets/images/sidebar/dark-mini.png';
8 // import lightMiniImg from '/@/assets/images/sidebar/light-mini.png'; 8 // import lightMiniImg from '/@/assets/images/sidebar/light-mini.png';
9 // import lightImg from '/@/assets/images/sidebar/light.png'; 9 // import lightImg from '/@/assets/images/sidebar/light.png';
10 import { appStore } from '/@/store/modules/app'; 10 import { appStore } from '/@/store/modules/app';
11 -import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum'; 11 +import { MenuModeEnum, MenuSplitTyeEnum, TriggerEnum } from '/@/enums/menuEnum';
12 import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum'; 12 import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum';
13 import { useDebounce } from '/@/hooks/core/useDebounce'; 13 import { useDebounce } from '/@/hooks/core/useDebounce';
14 import LayoutMenu from './LayoutMenu'; 14 import LayoutMenu from './LayoutMenu';
@@ -133,6 +133,25 @@ export default defineComponent({ @@ -133,6 +133,25 @@ export default defineComponent({
133 return unref(brokenRef) ? 0 : unref(getMiniWidth); 133 return unref(brokenRef) ? 0 : unref(getMiniWidth);
134 }); 134 });
135 135
  136 + const showTrigger = computed(() => {
  137 + const {
  138 + menuSetting: { trigger },
  139 + } = unref(getProjectConfigRef);
  140 + return trigger !== TriggerEnum.NONE && trigger === TriggerEnum.FOOTER;
  141 + });
  142 +
  143 + function handleSiderClick(e: ChangeEvent) {
  144 + if (!e || !e.target || e.target.className !== 'basic-menu__content') return;
  145 +
  146 + const { collapsed, show } = appStore.getProjectConfig.menuSetting;
  147 + if (!collapsed || !show) return;
  148 + appStore.commitProjectConfigState({
  149 + menuSetting: {
  150 + collapsed: false,
  151 + },
  152 + });
  153 + }
  154 +
136 function renderDragLine() { 155 function renderDragLine() {
137 const { menuSetting: { hasDrag = true } = {} } = unref(getProjectConfigRef); 156 const { menuSetting: { hasDrag = true } = {} } = unref(getProjectConfigRef);
138 return ( 157 return (
@@ -149,8 +168,22 @@ export default defineComponent({ @@ -149,8 +168,22 @@ export default defineComponent({
149 menuSetting: { theme, split: splitMenu }, 168 menuSetting: { theme, split: splitMenu },
150 } = unref(getProjectConfigRef); 169 } = unref(getProjectConfigRef);
151 const { getCollapsedState, getMenuWidthState } = menuStore; 170 const { getCollapsedState, getMenuWidthState } = menuStore;
  171 +
  172 + const triggerDom = unref(showTrigger)
  173 + ? {
  174 + trigger: () => <LayoutTrigger />,
  175 + }
  176 + : {};
  177 +
  178 + const triggerAttr = unref(showTrigger)
  179 + ? {}
  180 + : {
  181 + trigger: null,
  182 + };
  183 +
152 return ( 184 return (
153 <Layout.Sider 185 <Layout.Sider
  186 + onClick={handleSiderClick}
154 onCollapse={onCollapseChange} 187 onCollapse={onCollapseChange}
155 breakpoint="md" 188 breakpoint="md"
156 width={getMenuWidthState} 189 width={getMenuWidthState}
@@ -161,9 +194,10 @@ export default defineComponent({ @@ -161,9 +194,10 @@ export default defineComponent({
161 class="layout-sidebar" 194 class="layout-sidebar"
162 ref={sideRef} 195 ref={sideRef}
163 onBreakpoint={handleBreakpoint} 196 onBreakpoint={handleBreakpoint}
  197 + {...triggerAttr}
164 > 198 >
165 {{ 199 {{
166 - trigger: () => <SideBarTrigger />, 200 + ...triggerDom,
167 default: () => ( 201 default: () => (
168 <> 202 <>
169 <LayoutMenu 203 <LayoutMenu
src/layouts/default/LayoutTrigger.tsx 0 → 100644
  1 +import {
  2 + DoubleRightOutlined,
  3 + DoubleLeftOutlined,
  4 + MenuUnfoldOutlined,
  5 + MenuFoldOutlined,
  6 +} from '@ant-design/icons-vue';
  7 +import { defineComponent } from 'vue';
  8 +
  9 +// store
  10 +import { menuStore } from '/@/store/modules/menu';
  11 +
  12 +export default defineComponent({
  13 + name: 'LayoutTrigger',
  14 + props: {
  15 + sider: {
  16 + type: Boolean,
  17 + default: true,
  18 + },
  19 + theme: {
  20 + type: String,
  21 + },
  22 + },
  23 + setup(props) {
  24 + function toggleMenu() {
  25 + menuStore.commitCollapsedState(!menuStore.getCollapsedState);
  26 + }
  27 +
  28 + return () => {
  29 + const siderTrigger = menuStore.getCollapsedState ? (
  30 + <DoubleRightOutlined />
  31 + ) : (
  32 + <DoubleLeftOutlined />
  33 + );
  34 + if (props.sider) return siderTrigger;
  35 +
  36 + return (
  37 + <span class={['layout-trigger', props.theme]} onClick={toggleMenu}>
  38 + {menuStore.getCollapsedState ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
  39 + </span>
  40 + );
  41 + };
  42 + },
  43 +});
src/layouts/default/SideBarTrigger.tsx deleted 100644 → 0
1 -import { DoubleRightOutlined, DoubleLeftOutlined } from '@ant-design/icons-vue';  
2 -import { defineComponent } from 'vue';  
3 -  
4 -// store  
5 -import { menuStore } from '/@/store/modules/menu';  
6 -  
7 -export default defineComponent({  
8 - name: 'SideBarTrigger',  
9 - setup() {  
10 - return () => (menuStore.getCollapsedState ? <DoubleRightOutlined /> : <DoubleLeftOutlined />);  
11 - },  
12 -});  
src/layouts/default/index.less
@@ -17,11 +17,10 @@ @@ -17,11 +17,10 @@
17 &__main { 17 &__main {
18 position: relative; 18 position: relative;
19 height: 100%; 19 height: 100%;
20 - // overflow: hidden;  
21 - // overflow: auto;  
22 20
23 &.fixed { 21 &.fixed {
24 - overflow: auto; 22 + overflow-x: hidden;
  23 + overflow-y: auto;
25 } 24 }
26 25
27 &.fixed.lock { 26 &.fixed.lock {
@@ -373,9 +372,39 @@ @@ -373,9 +372,39 @@
373 } 372 }
374 } 373 }
375 374
376 -.layout-breadcrumb {  
377 - padding: 0 16px; 375 +.layout-header__left {
378 flex-grow: 1; 376 flex-grow: 1;
  377 + display: flex;
  378 + align-items: center;
  379 +
  380 + .layout-trigger {
  381 + padding: 4px 10px 0 16px;
  382 + cursor: pointer;
  383 +
  384 + .anticon {
  385 + font-size: 17px;
  386 + }
  387 +
  388 + &.light {
  389 + &:hover {
  390 + background: @header-light-bg-hover-color;
  391 + }
  392 +
  393 + svg {
  394 + fill: #000;
  395 + }
  396 + }
  397 +
  398 + &.dark {
  399 + &:hover {
  400 + background: @header-dark-bg-hover-color;
  401 + }
  402 + }
  403 + }
  404 +
  405 + .layout-breadcrumb {
  406 + padding: 0 8px;
  407 + }
379 } 408 }
380 409
381 .ant-layout-sider-trigger { 410 .ant-layout-sider-trigger {
src/layouts/default/index.tsx
@@ -73,7 +73,7 @@ export default defineComponent({ @@ -73,7 +73,7 @@ export default defineComponent({
73 showSettingButton, 73 showSettingButton,
74 multiTabsSetting: { show: showTabs }, 74 multiTabsSetting: { show: showTabs },
75 headerSetting: { fixed }, 75 headerSetting: { fixed },
76 - menuSetting: { split, show }, 76 + menuSetting: { split, hidden },
77 } = unref(getProjectConfigRef); 77 } = unref(getProjectConfigRef);
78 78
79 const fixedHeaderCls = fixed 79 const fixedHeaderCls = fixed
@@ -82,7 +82,7 @@ export default defineComponent({ @@ -82,7 +82,7 @@ export default defineComponent({
82 82
83 const { isLock } = getLockInfo; 83 const { isLock } = getLockInfo;
84 84
85 - const showSideBar = split ? show : true; 85 + const showSideBar = split ? hidden : true;
86 return ( 86 return (
87 <Layout class="default-layout relative"> 87 <Layout class="default-layout relative">
88 {() => ( 88 {() => (
@@ -107,9 +107,7 @@ export default defineComponent({ @@ -107,9 +107,7 @@ export default defineComponent({
107 unref(showHeaderRef) && <LayoutHeader />} 107 unref(showHeaderRef) && <LayoutHeader />}
108 108
109 {showTabs && !unref(getFullContent) && ( 109 {showTabs && !unref(getFullContent) && (
110 - <Layout.Header class={`default-layout__tabs`}>  
111 - {() => <MultipleTabs />}  
112 - </Layout.Header> 110 + <MultipleTabs class={`default-layout__tabs`} />
113 )} 111 )}
114 112
115 {useOpenBackTop && <BackTop target={getTarget} />} 113 {useOpenBackTop && <BackTop target={getTarget} />}
src/layouts/default/setting/SettingDrawer.tsx
@@ -2,7 +2,13 @@ import { defineComponent, computed, unref, ref } from &#39;vue&#39;; @@ -2,7 +2,13 @@ import { defineComponent, computed, unref, ref } from &#39;vue&#39;;
2 import { BasicDrawer } from '/@/components/Drawer/index'; 2 import { BasicDrawer } from '/@/components/Drawer/index';
3 import { Divider, Switch, Tooltip, InputNumber, Select } from 'ant-design-vue'; 3 import { Divider, Switch, Tooltip, InputNumber, Select } from 'ant-design-vue';
4 import Button from '/@/components/Button/index.vue'; 4 import Button from '/@/components/Button/index.vue';
5 -import { MenuModeEnum, MenuTypeEnum, MenuThemeEnum, TopMenuAlignEnum } from '/@/enums/menuEnum'; 5 +import {
  6 + MenuModeEnum,
  7 + MenuTypeEnum,
  8 + MenuThemeEnum,
  9 + TopMenuAlignEnum,
  10 + TriggerEnum,
  11 +} from '/@/enums/menuEnum';
6 import { ContentEnum, RouterTransitionEnum } from '/@/enums/appEnum'; 12 import { ContentEnum, RouterTransitionEnum } from '/@/enums/appEnum';
7 import { CopyOutlined, RedoOutlined, CheckOutlined } from '@ant-design/icons-vue'; 13 import { CopyOutlined, RedoOutlined, CheckOutlined } from '@ant-design/icons-vue';
8 import { appStore } from '/@/store/modules/app'; 14 import { appStore } from '/@/store/modules/app';
@@ -23,41 +29,49 @@ const themeOptions = [ @@ -23,41 +29,49 @@ const themeOptions = [
23 { 29 {
24 value: MenuThemeEnum.LIGHT, 30 value: MenuThemeEnum.LIGHT,
25 label: '亮色', 31 label: '亮色',
26 - key: MenuThemeEnum.LIGHT,  
27 }, 32 },
28 { 33 {
29 value: MenuThemeEnum.DARK, 34 value: MenuThemeEnum.DARK,
30 label: '暗色', 35 label: '暗色',
31 - key: MenuThemeEnum.DARK,  
32 }, 36 },
33 ]; 37 ];
34 const contentModeOptions = [ 38 const contentModeOptions = [
35 { 39 {
36 value: ContentEnum.FULL, 40 value: ContentEnum.FULL,
37 label: '流式', 41 label: '流式',
38 - key: ContentEnum.FULL,  
39 }, 42 },
40 { 43 {
41 value: ContentEnum.FIXED, 44 value: ContentEnum.FIXED,
42 label: '定宽', 45 label: '定宽',
43 - key: ContentEnum.FIXED,  
44 }, 46 },
45 ]; 47 ];
46 const topMenuAlignOptions = [ 48 const topMenuAlignOptions = [
47 { 49 {
48 value: TopMenuAlignEnum.CENTER, 50 value: TopMenuAlignEnum.CENTER,
49 label: '居中', 51 label: '居中',
50 - key: TopMenuAlignEnum.CENTER,  
51 }, 52 },
52 { 53 {
53 value: TopMenuAlignEnum.START, 54 value: TopMenuAlignEnum.START,
54 label: '居左', 55 label: '居左',
55 - key: TopMenuAlignEnum.START,  
56 }, 56 },
57 { 57 {
58 value: TopMenuAlignEnum.END, 58 value: TopMenuAlignEnum.END,
59 label: '居右', 59 label: '居右',
60 - key: TopMenuAlignEnum.END, 60 + },
  61 +];
  62 +
  63 +const menuTriggerOptions = [
  64 + {
  65 + value: TriggerEnum.NONE,
  66 + label: '不显示',
  67 + },
  68 + {
  69 + value: TriggerEnum.FOOTER,
  70 + label: '底部',
  71 + },
  72 + {
  73 + value: TriggerEnum.HEADER,
  74 + label: '顶部',
61 }, 75 },
62 ]; 76 ];
63 77
@@ -181,7 +195,7 @@ export default defineComponent({ @@ -181,7 +195,7 @@ export default defineComponent({
181 baseHandler('splitMenu', e); 195 baseHandler('splitMenu', e);
182 }, 196 },
183 def: split, 197 def: split,
184 - disabled: !unref(getShowMenuRef), 198 + disabled: !unref(getShowMenuRef) || type !== MenuTypeEnum.MIX,
185 }), 199 }),
186 renderSelectItem('顶栏主题', { 200 renderSelectItem('顶栏主题', {
187 handler: (e) => { 201 handler: (e) => {
@@ -215,6 +229,7 @@ export default defineComponent({ @@ -215,6 +229,7 @@ export default defineComponent({
215 menuWidth, 229 menuWidth,
216 topMenuAlign, 230 topMenuAlign,
217 collapsedShowTitle, 231 collapsedShowTitle,
  232 + trigger,
218 } = {}, 233 } = {},
219 } = appStore.getProjectConfig; 234 } = appStore.getProjectConfig;
220 return [ 235 return [
@@ -262,6 +277,13 @@ export default defineComponent({ @@ -262,6 +277,13 @@ export default defineComponent({
262 options: topMenuAlignOptions, 277 options: topMenuAlignOptions,
263 disabled: !unref(getShowHeaderRef), 278 disabled: !unref(getShowHeaderRef),
264 }), 279 }),
  280 + renderSelectItem('菜单折叠按钮', {
  281 + handler: (e) => {
  282 + baseHandler('menuTrigger', e);
  283 + },
  284 + def: trigger,
  285 + options: menuTriggerOptions,
  286 + }),
265 renderSelectItem('内容区域宽度', { 287 renderSelectItem('内容区域宽度', {
266 handler: (e) => { 288 handler: (e) => {
267 baseHandler('contentMode', e); 289 baseHandler('contentMode', e);
@@ -298,7 +320,7 @@ export default defineComponent({ @@ -298,7 +320,7 @@ export default defineComponent({
298 disabled={!unref(getShowMenuRef)} 320 disabled={!unref(getShowMenuRef)}
299 defaultValue={menuWidth} 321 defaultValue={menuWidth}
300 formatter={(value: string) => `${parseInt(value)}px`} 322 formatter={(value: string) => `${parseInt(value)}px`}
301 - onChange={(e) => { 323 + onChange={(e: any) => {
302 baseHandler('menuWidth', e); 324 baseHandler('menuWidth', e);
303 }} 325 }}
304 /> 326 />
@@ -424,13 +446,21 @@ export default defineComponent({ @@ -424,13 +446,21 @@ export default defineComponent({
424 if (event === 'layout') { 446 if (event === 'layout') {
425 const { mode, type, split } = value; 447 const { mode, type, split } = value;
426 const splitOpt = split === undefined ? { split } : {}; 448 const splitOpt = split === undefined ? { split } : {};
  449 + let headerSetting = {};
  450 + if (type === MenuTypeEnum.TOP_MENU) {
  451 + headerSetting = {
  452 + theme: MenuThemeEnum.DARK,
  453 + };
  454 + }
427 config = { 455 config = {
428 menuSetting: { 456 menuSetting: {
429 mode, 457 mode,
430 type, 458 type,
431 collapsed: false, 459 collapsed: false,
  460 + show: true,
432 ...splitOpt, 461 ...splitOpt,
433 }, 462 },
  463 + headerSetting,
434 }; 464 };
435 } 465 }
436 if (event === 'hasDrag') { 466 if (event === 'hasDrag') {
@@ -440,6 +470,13 @@ export default defineComponent({ @@ -440,6 +470,13 @@ export default defineComponent({
440 }, 470 },
441 }; 471 };
442 } 472 }
  473 + if (event === 'menuTrigger') {
  474 + config = {
  475 + menuSetting: {
  476 + trigger: value,
  477 + },
  478 + };
  479 + }
443 if (event === 'openPageLoading') { 480 if (event === 'openPageLoading') {
444 config = { 481 config = {
445 openPageLoading: value, 482 openPageLoading: value,
@@ -647,7 +684,7 @@ export default defineComponent({ @@ -647,7 +684,7 @@ export default defineComponent({
647 <Switch 684 <Switch
648 {...opt} 685 {...opt}
649 disabled={disabled} 686 disabled={disabled}
650 - onChange={(e) => { 687 + onChange={(e: any) => {
651 handler && handler(e); 688 handler && handler(e);
652 }} 689 }}
653 checkedChildren="开" 690 checkedChildren="开"
src/settings/projectSetting.ts
1 import type { ProjectConfig } from '/@/types/config'; 1 import type { ProjectConfig } from '/@/types/config';
2 2
3 -import { MenuTypeEnum, MenuThemeEnum, MenuModeEnum } from '/@/enums/menuEnum'; 3 +import { MenuTypeEnum, MenuThemeEnum, MenuModeEnum, TriggerEnum } from '/@/enums/menuEnum';
4 import { ContentEnum, PermissionModeEnum, RouterTransitionEnum } from '/@/enums/appEnum'; 4 import { ContentEnum, PermissionModeEnum, RouterTransitionEnum } from '/@/enums/appEnum';
5 import { primaryColor } from '../../build/config/lessModifyVars'; 5 import { primaryColor } from '../../build/config/lessModifyVars';
6 import { isProdMode } from '/@/utils/env'; 6 import { isProdMode } from '/@/utils/env';
@@ -23,6 +23,7 @@ const setting: ProjectConfig = { @@ -23,6 +23,7 @@ const setting: ProjectConfig = {
23 // 是否显示logo 23 // 是否显示logo
24 showLogo: true, 24 showLogo: true,
25 25
  26 + // 头部配置
26 headerSetting: { 27 headerSetting: {
27 fixed: true, 28 fixed: true,
28 // 是否显示顶部 29 // 是否显示顶部
@@ -50,8 +51,10 @@ const setting: ProjectConfig = { @@ -50,8 +51,10 @@ const setting: ProjectConfig = {
50 collapsedShowTitle: false, 51 collapsedShowTitle: false,
51 // 是否可拖拽 52 // 是否可拖拽
52 hasDrag: false, 53 hasDrag: false,
53 - // 是否显示 54 + // 是否显示 没有dom
54 show: true, 55 show: true,
  56 + // 是否显示 有dom
  57 + hidden: false,
55 // 是否显示搜索框 58 // 是否显示搜索框
56 showSearch: true, 59 showSearch: true,
57 // 菜单宽度 60 // 菜单宽度
@@ -67,7 +70,9 @@ const setting: ProjectConfig = { @@ -67,7 +70,9 @@ const setting: ProjectConfig = {
67 // 顶部菜单布局 70 // 顶部菜单布局
68 topMenuAlign: 'center', 71 topMenuAlign: 'center',
69 // 折叠菜单时候隐藏搜索框 72 // 折叠菜单时候隐藏搜索框
70 - collapsedShowSearch: true, 73 + collapsedShowSearch: false,
  74 + // 折叠触发器的位置
  75 + trigger: TriggerEnum.HEADER,
71 }, 76 },
72 // 消息配置 77 // 消息配置
73 messageSetting: { 78 messageSetting: {
src/types/config.d.ts
1 // 左侧菜单, 顶部菜单 1 // 左侧菜单, 顶部菜单
2 -import { MenuTypeEnum, MenuModeEnum, MenuThemeEnum } from '/@/enums/menuEnum'; 2 +import { MenuTypeEnum, MenuModeEnum, MenuThemeEnum, TriggerEnum } from '/@/enums/menuEnum';
3 import { ContentEnum, PermissionModeEnum, RouterTransitionEnum } from '/@/enums/appEnum'; 3 import { ContentEnum, PermissionModeEnum, RouterTransitionEnum } from '/@/enums/appEnum';
4 4
5 export interface MessageSetting { 5 export interface MessageSetting {
@@ -15,6 +15,7 @@ export interface MenuSetting { @@ -15,6 +15,7 @@ export interface MenuSetting {
15 hasDrag: boolean; 15 hasDrag: boolean;
16 showSearch: boolean; 16 showSearch: boolean;
17 show: boolean; 17 show: boolean;
  18 + hidden: boolean;
18 split: boolean; 19 split: boolean;
19 menuWidth: number; 20 menuWidth: number;
20 mode: MenuModeEnum; 21 mode: MenuModeEnum;
@@ -22,6 +23,7 @@ export interface MenuSetting { @@ -22,6 +23,7 @@ export interface MenuSetting {
22 theme: MenuThemeEnum; 23 theme: MenuThemeEnum;
23 topMenuAlign: 'start' | 'center' | 'end'; 24 topMenuAlign: 'start' | 'center' | 'end';
24 collapsedShowSearch: boolean; 25 collapsedShowSearch: boolean;
  26 + trigger: TriggerEnum;
25 } 27 }
26 28
27 export interface MultiTabsSetting { 29 export interface MultiTabsSetting {
src/utils/helper/routeHelper.ts
@@ -42,6 +42,7 @@ export function genRouteModule(moduleList: AppRouteModule[]) { @@ -42,6 +42,7 @@ export function genRouteModule(moduleList: AppRouteModule[]) {
42 } 42 }
43 43
44 // 动态引入 44 // 动态引入
  45 +// TODO 错误写法
45 function asyncImportRoute(routes: AppRouteRecordRaw[]) { 46 function asyncImportRoute(routes: AppRouteRecordRaw[]) {
46 routes.forEach((item) => { 47 routes.forEach((item) => {
47 let { component } = item; 48 let { component } = item;
vite.config.ts
@@ -119,12 +119,7 @@ const viteConfig: UserConfig = { @@ -119,12 +119,7 @@ const viteConfig: UserConfig = {
119 }, 119 },
120 // The package will be recompiled using rollup, and the new package compiled into the esm module specification will be put into node_modules/.vite_opt_cache 120 // The package will be recompiled using rollup, and the new package compiled into the esm module specification will be put into node_modules/.vite_opt_cache
121 optimizeDeps: { 121 optimizeDeps: {
122 - include: [  
123 - 'echarts/map/js/china',  
124 - 'ant-design-vue/es/locale/zh_CN',  
125 - '@ant-design/icons-vue',  
126 - 'moment/locale/zh-cn',  
127 - ], 122 + include: ['echarts/map/js/china', 'ant-design-vue/es/locale/zh_CN', '@ant-design/icons-vue'],
128 }, 123 },
129 124
130 // Local cross-domain proxy 125 // Local cross-domain proxy
yarn.lock
@@ -5513,7 +5513,7 @@ modify-values@^1.0.0: @@ -5513,7 +5513,7 @@ modify-values@^1.0.0:
5513 resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" 5513 resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
5514 integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== 5514 integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==
5515 5515
5516 -moment@^2.27.0: 5516 +moment@^2.27.0, moment@^2.29.1:
5517 version "2.29.1" 5517 version "2.29.1"
5518 resolved "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" 5518 resolved "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
5519 integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== 5519 integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==