diff --git a/src/components/Application/src/AppLogo.vue b/src/components/Application/src/AppLogo.vue index 426cba051..270bb4853 100644 --- a/src/components/Application/src/AppLogo.vue +++ b/src/components/Application/src/AppLogo.vue @@ -66,8 +66,9 @@ .@{prefix-cls} { display: flex; align-items: center; - padding-left: 12px; + padding-left: 7px; cursor: pointer; + transition: all 0.2s ease; &.collapsed-show-title { padding-left: 20px; diff --git a/src/components/Menu/index.ts b/src/components/Menu/index.ts index daef98bc2..e918d4eb1 100644 --- a/src/components/Menu/index.ts +++ b/src/components/Menu/index.ts @@ -2,6 +2,8 @@ import { withInstall } from '../util'; 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); diff --git a/src/components/Menu/src/BasicMenu.tsx b/src/components/Menu/src/BasicMenu.tsx deleted file mode 100644 index d33222066..000000000 --- a/src/components/Menu/src/BasicMenu.tsx +++ /dev/null @@ -1,267 +0,0 @@ -import './index.less'; - -import type { MenuState } from './types'; -import type { Menu as MenuType } from '/@/router/types'; - -import { - computed, - defineComponent, - unref, - reactive, - watch, - toRefs, - ComputedRef, - ref, - CSSProperties, -} from 'vue'; -import { Menu } from 'ant-design-vue'; -import MenuContent from './MenuContent'; -// import { ScrollContainer } from '/@/components/Container'; -// import { BasicArrow } from '/@/components/Basic'; - -import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum'; -import { ThemeEnum } from '/@/enums/appEnum'; - -import { appStore } from '/@/store/modules/app'; - -import { useOpenKeys } from './useOpenKeys'; -import { useRouter } from 'vue-router'; - -import { isFunction } from '/@/utils/is'; -import { getSlot } from '/@/utils/helper/tsxHelper'; -import { menuHasChildren } from './helper'; -import { getCurrentParentPath } from '/@/router/menus'; - -import { basicProps } from './props'; -import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; -import { REDIRECT_NAME } from '/@/router/constant'; -import { tabStore } from '/@/store/modules/tab'; -import { useDesign } from '/@/hooks/web/useDesign'; -export default defineComponent({ - name: 'BasicMenu', - props: basicProps, - emits: ['menuClick'], - setup(props, { slots, emit }) { - const currentParentPath = ref(''); - const isClickGo = ref(false); - - const menuState = reactive({ - defaultSelectedKeys: [], - mode: props.mode, - theme: computed(() => props.theme) as ComputedRef, - openKeys: [], - selectedKeys: [], - collapsedOpenKeys: [], - }); - - const { prefixCls } = useDesign('basic-menu'); - - const { items, mode, accordion } = toRefs(props); - - const { getCollapsed, getIsHorizontal, getTopMenuAlign, getSplit } = useMenuSetting(); - - const { currentRoute } = useRouter(); - - const { handleOpenChange, setOpenKeys, getOpenKeys } = useOpenKeys( - menuState, - items, - mode, - accordion - ); - - const getMenuClass = computed(() => { - const { type } = props; - const { mode } = menuState; - return [ - prefixCls, - `justify-${unref(getTopMenuAlign)}`, - { - [`${prefixCls}--hide-title`]: !unref(showTitle), - [`${prefixCls}--collapsed-show-title`]: props.collapsedShowTitle, - [`${prefixCls}__second`]: - !props.isHorizontal && appStore.getProjectConfig.menuSetting.split, - [`${prefixCls}__sidebar-hor`]: - type === MenuTypeEnum.TOP_MENU && mode === MenuModeEnum.HORIZONTAL, - }, - ]; - }); - - 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) || getSplit.value; - - return { - height: isHorizontal ? '100%' : `calc(100% - ${props.showLogo ? '48px' : '0px'})`, - overflowY: isHorizontal ? 'hidden' : 'auto', - }; - } - ); - - watch( - () => tabStore.getCurrentTab, - () => { - if (unref(currentRoute).name === REDIRECT_NAME) return; - handleMenuChange(); - unref(getSplit) && getParentPath(); - } - ); - - watch( - () => props.items, - () => { - handleMenuChange(); - }, - { - immediate: true, - } - ); - - getParentPath(); - - async function getParentPath() { - const { appendClass } = props; - if (!appendClass) return ''; - const parentPath = await getCurrentParentPath(unref(currentRoute).path); - - currentParentPath.value = parentPath; - } - - async function handleMenuClick({ key, keyPath }: { key: string; keyPath: string[] }) { - const { beforeClickFn } = props; - if (beforeClickFn && isFunction(beforeClickFn)) { - const flag = await beforeClickFn(key); - if (!flag) return; - } - emit('menuClick', key); - - isClickGo.value = true; - menuState.openKeys = keyPath; - menuState.selectedKeys = [key]; - } - - function handleMenuChange() { - if (unref(isClickGo)) { - isClickGo.value = false; - return; - } - const path = unref(currentRoute).path; - if (menuState.mode !== MenuModeEnum.HORIZONTAL) { - setOpenKeys(path); - } - menuState.selectedKeys = [path]; - } - - // function renderExpandIcon({ key }: { key: string }) { - // const isOpen = getOpenKeys.value.includes(key); - // const collapsed = unref(getCollapsed); - // return ( - // - // ); - // } - - 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 isAppendActiveCls = - appendClass && level === 1 && menu.path === unref(currentParentPath); - - const levelCls = [ - `${prefixCls}-item__level${level}`, - ` ${menuState.theme} `, - { - 'top-active-menu': isAppendActiveCls, - }, - ]; - return ( - - {() => [ - , - ]} - - ); - } - - function renderSubMenu(menu: MenuType, level: number) { - const levelCls = `${prefixCls}-item__level${level} ${menuState.theme} `; - return ( - - {{ - title: () => [ - , - ], - // expandIcon: renderExpandIcon, - default: () => (menu.children || []).map((item) => renderItem(item, level + 1)), - }} - - ); - } - - function renderMenu() { - const { selectedKeys, defaultSelectedKeys, mode, theme } = menuState; - - return ( - - {{ - default: () => unref(items).map((item) => renderItem(item)), - }} - - ); - } - - return () => { - return ( - <> - {!unref(getIsHorizontal) && getSlot(slots, 'header')} -
- {renderMenu()} -
- - ); - }; - }, -}); diff --git a/src/components/Menu/src/BasicMenu.vue b/src/components/Menu/src/BasicMenu.vue new file mode 100644 index 000000000..ecd730f7e --- /dev/null +++ b/src/components/Menu/src/BasicMenu.vue @@ -0,0 +1,209 @@ + + + diff --git a/src/components/Menu/src/components/BasicMenuItem.vue b/src/components/Menu/src/components/BasicMenuItem.vue new file mode 100644 index 000000000..f8d8ee795 --- /dev/null +++ b/src/components/Menu/src/components/BasicMenuItem.vue @@ -0,0 +1,39 @@ + + diff --git a/src/components/Menu/src/components/BasicSubMenuItem.vue b/src/components/Menu/src/components/BasicSubMenuItem.vue new file mode 100644 index 000000000..1a87265bb --- /dev/null +++ b/src/components/Menu/src/components/BasicSubMenuItem.vue @@ -0,0 +1,53 @@ + + diff --git a/src/components/Menu/src/components/ExpandIcon.vue b/src/components/Menu/src/components/ExpandIcon.vue new file mode 100644 index 000000000..eafd5ed63 --- /dev/null +++ b/src/components/Menu/src/components/ExpandIcon.vue @@ -0,0 +1,43 @@ + + diff --git a/src/components/Menu/src/helper.ts b/src/components/Menu/src/helper.ts deleted file mode 100644 index 51f069432..000000000 --- a/src/components/Menu/src/helper.ts +++ /dev/null @@ -1,12 +0,0 @@ -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 - ); -} diff --git a/src/components/Menu/src/index.less b/src/components/Menu/src/index.less index b6e7ba5ef..0a67946d5 100644 --- a/src/components/Menu/src/index.less +++ b/src/components/Menu/src/index.less @@ -4,6 +4,7 @@ .active-style() { color: @white; + // background: @primary-color !important; background: linear-gradient( 118deg, rgba(@primary-color, 0.8), @@ -27,6 +28,7 @@ // right: 16px; // width: 10px; // transform-origin: none; + // opacity: 0.45; // span[role='img'] { // margin-right: 0; @@ -52,9 +54,9 @@ > .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 .ant-menu-submenu-title { + padding-right: 16px !important; + padding-left: 16px !important; } } @@ -87,32 +89,33 @@ } } - // .ant-menu-item { - // transition: unset; - // } + .ant-menu-item { + transition: unset; + } // scrollbar -s tart - &-wrapper { - /* 滚动槽 */ - &::-webkit-scrollbar { - width: 5px; - height: 5px; - } + // &-wrapper { - &::-webkit-scrollbar-track { - background: rgba(0, 0, 0, 0); - } + /* 滚动槽 */ + // &::-webkit-scrollbar { + // width: 5px; + // height: 5px; + // } - &::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.2); - border-radius: 3px; - box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.1); - } + // &::-webkit-scrollbar-track { + // background: rgba(0, 0, 0, 0); + // } - ::-webkit-scrollbar-thumb:hover { - background: @border-color-dark; - } - } + // &::-webkit-scrollbar-thumb { + // 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 @@ -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) { // Reset menu item row height .ant-menu-item:not(.@{basic-menu-prefix-cls}-item__level1), @@ -255,10 +250,6 @@ background: @sider-dark-bg-color; .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-submenu-selected.@{basic-menu-prefix-cls}-menu-item__level1 { color: @white; @@ -304,10 +295,6 @@ overflow: hidden; 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-submenu-selected.@{basic-menu-prefix-cls}-menu-item__level1 { color: @primary-color; @@ -332,6 +319,7 @@ align-items: center; } } + .@{basic-menu-prefix-cls}__tag { position: absolute; top: calc(50% - 8px); @@ -368,6 +356,20 @@ 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 { @@ -375,7 +377,6 @@ > ul { background: @sider-dark-bg-color; } - .active-menu-style(); } } diff --git a/src/components/Menu/src/props.ts b/src/components/Menu/src/props.ts index 0240ed0ff..85aac1f14 100644 --- a/src/components/Menu/src/props.ts +++ b/src/components/Menu/src/props.ts @@ -3,57 +3,47 @@ import type { PropType } from 'vue'; import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum'; import { ThemeEnum } from '/@/enums/appEnum'; +import { propTypes } from '/@/utils/propTypes'; export const basicProps = { items: { type: Array as PropType, default: () => [], }, - appendClass: { - type: Boolean as PropType, - default: false, - }, + appendClass: propTypes.bool, - collapsedShowTitle: { - type: Boolean as PropType, - default: false, - }, + collapsedShowTitle: propTypes.bool, // 最好是4 倍数 - inlineIndent: { - type: Number as PropType, - default: 20, - }, + inlineIndent: propTypes.number.def(20), // 菜单组件的mode属性 mode: { type: String as PropType, default: MenuModeEnum.INLINE, }, - showLogo: { - type: Boolean as PropType, - default: false, - }, + showLogo: propTypes.bool, type: { type: String as PropType, default: MenuTypeEnum.MIX, }, - theme: { - type: String as PropType, - default: ThemeEnum.DARK, - }, - inlineCollapsed: { - type: Boolean as PropType, - default: false, - }, + theme: propTypes.string.def(ThemeEnum.DARK), + inlineCollapsed: propTypes.bool, - isHorizontal: { - type: Boolean as PropType, - default: false, - }, - accordion: { - type: Boolean as PropType, - default: true, - }, + isHorizontal: propTypes.bool, + accordion: propTypes.bool.def(true), beforeClickFn: { type: Function as PropType<(key: string) => Promise>, }, }; + +export const itemProps = { + item: { + type: Object as PropType, + default: {}, + }, + level: propTypes.number, + theme: propTypes.oneOf(['dark', 'light']), + appendClass: propTypes.bool, + parentPath: propTypes.string, + showTitle: propTypes.bool, + isHorizontal: propTypes.bool, +}; diff --git a/src/components/Menu/src/types.ts b/src/components/Menu/src/types.ts index 2415d39c6..ad711c27b 100644 --- a/src/components/Menu/src/types.ts +++ b/src/components/Menu/src/types.ts @@ -1,15 +1,15 @@ -import { ComputedRef } from 'vue'; -import { ThemeEnum } from '/@/enums/appEnum'; -import { MenuModeEnum } from '/@/enums/menuEnum'; +// import { ComputedRef } from 'vue'; +// import { ThemeEnum } from '/@/enums/appEnum'; +// import { MenuModeEnum } from '/@/enums/menuEnum'; export interface MenuState { // 默认选中的列表 defaultSelectedKeys: string[]; // 模式 - mode: MenuModeEnum; + // mode: MenuModeEnum; - // 主题 - theme: ComputedRef | ThemeEnum; + // // 主题 + // theme: ComputedRef | ThemeEnum; // 缩进 inlineIndent?: number; diff --git a/src/enums/appEnum.ts b/src/enums/appEnum.ts index f703ed3b9..4ff0ed83a 100644 --- a/src/enums/appEnum.ts +++ b/src/enums/appEnum.ts @@ -1,4 +1,4 @@ -export const SIDE_BAR_MINI_WIDTH = 58; +export const SIDE_BAR_MINI_WIDTH = 48; export const SIDE_BAR_SHOW_TIT_MINI_WIDTH = 80; export enum ContentEnum { diff --git a/src/layouts/default/tabs/index.less b/src/layouts/default/tabs/index.less index b4cd9c7c4..44f6ffd87 100644 --- a/src/layouts/default/tabs/index.less +++ b/src/layouts/default/tabs/index.less @@ -34,9 +34,9 @@ border: 1px solid darken(@border-color-light, 6%); transition: none; - &:not(.ant-tabs-tab-active)::before { + &:not(.ant-tabs-tab-active)::after { position: absolute; - top: -1px; + bottom: -1px; left: 50%; width: 100%; height: 2px; @@ -53,7 +53,7 @@ opacity: 1; } - &:not(.ant-tabs-tab-active)::before { + &:not(.ant-tabs-tab-active)::after { opacity: 1; transform: translate(-50%, 0) scaleX(1); transition: all 0.3s ease-in-out; diff --git a/src/router/helper/menuHelper.ts b/src/router/helper/menuHelper.ts index 9c5913265..59f6db7b9 100644 --- a/src/router/helper/menuHelper.ts +++ b/src/router/helper/menuHelper.ts @@ -42,6 +42,7 @@ export function transformMenuModule(menuModule: MenuModule): Menu { forEach(menuList, (m) => { !isUrl(m.path) && joinParentPath(menuList, m); }); + return menuList[0]; }