提交 cbcd9098 作者: vben

wip(menu): perf menu

上级 f81c4019
...@@ -66,8 +66,9 @@ ...@@ -66,8 +66,9 @@
.@{prefix-cls} { .@{prefix-cls} {
display: flex; display: flex;
align-items: center; align-items: center;
padding-left: 12px; padding-left: 7px;
cursor: pointer; cursor: pointer;
transition: all 0.2s ease;
&.collapsed-show-title { &.collapsed-show-title {
padding-left: 20px; padding-left: 20px;
......
...@@ -2,6 +2,8 @@ import { withInstall } from '../util'; ...@@ -2,6 +2,8 @@ import { withInstall } from '../util';
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
export const BasicMenu = createAsyncComponent(() => import('./src/BasicMenu'), { loading: false }); export const BasicMenu = createAsyncComponent(() => import('./src/BasicMenu.vue'), {
loading: false,
});
withInstall(BasicMenu); withInstall(BasicMenu);
<template>
<MenuItem :class="getLevelClass">
<MenuContent v-bind="$props" :item="item" />
</MenuItem>
</template>
<script lang="ts">
import { defineComponent, computed } from 'vue';
import { Menu } from 'ant-design-vue';
import { useDesign } from '/@/hooks/web/useDesign';
import { itemProps } from '../props';
import MenuContent from '../MenuContent';
export default defineComponent({
name: 'BasicMenuItem',
components: { MenuItem: Menu.Item, MenuContent },
props: itemProps,
setup(props) {
const { prefixCls } = useDesign('basic-menu-item');
const getLevelClass = computed(() => {
const { appendClass, level, item, parentPath, theme } = props;
const isAppendActiveCls = appendClass && level === 1 && item.path === parentPath;
const levelCls = [
`${prefixCls}__level${level}`,
theme,
{
'top-active-menu': isAppendActiveCls,
},
];
return levelCls;
});
return {
prefixCls,
getLevelClass,
};
},
});
</script>
<template>
<BasicMenuItem v-if="!menuHasChildren(item)" v-bind="$props" />
<SubMenu v-else :class="[`${prefixCls}__level${level}`, theme]">
<template #title>
<MenuContent v-bind="$props" :item="item" />
</template>
<!-- <template #expandIcon="{ key }">
<ExpandIcon :key="key" />
</template> -->
<template v-for="childrenItem in item.children || []" :key="childrenItem.path">
<BasicSubMenuItem v-bind="$props" :item="childrenItem" :level="level + 1" />
</template>
</SubMenu>
</template>
<script lang="ts">
import type { Menu as MenuType } from '/@/router/types';
import { defineComponent } from 'vue';
import { Menu } from 'ant-design-vue';
import { useDesign } from '/@/hooks/web/useDesign';
import { itemProps } from '../props';
import BasicMenuItem from './BasicMenuItem.vue';
import MenuContent from '../MenuContent';
// import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
export default defineComponent({
name: 'BasicSubMenuItem',
components: {
BasicMenuItem,
SubMenu: Menu.SubMenu,
MenuItem: Menu.Item,
MenuContent,
// ExpandIcon: createAsyncComponent(() => import('./ExpandIcon.vue')),
},
props: itemProps,
setup() {
const { prefixCls } = useDesign('basic-menu-item');
function menuHasChildren(menuTreeItem: MenuType): boolean {
return (
Reflect.has(menuTreeItem, 'children') &&
!!menuTreeItem.children &&
menuTreeItem.children.length > 0
);
}
return {
prefixCls,
menuHasChildren,
};
},
});
</script>
<template>
<BasicArrow :expand="getIsOpen" bottom inset :class="getWrapperClass" />
</template>
<script lang="ts">
import { defineComponent, PropType, computed } from 'vue';
import { useDesign } from '/@/hooks/web/useDesign';
import { BasicArrow } from '/@/components/Basic';
import { propTypes } from '/@/utils/propTypes';
export default defineComponent({
name: 'BasicMenuItem',
components: { BasicArrow },
props: {
key: propTypes.string,
openKeys: {
type: Array as PropType<string[]>,
default: [],
},
collapsed: propTypes.bool,
},
setup(props) {
const { prefixCls } = useDesign('basic-menu');
const getIsOpen = computed(() => {
return props.openKeys.includes(props.key);
});
const getWrapperClass = computed(() => {
return [
`${prefixCls}__expand-icon`,
{
[`${prefixCls}__expand-icon--collapsed`]: props.collapsed,
},
];
});
return {
prefixCls,
getIsOpen,
getWrapperClass,
};
},
});
</script>
import type { Menu as MenuType } from '/@/router/types';
/**
* @description: Whether the menu has child nodes
*/
export function menuHasChildren(menuTreeItem: MenuType): boolean {
return (
Reflect.has(menuTreeItem, 'children') &&
!!menuTreeItem.children &&
menuTreeItem.children.length > 0
);
}
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
.active-style() { .active-style() {
color: @white; color: @white;
// background: @primary-color !important;
background: linear-gradient( background: linear-gradient(
118deg, 118deg,
rgba(@primary-color, 0.8), rgba(@primary-color, 0.8),
...@@ -27,6 +28,7 @@ ...@@ -27,6 +28,7 @@
// right: 16px; // right: 16px;
// width: 10px; // width: 10px;
// transform-origin: none; // transform-origin: none;
// opacity: 0.45;
// span[role='img'] { // span[role='img'] {
// margin-right: 0; // margin-right: 0;
...@@ -52,9 +54,9 @@ ...@@ -52,9 +54,9 @@
> .ant-menu-item-group-list > .ant-menu-item-group-list
> .ant-menu-submenu > .ant-menu-submenu
> .ant-menu-submenu-title, > .ant-menu-submenu-title,
&.ant-menu-inline-collapsed > .ant-menu-submenu > .ant-menu-submenu-title { &.ant-menu-inline-collapsed .ant-menu-submenu-title {
padding-right: 20px !important; padding-right: 16px !important;
padding-left: 20px !important; padding-left: 16px !important;
} }
} }
...@@ -87,32 +89,33 @@ ...@@ -87,32 +89,33 @@
} }
} }
// .ant-menu-item { .ant-menu-item {
// transition: unset; transition: unset;
// } }
// scrollbar -s tart // scrollbar -s tart
&-wrapper { // &-wrapper {
/* 滚动槽 */
&::-webkit-scrollbar {
width: 5px;
height: 5px;
}
&::-webkit-scrollbar-track { /* 滚动槽 */
background: rgba(0, 0, 0, 0); // &::-webkit-scrollbar {
} // width: 5px;
// height: 5px;
// }
&::-webkit-scrollbar-thumb { // &::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.2); // background: rgba(0, 0, 0, 0);
border-radius: 3px; // }
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.1);
}
::-webkit-scrollbar-thumb:hover { // &::-webkit-scrollbar-thumb {
background: @border-color-dark; // background: rgba(255, 255, 255, 0.2);
} // border-radius: 3px;
} // box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.1);
// }
// ::-webkit-scrollbar-thumb:hover {
// background: @border-color-dark;
// }
// }
// scrollbar end // scrollbar end
...@@ -225,14 +228,6 @@ ...@@ -225,14 +228,6 @@
} }
} }
&:not(.@{basic-menu-prefix-cls}__sidebar-hor).ant-menu-inline-collapsed {
.@{basic-menu-prefix-cls}-item__level1 {
> div {
align-items: center;
}
}
}
&.ant-menu-dark:not(.@{basic-menu-prefix-cls}__sidebar-hor):not(.@{basic-menu-prefix-cls}__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-prefix-cls}-item__level1), .ant-menu-item:not(.@{basic-menu-prefix-cls}-item__level1),
...@@ -255,10 +250,6 @@ ...@@ -255,10 +250,6 @@
background: @sider-dark-bg-color; background: @sider-dark-bg-color;
.active-menu-style(); .active-menu-style();
// .menu-item-icon.app-iconify {
// display: inline-block !important;
// }
.ant-menu-item.ant-menu-item-selected.@{basic-menu-prefix-cls}-menu-item__level1, .ant-menu-item.ant-menu-item-selected.@{basic-menu-prefix-cls}-menu-item__level1,
.ant-menu-submenu-selected.@{basic-menu-prefix-cls}-menu-item__level1 { .ant-menu-submenu-selected.@{basic-menu-prefix-cls}-menu-item__level1 {
color: @white; color: @white;
...@@ -304,10 +295,6 @@ ...@@ -304,10 +295,6 @@
overflow: hidden; overflow: hidden;
border-right: none; border-right: none;
// .menu-item-icon.app-iconify {
// display: inline-block !important;
// }
.ant-menu-item.ant-menu-item-selected.@{basic-menu-prefix-cls}-menu-item__level1, .ant-menu-item.ant-menu-item-selected.@{basic-menu-prefix-cls}-menu-item__level1,
.ant-menu-submenu-selected.@{basic-menu-prefix-cls}-menu-item__level1 { .ant-menu-submenu-selected.@{basic-menu-prefix-cls}-menu-item__level1 {
color: @primary-color; color: @primary-color;
...@@ -332,6 +319,7 @@ ...@@ -332,6 +319,7 @@
align-items: center; align-items: center;
} }
} }
.@{basic-menu-prefix-cls}__tag { .@{basic-menu-prefix-cls}__tag {
position: absolute; position: absolute;
top: calc(50% - 8px); top: calc(50% - 8px);
...@@ -368,6 +356,20 @@ ...@@ -368,6 +356,20 @@
background: @warning-color; background: @warning-color;
} }
} }
.ant-menu-submenu,
.ant-menu-submenu-inline {
transition: unset;
}
// .ant-menu-submenu-arrow {
// transition: all 0.15s ease 0s;
// }
.ant-menu-inline.ant-menu-sub {
box-shadow: unset !important;
transition: unset;
}
} }
.ant-menu-dark { .ant-menu-dark {
...@@ -375,7 +377,6 @@ ...@@ -375,7 +377,6 @@
> ul { > ul {
background: @sider-dark-bg-color; background: @sider-dark-bg-color;
} }
.active-menu-style(); .active-menu-style();
} }
} }
......
...@@ -3,57 +3,47 @@ import type { PropType } from 'vue'; ...@@ -3,57 +3,47 @@ import type { PropType } from 'vue';
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum'; import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
import { ThemeEnum } from '/@/enums/appEnum'; import { ThemeEnum } from '/@/enums/appEnum';
import { propTypes } from '/@/utils/propTypes';
export const basicProps = { export const basicProps = {
items: { items: {
type: Array as PropType<Menu[]>, type: Array as PropType<Menu[]>,
default: () => [], default: () => [],
}, },
appendClass: { appendClass: propTypes.bool,
type: Boolean as PropType<boolean>,
default: false,
},
collapsedShowTitle: { collapsedShowTitle: propTypes.bool,
type: Boolean as PropType<boolean>,
default: false,
},
// 最好是4 倍数 // 最好是4 倍数
inlineIndent: { inlineIndent: propTypes.number.def(20),
type: Number as PropType<number>,
default: 20,
},
// 菜单组件的mode属性 // 菜单组件的mode属性
mode: { mode: {
type: String as PropType<MenuModeEnum>, type: String as PropType<MenuModeEnum>,
default: MenuModeEnum.INLINE, default: MenuModeEnum.INLINE,
}, },
showLogo: { showLogo: propTypes.bool,
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,
}, },
theme: { theme: propTypes.string.def(ThemeEnum.DARK),
type: String as PropType<string>, inlineCollapsed: propTypes.bool,
default: ThemeEnum.DARK,
},
inlineCollapsed: {
type: Boolean as PropType<boolean>,
default: false,
},
isHorizontal: { isHorizontal: propTypes.bool,
type: Boolean as PropType<boolean>, accordion: propTypes.bool.def(true),
default: false,
},
accordion: {
type: Boolean as PropType<boolean>,
default: true,
},
beforeClickFn: { beforeClickFn: {
type: Function as PropType<(key: string) => Promise<boolean>>, type: Function as PropType<(key: string) => Promise<boolean>>,
}, },
}; };
export const itemProps = {
item: {
type: Object as PropType<Menu>,
default: {},
},
level: propTypes.number,
theme: propTypes.oneOf(['dark', 'light']),
appendClass: propTypes.bool,
parentPath: propTypes.string,
showTitle: propTypes.bool,
isHorizontal: propTypes.bool,
};
import { ComputedRef } from 'vue'; // import { ComputedRef } from 'vue';
import { ThemeEnum } from '/@/enums/appEnum'; // import { ThemeEnum } from '/@/enums/appEnum';
import { MenuModeEnum } from '/@/enums/menuEnum'; // import { MenuModeEnum } from '/@/enums/menuEnum';
export interface MenuState { export interface MenuState {
// 默认选中的列表 // 默认选中的列表
defaultSelectedKeys: string[]; defaultSelectedKeys: string[];
// 模式 // 模式
mode: MenuModeEnum; // mode: MenuModeEnum;
// 主题 // // 主题
theme: ComputedRef<ThemeEnum> | ThemeEnum; // theme: ComputedRef<ThemeEnum> | ThemeEnum;
// 缩进 // 缩进
inlineIndent?: number; inlineIndent?: number;
......
export const SIDE_BAR_MINI_WIDTH = 58; export const SIDE_BAR_MINI_WIDTH = 48;
export const SIDE_BAR_SHOW_TIT_MINI_WIDTH = 80; export const SIDE_BAR_SHOW_TIT_MINI_WIDTH = 80;
export enum ContentEnum { export enum ContentEnum {
......
...@@ -34,9 +34,9 @@ ...@@ -34,9 +34,9 @@
border: 1px solid darken(@border-color-light, 6%); border: 1px solid darken(@border-color-light, 6%);
transition: none; transition: none;
&:not(.ant-tabs-tab-active)::before { &:not(.ant-tabs-tab-active)::after {
position: absolute; position: absolute;
top: -1px; bottom: -1px;
left: 50%; left: 50%;
width: 100%; width: 100%;
height: 2px; height: 2px;
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
opacity: 1; opacity: 1;
} }
&:not(.ant-tabs-tab-active)::before { &:not(.ant-tabs-tab-active)::after {
opacity: 1; opacity: 1;
transform: translate(-50%, 0) scaleX(1); transform: translate(-50%, 0) scaleX(1);
transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out;
......
...@@ -42,6 +42,7 @@ export function transformMenuModule(menuModule: MenuModule): Menu { ...@@ -42,6 +42,7 @@ export function transformMenuModule(menuModule: MenuModule): Menu {
forEach(menuList, (m) => { forEach(menuList, (m) => {
!isUrl(m.path) && joinParentPath(menuList, m); !isUrl(m.path) && joinParentPath(menuList, m);
}); });
return menuList[0]; return menuList[0];
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论