mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-27 16:15:19 +08:00
refactor: refactor layout
This commit is contained in:
@@ -1,32 +0,0 @@
|
||||
import { computed, defineComponent, unref } from 'vue';
|
||||
import { Layout } from 'ant-design-vue';
|
||||
import { FullLoading } from '/@/components/Loading/index';
|
||||
|
||||
import { RouterView } from 'vue-router';
|
||||
|
||||
import { ContentEnum } from '/@/enums/appEnum';
|
||||
import { appStore } from '/@/store/modules/app';
|
||||
export default defineComponent({
|
||||
name: 'DefaultLayoutContent',
|
||||
setup() {
|
||||
const getProjectConfigRef = computed(() => {
|
||||
return appStore.getProjectConfig;
|
||||
});
|
||||
|
||||
return () => {
|
||||
const { contentMode, openPageLoading } = unref(getProjectConfigRef);
|
||||
const { getPageLoading } = appStore;
|
||||
const wrapClass = contentMode === ContentEnum.FULL ? 'full' : 'fixed';
|
||||
return (
|
||||
<div class={[`default-layout__main`]}>
|
||||
{openPageLoading && (
|
||||
<FullLoading class={[`default-layout__loading`, !getPageLoading && 'hidden']} />
|
||||
)}
|
||||
<Layout.Content class={`layout-content ${wrapClass} `}>
|
||||
{() => <RouterView />}
|
||||
</Layout.Content>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
@@ -1,4 +1,4 @@
|
||||
import type { PropType } from 'vue';
|
||||
import type { PropType, FunctionalComponent } from 'vue';
|
||||
|
||||
import { defineComponent, unref } from 'vue';
|
||||
import {
|
||||
@@ -10,6 +10,22 @@ import {
|
||||
|
||||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
||||
|
||||
const SiderTrigger: FunctionalComponent = () => {
|
||||
const { getCollapsed } = useMenuSetting();
|
||||
return unref(getCollapsed) ? <DoubleRightOutlined /> : <DoubleLeftOutlined />;
|
||||
};
|
||||
|
||||
const HeaderTrigger: FunctionalComponent<{
|
||||
theme?: string;
|
||||
}> = (props) => {
|
||||
const { toggleCollapsed, getCollapsed } = useMenuSetting();
|
||||
return (
|
||||
<span class={['layout-trigger', props.theme]} onClick={toggleCollapsed}>
|
||||
{unref(getCollapsed) ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LayoutTrigger',
|
||||
props: {
|
||||
@@ -22,20 +38,8 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { toggleCollapsed, getCollapsed } = useMenuSetting();
|
||||
|
||||
return () => {
|
||||
const siderTrigger = unref(getCollapsed) ? <DoubleRightOutlined /> : <DoubleLeftOutlined />;
|
||||
|
||||
if (props.sider) {
|
||||
return siderTrigger;
|
||||
}
|
||||
|
||||
return (
|
||||
<span class={['layout-trigger', props.theme]} onClick={toggleCollapsed}>
|
||||
{unref(getCollapsed) ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
||||
</span>
|
||||
);
|
||||
return props.sider ? <SiderTrigger /> : <HeaderTrigger theme={props.theme} />;
|
||||
};
|
||||
},
|
||||
});
|
||||
|
21
src/layouts/default/content/index.less
Normal file
21
src/layouts/default/content/index.less
Normal file
@@ -0,0 +1,21 @@
|
||||
@import (reference) '../../../design/index.less';
|
||||
|
||||
.layout-content {
|
||||
position: relative;
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
|
||||
&.fixed {
|
||||
width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
&__loading {
|
||||
position: fixed;
|
||||
z-index: @page-loading-z-index;
|
||||
|
||||
> .basic-loading {
|
||||
margin-bottom: 20%;
|
||||
}
|
||||
}
|
||||
}
|
26
src/layouts/default/content/index.tsx
Normal file
26
src/layouts/default/content/index.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import './index.less';
|
||||
|
||||
import { defineComponent, unref } from 'vue';
|
||||
import { FullLoading } from '/@/components/Loading/index';
|
||||
|
||||
import { RouterView } from 'vue-router';
|
||||
|
||||
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LayoutContent',
|
||||
setup() {
|
||||
const { getOpenPageLoading, getLayoutContentMode, getPageLoading } = useRootSetting();
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<div class={['layout-content', unref(getLayoutContentMode)]}>
|
||||
{unref(getOpenPageLoading) && (
|
||||
<FullLoading class={[`layout-content__loading`, { hidden: !unref(getPageLoading) }]} />
|
||||
)}
|
||||
<RouterView />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
28
src/layouts/default/footer/index.less
Normal file
28
src/layouts/default/footer/index.less
Normal file
@@ -0,0 +1,28 @@
|
||||
@normal-color: rgba(0, 0, 0, 0.45);
|
||||
|
||||
@hover-color: rgba(0, 0, 0, 0.85);
|
||||
|
||||
.layout-footer {
|
||||
color: @normal-color;
|
||||
text-align: center;
|
||||
|
||||
&__links {
|
||||
margin-bottom: 8px;
|
||||
|
||||
a {
|
||||
color: @normal-color;
|
||||
|
||||
&:hover {
|
||||
color: @hover-color;
|
||||
}
|
||||
}
|
||||
|
||||
.github {
|
||||
margin: 0 30px;
|
||||
|
||||
&:hover {
|
||||
color: @hover-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
31
src/layouts/default/footer/index.tsx
Normal file
31
src/layouts/default/footer/index.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import './index.less';
|
||||
|
||||
import { defineComponent } from 'vue';
|
||||
import { Layout } from 'ant-design-vue';
|
||||
|
||||
import { GithubFilled } from '@ant-design/icons-vue';
|
||||
|
||||
import { DOC_URL, GITHUB_URL, SITE_URL } from '/@/settings/siteSetting';
|
||||
import { openWindow } from '/@/utils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LayoutContent',
|
||||
setup() {
|
||||
return () => {
|
||||
return (
|
||||
<Layout.Footer class="layout-footer">
|
||||
{() => (
|
||||
<>
|
||||
<div class="layout-footer__links">
|
||||
<a onClick={() => openWindow(SITE_URL)}>在线预览</a>
|
||||
<GithubFilled onClick={() => openWindow(GITHUB_URL)} class="github" />
|
||||
<a onClick={() => openWindow(DOC_URL)}>在线文档</a>
|
||||
</div>
|
||||
<div>Copyright ©2020 Vben Admin</div>
|
||||
</>
|
||||
)}
|
||||
</Layout.Footer>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
@@ -1,13 +1,15 @@
|
||||
import './index.less';
|
||||
|
||||
import type { FunctionalComponent } from 'vue';
|
||||
|
||||
import { defineComponent, unref, computed, ref, nextTick } from 'vue';
|
||||
|
||||
import { Layout, Tooltip, Badge } from 'ant-design-vue';
|
||||
import { AppLogo } from '/@/components/Application';
|
||||
import UserDropdown from './UserDropdown';
|
||||
import LayoutMenu from '/@/layouts/default/menu/LayoutMenu';
|
||||
import LayoutMenu from '../menu';
|
||||
import LayoutBreadcrumb from './LayoutBreadcrumb';
|
||||
import LockAction from './LockActionItem';
|
||||
import LockAction from '../lock/LockAction';
|
||||
import LayoutTrigger from '../LayoutTrigger';
|
||||
import NoticeAction from './notice/NoticeActionItem.vue';
|
||||
import {
|
||||
@@ -34,9 +36,30 @@ import { PageEnum } from '/@/enums/pageEnum';
|
||||
import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
|
||||
import { Component } from '/@/components/types';
|
||||
|
||||
interface TooltipItemProps {
|
||||
title: string;
|
||||
}
|
||||
|
||||
const TooltipItem: FunctionalComponent<TooltipItemProps> = (props, { slots }) => {
|
||||
return (
|
||||
<Tooltip>
|
||||
{{
|
||||
title: () => props.title,
|
||||
default: () => slots.default?.(),
|
||||
}}
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LayoutHeader',
|
||||
setup() {
|
||||
props: {
|
||||
fixed: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
let logoEl: Element | null;
|
||||
|
||||
const logoWidthRef = ref(200);
|
||||
@@ -48,7 +71,7 @@ export default defineComponent({
|
||||
const { getUseErrorHandle, getShowBreadCrumbIcon } = useRootSetting();
|
||||
|
||||
const {
|
||||
getTheme,
|
||||
getHeaderTheme,
|
||||
getShowRedo,
|
||||
getUseLockPage,
|
||||
getShowFullScreen,
|
||||
@@ -69,8 +92,7 @@ export default defineComponent({
|
||||
let width = 0;
|
||||
if (!logoEl) {
|
||||
logoEl = logoRef.value.$el;
|
||||
}
|
||||
if (logoEl) {
|
||||
} else {
|
||||
width += logoEl.clientWidth;
|
||||
}
|
||||
logoWidthRef.value = width + 80;
|
||||
@@ -81,7 +103,7 @@ export default defineComponent({
|
||||
);
|
||||
|
||||
const headerClass = computed(() => {
|
||||
const theme = unref(getTheme);
|
||||
const theme = unref(getHeaderTheme);
|
||||
return theme ? `layout-header__header--${theme}` : '';
|
||||
});
|
||||
|
||||
@@ -99,9 +121,6 @@ export default defineComponent({
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 锁定屏幕
|
||||
*/
|
||||
function handleLockPage() {
|
||||
openModal(true);
|
||||
}
|
||||
@@ -111,13 +130,13 @@ export default defineComponent({
|
||||
return (
|
||||
<div class="layout-header__content ">
|
||||
{unref(getShowHeaderLogo) && (
|
||||
<AppLogo class={`layout-header__logo`} ref={logoRef} theme={unref(getTheme)} />
|
||||
<AppLogo class={`layout-header__logo`} ref={logoRef} theme={unref(getHeaderTheme)} />
|
||||
)}
|
||||
|
||||
{unref(getShowContent) && (
|
||||
<div class="layout-header__left">
|
||||
{unref(getShowHeaderTrigger) && (
|
||||
<LayoutTrigger theme={unref(getTheme)} sider={false} />
|
||||
<LayoutTrigger theme={unref(getHeaderTheme)} sider={false} />
|
||||
)}
|
||||
{unref(getShowBread) && <LayoutBreadcrumb showIcon={unref(getShowBreadCrumbIcon)} />}
|
||||
</div>
|
||||
@@ -128,7 +147,7 @@ export default defineComponent({
|
||||
<LayoutMenu
|
||||
isHorizontal={true}
|
||||
class={`justify-${unref(getTopMenuAlign)}`}
|
||||
theme={unref(getTheme)}
|
||||
theme={unref(getHeaderTheme)}
|
||||
splitType={unref(getSplitType)}
|
||||
menuMode={unref(getMenuMode)}
|
||||
showSearch={false}
|
||||
@@ -151,64 +170,47 @@ export default defineComponent({
|
||||
return (
|
||||
<div class={`layout-header__action`}>
|
||||
{unref(getUseErrorHandle) && (
|
||||
<Tooltip>
|
||||
{{
|
||||
title: () => '错误日志',
|
||||
default: () => (
|
||||
<Badge
|
||||
count={errorStore.getErrorListCountState}
|
||||
offset={[0, 10]}
|
||||
dot
|
||||
overflowCount={99}
|
||||
>
|
||||
{() => renderActionDefault(BugOutlined, handleToErrorList)}
|
||||
</Badge>
|
||||
),
|
||||
}}
|
||||
</Tooltip>
|
||||
<TooltipItem title="错误日志">
|
||||
{() => (
|
||||
<Badge
|
||||
count={errorStore.getErrorListCountState}
|
||||
offset={[0, 10]}
|
||||
dot
|
||||
overflowCount={99}
|
||||
>
|
||||
{() => renderActionDefault(BugOutlined, handleToErrorList)}
|
||||
</Badge>
|
||||
)}
|
||||
</TooltipItem>
|
||||
)}
|
||||
|
||||
{unref(getUseLockPage) && (
|
||||
<Tooltip>
|
||||
{{
|
||||
title: () => '锁定屏幕',
|
||||
default: () => renderActionDefault(LockOutlined, handleLockPage),
|
||||
}}
|
||||
</Tooltip>
|
||||
<TooltipItem title="锁定屏幕">
|
||||
{() => renderActionDefault(LockOutlined, handleLockPage)}
|
||||
</TooltipItem>
|
||||
)}
|
||||
|
||||
{unref(getShowNotice) && (
|
||||
<Tooltip>
|
||||
{{
|
||||
title: () => '消息通知',
|
||||
default: () => <NoticeAction />,
|
||||
}}
|
||||
</Tooltip>
|
||||
<TooltipItem title="消息通知">{() => <NoticeAction />}</TooltipItem>
|
||||
)}
|
||||
|
||||
{unref(getShowRedo) && (
|
||||
<Tooltip>
|
||||
{{
|
||||
title: () => '刷新',
|
||||
default: () => renderActionDefault(RedoOutlined, refreshPage),
|
||||
}}
|
||||
</Tooltip>
|
||||
<TooltipItem title="刷新">
|
||||
{() => renderActionDefault(RedoOutlined, refreshPage)}
|
||||
</TooltipItem>
|
||||
)}
|
||||
|
||||
{unref(getShowFullScreen) && (
|
||||
<Tooltip>
|
||||
{{
|
||||
title: () => (unref(isFullscreenRef) ? '退出全屏' : '全屏'),
|
||||
default: () => {
|
||||
const Icon = !unref(isFullscreenRef) ? (
|
||||
<FullscreenOutlined />
|
||||
) : (
|
||||
<FullscreenExitOutlined />
|
||||
);
|
||||
return renderActionDefault(Icon, toggleFullscreen);
|
||||
},
|
||||
<TooltipItem title={unref(isFullscreenRef) ? '退出全屏' : '全屏'}>
|
||||
{() => {
|
||||
const Icon = !unref(isFullscreenRef) ? (
|
||||
<FullscreenOutlined />
|
||||
) : (
|
||||
<FullscreenExitOutlined />
|
||||
);
|
||||
return renderActionDefault(Icon, toggleFullscreen);
|
||||
}}
|
||||
</Tooltip>
|
||||
</TooltipItem>
|
||||
)}
|
||||
<UserDropdown class={`layout-header__user-dropdown`} />
|
||||
</div>
|
||||
@@ -227,7 +229,9 @@ export default defineComponent({
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<Layout.Header class={['layout-header', 'flex p-0 px-4 ', unref(headerClass)]}>
|
||||
<Layout.Header
|
||||
class={['layout-header', 'flex p-0 px-4 ', unref(headerClass), { fixed: props.fixed }]}
|
||||
>
|
||||
{() => renderHeaderDefault()}
|
||||
</Layout.Header>
|
||||
);
|
||||
|
10
src/layouts/default/header/LayoutMultipleHeader.less
Normal file
10
src/layouts/default/header/LayoutMultipleHeader.less
Normal file
@@ -0,0 +1,10 @@
|
||||
.multiple-tab-header {
|
||||
flex: 0 0 auto;
|
||||
|
||||
&.fixed {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
118
src/layouts/default/header/LayoutMultipleHeader.tsx
Normal file
118
src/layouts/default/header/LayoutMultipleHeader.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import './LayoutMultipleHeader.less';
|
||||
|
||||
import { defineComponent, unref, computed, ref, watch, nextTick, CSSProperties } from 'vue';
|
||||
|
||||
import LayoutHeader from './LayoutHeader';
|
||||
import MultipleTabs from '../multitabs/index';
|
||||
|
||||
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
|
||||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
||||
import { useFullContent } from '/@/hooks/web/useFullContent';
|
||||
import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
|
||||
import { useLayoutContext } from '../useLayoutContext';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LayoutMultipleHeader',
|
||||
setup() {
|
||||
const placeholderHeightRef = ref(0);
|
||||
const fullHeaderHeightRef = ref(0);
|
||||
const headerElRef = ref<ComponentRef>(null);
|
||||
const tabElRef = ref<ComponentRef>(null);
|
||||
|
||||
const injectValue = useLayoutContext();
|
||||
|
||||
const { getCalcContentWidth } = useMenuSetting();
|
||||
|
||||
const {
|
||||
getFixed,
|
||||
getShowInsetHeaderRef,
|
||||
getShowFullHeaderRef,
|
||||
getShowHeader,
|
||||
getUnFixedAndFull,
|
||||
} = useHeaderSetting();
|
||||
|
||||
const { getFullContent } = useFullContent();
|
||||
|
||||
const { getShowMultipleTab } = useMultipleTabSetting();
|
||||
|
||||
const showTabsRef = computed(() => {
|
||||
return unref(getShowMultipleTab) && !unref(getFullContent);
|
||||
});
|
||||
|
||||
const getPlaceholderDomStyle = computed(() => {
|
||||
return {
|
||||
height: `${unref(placeholderHeightRef)}px`,
|
||||
};
|
||||
});
|
||||
|
||||
const getIsShowPlaceholderDom = computed(() => {
|
||||
return unref(getFixed) || unref(getShowFullHeaderRef);
|
||||
});
|
||||
|
||||
const getWrapStyle = computed(() => {
|
||||
const style: CSSProperties = {};
|
||||
if (unref(getFixed)) {
|
||||
style.width = unref(getCalcContentWidth);
|
||||
}
|
||||
if (unref(getShowFullHeaderRef)) {
|
||||
style.top = `${unref(fullHeaderHeightRef)}px`;
|
||||
}
|
||||
return style;
|
||||
});
|
||||
|
||||
const getIsFixed = computed(() => {
|
||||
return unref(getFixed) || unref(getShowFullHeaderRef);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => [
|
||||
unref(getFixed),
|
||||
unref(getShowFullHeaderRef),
|
||||
unref(getShowHeader),
|
||||
unref(getShowMultipleTab),
|
||||
],
|
||||
() => {
|
||||
if (unref(getUnFixedAndFull)) return;
|
||||
nextTick(() => {
|
||||
const headerEl = unref(headerElRef)?.$el;
|
||||
const tabEl = unref(tabElRef)?.$el;
|
||||
const fullHeaderEl = unref(injectValue.fullHeaderRef)?.$el;
|
||||
|
||||
let height = 0;
|
||||
if (headerEl && !unref(getShowFullHeaderRef)) {
|
||||
height += headerEl.offsetHeight;
|
||||
}
|
||||
|
||||
if (tabEl) {
|
||||
height += tabEl.offsetHeight;
|
||||
}
|
||||
|
||||
if (fullHeaderEl && unref(getShowFullHeaderRef)) {
|
||||
const fullHeaderHeight = fullHeaderEl.offsetHeight;
|
||||
height += fullHeaderHeight;
|
||||
fullHeaderHeightRef.value = fullHeaderHeight;
|
||||
}
|
||||
placeholderHeightRef.value = height;
|
||||
});
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<>
|
||||
{unref(getIsShowPlaceholderDom) && <div style={unref(getPlaceholderDomStyle)} />}
|
||||
<div
|
||||
style={unref(getWrapStyle)}
|
||||
class={['multiple-tab-header', { fixed: unref(getIsFixed) }]}
|
||||
>
|
||||
{unref(getShowInsetHeaderRef) && <LayoutHeader ref={headerElRef} />}
|
||||
{unref(showTabsRef) && <MultipleTabs ref={tabElRef} />}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
@@ -15,15 +15,31 @@ import { DOC_URL } from '/@/settings/siteSetting';
|
||||
import { openWindow } from '/@/utils';
|
||||
|
||||
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
|
||||
import { FunctionalComponent } from 'vue';
|
||||
|
||||
interface RenderItemParams {
|
||||
type MenuEvent = 'loginOut' | 'doc';
|
||||
interface MenuItemProps {
|
||||
icon: string;
|
||||
text: string;
|
||||
key: string;
|
||||
key: MenuEvent;
|
||||
}
|
||||
|
||||
const prefixCls = 'user-dropdown';
|
||||
|
||||
const MenuItem: FunctionalComponent<MenuItemProps> = (props) => {
|
||||
const { key, icon, text } = props;
|
||||
return (
|
||||
<Menu.Item key={key}>
|
||||
{() => (
|
||||
<span class="flex items-center">
|
||||
<Icon icon={icon} class="mr-1" />
|
||||
<span>{text}</span>
|
||||
</span>
|
||||
)}
|
||||
</Menu.Item>
|
||||
);
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'UserDropdown',
|
||||
setup() {
|
||||
@@ -44,27 +60,17 @@ export default defineComponent({
|
||||
openWindow(DOC_URL);
|
||||
}
|
||||
|
||||
function handleMenuClick(e: any) {
|
||||
if (e.key === 'loginOut') {
|
||||
handleLoginOut();
|
||||
} else if (e.key === 'doc') {
|
||||
openDoc();
|
||||
function handleMenuClick(e: { key: MenuEvent }) {
|
||||
switch (e.key) {
|
||||
case 'loginOut':
|
||||
handleLoginOut();
|
||||
break;
|
||||
case 'doc':
|
||||
openDoc();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function renderItem({ icon, text, key }: RenderItemParams) {
|
||||
return (
|
||||
<Menu.Item key={key}>
|
||||
{() => (
|
||||
<span class="flex items-center">
|
||||
<Icon icon={icon} class="mr-1" />
|
||||
<span>{text}</span>
|
||||
</span>
|
||||
)}
|
||||
</Menu.Item>
|
||||
);
|
||||
}
|
||||
|
||||
function renderSlotsDefault() {
|
||||
const { realName } = unref(getUserInfo);
|
||||
return (
|
||||
@@ -83,13 +89,9 @@ export default defineComponent({
|
||||
<Menu onClick={handleMenuClick}>
|
||||
{() => (
|
||||
<>
|
||||
{showDoc && renderItem({ key: 'doc', text: '文档', icon: 'gg:loadbar-doc' })}
|
||||
{showDoc && <MenuItem key="doc" text="文档" icon="gg:loadbar-doc" />}
|
||||
{showDoc && <Divider />}
|
||||
{renderItem({
|
||||
key: 'loginOut',
|
||||
text: '退出系统',
|
||||
icon: 'ant-design:poweroff-outlined',
|
||||
})}
|
||||
<MenuItem key="loginOut" text="退出系统" icon="ant-design:poweroff-outlined" />
|
||||
</>
|
||||
)}
|
||||
</Menu>
|
||||
|
@@ -10,13 +10,21 @@
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
&.fixed {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__left {
|
||||
display: flex;
|
||||
// flex-grow: 1;
|
||||
align-items: center;
|
||||
|
||||
.layout-trigger {
|
||||
padding: 4px 10px 0 16px;
|
||||
padding: 1px 10px 0 16px;
|
||||
cursor: pointer;
|
||||
|
||||
.anticon {
|
||||
@@ -150,6 +158,7 @@
|
||||
}
|
||||
|
||||
&__inner,
|
||||
&__inner.is-link,
|
||||
&__separator {
|
||||
color: @white;
|
||||
}
|
||||
|
@@ -1,39 +1,12 @@
|
||||
@import (reference) '../../design/index.less';
|
||||
|
||||
.default-layout {
|
||||
&__content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
|
||||
&.fixed {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&__loading {
|
||||
position: absolute;
|
||||
z-index: @page-loading-z-index;
|
||||
}
|
||||
|
||||
&__main {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
|
||||
&.fixed {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&.fixed.lock {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.layout-content {
|
||||
position: relative;
|
||||
|
||||
&.fixed {
|
||||
width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
> .ant-layout {
|
||||
min-height: 100%;
|
||||
}
|
||||
}
|
||||
|
@@ -1,129 +1,92 @@
|
||||
import { defineComponent, unref, computed } from 'vue';
|
||||
import './index.less';
|
||||
|
||||
import { defineComponent, unref, computed, ref } from 'vue';
|
||||
import { Layout, BackTop } from 'ant-design-vue';
|
||||
import LayoutHeader from './header/LayoutHeader';
|
||||
|
||||
import { appStore } from '/@/store/modules/app';
|
||||
import LayoutContent from './LayoutContent';
|
||||
import LayoutSideBar from './sider/LayoutSideBar';
|
||||
import LayoutContent from './content';
|
||||
import LayoutFooter from './footer';
|
||||
import LayoutLockPage from './lock';
|
||||
import LayoutSideBar from './sider';
|
||||
import SettingBtn from './setting/index.vue';
|
||||
import MultipleTabs from './multitabs/index';
|
||||
import LayoutMultipleHeader from './header/LayoutMultipleHeader';
|
||||
|
||||
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
|
||||
import { MenuModeEnum } from '/@/enums/menuEnum';
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useFullContent } from '/@/hooks/web/useFullContent';
|
||||
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
|
||||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
||||
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
|
||||
import { createLayoutContext } from './useLayoutContext';
|
||||
|
||||
import LockPage from '/@/views/sys/lock/index.vue';
|
||||
import { registerGlobComp } from '/@/components/registerGlobComp';
|
||||
|
||||
import './index.less';
|
||||
export default defineComponent({
|
||||
name: 'DefaultLayout',
|
||||
setup() {
|
||||
const { currentRoute } = useRouter();
|
||||
const headerRef = ref<ComponentRef>(null);
|
||||
|
||||
createLayoutContext({ fullHeaderRef: headerRef });
|
||||
|
||||
// ! Only register global components here
|
||||
// ! Can reduce the size of the first screen code
|
||||
// default layout It is loaded after login. So it won’t be packaged to the first screen
|
||||
registerGlobComp();
|
||||
|
||||
const { getShowFullHeaderRef } = useHeaderSetting();
|
||||
|
||||
const { getUseOpenBackTop, getShowSettingButton, getShowFooter } = useRootSetting();
|
||||
|
||||
const { getShowMenu, getMenuMode, getSplit } = useMenuSetting();
|
||||
|
||||
const { getFullContent } = useFullContent();
|
||||
|
||||
const getProjectConfigRef = computed(() => appStore.getProjectConfig);
|
||||
|
||||
const getLockMainScrollStateRef = computed(() => appStore.getLockMainScrollState);
|
||||
|
||||
const showHeaderRef = computed(() => {
|
||||
const {
|
||||
headerSetting: { show },
|
||||
} = unref(getProjectConfigRef);
|
||||
return show;
|
||||
});
|
||||
|
||||
const showMixHeaderRef = computed(() => {
|
||||
const {
|
||||
menuSetting: { type },
|
||||
} = unref(getProjectConfigRef);
|
||||
return type !== MenuTypeEnum.SIDEBAR && unref(showHeaderRef);
|
||||
});
|
||||
|
||||
const getIsLockRef = computed(() => {
|
||||
const { getLockInfo } = appStore;
|
||||
const { isLock } = getLockInfo;
|
||||
return isLock;
|
||||
const getShowLayoutFooter = computed(() => {
|
||||
return unref(getShowFooter) && !unref(currentRoute).meta?.hiddenFooter;
|
||||
});
|
||||
|
||||
const showSideBarRef = computed(() => {
|
||||
const {
|
||||
menuSetting: { show, mode, split },
|
||||
} = unref(getProjectConfigRef);
|
||||
return split || (show && mode !== MenuModeEnum.HORIZONTAL && !unref(getFullContent));
|
||||
return (
|
||||
unref(getSplit) ||
|
||||
(unref(getShowMenu) &&
|
||||
unref(getMenuMode) !== MenuModeEnum.HORIZONTAL &&
|
||||
!unref(getFullContent))
|
||||
);
|
||||
});
|
||||
|
||||
const showFullHeaderRef = computed(() => {
|
||||
return !unref(getFullContent) && unref(showMixHeaderRef) && unref(showHeaderRef);
|
||||
});
|
||||
|
||||
const showInsetHeaderRef = computed(() => {
|
||||
return !unref(getFullContent) && !unref(showMixHeaderRef) && unref(showHeaderRef);
|
||||
});
|
||||
|
||||
const fixedHeaderClsRef = computed(() => {
|
||||
const {
|
||||
headerSetting: { fixed },
|
||||
} = unref(getProjectConfigRef);
|
||||
const fixedHeaderCls = fixed
|
||||
? 'fixed' + (unref(getLockMainScrollStateRef) ? ' lock' : '')
|
||||
: '';
|
||||
return fixedHeaderCls;
|
||||
});
|
||||
|
||||
const showTabsRef = computed(() => {
|
||||
const {
|
||||
multiTabsSetting: { show },
|
||||
} = unref(getProjectConfigRef);
|
||||
return show && !unref(getFullContent);
|
||||
});
|
||||
|
||||
const showClassSideBarRef = computed(() => {
|
||||
const {
|
||||
menuSetting: { split, hidden },
|
||||
} = unref(getProjectConfigRef);
|
||||
return split ? hidden : true;
|
||||
});
|
||||
|
||||
function getTarget(): any {
|
||||
const {
|
||||
headerSetting: { fixed },
|
||||
} = unref(getProjectConfigRef);
|
||||
return document.querySelector(`.default-layout__${fixed ? 'main' : 'content'}`);
|
||||
function renderFeatures() {
|
||||
return (
|
||||
<>
|
||||
<LayoutLockPage />
|
||||
{/* back top */}
|
||||
{unref(getUseOpenBackTop) && <BackTop target={() => document.body} />}
|
||||
{/* open setting drawer */}
|
||||
{unref(getShowSettingButton) && <SettingBtn />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return () => {
|
||||
const { useOpenBackTop, showSettingButton } = unref(getProjectConfigRef);
|
||||
return (
|
||||
<Layout class="default-layout relative">
|
||||
<Layout class="default-layout">
|
||||
{() => (
|
||||
<>
|
||||
{/* lock page */}
|
||||
{unref(getIsLockRef) && <LockPage />}
|
||||
{/* back top */}
|
||||
{useOpenBackTop && <BackTop target={getTarget} />}
|
||||
{/* open setting drawer */}
|
||||
{showSettingButton && <SettingBtn />}
|
||||
{renderFeatures()}
|
||||
|
||||
{unref(showFullHeaderRef) && <LayoutHeader />}
|
||||
{unref(getShowFullHeaderRef) && <LayoutHeader fixed={true} ref={headerRef} />}
|
||||
|
||||
<Layout>
|
||||
{() => (
|
||||
<>
|
||||
{unref(showSideBarRef) && (
|
||||
<LayoutSideBar class={unref(showClassSideBarRef) ? '' : 'hidden'} />
|
||||
)}
|
||||
<Layout class={[`default-layout__content`, unref(fixedHeaderClsRef)]}>
|
||||
{unref(showSideBarRef) && <LayoutSideBar />}
|
||||
<Layout>
|
||||
{() => (
|
||||
<>
|
||||
{unref(showInsetHeaderRef) && <LayoutHeader />}
|
||||
|
||||
{unref(showTabsRef) && <MultipleTabs />}
|
||||
|
||||
<LayoutContent class={unref(fixedHeaderClsRef)} />
|
||||
<LayoutMultipleHeader />
|
||||
<LayoutContent />
|
||||
{unref(getShowLayoutFooter) && <LayoutFooter />}
|
||||
</>
|
||||
)}
|
||||
</Layout>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import './LockActionItem.less';
|
||||
import './LockAction.less';
|
||||
|
||||
import { defineComponent } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal/index';
|
17
src/layouts/default/lock/index.tsx
Normal file
17
src/layouts/default/lock/index.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { defineComponent, unref, computed } from 'vue';
|
||||
import { appStore } from '/@/store/modules/app';
|
||||
import LockPage from '/@/views/sys/lock/index.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LayoutLockPage',
|
||||
setup() {
|
||||
const getIsLockRef = computed(() => {
|
||||
const { getLockInfo } = appStore;
|
||||
const { isLock } = getLockInfo;
|
||||
return isLock;
|
||||
});
|
||||
return () => {
|
||||
return unref(getIsLockRef) ? <LockPage /> : null;
|
||||
};
|
||||
},
|
||||
});
|
@@ -17,7 +17,7 @@ import { useSplitMenu } from './useLayoutMenu';
|
||||
import { openWindow } from '/@/utils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DefaultLayoutMenu',
|
||||
name: 'LayoutMenu',
|
||||
props: {
|
||||
theme: {
|
||||
type: String as PropType<string>,
|
||||
@@ -50,12 +50,12 @@ export default defineComponent({
|
||||
const {
|
||||
setMenuSetting,
|
||||
getShowSearch,
|
||||
getMode,
|
||||
getType,
|
||||
getMenuMode,
|
||||
getMenuType,
|
||||
getCollapsedShowTitle,
|
||||
getCollapsedShowSearch,
|
||||
getIsSidebarType,
|
||||
getTheme,
|
||||
getMenuTheme,
|
||||
getCollapsed,
|
||||
getAccordion,
|
||||
} = useMenuSetting();
|
||||
@@ -66,9 +66,9 @@ export default defineComponent({
|
||||
|
||||
const showLogo = computed(() => unref(getShowLogo) && unref(getIsSidebarType));
|
||||
|
||||
const getMenuMode = computed(() => props.menuMode || unref(getMode));
|
||||
const getComputedMenuMode = computed(() => props.menuMode || unref(getMenuMode));
|
||||
|
||||
const getMenuTheme = computed(() => props.theme || unref(getTheme));
|
||||
const getComputedMenuTheme = computed(() => props.theme || unref(getMenuTheme));
|
||||
|
||||
const appendClass = computed(() => props.splitType === MenuSplitTyeEnum.TOP);
|
||||
|
||||
@@ -111,8 +111,8 @@ export default defineComponent({
|
||||
return (
|
||||
<AppLogo
|
||||
showTitle={!unref(getCollapsed)}
|
||||
class={[`layout-menu__logo`, unref(getMenuTheme)]}
|
||||
theme={unref(getMenuTheme)}
|
||||
class={[`layout-menu__logo`, unref(getComputedMenuTheme)]}
|
||||
theme={unref(getComputedMenuTheme)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -124,10 +124,10 @@ export default defineComponent({
|
||||
beforeClickFn={beforeMenuClickFn}
|
||||
isHorizontal={props.isHorizontal}
|
||||
appendClass={unref(appendClass)}
|
||||
type={unref(getType)}
|
||||
mode={unref(getMenuMode)}
|
||||
type={unref(getMenuType)}
|
||||
mode={unref(getComputedMenuMode)}
|
||||
collapsedShowTitle={unref(getCollapsedShowTitle)}
|
||||
theme={unref(getMenuTheme)}
|
||||
theme={unref(getComputedMenuTheme)}
|
||||
showLogo={unref(showLogo)}
|
||||
search={unref(showSearch)}
|
||||
items={unref(menusRef)}
|
@@ -6,7 +6,6 @@ import { TabItem, tabStore } from '/@/store/modules/tab';
|
||||
import { getScaleAction, TabContentProps } from './tab.data';
|
||||
|
||||
import { Dropdown } from '/@/components/Dropdown/index';
|
||||
import Icon from '/@/components/Icon/index';
|
||||
import { RightOutlined } from '@ant-design/icons-vue';
|
||||
import { appStore } from '/@/store/modules/app';
|
||||
|
||||
@@ -57,18 +56,11 @@ export default defineComponent({
|
||||
/**
|
||||
* @description: 渲染图标
|
||||
*/
|
||||
function renderIcon() {
|
||||
const { tabItem } = props;
|
||||
if (!tabItem) return;
|
||||
const icon = tabItem.meta && tabItem.meta.icon;
|
||||
if (!icon || !unref(getProjectConfigRef).multiTabsSetting.showIcon) return null;
|
||||
return <Icon icon={icon} class="align-middle " style={{ marginBottom: '2px' }} />;
|
||||
}
|
||||
|
||||
function renderTabContent() {
|
||||
const { tabItem: { meta } = {} } = props;
|
||||
return (
|
||||
<div class={`multiple-tabs-content__content `} onContextmenu={handleContextMenu}>
|
||||
{renderIcon()}
|
||||
<span class="ml-1">{meta && meta.title}</span>
|
||||
</div>
|
||||
);
|
||||
|
@@ -1,29 +1,36 @@
|
||||
import { defineComponent, computed, unref, ref } from 'vue';
|
||||
import { BasicDrawer } from '/@/components/Drawer/index';
|
||||
import { Divider, Switch, Tooltip, InputNumber, Select } from 'ant-design-vue';
|
||||
import Button from '/@/components/Button/index.vue';
|
||||
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
|
||||
import { CopyOutlined, RedoOutlined, CheckOutlined } from '@ant-design/icons-vue';
|
||||
import { appStore } from '/@/store/modules/app';
|
||||
import { ProjectConfig } from '/@/types/config';
|
||||
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
|
||||
import type { ProjectConfig } from '/@/types/config';
|
||||
|
||||
import defaultSetting from '/@/settings/projectSetting';
|
||||
|
||||
import mixImg from '/@/assets/images/layout/menu-mix.svg';
|
||||
import sidebarImg from '/@/assets/images/layout/menu-sidebar.svg';
|
||||
import menuTopImg from '/@/assets/images/layout/menu-top.svg';
|
||||
import { defineComponent, computed, unref, FunctionalComponent } from 'vue';
|
||||
import { BasicDrawer } from '/@/components/Drawer/index';
|
||||
import { Divider, Switch, Tooltip, InputNumber, Select } from 'ant-design-vue';
|
||||
import Button from '/@/components/Button/index.vue';
|
||||
import { CopyOutlined, RedoOutlined, CheckOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
import { MenuTypeEnum } from '/@/enums/menuEnum';
|
||||
import { appStore } from '/@/store/modules/app';
|
||||
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
|
||||
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
|
||||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
||||
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
|
||||
import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
|
||||
|
||||
import { updateColorWeak, updateGrayMode } from '/@/setup/theme';
|
||||
|
||||
import { baseHandler } from './handler';
|
||||
|
||||
import {
|
||||
HandlerEnum,
|
||||
contentModeOptions,
|
||||
topMenuAlignOptions,
|
||||
menuTriggerOptions,
|
||||
routerTransitionOptions,
|
||||
} from './const';
|
||||
menuTypeList,
|
||||
} from './enum';
|
||||
|
||||
import { HEADER_PRESET_BG_COLOR_LIST, SIDE_BAR_BG_COLOR_LIST } from '/@/settings/colorSetting';
|
||||
|
||||
interface SwitchOptions {
|
||||
@@ -40,215 +47,280 @@ interface SelectConfig {
|
||||
handler?: Fn;
|
||||
}
|
||||
|
||||
interface ThemeOptions {
|
||||
def?: string;
|
||||
handler?: Fn;
|
||||
interface ThemePickerProps {
|
||||
colorList: string[];
|
||||
handler: Fn;
|
||||
def: string;
|
||||
}
|
||||
|
||||
const { createSuccessModal, createMessage } = useMessage();
|
||||
|
||||
/**
|
||||
* Menu type Picker comp
|
||||
*/
|
||||
const MenuTypePicker: FunctionalComponent = () => {
|
||||
const { getIsHorizontal, getMenuType } = useMenuSetting();
|
||||
return (
|
||||
<div class={`setting-drawer__siderbar`}>
|
||||
{menuTypeList.map((item) => {
|
||||
const { title, type: ItemType, mode, src } = item;
|
||||
return (
|
||||
<Tooltip title={title} placement="bottom" key={title}>
|
||||
{{
|
||||
default: () => (
|
||||
<div
|
||||
onClick={baseHandler.bind(null, HandlerEnum.CHANGE_LAYOUT, {
|
||||
mode: mode,
|
||||
type: ItemType,
|
||||
split: unref(getIsHorizontal) ? false : undefined,
|
||||
})}
|
||||
>
|
||||
<CheckOutlined
|
||||
class={['check-icon', unref(getMenuType) === ItemType ? 'active' : '']}
|
||||
/>
|
||||
<img src={src} />
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
</Tooltip>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ThemePicker: FunctionalComponent<ThemePickerProps> = (props) => {
|
||||
return (
|
||||
<div class={`setting-drawer__theme-item`}>
|
||||
{props.colorList.map((color) => {
|
||||
return (
|
||||
<span
|
||||
onClick={() => props.handler?.(color)}
|
||||
key={color}
|
||||
class={[props.def === color ? 'active' : '']}
|
||||
style={{
|
||||
background: color,
|
||||
}}
|
||||
>
|
||||
<CheckOutlined class="icon" />
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* FooterButton component
|
||||
*/
|
||||
const FooterButton: FunctionalComponent = () => {
|
||||
const { getRootSetting } = useRootSetting();
|
||||
function handleCopy() {
|
||||
const { isSuccessRef } = useCopyToClipboard(JSON.stringify(unref(getRootSetting), null, 2));
|
||||
unref(isSuccessRef) &&
|
||||
createSuccessModal({
|
||||
title: '操作成功',
|
||||
content: '复制成功,请到 src/settings/projectSetting.ts 中修改配置!',
|
||||
});
|
||||
}
|
||||
function handleResetSetting() {
|
||||
try {
|
||||
appStore.commitProjectConfigState(defaultSetting);
|
||||
const { colorWeak, grayMode } = defaultSetting;
|
||||
// updateTheme(themeColor);
|
||||
updateColorWeak(colorWeak);
|
||||
updateGrayMode(grayMode);
|
||||
createMessage.success('重置成功!');
|
||||
} catch (error) {
|
||||
createMessage.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
function handleClearAndRedo() {
|
||||
localStorage.clear();
|
||||
appStore.resumeAllState();
|
||||
location.reload();
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="setting-drawer__footer">
|
||||
<Button type="primary" block onClick={handleCopy}>
|
||||
{() => (
|
||||
<>
|
||||
<CopyOutlined class="mr-2" />
|
||||
拷贝
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
<Button block class="mt-2" onClick={handleResetSetting} color="warning">
|
||||
{() => (
|
||||
<>
|
||||
<RedoOutlined class="mr-2" />
|
||||
重置
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
<Button block class="mt-2" onClick={handleClearAndRedo} color="error">
|
||||
{() => (
|
||||
<>
|
||||
<RedoOutlined class="mr-2" />
|
||||
清空缓存并返回登录页
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SettingDrawer',
|
||||
setup(_, { attrs }) {
|
||||
const { createSuccessModal, createMessage } = useMessage();
|
||||
const {
|
||||
getContentMode,
|
||||
getRouterTransition,
|
||||
getOpenRouterTransition,
|
||||
getOpenPageLoading,
|
||||
getShowFooter,
|
||||
getShowBreadCrumb,
|
||||
getShowBreadCrumbIcon,
|
||||
getShowLogo,
|
||||
getFullContent,
|
||||
getColorWeak,
|
||||
getGrayMode,
|
||||
} = useRootSetting();
|
||||
|
||||
const getProjectConfigRef = computed(() => {
|
||||
return appStore.getProjectConfig;
|
||||
});
|
||||
const {
|
||||
getIsHorizontal,
|
||||
getShowMenu,
|
||||
getMenuType,
|
||||
getTrigger,
|
||||
getCollapsedShowTitle,
|
||||
getMenuFixed,
|
||||
getCollapsed,
|
||||
getShowSearch,
|
||||
getHasDrag,
|
||||
getTopMenuAlign,
|
||||
getAccordion,
|
||||
getMenuWidth,
|
||||
getMenuBgColor,
|
||||
getIsTopMenu,
|
||||
getSplit,
|
||||
} = useMenuSetting();
|
||||
|
||||
const getIsHorizontalRef = computed(() => {
|
||||
return unref(getProjectConfigRef).menuSetting.mode === MenuModeEnum.HORIZONTAL;
|
||||
});
|
||||
const { getShowHeader, getFixed: getHeaderFixed, getHeaderBgColor } = useHeaderSetting();
|
||||
|
||||
const getShowHeaderRef = computed(() => {
|
||||
return unref(getProjectConfigRef).headerSetting.show;
|
||||
});
|
||||
const { getShowMultipleTab, getShowQuick } = useMultipleTabSetting();
|
||||
|
||||
const getShowMenuRef = computed(() => {
|
||||
return unref(getProjectConfigRef).menuSetting.show && !unref(getIsHorizontalRef);
|
||||
return unref(getShowMenu) && !unref(getIsHorizontal);
|
||||
});
|
||||
|
||||
const getShowTabsRef = computed(() => {
|
||||
return unref(getProjectConfigRef).multiTabsSetting.show;
|
||||
});
|
||||
|
||||
function handleCopy() {
|
||||
const { isSuccessRef } = useCopyToClipboard(
|
||||
JSON.stringify(unref(getProjectConfigRef), null, 2)
|
||||
);
|
||||
unref(isSuccessRef) &&
|
||||
createSuccessModal({
|
||||
title: '操作成功',
|
||||
content: '复制成功,请到 src/settings/projectSetting.ts 中修改配置!',
|
||||
});
|
||||
}
|
||||
|
||||
function handleResetSetting() {
|
||||
try {
|
||||
appStore.commitProjectConfigState(defaultSetting);
|
||||
const { colorWeak, grayMode } = defaultSetting;
|
||||
// updateTheme(themeColor);
|
||||
updateColorWeak(colorWeak);
|
||||
updateGrayMode(grayMode);
|
||||
createMessage.success('重置成功!');
|
||||
} catch (error) {
|
||||
createMessage.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
function handleClearAndRedo() {
|
||||
localStorage.clear();
|
||||
appStore.resumeAllState();
|
||||
location.reload();
|
||||
}
|
||||
|
||||
function renderSidebar() {
|
||||
const {
|
||||
menuSetting: { type, split },
|
||||
} = unref(getProjectConfigRef);
|
||||
|
||||
const typeList = ref([
|
||||
{
|
||||
title: '左侧菜单模式',
|
||||
mode: MenuModeEnum.INLINE,
|
||||
type: MenuTypeEnum.SIDEBAR,
|
||||
src: sidebarImg,
|
||||
},
|
||||
{
|
||||
title: '混合模式',
|
||||
mode: MenuModeEnum.INLINE,
|
||||
type: MenuTypeEnum.MIX,
|
||||
src: mixImg,
|
||||
},
|
||||
|
||||
{
|
||||
title: '顶部菜单模式',
|
||||
mode: MenuModeEnum.HORIZONTAL,
|
||||
type: MenuTypeEnum.TOP_MENU,
|
||||
src: menuTopImg,
|
||||
},
|
||||
]);
|
||||
return [
|
||||
<div class={`setting-drawer__siderbar`}>
|
||||
{unref(typeList).map((item) => {
|
||||
const { title, type: ItemType, mode, src } = item;
|
||||
return (
|
||||
<Tooltip title={title} placement="bottom" key={title}>
|
||||
{{
|
||||
default: () => (
|
||||
<div
|
||||
onClick={baseHandler.bind(null, HandlerEnum.CHANGE_LAYOUT, {
|
||||
mode: mode,
|
||||
type: ItemType,
|
||||
split: unref(getIsHorizontalRef) ? false : undefined,
|
||||
})}
|
||||
>
|
||||
<CheckOutlined class={['check-icon', type === ItemType ? 'active' : '']} />
|
||||
<img src={src} />
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
</Tooltip>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<MenuTypePicker />
|
||||
{renderSwitchItem('分割菜单', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.MENU_SPLIT, e);
|
||||
},
|
||||
def: unref(getSplit),
|
||||
disabled: !unref(getShowMenuRef) || unref(getMenuType) !== MenuTypeEnum.MIX,
|
||||
})}
|
||||
</div>,
|
||||
renderSwitchItem('分割菜单', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.MENU_SPLIT, e);
|
||||
},
|
||||
def: split,
|
||||
disabled: !unref(getShowMenuRef) || type !== MenuTypeEnum.MIX,
|
||||
}),
|
||||
// renderSelectItem('顶栏主题', {
|
||||
// handler: (e) => {
|
||||
// baseHandler(HandlerEnum.HEADER_THEME, e);
|
||||
// },
|
||||
// def: headerTheme,
|
||||
// options: themeOptions,
|
||||
// disabled: !unref(getShowHeaderRef),
|
||||
// }),
|
||||
// renderSelectItem('菜单主题', {
|
||||
// handler: (e) => {
|
||||
// baseHandler(HandlerEnum.MENU_THEME, e);
|
||||
// },
|
||||
// def: menuTheme,
|
||||
// options: themeOptions,
|
||||
// disabled: !unref(getShowMenuRef),
|
||||
// }),
|
||||
];
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function renderTheme() {
|
||||
return (
|
||||
<>
|
||||
<Divider>{() => '顶栏主题'}</Divider>
|
||||
<ThemePicker
|
||||
colorList={HEADER_PRESET_BG_COLOR_LIST}
|
||||
def={unref(getHeaderBgColor)}
|
||||
handler={(e) => {
|
||||
baseHandler(HandlerEnum.HEADER_THEME, e);
|
||||
}}
|
||||
/>
|
||||
<Divider>{() => '菜单主题'}</Divider>
|
||||
<ThemePicker
|
||||
colorList={SIDE_BAR_BG_COLOR_LIST}
|
||||
def={unref(getMenuBgColor)}
|
||||
handler={(e) => {
|
||||
baseHandler(HandlerEnum.MENU_THEME, e);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description:
|
||||
*/
|
||||
function renderFeatures() {
|
||||
const {
|
||||
contentMode,
|
||||
headerSetting: { fixed },
|
||||
menuSetting: {
|
||||
hasDrag,
|
||||
collapsed,
|
||||
showSearch,
|
||||
menuWidth,
|
||||
topMenuAlign,
|
||||
collapsedShowTitle,
|
||||
trigger,
|
||||
accordion,
|
||||
} = {},
|
||||
} = appStore.getProjectConfig;
|
||||
return [
|
||||
renderSwitchItem('侧边菜单拖拽', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.MENU_HAS_DRAG, e);
|
||||
},
|
||||
def: hasDrag,
|
||||
def: unref(getHasDrag),
|
||||
disabled: !unref(getShowMenuRef),
|
||||
}),
|
||||
renderSwitchItem('侧边菜单搜索', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.MENU_SHOW_SEARCH, e);
|
||||
},
|
||||
def: showSearch,
|
||||
def: unref(getShowSearch),
|
||||
disabled: !unref(getShowMenuRef),
|
||||
}),
|
||||
renderSwitchItem('侧边菜单手风琴模式', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.MENU_ACCORDION, e);
|
||||
},
|
||||
def: accordion,
|
||||
def: unref(getAccordion),
|
||||
disabled: !unref(getShowMenuRef),
|
||||
}),
|
||||
renderSwitchItem('折叠菜单', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.MENU_COLLAPSED, e);
|
||||
},
|
||||
def: collapsed,
|
||||
def: unref(getCollapsed),
|
||||
disabled: !unref(getShowMenuRef),
|
||||
}),
|
||||
renderSwitchItem('折叠菜单显示名称', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.MENU_COLLAPSED_SHOW_TITLE, e);
|
||||
},
|
||||
def: collapsedShowTitle,
|
||||
disabled: !unref(getShowMenuRef) || !collapsed,
|
||||
def: unref(getCollapsedShowTitle),
|
||||
disabled: !unref(getShowMenuRef) || !unref(getCollapsed),
|
||||
}),
|
||||
renderSwitchItem('固定header', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.HEADER_FIXED, e);
|
||||
},
|
||||
def: fixed,
|
||||
disabled: !unref(getShowHeaderRef),
|
||||
def: unref(getHeaderFixed),
|
||||
disabled: !unref(getShowHeader),
|
||||
}),
|
||||
renderSwitchItem('固定Siderbar', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.MENU_FIXED, e);
|
||||
},
|
||||
def: unref(getMenuFixed),
|
||||
disabled: !unref(getShowMenuRef),
|
||||
}),
|
||||
renderSelectItem('顶部菜单布局', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.MENU_TOP_ALIGN, e);
|
||||
},
|
||||
def: topMenuAlign,
|
||||
def: unref(getTopMenuAlign),
|
||||
options: topMenuAlignOptions,
|
||||
disabled: !unref(getShowHeaderRef),
|
||||
disabled: !unref(getShowHeader) || (!unref(getIsTopMenu) && !unref(getSplit)),
|
||||
}),
|
||||
renderSelectItem('菜单折叠按钮', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.MENU_TRIGGER, e);
|
||||
},
|
||||
def: trigger,
|
||||
disabled: !unref(getShowMenuRef),
|
||||
def: unref(getTrigger),
|
||||
options: menuTriggerOptions,
|
||||
}),
|
||||
|
||||
@@ -256,7 +328,7 @@ export default defineComponent({
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.CONTENT_MODE, e);
|
||||
},
|
||||
def: contentMode,
|
||||
def: unref(getContentMode),
|
||||
options: contentModeOptions,
|
||||
}),
|
||||
<div class={`setting-drawer__cell-item`}>
|
||||
@@ -286,7 +358,7 @@ export default defineComponent({
|
||||
min={100}
|
||||
step={10}
|
||||
disabled={!unref(getShowMenuRef)}
|
||||
defaultValue={menuWidth}
|
||||
defaultValue={unref(getMenuWidth)}
|
||||
formatter={(value: string) => `${parseInt(value)}px`}
|
||||
onChange={(e: any) => {
|
||||
baseHandler(HandlerEnum.MENU_WIDTH, e);
|
||||
@@ -295,120 +367,111 @@ export default defineComponent({
|
||||
</div>,
|
||||
];
|
||||
}
|
||||
function renderTransition() {
|
||||
const { routerTransition, openRouterTransition, openPageLoading } = appStore.getProjectConfig;
|
||||
|
||||
function renderContent() {
|
||||
return [
|
||||
renderSwitchItem('面包屑', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.SHOW_BREADCRUMB, e);
|
||||
},
|
||||
def: unref(getShowBreadCrumb),
|
||||
disabled: !unref(getShowHeader),
|
||||
}),
|
||||
renderSwitchItem('面包屑图标', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.SHOW_BREADCRUMB_ICON, e);
|
||||
},
|
||||
def: unref(getShowBreadCrumbIcon),
|
||||
disabled: !unref(getShowHeader),
|
||||
}),
|
||||
renderSwitchItem('标签页', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.TABS_SHOW, e);
|
||||
},
|
||||
def: unref(getShowMultipleTab),
|
||||
}),
|
||||
renderSwitchItem('标签页快捷按钮', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.TABS_SHOW_QUICK, e);
|
||||
},
|
||||
def: unref(getShowQuick),
|
||||
disabled: !unref(getShowMultipleTab),
|
||||
}),
|
||||
|
||||
renderSwitchItem('左侧菜单', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.MENU_SHOW_SIDEBAR, e);
|
||||
},
|
||||
def: unref(getShowMenu),
|
||||
disabled: unref(getIsHorizontal),
|
||||
}),
|
||||
renderSwitchItem('顶栏', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.HEADER_SHOW, e);
|
||||
},
|
||||
def: unref(getShowHeader),
|
||||
}),
|
||||
renderSwitchItem('Logo', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.SHOW_LOGO, e);
|
||||
},
|
||||
def: unref(getShowLogo),
|
||||
}),
|
||||
renderSwitchItem('页脚', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.SHOW_FOOTER, e);
|
||||
},
|
||||
def: unref(getShowFooter),
|
||||
}),
|
||||
renderSwitchItem('全屏内容', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.FULL_CONTENT, e);
|
||||
},
|
||||
def: unref(getFullContent),
|
||||
}),
|
||||
renderSwitchItem('灰色模式', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.GRAY_MODE, e);
|
||||
},
|
||||
def: unref(getGrayMode),
|
||||
}),
|
||||
renderSwitchItem('色弱模式', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.COLOR_WEAK, e);
|
||||
},
|
||||
def: unref(getColorWeak),
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
function renderTransition() {
|
||||
return (
|
||||
<>
|
||||
{renderSwitchItem('页面切换loading', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.OPEN_PAGE_LOADING, e);
|
||||
},
|
||||
def: openPageLoading,
|
||||
def: unref(getOpenPageLoading),
|
||||
})}
|
||||
|
||||
{renderSwitchItem('切换动画', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.OPEN_ROUTE_TRANSITION, e);
|
||||
},
|
||||
def: openRouterTransition,
|
||||
def: unref(getOpenRouterTransition),
|
||||
})}
|
||||
|
||||
{renderSelectItem('路由动画', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.ROUTER_TRANSITION, e);
|
||||
},
|
||||
def: routerTransition,
|
||||
def: unref(getRouterTransition),
|
||||
options: routerTransitionOptions,
|
||||
disabled: !openRouterTransition,
|
||||
disabled: !unref(getOpenRouterTransition),
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
function renderContent() {
|
||||
const {
|
||||
grayMode,
|
||||
colorWeak,
|
||||
fullContent,
|
||||
showLogo,
|
||||
headerSetting: { show: showHeader },
|
||||
menuSetting: { show: showMenu },
|
||||
multiTabsSetting: { show: showMultiple, showQuick, showIcon: showTabIcon },
|
||||
showBreadCrumb,
|
||||
showBreadCrumbIcon,
|
||||
} = unref(getProjectConfigRef);
|
||||
return [
|
||||
renderSwitchItem('面包屑', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.SHOW_BREADCRUMB, e);
|
||||
},
|
||||
def: showBreadCrumb,
|
||||
disabled: !unref(getShowHeaderRef),
|
||||
}),
|
||||
renderSwitchItem('面包屑图标', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.SHOW_BREADCRUMB_ICON, e);
|
||||
},
|
||||
def: showBreadCrumbIcon,
|
||||
disabled: !unref(getShowHeaderRef),
|
||||
}),
|
||||
renderSwitchItem('标签页', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.TABS_SHOW, e);
|
||||
},
|
||||
def: showMultiple,
|
||||
}),
|
||||
renderSwitchItem('标签页快捷按钮', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.TABS_SHOW_QUICK, e);
|
||||
},
|
||||
def: showQuick,
|
||||
disabled: !unref(getShowTabsRef),
|
||||
}),
|
||||
renderSwitchItem('标签页图标', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.TABS_SHOW_ICON, e);
|
||||
},
|
||||
def: showTabIcon,
|
||||
disabled: !unref(getShowTabsRef),
|
||||
}),
|
||||
renderSwitchItem('左侧菜单', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.MENU_SHOW_SIDEBAR, e);
|
||||
},
|
||||
def: showMenu,
|
||||
disabled: unref(getIsHorizontalRef),
|
||||
}),
|
||||
renderSwitchItem('顶栏', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.HEADER_SHOW, e);
|
||||
},
|
||||
def: showHeader,
|
||||
}),
|
||||
renderSwitchItem('Logo', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.SHOW_LOGO, e);
|
||||
},
|
||||
def: showLogo,
|
||||
}),
|
||||
renderSwitchItem('全屏内容', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.FULL_CONTENT, e);
|
||||
},
|
||||
def: fullContent,
|
||||
}),
|
||||
renderSwitchItem('灰色模式', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.GRAY_MODE, e);
|
||||
},
|
||||
def: grayMode,
|
||||
}),
|
||||
renderSwitchItem('色弱模式', {
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.COLOR_WEAK, e);
|
||||
},
|
||||
def: colorWeak,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
function renderSelectItem(text: string, config?: SelectConfig) {
|
||||
const { handler, def, disabled = false, options } = config || {};
|
||||
@@ -449,50 +512,6 @@ export default defineComponent({
|
||||
);
|
||||
}
|
||||
|
||||
function renderTheme() {
|
||||
const { headerBgColor, menuBgColor } = unref(getProjectConfigRef);
|
||||
return (
|
||||
<>
|
||||
<Divider>{() => '顶栏主题'}</Divider>
|
||||
{renderThemeItem(HEADER_PRESET_BG_COLOR_LIST, {
|
||||
def: headerBgColor,
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.HEADER_THEME, e);
|
||||
},
|
||||
})}
|
||||
<Divider>{() => '菜单主题'}</Divider>
|
||||
{renderThemeItem(SIDE_BAR_BG_COLOR_LIST, {
|
||||
def: menuBgColor,
|
||||
handler: (e) => {
|
||||
baseHandler(HandlerEnum.MENU_THEME, e);
|
||||
},
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function renderThemeItem(colorList: string[], opt: ThemeOptions) {
|
||||
const { def, handler } = opt;
|
||||
return (
|
||||
<div class={`setting-drawer__theme-item`}>
|
||||
{colorList.map((item) => {
|
||||
return (
|
||||
<span
|
||||
onClick={() => handler && handler(item)}
|
||||
key={item}
|
||||
class={[def === item ? 'active' : '']}
|
||||
style={{
|
||||
background: item,
|
||||
}}
|
||||
>
|
||||
<CheckOutlined class="icon" />
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return () => (
|
||||
<BasicDrawer {...attrs} title="项目配置" width={300} wrapClassName="setting-drawer">
|
||||
{{
|
||||
@@ -500,9 +519,7 @@ export default defineComponent({
|
||||
<>
|
||||
<Divider>{() => '导航栏模式'}</Divider>
|
||||
{renderSidebar()}
|
||||
|
||||
{renderTheme()}
|
||||
|
||||
<Divider>{() => '界面功能'}</Divider>
|
||||
{renderFeatures()}
|
||||
<Divider>{() => '界面显示'}</Divider>
|
||||
@@ -510,32 +527,7 @@ export default defineComponent({
|
||||
<Divider>{() => '切换动画'}</Divider>
|
||||
{renderTransition()}
|
||||
<Divider />
|
||||
<div class="setting-drawer__footer">
|
||||
<Button type="primary" block onClick={handleCopy}>
|
||||
{() => (
|
||||
<>
|
||||
<CopyOutlined class="mr-2" />
|
||||
拷贝
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
<Button block class="mt-2" onClick={handleResetSetting} color="warning">
|
||||
{() => (
|
||||
<>
|
||||
<RedoOutlined class="mr-2" />
|
||||
重置
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
<Button block class="mt-2" onClick={handleClearAndRedo} color="error">
|
||||
{() => (
|
||||
<>
|
||||
<RedoOutlined class="mr-2" />
|
||||
清空缓存并返回登录页
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
<FooterButton />
|
||||
</>
|
||||
),
|
||||
}}
|
||||
|
@@ -1,5 +1,9 @@
|
||||
import { ContentEnum, RouterTransitionEnum, ThemeEnum } from '/@/enums/appEnum';
|
||||
import { TopMenuAlignEnum, TriggerEnum } from '/@/enums/menuEnum';
|
||||
import { MenuModeEnum, MenuTypeEnum, TopMenuAlignEnum, TriggerEnum } from '/@/enums/menuEnum';
|
||||
|
||||
import mixImg from '/@/assets/images/layout/menu-mix.svg';
|
||||
import sidebarImg from '/@/assets/images/layout/menu-sidebar.svg';
|
||||
import menuTopImg from '/@/assets/images/layout/menu-top.svg';
|
||||
|
||||
export enum HandlerEnum {
|
||||
CHANGE_LAYOUT,
|
||||
@@ -15,6 +19,7 @@ export enum HandlerEnum {
|
||||
MENU_THEME,
|
||||
MENU_SPLIT,
|
||||
MENU_SHOW_SEARCH,
|
||||
MENU_FIXED,
|
||||
|
||||
// header
|
||||
HEADER_SHOW,
|
||||
@@ -23,7 +28,6 @@ export enum HandlerEnum {
|
||||
|
||||
TABS_SHOW_QUICK,
|
||||
TABS_SHOW,
|
||||
TABS_SHOW_ICON,
|
||||
|
||||
OPEN_PAGE_LOADING,
|
||||
OPEN_ROUTE_TRANSITION,
|
||||
@@ -36,6 +40,7 @@ export enum HandlerEnum {
|
||||
GRAY_MODE,
|
||||
COLOR_WEAK,
|
||||
SHOW_LOGO,
|
||||
SHOW_FOOTER,
|
||||
}
|
||||
|
||||
export const themeOptions = [
|
||||
@@ -102,3 +107,25 @@ export const routerTransitionOptions = [
|
||||
value: item,
|
||||
};
|
||||
});
|
||||
|
||||
export const menuTypeList = [
|
||||
{
|
||||
title: '左侧菜单模式',
|
||||
mode: MenuModeEnum.INLINE,
|
||||
type: MenuTypeEnum.SIDEBAR,
|
||||
src: sidebarImg,
|
||||
},
|
||||
{
|
||||
title: '混合模式',
|
||||
mode: MenuModeEnum.INLINE,
|
||||
type: MenuTypeEnum.MIX,
|
||||
src: mixImg,
|
||||
},
|
||||
|
||||
{
|
||||
title: '顶部菜单模式',
|
||||
mode: MenuModeEnum.HORIZONTAL,
|
||||
type: MenuTypeEnum.TOP_MENU,
|
||||
src: menuTopImg,
|
||||
},
|
||||
];
|
@@ -1,5 +1,4 @@
|
||||
import { HandlerEnum } from './const';
|
||||
// import { MenuThemeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
|
||||
import { HandlerEnum } from './enum';
|
||||
import {
|
||||
updateColorWeak,
|
||||
updateGrayMode,
|
||||
@@ -19,12 +18,7 @@ export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConf
|
||||
case HandlerEnum.CHANGE_LAYOUT:
|
||||
const { mode, type, split } = value;
|
||||
const splitOpt = split === undefined ? { split } : {};
|
||||
// let headerSetting = {};
|
||||
// if (type === MenuTypeEnum.TOP_MENU) {
|
||||
// headerSetting = {
|
||||
// theme: MenuThemeEnum.DARK,
|
||||
// };
|
||||
// }
|
||||
|
||||
return {
|
||||
menuSetting: {
|
||||
mode,
|
||||
@@ -33,159 +27,103 @@ export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConf
|
||||
show: true,
|
||||
...splitOpt,
|
||||
},
|
||||
// headerSetting,
|
||||
};
|
||||
|
||||
case HandlerEnum.MENU_HAS_DRAG:
|
||||
return {
|
||||
menuSetting: {
|
||||
hasDrag: value,
|
||||
},
|
||||
};
|
||||
return { menuSetting: { hasDrag: value } };
|
||||
|
||||
case HandlerEnum.MENU_ACCORDION:
|
||||
return {
|
||||
menuSetting: {
|
||||
accordion: value,
|
||||
},
|
||||
};
|
||||
return { menuSetting: { accordion: value } };
|
||||
|
||||
case HandlerEnum.MENU_TRIGGER:
|
||||
return {
|
||||
menuSetting: {
|
||||
trigger: value,
|
||||
},
|
||||
};
|
||||
return { menuSetting: { trigger: value } };
|
||||
|
||||
case HandlerEnum.MENU_TOP_ALIGN:
|
||||
return {
|
||||
menuSetting: {
|
||||
topMenuAlign: value,
|
||||
},
|
||||
};
|
||||
return { menuSetting: { topMenuAlign: value } };
|
||||
|
||||
case HandlerEnum.MENU_COLLAPSED:
|
||||
return {
|
||||
menuSetting: {
|
||||
collapsed: value,
|
||||
},
|
||||
};
|
||||
return { menuSetting: { collapsed: value } };
|
||||
|
||||
case HandlerEnum.MENU_WIDTH:
|
||||
return {
|
||||
menuSetting: {
|
||||
menuWidth: value,
|
||||
},
|
||||
};
|
||||
return { menuSetting: { menuWidth: value } };
|
||||
|
||||
case HandlerEnum.MENU_COLLAPSED_SHOW_TITLE:
|
||||
return {
|
||||
menuSetting: {
|
||||
collapsedShowTitle: value,
|
||||
},
|
||||
};
|
||||
return { menuSetting: { collapsedShowTitle: value } };
|
||||
|
||||
case HandlerEnum.MENU_SHOW_SIDEBAR:
|
||||
return {
|
||||
menuSetting: {
|
||||
show: value,
|
||||
},
|
||||
};
|
||||
return { menuSetting: { show: value } };
|
||||
|
||||
case HandlerEnum.MENU_THEME:
|
||||
updateSidebarBgColor(value);
|
||||
return {
|
||||
menuBgColor: value,
|
||||
// menuSetting: {
|
||||
// theme: value,
|
||||
// },
|
||||
};
|
||||
return { menuSetting: { bgColor: value } };
|
||||
|
||||
case HandlerEnum.MENU_SPLIT:
|
||||
return {
|
||||
menuSetting: {
|
||||
split: value,
|
||||
},
|
||||
};
|
||||
return { menuSetting: { split: value } };
|
||||
|
||||
case HandlerEnum.MENU_FIXED:
|
||||
return { menuSetting: { fixed: value } };
|
||||
|
||||
case HandlerEnum.MENU_SHOW_SEARCH:
|
||||
return {
|
||||
menuSetting: {
|
||||
showSearch: value,
|
||||
},
|
||||
};
|
||||
return { menuSetting: { showSearch: value } };
|
||||
|
||||
// ============root==================
|
||||
|
||||
case HandlerEnum.OPEN_PAGE_LOADING:
|
||||
return {
|
||||
openPageLoading: value,
|
||||
};
|
||||
appStore.commitPageLoadingState(false);
|
||||
return { openPageLoading: value };
|
||||
|
||||
case HandlerEnum.OPEN_ROUTE_TRANSITION:
|
||||
return {
|
||||
openRouterTransition: value,
|
||||
};
|
||||
return { openRouterTransition: value };
|
||||
|
||||
case HandlerEnum.ROUTER_TRANSITION:
|
||||
return {
|
||||
routerTransition: value,
|
||||
};
|
||||
return { routerTransition: value };
|
||||
|
||||
case HandlerEnum.LOCK_TIME:
|
||||
return {
|
||||
lockTime: value,
|
||||
};
|
||||
return { lockTime: value };
|
||||
|
||||
case HandlerEnum.FULL_CONTENT:
|
||||
return {
|
||||
fullContent: value,
|
||||
};
|
||||
return { fullContent: value };
|
||||
|
||||
case HandlerEnum.CONTENT_MODE:
|
||||
return {
|
||||
contentMode: value,
|
||||
};
|
||||
return { contentMode: value };
|
||||
|
||||
case HandlerEnum.SHOW_BREADCRUMB:
|
||||
return {
|
||||
showBreadCrumb: value,
|
||||
};
|
||||
return { showBreadCrumb: value };
|
||||
|
||||
case HandlerEnum.SHOW_BREADCRUMB_ICON:
|
||||
return {
|
||||
showBreadCrumbIcon: value,
|
||||
};
|
||||
return { showBreadCrumbIcon: value };
|
||||
|
||||
case HandlerEnum.GRAY_MODE:
|
||||
updateGrayMode(value);
|
||||
return {
|
||||
grayMode: value,
|
||||
};
|
||||
return { grayMode: value };
|
||||
|
||||
case HandlerEnum.SHOW_FOOTER:
|
||||
return { showFooter: value };
|
||||
|
||||
case HandlerEnum.COLOR_WEAK:
|
||||
updateColorWeak(value);
|
||||
return {
|
||||
colorWeak: value,
|
||||
};
|
||||
return { colorWeak: value };
|
||||
|
||||
case HandlerEnum.SHOW_LOGO:
|
||||
return {
|
||||
showLogo: value,
|
||||
};
|
||||
return { showLogo: value };
|
||||
|
||||
// ============tabs==================
|
||||
case HandlerEnum.TABS_SHOW_QUICK:
|
||||
return {
|
||||
multiTabsSetting: {
|
||||
showQuick: value,
|
||||
},
|
||||
};
|
||||
case HandlerEnum.TABS_SHOW_ICON:
|
||||
return {
|
||||
multiTabsSetting: {
|
||||
showIcon: value,
|
||||
},
|
||||
};
|
||||
return { multiTabsSetting: { showQuick: value } };
|
||||
|
||||
case HandlerEnum.TABS_SHOW:
|
||||
return {
|
||||
multiTabsSetting: {
|
||||
show: value,
|
||||
},
|
||||
};
|
||||
return { multiTabsSetting: { show: value } };
|
||||
|
||||
// ============header==================
|
||||
case HandlerEnum.HEADER_THEME:
|
||||
updateHeaderBgColor(value);
|
||||
return {
|
||||
headerBgColor: value,
|
||||
};
|
||||
return { headerSetting: { bgColor: value } };
|
||||
|
||||
case HandlerEnum.HEADER_FIXED:
|
||||
return {
|
||||
headerSetting: {
|
||||
fixed: value,
|
||||
},
|
||||
};
|
||||
return { headerSetting: { fixed: value } };
|
||||
|
||||
case HandlerEnum.HEADER_SHOW:
|
||||
return {
|
||||
headerSetting: {
|
||||
show: value,
|
||||
},
|
||||
};
|
||||
return { headerSetting: { show: value } };
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@
|
||||
import SettingDrawer from './SettingDrawer';
|
||||
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
//
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SettingBtn',
|
||||
components: { SettingOutlined, SettingDrawer },
|
||||
|
@@ -1,77 +0,0 @@
|
||||
import './index.less';
|
||||
|
||||
import { computed, defineComponent, ref, unref } from 'vue';
|
||||
|
||||
import { Layout } from 'ant-design-vue';
|
||||
import LayoutMenu from '/@/layouts/default/menu/LayoutMenu';
|
||||
|
||||
import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
|
||||
|
||||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
||||
import { useTrigger, useDragLine, useSiderEvent } from './useLayoutSider';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LayoutSideBar',
|
||||
setup() {
|
||||
const dragBarRef = ref<Nullable<HTMLDivElement>>(null);
|
||||
const sideRef = ref<Nullable<HTMLDivElement>>(null);
|
||||
|
||||
const { getCollapsed, getMenuWidth, getSplit, getTheme } = useMenuSetting();
|
||||
|
||||
const { getTriggerAttr, getTriggerSlot } = useTrigger();
|
||||
|
||||
const { renderDragLine } = useDragLine(sideRef, dragBarRef);
|
||||
|
||||
const {
|
||||
getCollapsedWidth,
|
||||
onBreakpointChange,
|
||||
onCollapseChange,
|
||||
onSiderClick,
|
||||
} = useSiderEvent();
|
||||
|
||||
const getMode = computed(() => {
|
||||
return unref(getSplit) ? MenuModeEnum.INLINE : null;
|
||||
});
|
||||
|
||||
const getSplitType = computed(() => {
|
||||
return unref(getSplit) ? MenuSplitTyeEnum.LEFT : MenuSplitTyeEnum.NONE;
|
||||
});
|
||||
|
||||
function renderDefault() {
|
||||
return (
|
||||
<>
|
||||
<LayoutMenu
|
||||
theme={unref(getTheme)}
|
||||
menuMode={unref(getMode)}
|
||||
splitType={unref(getSplitType)}
|
||||
/>
|
||||
{renderDragLine()}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<Layout.Sider
|
||||
ref={sideRef}
|
||||
class="layout-sidebar"
|
||||
breakpoint="md"
|
||||
collapsible
|
||||
width={unref(getMenuWidth)}
|
||||
collapsed={unref(getCollapsed)}
|
||||
collapsedWidth={unref(getCollapsedWidth)}
|
||||
theme={unref(getTheme)}
|
||||
onClick={onSiderClick}
|
||||
onCollapse={onCollapseChange}
|
||||
onBreakpoint={onBreakpointChange}
|
||||
{...unref(getTriggerAttr)}
|
||||
>
|
||||
{{
|
||||
...unref(getTriggerSlot),
|
||||
default: () => renderDefault(),
|
||||
}}
|
||||
</Layout.Sider>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
@@ -1,7 +1,14 @@
|
||||
@import (reference) '../../../design/index.less';
|
||||
|
||||
.layout-sidebar {
|
||||
background-size: 100% 100%;
|
||||
overflow: hidden;
|
||||
|
||||
&.fixed {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&.ant-layout-sider-dark {
|
||||
background: @sider-dark-bg-color;
|
||||
@@ -9,6 +16,7 @@
|
||||
|
||||
&:not(.ant-layout-sider-dark) {
|
||||
border-right: 1px solid @border-color-light;
|
||||
box-shadow: 2px 0 8px 0 rgba(29, 35, 41, 0.05);
|
||||
}
|
||||
|
||||
.ant-layout-sider-zero-width-trigger {
|
||||
|
150
src/layouts/default/sider/index.tsx
Normal file
150
src/layouts/default/sider/index.tsx
Normal file
@@ -0,0 +1,150 @@
|
||||
import './index.less';
|
||||
|
||||
import { computed, defineComponent, ref, unref, watch, nextTick } from 'vue';
|
||||
|
||||
import { Layout } from 'ant-design-vue';
|
||||
import LayoutMenu from '../menu';
|
||||
|
||||
import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
|
||||
|
||||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
||||
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
|
||||
import { useTrigger, useDragLine, useSiderEvent } from './useLayoutSider';
|
||||
import { useLayoutContext } from '../useLayoutContext';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LayoutSideBar',
|
||||
setup() {
|
||||
const topRef = ref(0);
|
||||
const dragBarRef = ref<ElRef>(null);
|
||||
const sideRef = ref<ElRef>(null);
|
||||
|
||||
const {
|
||||
getCollapsed,
|
||||
getMenuWidth,
|
||||
getSplit,
|
||||
getMenuTheme,
|
||||
getRealWidth,
|
||||
getMenuHidden,
|
||||
getMenuFixed,
|
||||
} = useMenuSetting();
|
||||
|
||||
const { getShowFullHeaderRef, getUnFixedAndFull } = useHeaderSetting();
|
||||
|
||||
const injectValue = useLayoutContext();
|
||||
|
||||
const { getTriggerAttr, getTriggerSlot } = useTrigger();
|
||||
|
||||
const { renderDragLine } = useDragLine(sideRef, dragBarRef);
|
||||
|
||||
const {
|
||||
getCollapsedWidth,
|
||||
onBreakpointChange,
|
||||
onCollapseChange,
|
||||
onSiderClick,
|
||||
} = useSiderEvent();
|
||||
|
||||
const getMode = computed(() => {
|
||||
return unref(getSplit) ? MenuModeEnum.INLINE : null;
|
||||
});
|
||||
|
||||
const getSplitType = computed(() => {
|
||||
return unref(getSplit) ? MenuSplitTyeEnum.LEFT : MenuSplitTyeEnum.NONE;
|
||||
});
|
||||
|
||||
const showClassSideBarRef = computed(() => {
|
||||
return unref(getSplit) ? unref(getMenuHidden) : true;
|
||||
});
|
||||
|
||||
const getSiderClass = computed(() => {
|
||||
return {
|
||||
'layout-sidebar': true,
|
||||
fixed: unref(getMenuFixed),
|
||||
hidden: !unref(showClassSideBarRef),
|
||||
};
|
||||
});
|
||||
|
||||
const getSiderStyle = computed(() => {
|
||||
const top = `${unref(topRef)}px`;
|
||||
if (!unref(getMenuFixed)) {
|
||||
return { top };
|
||||
}
|
||||
return {
|
||||
top,
|
||||
height: `calc(100% - ${top})`,
|
||||
};
|
||||
});
|
||||
|
||||
watch(
|
||||
() => getShowFullHeaderRef.value,
|
||||
() => {
|
||||
topRef.value = 0;
|
||||
if (unref(getUnFixedAndFull)) return;
|
||||
nextTick(() => {
|
||||
const fullHeaderEl = unref(injectValue.fullHeaderRef)?.$el;
|
||||
if (!fullHeaderEl) return;
|
||||
topRef.value = fullHeaderEl.offsetHeight;
|
||||
});
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
const getHiddenDomStyle = computed(() => {
|
||||
const width = `${unref(getRealWidth)}px`;
|
||||
return {
|
||||
width: width,
|
||||
overflow: 'hidden',
|
||||
flex: `0 0 ${width}`,
|
||||
'max-width': width,
|
||||
'min-width': width,
|
||||
transition: 'all 0.2s',
|
||||
};
|
||||
});
|
||||
|
||||
function renderDefault() {
|
||||
return (
|
||||
<>
|
||||
<LayoutMenu
|
||||
theme={unref(getMenuTheme)}
|
||||
menuMode={unref(getMode)}
|
||||
splitType={unref(getSplitType)}
|
||||
/>
|
||||
{renderDragLine()}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<>
|
||||
{unref(getMenuFixed) && (
|
||||
<div style={unref(getHiddenDomStyle)} class={{ hidden: !unref(showClassSideBarRef) }} />
|
||||
)}
|
||||
|
||||
<Layout.Sider
|
||||
ref={sideRef}
|
||||
breakpoint="md"
|
||||
collapsible
|
||||
class={unref(getSiderClass)}
|
||||
style={unref(getSiderStyle)}
|
||||
width={unref(getMenuWidth)}
|
||||
collapsed={unref(getCollapsed)}
|
||||
collapsedWidth={unref(getCollapsedWidth)}
|
||||
theme={unref(getMenuTheme)}
|
||||
onClick={onSiderClick}
|
||||
onCollapse={onCollapseChange}
|
||||
onBreakpoint={onBreakpointChange}
|
||||
{...unref(getTriggerAttr)}
|
||||
>
|
||||
{{
|
||||
...unref(getTriggerSlot),
|
||||
default: () => renderDefault(),
|
||||
}}
|
||||
</Layout.Sider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
@@ -16,7 +16,7 @@ export function useSiderEvent() {
|
||||
const brokenRef = ref(false);
|
||||
const collapseRef = ref(true);
|
||||
|
||||
const { setMenuSetting, getCollapsed, getMiniWidthNumber, getShow } = useMenuSetting();
|
||||
const { setMenuSetting, getCollapsed, getMiniWidthNumber, getShowMenu } = useMenuSetting();
|
||||
|
||||
const getCollapsedWidth = computed(() => {
|
||||
return unref(brokenRef) ? 0 : unref(getMiniWidthNumber);
|
||||
@@ -38,7 +38,7 @@ export function useSiderEvent() {
|
||||
|
||||
function onSiderClick(e: ChangeEvent) {
|
||||
if (!e || !e.target || e.target.className !== 'basic-menu__content') return;
|
||||
if (!unref(getCollapsed) || !unref(getShow)) return;
|
||||
if (!unref(getCollapsed) || !unref(getShowMenu)) return;
|
||||
setMenuSetting({ collapsed: false });
|
||||
}
|
||||
return { getCollapsedWidth, onCollapseChange, onBreakpointChange, onSiderClick };
|
||||
|
16
src/layouts/default/useLayoutContext.ts
Normal file
16
src/layouts/default/useLayoutContext.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { InjectionKey, Ref } from 'vue';
|
||||
import { createContext, useContext } from '/@/hooks/core/useContext';
|
||||
|
||||
export interface LayoutContextProps {
|
||||
fullHeaderRef: Ref<ComponentRef>;
|
||||
}
|
||||
|
||||
const layoutContextInjectKey: InjectionKey<LayoutContextProps> = Symbol();
|
||||
|
||||
export function createLayoutContext(context: LayoutContextProps) {
|
||||
return createContext<LayoutContextProps>(context, layoutContextInjectKey);
|
||||
}
|
||||
|
||||
export function useLayoutContext() {
|
||||
return useContext<LayoutContextProps>(layoutContextInjectKey);
|
||||
}
|
Reference in New Issue
Block a user