perf: perf menu

This commit is contained in:
vben 2020-12-07 22:18:57 +08:00
parent 74e62cbc71
commit 88f4a3f02a
24 changed files with 634 additions and 656 deletions

View File

@ -8,10 +8,10 @@
:dropMenuList="localeList" :dropMenuList="localeList"
:selectedKeys="selectedKeys" :selectedKeys="selectedKeys"
@menuEvent="handleMenuEvent" @menuEvent="handleMenuEvent"
overlayClassName="app-locale-picker-overlay" :overlayClassName="`${prefixCls}-overlay`"
> >
<span class="app-local-picker"> <span :class="prefixCls">
<GlobalOutlined class="app-local-picker__icon" /> <GlobalOutlined :class="`${prefixCls}__icon`" />
<span v-if="showText">{{ getLangText }}</span> <span v-if="showText">{{ getLangText }}</span>
</span> </span>
</Dropdown> </Dropdown>
@ -28,6 +28,7 @@
import { LocaleType } from '/@/locales/types'; import { LocaleType } from '/@/locales/types';
import { propTypes } from '/@/utils/propTypes'; import { propTypes } from '/@/utils/propTypes';
import { useDesign } from '/@/hooks/web/useDesign';
export default defineComponent({ export default defineComponent({
name: 'AppLocalPicker', name: 'AppLocalPicker',
@ -39,9 +40,12 @@
reload: propTypes.bool, reload: propTypes.bool,
}, },
setup(props) { setup(props) {
const { localeList } = useLocaleSetting();
const selectedKeys = ref<string[]>([]); const selectedKeys = ref<string[]>([]);
const { prefixCls } = useDesign('app-locale-picker');
const { localeList } = useLocaleSetting();
const { changeLocale, getLang } = useLocale(); const { changeLocale, getLang } = useLocale();
const getLangText = computed(() => { const getLangText = computed(() => {
@ -64,19 +68,22 @@
toggleLocale(menu.event as string); toggleLocale(menu.event as string);
} }
return { localeList, handleMenuEvent, selectedKeys, getLangText }; return { localeList, handleMenuEvent, selectedKeys, getLangText, prefixCls };
}, },
}); });
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
:global(.app-locale-picker-overlay) { @import (reference) '../../../design/index.less';
@prefix-cls: ~'@{namespace}-app-locale-picker';
:global(.@{prefix-cls}-overlay) {
.ant-dropdown-menu-item { .ant-dropdown-menu-item {
min-width: 160px; min-width: 160px;
} }
} }
.app-local-picker { .@{prefix-cls} {
display: flex; display: flex;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;

View File

@ -20,15 +20,17 @@
expand: propTypes.bool, expand: propTypes.bool,
top: propTypes.bool, top: propTypes.bool,
bottom: propTypes.bool, bottom: propTypes.bool,
inset: propTypes.bool,
}, },
setup(props) { setup(props) {
const getClass = computed(() => { const getClass = computed(() => {
const { expand, top, bottom } = props; const { expand, top, bottom, inset } = props;
return [ return [
'base-arrow', 'base-arrow',
{ {
'base-arrow__active': expand, 'base-arrow__active': expand,
top, top,
inset,
bottom, bottom,
}, },
]; ];
@ -47,6 +49,10 @@
transition: all 0.3s ease 0.1s; transition: all 0.3s ease 0.1s;
transform-origin: center center; transform-origin: center center;
&.inset {
line-height: 0px;
}
&__active { &__active {
transform: rotate(90deg); transform: rotate(90deg);
} }

View File

@ -9,14 +9,15 @@ import {
unref, unref,
reactive, reactive,
watch, watch,
onMounted,
ref,
toRefs, toRefs,
ComputedRef, ComputedRef,
ref,
CSSProperties,
} from 'vue'; } from 'vue';
import { Menu } from 'ant-design-vue'; import { Menu } from 'ant-design-vue';
import MenuContent from './MenuContent'; import MenuContent from './MenuContent';
// import { ScrollContainer } from '/@/components/Container'; // import { ScrollContainer } from '/@/components/Container';
// import { BasicArrow } from '/@/components/Basic';
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum'; import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
import { ThemeEnum } from '/@/enums/appEnum'; import { ThemeEnum } from '/@/enums/appEnum';
@ -29,18 +30,20 @@ import { useRouter } from 'vue-router';
import { isFunction } from '/@/utils/is'; import { isFunction } from '/@/utils/is';
import { getSlot } from '/@/utils/helper/tsxHelper'; import { getSlot } from '/@/utils/helper/tsxHelper';
import { menuHasChildren } from './helper'; import { menuHasChildren } from './helper';
import { getCurrentParentPath } from '/@/router/menus'; import { getCurrentParentPath } from '/@/router/menus';
import { basicProps } from './props'; import { basicProps } from './props';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { REDIRECT_NAME } from '/@/router/constant'; import { REDIRECT_NAME } from '/@/router/constant';
import { tabStore } from '/@/store/modules/tab';
import { useDesign } from '/@/hooks/web/useDesign';
export default defineComponent({ export default defineComponent({
name: 'BasicMenu', name: 'BasicMenu',
props: basicProps, props: basicProps,
emits: ['menuClick'], emits: ['menuClick'],
setup(props, { slots, emit }) { setup(props, { slots, emit }) {
const currentParentPath = ref(''); const currentParentPath = ref('');
const isClickGo = ref(false);
const menuState = reactive<MenuState>({ const menuState = reactive<MenuState>({
defaultSelectedKeys: [], defaultSelectedKeys: [],
@ -51,143 +54,164 @@ export default defineComponent({
collapsedOpenKeys: [], collapsedOpenKeys: [],
}); });
const { getCollapsed } = useMenuSetting(); const { prefixCls } = useDesign('basic-menu');
const { items, mode, accordion } = toRefs(props);
const { getCollapsed, getIsHorizontal, getTopMenuAlign, getSplit } = useMenuSetting();
const { currentRoute } = useRouter(); const { currentRoute } = useRouter();
const { items, flatItems, mode, accordion } = toRefs(props); const { handleOpenChange, setOpenKeys, getOpenKeys } = useOpenKeys(
const { handleOpenChange, resetKeys, setOpenKeys } = useOpenKeys(
menuState, menuState,
items, items,
flatItems,
mode, mode,
accordion accordion
); );
const getOpenKeys = computed(() => { const getMenuClass = computed(() => {
return unref(getCollapsed) ? menuState.collapsedOpenKeys : menuState.openKeys;
});
// menu外层样式
const getMenuWrapStyle = computed((): any => {
const { showLogo } = props;
let offset = 0;
if (showLogo) {
offset += 46;
}
return {
height: `calc(100% - ${offset}px)`,
position: 'relative',
overflowY: 'auto',
};
});
// 是否透明化左侧一级菜单
const transparentMenuClass = computed(() => {
const { type } = props; const { type } = props;
const { mode } = menuState; const { mode } = menuState;
const cls: string[] = []; return [
if ( prefixCls,
(type === MenuTypeEnum.TOP_MENU && mode === MenuModeEnum.HORIZONTAL) || `justify-${unref(getTopMenuAlign)}`,
props.appendClass {
) { [`${prefixCls}--hide-title`]: !unref(showTitle),
cls.push('basic-menu__sidebar-hor'); [`${prefixCls}--collapsed-show-title`]: props.collapsedShowTitle,
} [`${prefixCls}__second`]:
!props.isHorizontal && appStore.getProjectConfig.menuSetting.split,
if (!props.isHorizontal && appStore.getProjectConfig.menuSetting.split) { [`${prefixCls}__sidebar-hor`]:
cls.push('basic-menu__second'); type === MenuTypeEnum.TOP_MENU && mode === MenuModeEnum.HORIZONTAL,
} },
return cls; ];
}); });
const showTitle = computed(() => props.collapsedShowTitle && unref(getCollapsed)); const showTitle = computed(() => props.collapsedShowTitle && unref(getCollapsed));
const getInlineCollapseOptions = computed(() => {
const isInline = props.mode === MenuModeEnum.INLINE;
const inlineCollapseOptions: { inlineCollapsed?: boolean } = {};
if (isInline) {
inlineCollapseOptions.inlineCollapsed = unref(getCollapsed);
}
return inlineCollapseOptions;
});
const getWrapperStyle = computed(
(): CSSProperties => {
const isHorizontal = unref(getIsHorizontal);
return {
height: isHorizontal
? `calc(100% + 1px)`
: `calc(100% - ${props.showLogo ? '48px' : '0'})`,
overflowY: isHorizontal ? 'hidden' : 'auto',
};
}
);
watch( watch(
() => currentRoute.value.name, () => tabStore.getCurrentTab,
(name: string) => { () => {
if (name === REDIRECT_NAME) return; if (unref(currentRoute).name === REDIRECT_NAME) return;
handleMenuChange(); handleMenuChange();
props.isHorizontal && appStore.getProjectConfig.menuSetting.split && getParentPath(); unref(getSplit) && getParentPath();
} }
); );
watch( watch(
() => props.items, () => props.items,
() => { () => {
if (props.items) {
handleMenuChange(); handleMenuChange();
}
}, },
{ {
immediate: true, immediate: true,
} }
); );
getParentPath();
async function getParentPath() { async function getParentPath() {
const { appendClass } = props; const { appendClass } = props;
if (!appendClass) return ''; if (!appendClass) return '';
const parentPath = await getCurrentParentPath(unref(currentRoute).path); const parentPath = await getCurrentParentPath(unref(currentRoute).path);
currentParentPath.value = parentPath; currentParentPath.value = parentPath;
} }
async function handleMenuClick(menu: MenuType) { async function handleMenuClick({ key, keyPath }: { key: string; keyPath: string[] }) {
const { beforeClickFn } = props; const { beforeClickFn } = props;
if (beforeClickFn && isFunction(beforeClickFn)) { if (beforeClickFn && isFunction(beforeClickFn)) {
const flag = await beforeClickFn(menu); const flag = await beforeClickFn(key);
if (!flag) return; if (!flag) return;
} }
emit('menuClick', menu); emit('menuClick', key);
const { path } = menu;
menuState.selectedKeys = [path]; isClickGo.value = true;
menuState.openKeys = keyPath;
menuState.selectedKeys = [key];
} }
function handleMenuChange() { function handleMenuChange() {
const { flatItems } = props; if (unref(isClickGo)) {
if (!unref(flatItems) || flatItems.length === 0) return; isClickGo.value = false;
const findMenu = flatItems.find((menu) => menu.path === unref(currentRoute).path); return;
if (findMenu) { }
const path = unref(currentRoute).path;
if (menuState.mode !== MenuModeEnum.HORIZONTAL) { if (menuState.mode !== MenuModeEnum.HORIZONTAL) {
setOpenKeys(findMenu); setOpenKeys(path);
}
menuState.selectedKeys = [findMenu.path];
} else {
resetKeys();
} }
menuState.selectedKeys = [path];
} }
// render menu item // function renderExpandIcon({ key }: { key: string }) {
function renderMenuItem(menuList?: MenuType[], index = 1) { // const isOpen = getOpenKeys.value.includes(key);
if (!menuList) return; // const collapsed = unref(getCollapsed);
// return (
// <BasicArrow
// expand={isOpen}
// bottom
// inset
// class={[
// `${prefixCls}__expand-icon`,
// {
// [`${prefixCls}__expand-icon--collapsed`]: collapsed,
// },
// ]}
// />
// );
// }
function renderItem(menu: MenuType, level = 1) {
return !menuHasChildren(menu) ? renderMenuItem(menu, level) : renderSubMenu(menu, level);
}
function renderMenuItem(menu: MenuType, level: number) {
const { appendClass } = props; const { appendClass } = props;
const levelCls = `basic-menu-item__level${index} ${menuState.theme} `;
return menuList.map((menu) => {
if (!menu) {
return null;
}
const isAppendActiveCls = const isAppendActiveCls =
appendClass && index === 1 && menu.path === unref(currentParentPath); appendClass && level === 1 && menu.path === unref(currentParentPath);
// 没有子节点 const levelCls = [
if (!menuHasChildren(menu)) { `${prefixCls}-item__level${level}`,
` ${menuState.theme} `,
{
'top-active-menu': isAppendActiveCls,
},
];
return ( return (
<Menu.Item <Menu.Item key={menu.path} class={levelCls}>
key={menu.path}
class={`${levelCls}${isAppendActiveCls ? ' top-active-menu ' : ''}`}
onClick={handleMenuClick.bind(null, menu)}
>
{() => [ {() => [
<MenuContent <MenuContent
item={menu} item={menu}
level={index}
isHorizontal={props.isHorizontal}
showTitle={unref(showTitle)} showTitle={unref(showTitle)}
isHorizontal={props.isHorizontal}
/>, />,
]} ]}
</Menu.Item> </Menu.Item>
); );
} }
function renderSubMenu(menu: MenuType, level: number) {
const levelCls = `${prefixCls}-item__level${level} ${menuState.theme} `;
return ( return (
<Menu.SubMenu key={menu.path} class={levelCls}> <Menu.SubMenu key={menu.path} class={levelCls}>
{{ {{
@ -195,26 +219,19 @@ export default defineComponent({
<MenuContent <MenuContent
showTitle={unref(showTitle)} showTitle={unref(showTitle)}
item={menu} item={menu}
level={index}
isHorizontal={props.isHorizontal} isHorizontal={props.isHorizontal}
/>, />,
], ],
default: () => renderMenuItem(menu.children, index + 1), // expandIcon: renderExpandIcon,
default: () => (menu.children || []).map((item) => renderItem(item, level + 1)),
}} }}
</Menu.SubMenu> </Menu.SubMenu>
); );
});
} }
function renderMenu() { function renderMenu() {
const isInline = props.mode === MenuModeEnum.INLINE;
const { selectedKeys, defaultSelectedKeys, mode, theme } = menuState; const { selectedKeys, defaultSelectedKeys, mode, theme } = menuState;
const inlineCollapsedObj = isInline
? {
inlineCollapsed: unref(getCollapsed),
}
: {};
return ( return (
<Menu <Menu
selectedKeys={selectedKeys} selectedKeys={selectedKeys}
@ -224,36 +241,25 @@ export default defineComponent({
inlineIndent={props.inlineIndent} inlineIndent={props.inlineIndent}
theme={unref(theme)} theme={unref(theme)}
onOpenChange={handleOpenChange} onOpenChange={handleOpenChange}
class={[ class={unref(getMenuClass)}
'basic-menu', onClick={handleMenuClick}
props.collapsedShowTitle && 'collapsed-show-title', {...unref(getInlineCollapseOptions)}
...unref(transparentMenuClass),
]}
{...inlineCollapsedObj}
> >
{{ {{
default: () => renderMenuItem(props.items, 1), default: () => unref(items).map((item) => renderItem(item)),
}} }}
</Menu> </Menu>
); );
} }
onMounted(async () => {
getParentPath();
});
return () => { return () => {
const { mode } = props; return (
return mode === MenuModeEnum.HORIZONTAL ? ( <>
renderMenu() {!unref(getIsHorizontal) && getSlot(slots, 'header')}
) : ( <div class={`${prefixCls}-wrapper`} style={unref(getWrapperStyle)}>
<section class={[`basic-menu-wrap`, !unref(showTitle) && 'hide-title']}>
{getSlot(slots, 'header')}
<section style={unref(getMenuWrapStyle)} class="basic-menu__content">
{renderMenu()} {renderMenu()}
</section> </div>
</section> </>
); );
}; };
}, },

View File

@ -1,11 +0,0 @@
<template>
<div> </div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
setup() {
return {};
},
});
</script>

View File

@ -1,9 +1,13 @@
import type { Menu as MenuType } from '/@/router/types'; import type { Menu as MenuType } from '/@/router/types';
import { computed, PropType, unref } from 'vue'; import type { PropType } from 'vue';
import { computed, unref } from 'vue';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import Icon from '/@/components/Icon/index'; import Icon from '/@/components/Icon/index';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { useDesign } from '/@/hooks/web/useDesign';
const { t } = useI18n();
export default defineComponent({ export default defineComponent({
name: 'MenuContent', name: 'MenuContent',
@ -12,12 +16,10 @@ export default defineComponent({
type: Object as PropType<MenuType>, type: Object as PropType<MenuType>,
default: null, default: null,
}, },
showTitle: { showTitle: {
type: Boolean as PropType<boolean>, type: Boolean as PropType<boolean>,
default: true, default: true,
}, },
level: { level: {
type: Number as PropType<number>, type: Number as PropType<number>,
default: 0, default: 0,
@ -28,13 +30,32 @@ export default defineComponent({
}, },
}, },
setup(props) { setup(props) {
const { t } = useI18n(); const { prefixCls } = useDesign('basic-menu');
const getI18nName = computed(() => t(props.item?.name)); const getI18nName = computed(() => t(props.item?.name));
const getTagClass = computed(() => {
const { item } = props;
const { tag = {} } = item || {};
const { dot, type = 'error' } = tag;
return [
`${prefixCls}__tag`,
type,
{
dot,
},
];
});
const getNameClass = computed(() => {
const { showTitle } = props;
return { [`${prefixCls}--show-title`]: showTitle, [`${prefixCls}__name`]: !showTitle };
});
/** /**
* @description: * @description:
*/ */
function renderIcon(icon: string) { function renderIcon(icon?: string) {
return icon ? <Icon icon={icon} size={18} class="menu-item-icon" /> : null; return icon ? <Icon icon={icon} size={18} class="menu-item-icon" /> : null;
} }
@ -45,36 +66,30 @@ export default defineComponent({
const { tag } = item; const { tag } = item;
if (!tag) return null; if (!tag) return null;
const { dot, content, type = 'error' } = tag; const { dot, content } = tag;
if (!dot && !content) return null; if (!dot && !content) return null;
const cls = ['basic-menu__tag'];
dot && cls.push('dot'); return <span class={unref(getTagClass)}>{dot ? '' : content}</span>;
type && cls.push(type);
return <span class={cls}>{dot ? '' : content}</span>;
} }
return () => { return () => {
if (!props.item) { const { item } = props;
if (!item) {
return null; return null;
} }
const { showTitle } = props; const { icon } = item;
const { icon } = props.item;
const name = unref(getI18nName); const name = unref(getI18nName);
const cls = showTitle ? ['show-title'] : ['basic-menu__name'];
return ( return (
<> <span class={`${prefixCls}__content-wrapper`}>
{renderIcon(icon!)} {renderIcon(icon)}
{ {
<span class={[cls]}> <span class={unref(getNameClass)}>
{name} {name}
{renderTag()} {renderTag()}
</span> </span>
} }
</> </span>
); );
}; };
}, },

View File

@ -1,5 +1,7 @@
@import (reference) '../../../design/index.less'; @import (reference) '../../../design/index.less';
@basic-menu-prefix-cls: ~'@{namespace}-basic-menu';
.active-style() { .active-style() {
color: @white; color: @white;
background: linear-gradient( background: linear-gradient(
@ -16,21 +18,48 @@
} }
} }
.basic-menu { .@{basic-menu-prefix-cls} {
width: 100%; width: 100%;
&-wrap { // &__expand-icon {
height: 100%; // position: absolute;
} // top: calc(50% - 6px);
// right: 16px;
// width: 10px;
// transform-origin: none;
// span[role='img'] {
// margin-right: 0;
// font-size: 11px;
// }
// &--collapsed {
// opacity: 0;
// }
// }
// collapsed show title start // collapsed show title start
.show-title { &--show-title {
max-width: unset !important; max-width: unset !important;
opacity: 1 !important; opacity: 1 !important;
} }
&.collapsed-show-title.ant-menu-inline-collapsed { &--hide-title {
.basic-menu-item__level1 { &.ant-menu-inline-collapsed > .ant-menu-item,
&.ant-menu-inline-collapsed > .ant-menu-item-group > .ant-menu-item-group-list > .ant-menu-item,
&.ant-menu-inline-collapsed
> .ant-menu-item-group
> .ant-menu-item-group-list
> .ant-menu-submenu
> .ant-menu-submenu-title,
&.ant-menu-inline-collapsed > .ant-menu-submenu > .ant-menu-submenu-title {
padding-right: 20px !important;
padding-left: 20px !important;
}
}
&--collapsed-show-title.ant-menu-inline-collapsed {
.@{basic-menu-prefix-cls}-item__level1 {
padding: 2px 0; padding: 2px 0;
} }
@ -47,14 +76,23 @@
& > li > .ant-menu-submenu-title { & > li > .ant-menu-submenu-title {
line-height: 24px; line-height: 24px;
} }
.@{basic-menu-prefix-cls}__content-wrapper {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.@{basic-menu-prefix-cls}--show-title {
line-height: 30px;
}
}
} }
.ant-menu-item { // .ant-menu-item {
transition: unset; // transition: unset;
} // }
// scrollbar -s tart // scrollbar -s tart
&__content { &-wrapper {
/* 滚动槽 */ /* 滚动槽 */
&::-webkit-scrollbar { &::-webkit-scrollbar {
width: 5px; width: 5px;
@ -75,8 +113,20 @@
background: @border-color-dark; background: @border-color-dark;
} }
} }
// scrollbar end // scrollbar end
&-item__level1.light {
&.top-active-menu {
top: 0 !important;
}
&.top-active-menu:not(.ant-menu-item-selected) {
color: @primary-color;
border-bottom: 3px solid @primary-color;
}
}
&__sidebar-hor { &__sidebar-hor {
// overflow: hidden; // overflow: hidden;
@ -85,20 +135,13 @@
border: 0; border: 0;
align-items: center; align-items: center;
.basic-menu-item__level1 { .@{basic-menu-prefix-cls}-item__level1 {
margin-right: 2px; margin-right: 2px;
} }
&.ant-menu-light { &.ant-menu-light {
.basic-menu-item__level1 {
&.top-active-menu {
color: @primary-color;
border-bottom: 3px solid @primary-color;
}
}
.ant-menu-item { .ant-menu-item {
&.basic-menu-item__level1 { &.@{basic-menu-prefix-cls}-item__level1 {
height: @header-height; height: @header-height;
line-height: @header-height; line-height: @header-height;
} }
@ -155,7 +198,7 @@
background: @top-menu-active-bg-color; background: @top-menu-active-bg-color;
} }
.basic-menu-item__level1 { .@{basic-menu-prefix-cls}-item__level1 {
background: transparent; background: transparent;
&.top-active-menu { &.top-active-menu {
@ -172,7 +215,7 @@
.ant-menu-item, .ant-menu-item,
.ant-menu-submenu { .ant-menu-submenu {
&.basic-menu-item__level1, &.@{basic-menu-prefix-cls}-item__level1,
.ant-menu-submenu-title { .ant-menu-submenu-title {
height: @header-height; height: @header-height;
line-height: @header-height; line-height: @header-height;
@ -182,17 +225,17 @@
} }
} }
&:not(.basic-menu__sidebar-hor).ant-menu-inline-collapsed { &:not(.@{basic-menu-prefix-cls}__sidebar-hor).ant-menu-inline-collapsed {
.basic-menu-item__level1 { .@{basic-menu-prefix-cls}-item__level1 {
> div { > div {
align-items: center; align-items: center;
} }
} }
} }
&.ant-menu-dark:not(.basic-menu__sidebar-hor):not(.basic-menu__second) { &.ant-menu-dark:not(.@{basic-menu-prefix-cls}__sidebar-hor):not(.@{basic-menu-prefix-cls}__second) {
// Reset menu item row height // Reset menu item row height
.ant-menu-item:not(.basic-menu-item__level1), .ant-menu-item:not(.@{basic-menu-prefix-cls}-item__level1),
.ant-menu-sub.ant-menu-inline > .ant-menu-item, .ant-menu-sub.ant-menu-inline > .ant-menu-item,
.ant-menu-sub.ant-menu-inline > .ant-menu-submenu > .ant-menu-submenu-title { .ant-menu-sub.ant-menu-inline > .ant-menu-submenu > .ant-menu-submenu-title {
height: @app-menu-item-height; height: @app-menu-item-height;
@ -200,28 +243,28 @@
line-height: @app-menu-item-height; line-height: @app-menu-item-height;
} }
.ant-menu-item.basic-menu-item__level1 { .ant-menu-item.@{basic-menu-prefix-cls}-item__level1 {
height: @app-menu-item-height; height: @app-menu-item-height;
line-height: @app-menu-item-height; line-height: @app-menu-item-height;
} }
} }
// 层级样式 // 层级样式
&.ant-menu-dark:not(.basic-menu__sidebar-hor) { &.ant-menu-dark:not(.@{basic-menu-prefix-cls}__sidebar-hor) {
overflow-x: hidden; overflow: hidden;
background: @sider-dark-bg-color; background: @sider-dark-bg-color;
.active-menu-style(); .active-menu-style();
.menu-item-icon.app-iconify { // .menu-item-icon.app-iconify {
display: inline-block !important; // display: inline-block !important;
} // }
.ant-menu-item.ant-menu-item-selected.basic-menu-menu-item__level1, .ant-menu-item.ant-menu-item-selected.@{basic-menu-prefix-cls}-menu-item__level1,
.ant-menu-submenu-selected.basic-menu-menu-item__level1 { .ant-menu-submenu-selected.@{basic-menu-prefix-cls}-menu-item__level1 {
color: @white; color: @white;
} }
.basic-menu-item__level1 { .@{basic-menu-prefix-cls}-item__level1 {
background-color: @sider-dark-bg-color; background-color: @sider-dark-bg-color;
> .ant-menu-sub > li { > .ant-menu-sub > li {
@ -229,12 +272,12 @@
} }
} }
.basic-menu-item__level2:not(.ant-menu-item-selected), .@{basic-menu-prefix-cls}-item__level2:not(.ant-menu-item-selected),
.ant-menu-sub { .ant-menu-sub {
background-color: @sider-dark-lighten-1-bg-color; background-color: @sider-dark-lighten-1-bg-color;
} }
.basic-menu-item__level3:not(.ant-menu-item-selected) { .@{basic-menu-prefix-cls}-item__level3:not(.ant-menu-item-selected) {
background-color: @sider-dark-lighten-2-bg-color; background-color: @sider-dark-lighten-2-bg-color;
.ant-menu-item { .ant-menu-item {
@ -257,35 +300,26 @@
} }
} }
&.ant-menu-light:not(.basic-menu__sidebar-hor) { &.ant-menu-light:not(.@{basic-menu-prefix-cls}__sidebar-hor) {
overflow-x: hidden; overflow: hidden;
border-right: none; border-right: none;
.menu-item-icon.app-iconify { // .menu-item-icon.app-iconify {
display: inline-block !important; // display: inline-block !important;
}
// .ant-menu-item-selected {
// background: fade(@primary-color, 18%);
// } // }
.ant-menu-item.ant-menu-item-selected.basic-menu-menu-item__level1, .ant-menu-item.ant-menu-item-selected.@{basic-menu-prefix-cls}-menu-item__level1,
.ant-menu-submenu-selected.basic-menu-menu-item__level1 { .ant-menu-submenu-selected.@{basic-menu-prefix-cls}-menu-item__level1 {
color: @primary-color; color: @primary-color;
} }
} }
// 关键字的颜色
&__keyword {
color: lighten(@primary-color, 20%);
}
// 激活的子菜单样式 // 激活的子菜单样式
.ant-menu-item.ant-menu-item-selected { .ant-menu-item.ant-menu-item-selected {
position: relative; position: relative;
} }
&.basic-menu__second.ant-menu-inline-collapsed:not(.basic-menu__sidebar-hor) { &.@{basic-menu-prefix-cls}__second.ant-menu-inline-collapsed:not(.@{basic-menu-prefix-cls}__sidebar-hor) {
// Reset menu item row height // Reset menu item row height
.ant-menu-item, .ant-menu-item,
.ant-menu-sub.ant-menu-inline > .ant-menu-item, .ant-menu-sub.ant-menu-inline > .ant-menu-item,
@ -298,75 +332,10 @@
align-items: center; align-items: center;
} }
} }
} .@{basic-menu-prefix-cls}__tag {
position: absolute;
// 触发器样式 top: calc(50% - 8px);
.ant-layout-sider { right: 30px;
&-dark {
.ant-layout-sider-trigger {
color: darken(@white, 25%);
background: @trigger-dark-bg-color;
&:hover {
color: @white;
background: @trigger-dark-hover-bg-color;
}
}
}
&-light {
.ant-layout-sider-trigger {
color: @text-color-base;
border-top: 1px solid @border-color-light;
}
}
}
.ant-menu-dark {
&.ant-menu-submenu-popup {
> ul {
background: @sider-dark-bg-color;
}
.active-menu-style();
}
}
.hide-title {
.ant-menu-inline-collapsed > .ant-menu-item,
.ant-menu-inline-collapsed > .ant-menu-item-group > .ant-menu-item-group-list > .ant-menu-item,
.ant-menu-inline-collapsed
> .ant-menu-item-group
> .ant-menu-item-group-list
> .ant-menu-submenu
> .ant-menu-submenu-title,
.ant-menu-inline-collapsed > .ant-menu-submenu > .ant-menu-submenu-title {
padding-right: 20px !important;
padding-left: 20px !important;
}
.ant-menu-inline-collapsed {
.basic-menu-item__level1 {
display: flex;
justify-content: center;
}
}
}
// collapsed show title end
.ant-menu-item,
.ant-menu-submenu-title {
> .basic-menu__name {
width: 100%;
.basic-menu__tag {
float: right;
margin-top: @app-menu-item-height / 2;
transform: translate(0%, -50%);
}
}
}
.basic-menu__tag {
display: inline-block; display: inline-block;
padding: 2px 4px; padding: 2px 4px;
margin-right: 4px; margin-right: 4px;
@ -376,11 +345,10 @@
border-radius: 2px; border-radius: 2px;
&.dot { &.dot {
top: calc(50% - 4px);
width: 8px; width: 8px;
height: 8px; height: 8px;
padding: 0; padding: 0;
margin-top: 21px !important;
margin-bottom: 2px;
border-radius: 50%; border-radius: 50%;
} }
@ -399,4 +367,29 @@
&.warn { &.warn {
background: @warning-color; background: @warning-color;
} }
}
}
.ant-menu-dark {
&.ant-menu-submenu-popup {
> ul {
background: @sider-dark-bg-color;
}
.active-menu-style();
}
}
// collapsed show title end
.ant-menu-item,
.ant-menu-submenu-title {
> .@{basic-menu-prefix-cls}__name {
width: 100%;
.@{basic-menu-prefix-cls}__tag {
float: right;
margin-top: @app-menu-item-height / 2;
transform: translate(0%, -50%);
}
}
} }

View File

@ -8,14 +8,11 @@ export const basicProps = {
type: Array as PropType<Menu[]>, type: Array as PropType<Menu[]>,
default: () => [], default: () => [],
}, },
flatItems: {
type: Array as PropType<Menu[]>,
default: () => [],
},
appendClass: { appendClass: {
type: Boolean as PropType<boolean>, type: Boolean as PropType<boolean>,
default: false, default: false,
}, },
collapsedShowTitle: { collapsedShowTitle: {
type: Boolean as PropType<boolean>, type: Boolean as PropType<boolean>,
default: false, default: false,
@ -31,6 +28,10 @@ export const basicProps = {
type: String as PropType<MenuModeEnum>, type: String as PropType<MenuModeEnum>,
default: MenuModeEnum.INLINE, default: MenuModeEnum.INLINE,
}, },
showLogo: {
type: Boolean as PropType<boolean>,
default: false,
},
type: { type: {
type: String as PropType<MenuTypeEnum>, type: String as PropType<MenuTypeEnum>,
default: MenuTypeEnum.MIX, default: MenuTypeEnum.MIX,
@ -39,10 +40,6 @@ export const basicProps = {
type: String as PropType<string>, type: String as PropType<string>,
default: ThemeEnum.DARK, default: ThemeEnum.DARK,
}, },
showLogo: {
type: Boolean as PropType<boolean>,
default: false,
},
inlineCollapsed: { inlineCollapsed: {
type: Boolean as PropType<boolean>, type: Boolean as PropType<boolean>,
default: false, default: false,
@ -57,7 +54,6 @@ export const basicProps = {
default: true, default: true,
}, },
beforeClickFn: { beforeClickFn: {
type: Function as PropType<Fn>, type: Function as PropType<(key: string) => Promise<boolean>>,
default: null,
}, },
}; };

View File

@ -1,34 +1,35 @@
import { MenuModeEnum } from '/@/enums/menuEnum'; import { MenuModeEnum } from '/@/enums/menuEnum';
import type { Menu as MenuType } from '/@/router/types'; import type { Menu as MenuType } from '/@/router/types';
import type { MenuState } from './types'; import type { MenuState } from './types';
import type { Ref } from 'vue';
import { computed, Ref, toRaw } from 'vue';
import { unref } from 'vue'; import { unref } from 'vue';
import { getAllParentPath } from '/@/router/helper/menuHelper';
import { es6Unique } from '/@/utils'; import { es6Unique } from '/@/utils';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { getAllParentPath } from '/@/router/helper/menuHelper';
export function useOpenKeys( export function useOpenKeys(
menuState: MenuState, menuState: MenuState,
menus: Ref<MenuType[]>, menus: Ref<MenuType[]>,
flatMenusRef: Ref<MenuType[]>,
mode: Ref<MenuModeEnum>, mode: Ref<MenuModeEnum>,
accordion: Ref<boolean> accordion: Ref<boolean>
) { ) {
const { getCollapsed } = useMenuSetting(); const { getCollapsed } = useMenuSetting();
function setOpenKeys(menu: MenuType) { function setOpenKeys(path: string) {
const flatMenus = unref(flatMenusRef); const menuList = toRaw(menus.value);
if (!unref(accordion)) { if (!unref(accordion)) {
menuState.openKeys = es6Unique([ menuState.openKeys = es6Unique([...menuState.openKeys, ...getAllParentPath(menuList, path)]);
...menuState.openKeys,
...getAllParentPath(flatMenus, menu.path),
]);
} else { } else {
menuState.openKeys = getAllParentPath(flatMenus, menu.path); menuState.openKeys = getAllParentPath(menuList, path);
} }
} }
const getOpenKeys = computed(() => {
return unref(getCollapsed) ? menuState.collapsedOpenKeys : menuState.openKeys;
});
/** /**
* @description: * @description:
*/ */
@ -59,5 +60,5 @@ export function useOpenKeys(
} }
} }
} }
return { setOpenKeys, resetKeys, handleOpenChange }; return { setOpenKeys, resetKeys, getOpenKeys, handleOpenChange };
} }

View File

@ -11,76 +11,76 @@ import { useFullContent } from '/@/hooks/web/useFullContent';
import { MenuModeEnum } from '/@/enums/menuEnum'; import { MenuModeEnum } from '/@/enums/menuEnum';
export function useHeaderSetting() { const { getFullContent } = useFullContent();
const { getFullContent } = useFullContent(); const { getShowMultipleTab } = useMultipleTabSetting();
const { getShowMultipleTab } = useMultipleTabSetting(); const {
const {
getMenuMode, getMenuMode,
getSplit, getSplit,
getShowHeaderTrigger, getShowHeaderTrigger,
getIsSidebarType, getIsSidebarType,
getIsTopMenu, getIsTopMenu,
} = useMenuSetting(); } = useMenuSetting();
const { getShowBreadCrumb, getShowLogo } = useRootSetting(); const { getShowBreadCrumb, getShowLogo } = useRootSetting();
const getShowMixHeaderRef = computed(() => !unref(getIsSidebarType) && unref(getShowHeader)); const getShowMixHeaderRef = computed(() => !unref(getIsSidebarType) && unref(getShowHeader));
const getShowFullHeaderRef = computed(() => { const getShowFullHeaderRef = computed(() => {
return ( return (
!unref(getFullContent) && !unref(getFullContent) &&
unref(getShowMixHeaderRef) && unref(getShowMixHeaderRef) &&
unref(getShowHeader) && unref(getShowHeader) &&
!unref(getIsTopMenu) !unref(getIsTopMenu)
); );
}); });
const getShowInsetHeaderRef = computed(() => { const getShowInsetHeaderRef = computed(() => {
const need = !unref(getFullContent) && unref(getShowHeader); const need = !unref(getFullContent) && unref(getShowHeader);
return (need && !unref(getShowMixHeaderRef)) || (need && unref(getIsTopMenu)); return (need && !unref(getShowMixHeaderRef)) || (need && unref(getIsTopMenu));
}); });
// Get header configuration // Get header configuration
const getHeaderSetting = computed(() => appStore.getProjectConfig.headerSetting); const getHeaderSetting = computed(() => appStore.getProjectConfig.headerSetting);
const getShowDoc = computed(() => unref(getHeaderSetting).showDoc); const getShowDoc = computed(() => unref(getHeaderSetting).showDoc);
const getHeaderTheme = computed(() => unref(getHeaderSetting).theme); const getHeaderTheme = computed(() => unref(getHeaderSetting).theme);
const getShowHeader = computed(() => unref(getHeaderSetting).show); const getShowHeader = computed(() => unref(getHeaderSetting).show);
const getFixed = computed(() => unref(getHeaderSetting).fixed); const getFixed = computed(() => unref(getHeaderSetting).fixed);
const getHeaderBgColor = computed(() => unref(getHeaderSetting).bgColor); const getHeaderBgColor = computed(() => unref(getHeaderSetting).bgColor);
const getShowRedo = computed(() => unref(getHeaderSetting).showRedo && unref(getShowMultipleTab)); const getShowRedo = computed(() => unref(getHeaderSetting).showRedo && unref(getShowMultipleTab));
const getUseLockPage = computed(() => unref(getHeaderSetting).useLockPage); const getUseLockPage = computed(() => unref(getHeaderSetting).useLockPage);
const getShowFullScreen = computed(() => unref(getHeaderSetting).showFullScreen); const getShowFullScreen = computed(() => unref(getHeaderSetting).showFullScreen);
const getShowNotice = computed(() => unref(getHeaderSetting).showNotice); const getShowNotice = computed(() => unref(getHeaderSetting).showNotice);
const getUnFixedAndFull = computed(() => !unref(getFixed) && !unref(getShowFullHeaderRef)); const getUnFixedAndFull = computed(() => !unref(getFixed) && !unref(getShowFullHeaderRef));
const getShowBread = computed(() => { const getShowBread = computed(() => {
return ( return (
unref(getMenuMode) !== MenuModeEnum.HORIZONTAL && unref(getShowBreadCrumb) && !unref(getSplit) unref(getMenuMode) !== MenuModeEnum.HORIZONTAL && unref(getShowBreadCrumb) && !unref(getSplit)
); );
}); });
const getShowHeaderLogo = computed(() => { const getShowHeaderLogo = computed(() => {
return unref(getShowLogo) && !unref(getIsSidebarType); return unref(getShowLogo) && !unref(getIsSidebarType);
}); });
const getShowContent = computed(() => { const getShowContent = computed(() => {
return unref(getShowBread) || unref(getShowHeaderTrigger); return unref(getShowBread) || unref(getShowHeaderTrigger);
}); });
// Set header configuration // Set header configuration
function setHeaderSetting(headerSetting: Partial<HeaderSetting>): void { function setHeaderSetting(headerSetting: Partial<HeaderSetting>): void {
appStore.commitProjectConfigState({ headerSetting }); appStore.commitProjectConfigState({ headerSetting });
} }
export function useHeaderSetting() {
return { return {
setHeaderSetting, setHeaderSetting,

View File

@ -6,26 +6,26 @@ import { appStore } from '/@/store/modules/app';
import getProjectSetting from '/@/settings/projectSetting'; import getProjectSetting from '/@/settings/projectSetting';
import { localeList } from '/@/locales'; import { localeList } from '/@/locales';
export function useLocaleSetting() { // Get locale configuration
// Get locale configuration const getLocale = computed(() => appStore.getProjectConfig.locale || getProjectSetting.locale);
const getLocale = computed(() => appStore.getProjectConfig.locale || getProjectSetting.locale);
// get current language // get current language
const getLang = computed(() => unref(getLocale).lang); const getLang = computed(() => unref(getLocale).lang);
// get Available Locales // get Available Locales
const getAvailableLocales = computed((): string[] => unref(getLocale).availableLocales); const getAvailableLocales = computed((): string[] => unref(getLocale).availableLocales);
// get Fallback Locales // get Fallback Locales
const getFallbackLocale = computed((): string => unref(getLocale).fallback); const getFallbackLocale = computed((): string => unref(getLocale).fallback);
const getShowLocale = computed(() => unref(getLocale).show); const getShowLocale = computed(() => unref(getLocale).show);
// Set locale configuration // Set locale configuration
function setLocale(locale: Partial<LocaleSetting>): void { function setLocale(locale: Partial<LocaleSetting>): void {
appStore.commitProjectConfigState({ locale }); appStore.commitProjectConfigState({ locale });
} }
export function useLocaleSetting() {
return { return {
getLocale, getLocale,
getLang, getLang,

View File

@ -7,49 +7,48 @@ import { appStore } from '/@/store/modules/app';
import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum'; import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum';
import { MenuModeEnum, MenuTypeEnum, TriggerEnum } from '/@/enums/menuEnum'; import { MenuModeEnum, MenuTypeEnum, TriggerEnum } from '/@/enums/menuEnum';
export function useMenuSetting() { // Get menu configuration
// Get menu configuration const getMenuSetting = computed(() => appStore.getProjectConfig.menuSetting);
const getMenuSetting = computed(() => appStore.getProjectConfig.menuSetting);
const getCollapsed = computed(() => unref(getMenuSetting).collapsed); const getCollapsed = computed(() => unref(getMenuSetting).collapsed);
const getMenuType = computed(() => unref(getMenuSetting).type); const getMenuType = computed(() => unref(getMenuSetting).type);
const getMenuMode = computed(() => unref(getMenuSetting).mode); const getMenuMode = computed(() => unref(getMenuSetting).mode);
const getMenuFixed = computed(() => unref(getMenuSetting).fixed); const getMenuFixed = computed(() => unref(getMenuSetting).fixed);
const getShowMenu = computed(() => unref(getMenuSetting).show); const getShowMenu = computed(() => unref(getMenuSetting).show);
const getMenuHidden = computed(() => unref(getMenuSetting).hidden); const getMenuHidden = computed(() => unref(getMenuSetting).hidden);
const getMenuWidth = computed(() => unref(getMenuSetting).menuWidth); const getMenuWidth = computed(() => unref(getMenuSetting).menuWidth);
const getTrigger = computed(() => unref(getMenuSetting).trigger); const getTrigger = computed(() => unref(getMenuSetting).trigger);
const getMenuTheme = computed(() => unref(getMenuSetting).theme); const getMenuTheme = computed(() => unref(getMenuSetting).theme);
const getSplit = computed(() => unref(getMenuSetting).split); const getSplit = computed(() => unref(getMenuSetting).split);
const getMenuBgColor = computed(() => unref(getMenuSetting).bgColor); const getMenuBgColor = computed(() => unref(getMenuSetting).bgColor);
const getCanDrag = computed(() => unref(getMenuSetting).canDrag); const getCanDrag = computed(() => unref(getMenuSetting).canDrag);
const getAccordion = computed(() => unref(getMenuSetting).accordion); const getAccordion = computed(() => unref(getMenuSetting).accordion);
const getCollapsedShowTitle = computed(() => unref(getMenuSetting).collapsedShowTitle); const getCollapsedShowTitle = computed(() => unref(getMenuSetting).collapsedShowTitle);
const getTopMenuAlign = computed(() => unref(getMenuSetting).topMenuAlign); const getTopMenuAlign = computed(() => unref(getMenuSetting).topMenuAlign);
const getIsSidebarType = computed(() => unref(getMenuType) === MenuTypeEnum.SIDEBAR); const getIsSidebarType = computed(() => unref(getMenuType) === MenuTypeEnum.SIDEBAR);
const getIsTopMenu = computed(() => unref(getMenuType) === MenuTypeEnum.TOP_MENU); const getIsTopMenu = computed(() => unref(getMenuType) === MenuTypeEnum.TOP_MENU);
const getShowTopMenu = computed(() => { const getShowTopMenu = computed(() => {
return unref(getMenuMode) === MenuModeEnum.HORIZONTAL || unref(getSplit); return unref(getMenuMode) === MenuModeEnum.HORIZONTAL || unref(getSplit);
}); });
const getShowHeaderTrigger = computed(() => { const getShowHeaderTrigger = computed(() => {
if ( if (
unref(getMenuType) === MenuTypeEnum.TOP_MENU || unref(getMenuType) === MenuTypeEnum.TOP_MENU ||
!unref(getShowMenu) || !unref(getShowMenu) ||
@ -59,37 +58,38 @@ export function useMenuSetting() {
} }
return unref(getTrigger) === TriggerEnum.HEADER; return unref(getTrigger) === TriggerEnum.HEADER;
}); });
const getIsHorizontal = computed(() => { const getIsHorizontal = computed(() => {
return unref(getMenuMode) === MenuModeEnum.HORIZONTAL; return unref(getMenuMode) === MenuModeEnum.HORIZONTAL;
}); });
const getRealWidth = computed(() => { const getRealWidth = computed(() => {
return unref(getCollapsed) ? unref(getMiniWidthNumber) : unref(getMenuWidth); return unref(getCollapsed) ? unref(getMiniWidthNumber) : unref(getMenuWidth);
}); });
const getMiniWidthNumber = computed(() => { const getMiniWidthNumber = computed(() => {
const { collapsedShowTitle } = unref(getMenuSetting); const { collapsedShowTitle } = unref(getMenuSetting);
return collapsedShowTitle ? SIDE_BAR_SHOW_TIT_MINI_WIDTH : SIDE_BAR_MINI_WIDTH; return collapsedShowTitle ? SIDE_BAR_SHOW_TIT_MINI_WIDTH : SIDE_BAR_MINI_WIDTH;
}); });
const getCalcContentWidth = computed(() => { const getCalcContentWidth = computed(() => {
const width = unref(getIsTopMenu) || !unref(getShowMenu) ? 0 : unref(getRealWidth); const width = unref(getIsTopMenu) || !unref(getShowMenu) ? 0 : unref(getRealWidth);
return `calc(100% - ${unref(width)}px)`; return `calc(100% - ${unref(width)}px)`;
}); });
// Set menu configuration // Set menu configuration
function setMenuSetting(menuSetting: Partial<MenuSetting>): void { function setMenuSetting(menuSetting: Partial<MenuSetting>): void {
appStore.commitProjectConfigState({ menuSetting }); appStore.commitProjectConfigState({ menuSetting });
} }
function toggleCollapsed() { function toggleCollapsed() {
setMenuSetting({ setMenuSetting({
collapsed: !unref(getCollapsed), collapsed: !unref(getCollapsed),
}); });
} }
export function useMenuSetting() {
return { return {
setMenuSetting, setMenuSetting,

View File

@ -4,17 +4,17 @@ import { computed, unref } from 'vue';
import { appStore } from '/@/store/modules/app'; import { appStore } from '/@/store/modules/app';
export function useMultipleTabSetting() { const getMultipleTabSetting = computed(() => appStore.getProjectConfig.multiTabsSetting);
const getMultipleTabSetting = computed(() => appStore.getProjectConfig.multiTabsSetting);
const getShowMultipleTab = computed(() => unref(getMultipleTabSetting).show); const getShowMultipleTab = computed(() => unref(getMultipleTabSetting).show);
const getShowQuick = computed(() => unref(getMultipleTabSetting).showQuick); const getShowQuick = computed(() => unref(getMultipleTabSetting).showQuick);
function setMultipleTabSetting(multiTabsSetting: Partial<MultiTabsSetting>) { function setMultipleTabSetting(multiTabsSetting: Partial<MultiTabsSetting>) {
appStore.commitProjectConfigState({ multiTabsSetting }); appStore.commitProjectConfigState({ multiTabsSetting });
} }
export function useMultipleTabSetting() {
return { return {
setMultipleTabSetting, setMultipleTabSetting,

View File

@ -9,47 +9,48 @@ type RootSetting = Omit<
ProjectConfig, ProjectConfig,
'locale' | 'headerSetting' | 'menuSetting' | 'multiTabsSetting' 'locale' | 'headerSetting' | 'menuSetting' | 'multiTabsSetting'
>; >;
export function useRootSetting() {
const getRootSetting = computed((): RootSetting => appStore.getProjectConfig);
const getPageLoading = computed(() => appStore.getPageLoading); const getRootSetting = computed((): RootSetting => appStore.getProjectConfig);
const getOpenKeepAlive = computed(() => unref(getRootSetting).openKeepAlive); const getPageLoading = computed(() => appStore.getPageLoading);
const getCanEmbedIFramePage = computed(() => unref(getRootSetting).canEmbedIFramePage); const getOpenKeepAlive = computed(() => unref(getRootSetting).openKeepAlive);
const getPermissionMode = computed(() => unref(getRootSetting).permissionMode); const getCanEmbedIFramePage = computed(() => unref(getRootSetting).canEmbedIFramePage);
const getShowLogo = computed(() => unref(getRootSetting).showLogo); const getPermissionMode = computed(() => unref(getRootSetting).permissionMode);
const getContentMode = computed(() => unref(getRootSetting).contentMode); const getShowLogo = computed(() => unref(getRootSetting).showLogo);
const getUseOpenBackTop = computed(() => unref(getRootSetting).useOpenBackTop); const getContentMode = computed(() => unref(getRootSetting).contentMode);
const getShowSettingButton = computed(() => unref(getRootSetting).showSettingButton); const getUseOpenBackTop = computed(() => unref(getRootSetting).useOpenBackTop);
const getUseErrorHandle = computed(() => unref(getRootSetting).useErrorHandle); const getShowSettingButton = computed(() => unref(getRootSetting).showSettingButton);
const getShowFooter = computed(() => unref(getRootSetting).showFooter); const getUseErrorHandle = computed(() => unref(getRootSetting).useErrorHandle);
const getShowBreadCrumb = computed(() => unref(getRootSetting).showBreadCrumb); const getShowFooter = computed(() => unref(getRootSetting).showFooter);
const getShowBreadCrumbIcon = computed(() => unref(getRootSetting).showBreadCrumbIcon); const getShowBreadCrumb = computed(() => unref(getRootSetting).showBreadCrumb);
const getFullContent = computed(() => unref(getRootSetting).fullContent); const getShowBreadCrumbIcon = computed(() => unref(getRootSetting).showBreadCrumbIcon);
const getColorWeak = computed(() => unref(getRootSetting).colorWeak); const getFullContent = computed(() => unref(getRootSetting).fullContent);
const getGrayMode = computed(() => unref(getRootSetting).grayMode); const getColorWeak = computed(() => unref(getRootSetting).colorWeak);
const getLayoutContentMode = computed(() => const getGrayMode = computed(() => unref(getRootSetting).grayMode);
const getLayoutContentMode = computed(() =>
unref(getRootSetting).contentMode === ContentEnum.FULL ? ContentEnum.FULL : ContentEnum.FIXED unref(getRootSetting).contentMode === ContentEnum.FULL ? ContentEnum.FULL : ContentEnum.FIXED
); );
function setRootSetting(setting: Partial<RootSetting>) { function setRootSetting(setting: Partial<RootSetting>) {
appStore.commitProjectConfigState(setting); appStore.commitProjectConfigState(setting);
} }
export function useRootSetting() {
return { return {
setRootSetting, setRootSetting,

View File

@ -4,23 +4,23 @@ import { computed, unref } from 'vue';
import { appStore } from '/@/store/modules/app'; import { appStore } from '/@/store/modules/app';
export function useTransitionSetting() { const getTransitionSetting = computed(() => appStore.getProjectConfig.transitionSetting);
const getTransitionSetting = computed(() => appStore.getProjectConfig.transitionSetting);
const getEnableTransition = computed(() => unref(getTransitionSetting)?.enable); const getEnableTransition = computed(() => unref(getTransitionSetting)?.enable);
const getOpenNProgress = computed(() => unref(getTransitionSetting)?.openNProgress); const getOpenNProgress = computed(() => unref(getTransitionSetting)?.openNProgress);
const getOpenPageLoading = computed((): boolean => { const getOpenPageLoading = computed((): boolean => {
return !!unref(getTransitionSetting)?.openPageLoading; return !!unref(getTransitionSetting)?.openPageLoading;
}); });
const getBasicTransition = computed(() => unref(getTransitionSetting)?.basicTransition); const getBasicTransition = computed(() => unref(getTransitionSetting)?.basicTransition);
function setTransitionSetting(transitionSetting: Partial<TransitionSetting>) { function setTransitionSetting(transitionSetting: Partial<TransitionSetting>) {
appStore.commitProjectConfigState({ transitionSetting }); appStore.commitProjectConfigState({ transitionSetting });
} }
export function useTransitionSetting() {
return { return {
setTransitionSetting, setTransitionSetting,

View File

@ -1,18 +1,21 @@
import { useAppProviderContext } from '/@/components/Application'; import { useAppProviderContext } from '/@/components/Application';
import { computed } from 'vue'; // import { computed } from 'vue';
// import { useCssModule, reactive } from 'vue'; // import { lowerFirst } from 'lodash-es';
export function useDesign(scope: string) { export function useDesign(scope: string) {
const values = useAppProviderContext(); const values = useAppProviderContext();
// const style = cssModule ? useCssModule() : {}; // const $style = cssModule ? useCssModule() : {};
// const style: Record<string, string> = {};
// if (cssModule) { // if (cssModule) {
// Object.keys(style).forEach((key) => { // Object.keys($style).forEach((key) => {
// const moduleCls = style[key]; // // const moduleCls = $style[key];
// style[key] = `${cls}-${moduleCls}`; // const k = key.replace(new RegExp(`^${values.prefixCls}-?`, 'ig'), '');
// style[lowerFirst(k)] = $style[key];
// }); // });
// } // }
return { return {
prefixCls: computed(() => `${values.prefixCls}-${scope}`), // prefixCls: computed(() => `${values.prefixCls}-${scope}`),
prefixCls: `${values.prefixCls}-${scope}`,
prefixVar: values.prefixCls, prefixVar: values.prefixCls,
// style, // style,
}; };

View File

@ -2,12 +2,12 @@ import { computed, unref } from 'vue';
import { appStore } from '/@/store/modules/app'; import { appStore } from '/@/store/modules/app';
import { useRouter } from 'vue-router'; import router from '/@/router';
/** /**
* @description: Full screen display content * @description: Full screen display content
*/ */
export const useFullContent = () => { export const useFullContent = () => {
const { currentRoute } = useRouter(); const { currentRoute } = router;
// Whether to display the content in full screen without displaying the menu // Whether to display the content in full screen without displaying the menu
const getFullContent = computed(() => { const getFullContent = computed(() => {

View File

@ -1,4 +1,3 @@
import { appStore } from '/@/store/modules/app';
import type { RouteLocationRaw } from 'vue-router'; import type { RouteLocationRaw } from 'vue-router';
import { PageEnum } from '/@/enums/pageEnum'; import { PageEnum } from '/@/enums/pageEnum';
@ -11,7 +10,6 @@ export type RouteLocationRawEx = Omit<RouteLocationRaw, 'path'> & { path: PageEn
function handleError(e: Error) { function handleError(e: Error) {
console.error(e); console.error(e);
appStore.commitPageLoadingState(false);
} }
// page switch // page switch

View File

@ -80,13 +80,7 @@ export default defineComponent({
const { refreshPage } = useTabs(); const { refreshPage } = useTabs();
const { t } = useI18n(); const { t } = useI18n();
const { const { getShowTopMenu, getShowHeaderTrigger, getSplit, getIsHorizontal } = useMenuSetting();
getShowTopMenu,
getShowHeaderTrigger,
getSplit,
getTopMenuAlign,
getIsHorizontal,
} = useMenuSetting();
const { getShowLocale } = useLocaleSetting(); const { getShowLocale } = useLocaleSetting();
const { getUseErrorHandle, getShowBreadCrumbIcon } = useRootSetting(); const { getUseErrorHandle, getShowBreadCrumbIcon } = useRootSetting();
@ -184,7 +178,7 @@ export default defineComponent({
{/* <div class={[`layout-header__menu `]}> */} {/* <div class={[`layout-header__menu `]}> */}
<LayoutMenu <LayoutMenu
isHorizontal={true} isHorizontal={true}
class={`justify-${unref(getTopMenuAlign)}`} // class={`justify-${unref(getTopMenuAlign)}`}
theme={unref(getHeaderTheme)} theme={unref(getHeaderTheme)}
splitType={unref(getSplitType)} splitType={unref(getSplitType)}
menuMode={unref(getMenuMode)} menuMode={unref(getMenuMode)}

View File

@ -21,7 +21,7 @@ export default defineComponent({
const injectValue = useLayoutContext(); const injectValue = useLayoutContext();
const { getCalcContentWidth } = useMenuSetting(); const { getCalcContentWidth, getSplit } = useMenuSetting();
const { const {
getFixed, getFixed,
@ -56,7 +56,8 @@ export default defineComponent({
(): CSSProperties => { (): CSSProperties => {
const style: CSSProperties = {}; const style: CSSProperties = {};
if (unref(getFixed)) { if (unref(getFixed)) {
style.width = unref(injectValue.isMobile) ? '100%' : unref(getCalcContentWidth); style.width =
unref(injectValue.isMobile) || unref(getSplit) ? '100%' : unref(getCalcContentWidth);
} }
if (unref(getShowFullHeaderRef)) { if (unref(getShowFullHeaderRef)) {
style.top = `${unref(fullHeaderHeightRef)}px`; style.top = `${unref(fullHeaderHeightRef)}px`;

View File

@ -1,21 +1,21 @@
import './index.less'; import './index.less';
import { PropType, toRef } from 'vue'; import { PropType, toRef } from 'vue';
import type { Menu } from '/@/router/types';
import { computed, defineComponent, unref } from 'vue'; import { computed, defineComponent, unref } from 'vue';
import { BasicMenu } from '/@/components/Menu/index'; import { BasicMenu } from '/@/components/Menu';
import { AppLogo } from '/@/components/Application'; import { AppLogo } from '/@/components/Application';
import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum'; import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { useGo } from '/@/hooks/web/usePage'; import { useGo } from '/@/hooks/web/usePage';
import { useSplitMenu } from './useLayoutMenu'; import { useSplitMenu } from './useLayoutMenu';
import { openWindow } from '/@/utils'; import { openWindow } from '/@/utils';
import { propTypes } from '/@/utils/propTypes'; import { propTypes } from '/@/utils/propTypes';
import { isUrl } from '/@/utils/is';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
export default defineComponent({ export default defineComponent({
name: 'LayoutMenu', name: 'LayoutMenu',
@ -38,56 +38,46 @@ export default defineComponent({
const go = useGo(); const go = useGo();
const { const {
setMenuSetting,
getMenuMode, getMenuMode,
getMenuType, getMenuType,
getCollapsedShowTitle, getCollapsedShowTitle,
getIsSidebarType,
getMenuTheme, getMenuTheme,
getCollapsed, getCollapsed,
getAccordion, getAccordion,
getIsSidebarType,
} = useMenuSetting(); } = useMenuSetting();
const { getShowLogo } = useRootSetting(); const { getShowLogo } = useRootSetting();
const { flatMenusRef, menusRef } = useSplitMenu(toRef(props, 'splitType')); const { menusRef } = useSplitMenu(toRef(props, 'splitType'));
const showLogo = computed(() => unref(getShowLogo) && unref(getIsSidebarType));
const getComputedMenuMode = computed(() => props.menuMode || unref(getMenuMode)); const getComputedMenuMode = computed(() => props.menuMode || unref(getMenuMode));
const getComputedMenuTheme = computed(() => props.theme || unref(getMenuTheme)); const getComputedMenuTheme = computed(() => props.theme || unref(getMenuTheme));
const showLogo = computed(() => unref(getShowLogo) && unref(getIsSidebarType));
const appendClass = computed(() => props.splitType === MenuSplitTyeEnum.TOP); const appendClass = computed(() => props.splitType === MenuSplitTyeEnum.TOP);
/** /**
* click menu * click menu
* @param menu * @param menu
*/ */
function handleMenuClick(menu: Menu) { function handleMenuClick(path: string) {
go(menu.path); go(path);
} }
/** /**
* before click menu * before click menu
* @param menu * @param menu
*/ */
async function beforeMenuClickFn(menu: Menu) { async function beforeMenuClickFn(path: string) {
const { meta: { externalLink } = {} } = menu; if (!isUrl(path)) {
if (externalLink) {
openWindow(externalLink);
return false;
}
return true; return true;
} }
openWindow(path);
function handleClickSearchInput() { return false;
unref(getCollapsed) && setMenuSetting({ collapsed: false });
} }
function renderHeader() { function renderHeader() {
if (!unref(showLogo)) return null; if (!unref(showLogo)) return null;
return ( return (
<AppLogo <AppLogo
showTitle={!unref(getCollapsed)} showTitle={!unref(getCollapsed)}
@ -100,20 +90,17 @@ export default defineComponent({
return () => { return () => {
return ( return (
<BasicMenu <BasicMenu
class="layout-menu"
beforeClickFn={beforeMenuClickFn} beforeClickFn={beforeMenuClickFn}
isHorizontal={props.isHorizontal} isHorizontal={props.isHorizontal}
appendClass={unref(appendClass)}
type={unref(getMenuType)} type={unref(getMenuType)}
mode={unref(getComputedMenuMode)} mode={unref(getComputedMenuMode)}
collapsedShowTitle={unref(getCollapsedShowTitle)} collapsedShowTitle={unref(getCollapsedShowTitle)}
theme={unref(getComputedMenuTheme)} theme={unref(getComputedMenuTheme)}
showLogo={unref(showLogo)}
items={unref(menusRef)} items={unref(menusRef)}
flatItems={unref(flatMenusRef)}
accordion={unref(getAccordion)} accordion={unref(getAccordion)}
onMenuClick={handleMenuClick} onMenuClick={handleMenuClick}
onClickSearchInput={handleClickSearchInput} appendClass={unref(appendClass)}
showLogo={unref(showLogo)}
> >
{{ {{
header: () => renderHeader(), header: () => renderHeader(),

View File

@ -8,24 +8,12 @@ import { MenuSplitTyeEnum } from '/@/enums/menuEnum';
import { useThrottle } from '/@/hooks/core/useThrottle'; import { useThrottle } from '/@/hooks/core/useThrottle';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { import { getChildrenMenus, getCurrentParentPath, getMenus, getShallowMenus } from '/@/router/menus';
getChildrenMenus,
getCurrentParentPath,
getFlatChildrenMenus,
getFlatMenus,
getMenus,
getShallowMenus,
} from '/@/router/menus';
import { permissionStore } from '/@/store/modules/permission'; import { permissionStore } from '/@/store/modules/permission';
// import { useI18n } from '/@/hooks/web/useI18n';
// import { cloneDeep } from 'lodash-es';
// const { t } = useI18n();
export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) { export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
// Menu array // Menu array
const menusRef = ref<Menu[]>([]); const menusRef = ref<Menu[]>([]);
// flat menu array
const flatMenusRef = ref<Menu[]>([]);
const { currentRoute } = useRouter(); const { currentRoute } = useRouter();
@ -45,14 +33,6 @@ export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
return unref(splitType) === MenuSplitTyeEnum.NONE || !unref(getSplit); return unref(splitType) === MenuSplitTyeEnum.NONE || !unref(getSplit);
}); });
// const getI18nFlatMenus = computed(() => {
// return setI18nName(flatMenusRef.value, true, false);
// });
// const getI18nMenus = computed(() => {
// return setI18nName(menusRef.value, true, true);
// });
watch( watch(
[() => unref(currentRoute).path, () => unref(splitType)], [() => unref(currentRoute).path, () => unref(splitType)],
async ([path]: [string, MenuSplitTyeEnum]) => { async ([path]: [string, MenuSplitTyeEnum]) => {
@ -83,20 +63,6 @@ export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
genMenus(); genMenus();
}); });
// function setI18nName(list: Menu[], clone = false, deep = true) {
// const menus = clone ? cloneDeep(list) : list;
// const arr: Menu[] = [];
// menus.forEach((item) => {
// if (!item.name.includes('.')) return;
// item.name = t(item.name);
// if (item.children && deep) {
// setI18nName(item.children, false, deep);
// }
// });
// return menus;
// }
// Handle left menu split // Handle left menu split
async function handleSplitLeftMenu(parentPath: string) { async function handleSplitLeftMenu(parentPath: string) {
if (unref(splitLeft)) return; if (unref(splitLeft)) return;
@ -105,14 +71,11 @@ export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
const children = await getChildrenMenus(parentPath); const children = await getChildrenMenus(parentPath);
if (!children) { if (!children) {
setMenuSetting({ hidden: false }); setMenuSetting({ hidden: false });
flatMenusRef.value = [];
menusRef.value = []; menusRef.value = [];
return; return;
} }
const flatChildren = await getFlatChildrenMenus(children);
setMenuSetting({ hidden: true }); setMenuSetting({ hidden: true });
flatMenusRef.value = flatChildren;
menusRef.value = children; menusRef.value = children;
} }
@ -120,7 +83,6 @@ export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
async function genMenus() { async function genMenus() {
// normal mode // normal mode
if (unref(normalType)) { if (unref(normalType)) {
flatMenusRef.value = await getFlatMenus();
menusRef.value = await getMenus(); menusRef.value = await getMenus();
return; return;
} }
@ -129,11 +91,10 @@ export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
if (unref(spiltTop)) { if (unref(spiltTop)) {
const shallowMenus = await getShallowMenus(); const shallowMenus = await getShallowMenus();
flatMenusRef.value = shallowMenus;
menusRef.value = shallowMenus; menusRef.value = shallowMenus;
return; return;
} }
} }
return { flatMenusRef, menusRef }; return { menusRef };
} }

View File

@ -12,10 +12,25 @@
&.ant-layout-sider-dark { &.ant-layout-sider-dark {
background: @sider-dark-bg-color; background: @sider-dark-bg-color;
.ant-layout-sider-trigger {
color: darken(@white, 25%);
background: @trigger-dark-bg-color;
&:hover {
color: @white;
background: @trigger-dark-hover-bg-color;
}
}
} }
&:not(.ant-layout-sider-dark) { &:not(.ant-layout-sider-dark) {
box-shadow: 2px 0 8px 0 rgba(29, 35, 41, 0.05); box-shadow: 2px 0 8px 0 rgba(29, 35, 41, 0.05);
.ant-layout-sider-trigger {
color: @text-color-base;
border-top: 1px solid @border-color-light;
}
} }
.ant-layout-sider-zero-width-trigger { .ant-layout-sider-zero-width-trigger {
@ -43,9 +58,9 @@
box-shadow: 0 0 4px 0 rgba(28, 36, 56, 0.15); box-shadow: 0 0 4px 0 rgba(28, 36, 56, 0.15);
} }
} }
}
.ant-layout-sider-trigger { & .ant-layout-sider-trigger {
height: 36px; height: 36px;
line-height: 36px; line-height: 36px;
}
} }

View File

@ -100,7 +100,7 @@ export default defineComponent({
flex: `0 0 ${width}`, flex: `0 0 ${width}`,
maxWidth: width, maxWidth: width,
minWidth: width, minWidth: width,
transition: 'all 0.2s', transition: 'all 0.15s',
}; };
} }
); );
@ -126,7 +126,7 @@ export default defineComponent({
)} )}
<Layout.Sider <Layout.Sider
ref={sideRef} ref={sideRef}
breakpoint="md" breakpoint="lg"
collapsible collapsible
class={unref(getSiderClass)} class={unref(getSiderClass)}
style={unref(getSiderStyle)} style={unref(getSiderStyle)}

View File

@ -79,3 +79,8 @@ export const isMobile = (): boolean => {
/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
); );
}; };
export const isUrl = (path: string): boolean => {
const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
return reg.test(path);
};