From 858b9bccead46cdefc99325b7c956d50a2964309 Mon Sep 17 00:00:00 2001 From: Ben Lin <maobin001@msn.com> Date: 星期六, 08 三月 2025 10:20:28 +0800 Subject: [PATCH] 一些优化 --- src/components/SimpleMenu/src/components/SubMenuItem.vue | 451 ++++++++++++++++++++++++++----------------------------- 1 files changed, 212 insertions(+), 239 deletions(-) diff --git a/src/components/SimpleMenu/src/components/SubMenuItem.vue b/src/components/SimpleMenu/src/components/SubMenuItem.vue index d06e461..9c4a8c6 100644 --- a/src/components/SimpleMenu/src/components/SubMenuItem.vue +++ b/src/components/SimpleMenu/src/components/SubMenuItem.vue @@ -10,7 +10,7 @@ /> </div> <CollapseTransition> - <ul :class="prefixCls" v-show="opened"> + <ul :class="prefixCls" v-show="state.opened"> <slot></slot> </ul> </CollapseTransition> @@ -20,9 +20,10 @@ placement="right" :overlayClassName="`${prefixCls}-menu-popover`" v-else - :visible="getIsOpend" - @visible-change="handleVisibleChange" + :open="getIsOpend" + @on-open-change="handleVisibleChange" :overlayStyle="getOverlayStyle" + :overlayInnerStyle="{ padding: 0 }" :align="{ offset: [0, 0] }" > <div :class="getSubClass" v-bind="getEvents(false)"> @@ -44,7 +45,7 @@ /> </div> <!-- eslint-disable-next-line --> - <template #content v-show="opened"> + <template #content v-show="state.opened"> <div v-bind="getEvents(true)"> <ul :class="[prefixCls, `${prefixCls}-${getTheme}`, `${prefixCls}-popup`]"> <slot></slot> @@ -55,281 +56,253 @@ </li> </template> -<script lang="ts"> +<script lang="ts" setup> import { type TimeoutHandle, type Recordable } from '@vben/types'; import type { CSSProperties, PropType } from 'vue'; import type { SubMenuProvider } from './types'; import { - defineComponent, computed, unref, getCurrentInstance, - toRefs, reactive, provide, onBeforeMount, inject, } from 'vue'; - import { useDesign } from '/@/hooks/web/useDesign'; - import { propTypes } from '/@/utils/propTypes'; + import { useDesign } from '@/hooks/web/useDesign'; + import { propTypes } from '@/utils/propTypes'; import { useMenuItem } from './useMenu'; import { useSimpleRootMenuContext } from './useSimpleMenuContext'; - import { CollapseTransition } from '/@/components/Transition'; + import { CollapseTransition } from '@/components/Transition'; import Icon from '@/components/Icon/Icon.vue'; import { Popover } from 'ant-design-vue'; - import { isBoolean, isObject } from '/@/utils/is'; - import { mitt } from '/@/utils/mitt'; + import { isBoolean, isObject } from '@/utils/is'; + import { mitt } from '@/utils/mitt'; + + defineOptions({ name: 'SubMenu' }); + + const props = defineProps({ + name: { + type: [String, Number] as PropType<string | number>, + required: true, + }, + disabled: propTypes.bool, + collapsedShowTitle: propTypes.bool, + }); const DELAY = 200; - export default defineComponent({ - name: 'SubMenu', - components: { - Icon, - CollapseTransition, - Popover, - }, - props: { - name: { - type: [String, Number] as PropType<string | number>, - required: true, + const instance = getCurrentInstance(); + + const state = reactive({ + active: false, + opened: false, + }); + + const data = reactive({ + timeout: null as TimeoutHandle | null, + mouseInChild: false, + isChild: false, + }); + + const { getParentSubMenu, getItemStyle, getParentMenu, getParentList } = useMenuItem(instance); + + const { prefixCls } = useDesign('menu'); + + const subMenuEmitter = mitt(); + + const { rootMenuEmitter } = useSimpleRootMenuContext(); + + const { + addSubMenu: parentAddSubmenu, + removeSubMenu: parentRemoveSubmenu, + removeAll: parentRemoveAll, + getOpenNames: parentGetOpenNames, + isRemoveAllPopup, + sliceIndex, + level, + props: rootProps, + handleMouseleave: parentHandleMouseleave, + } = inject<SubMenuProvider>(`subMenu:${getParentMenu.value?.uid}`)!; + + const getClass = computed(() => { + return [ + `${prefixCls}-submenu`, + { + [`${prefixCls}-opened`]: state.opened, + [`${prefixCls}-submenu-disabled`]: props.disabled, + [`${prefixCls}-submenu-has-parent-submenu`]: unref(getParentSubMenu), + [`${prefixCls}-child-item-active`]: state.active, }, - disabled: propTypes.bool, - collapsedShowTitle: propTypes.bool, - }, - setup(props) { - const instance = getCurrentInstance(); + ]; + }); - const state = reactive({ - active: false, - opened: false, + const getAccordion = computed(() => rootProps.accordion); + const getCollapse = computed(() => rootProps.collapse); + const getTheme = computed(() => rootProps.theme); + + const getOverlayStyle = computed((): CSSProperties => { + return { + minWidth: '200px', + }; + }); + + const getIsOpend = computed(() => { + const name = props.name; + if (unref(getCollapse)) { + return parentGetOpenNames().includes(name); + } + return state.opened; + }); + + const getSubClass = computed(() => { + const isActive = rootProps.activeSubMenuNames.includes(props.name); + return [ + `${prefixCls}-submenu-title`, + { + [`${prefixCls}-submenu-active`]: isActive, + [`${prefixCls}-submenu-active-border`]: isActive && level === 0, + [`${prefixCls}-submenu-collapse`]: unref(getCollapse) && level === 0, + }, + ]; + }); + + function getEvents(deep: boolean) { + if (!unref(getCollapse)) { + return {}; + } + return { + onMouseenter: handleMouseenter, + onMouseleave: () => handleMouseleave(deep), + }; + } + + function handleClick() { + const { disabled } = props; + if (disabled || unref(getCollapse)) return; + const opened = state.opened; + + if (unref(getAccordion)) { + const { uidList } = getParentList(); + rootMenuEmitter.emit('on-update-opened', { + opend: false, + parent: instance?.parent, + uidList: uidList, }); - - const data = reactive({ - timeout: null as TimeoutHandle | null, - mouseInChild: false, - isChild: false, + } else { + rootMenuEmitter.emit('open-name-change', { + name: props.name, + opened: !opened, }); + } + state.opened = !opened; + } - const { getParentSubMenu, getItemStyle, getParentMenu, getParentList } = - useMenuItem(instance); + function handleMouseenter() { + const disabled = props.disabled; + if (disabled) return; - const { prefixCls } = useDesign('menu'); + subMenuEmitter.emit('submenu:mouse-enter-child'); - const subMenuEmitter = mitt(); + const index = parentGetOpenNames().findIndex((item) => item === props.name); - const { rootMenuEmitter } = useSimpleRootMenuContext(); + sliceIndex(index); - const { - addSubMenu: parentAddSubmenu, - removeSubMenu: parentRemoveSubmenu, - removeAll: parentRemoveAll, - getOpenNames: parentGetOpenNames, - isRemoveAllPopup, - sliceIndex, - level, - props: rootProps, - handleMouseleave: parentHandleMouseleave, - } = inject<SubMenuProvider>(`subMenu:${getParentMenu.value?.uid}`)!; + const isRoot = level === 0 && parentGetOpenNames().length === 2; + if (isRoot) { + parentRemoveAll(); + } + data.isChild = parentGetOpenNames().includes(props.name); + clearTimeout(data.timeout!); + data.timeout = setTimeout(() => { + parentAddSubmenu(props.name); + }, DELAY); + } - const getClass = computed(() => { - return [ - `${prefixCls}-submenu`, - { - [`${prefixCls}-item-active`]: state.active, - [`${prefixCls}-opened`]: state.opened, - [`${prefixCls}-submenu-disabled`]: props.disabled, - [`${prefixCls}-submenu-has-parent-submenu`]: unref(getParentSubMenu), - [`${prefixCls}-child-item-active`]: state.active, - }, - ]; - }); + function handleMouseleave(deepDispatch = false) { + const parentName = getParentMenu.value?.props.name; + if (!parentName) { + isRemoveAllPopup.value = true; + } - const getAccordion = computed(() => rootProps.accordion); - const getCollapse = computed(() => rootProps.collapse); - const getTheme = computed(() => rootProps.theme); + if (parentGetOpenNames().slice(-1)[0] === props.name) { + data.isChild = false; + } - const getOverlayStyle = computed((): CSSProperties => { - return { - minWidth: '200px', - }; - }); - - const getIsOpend = computed(() => { - const name = props.name; - if (unref(getCollapse)) { - return parentGetOpenNames().includes(name); - } - return state.opened; - }); - - const getSubClass = computed(() => { - const isActive = rootProps.activeSubMenuNames.includes(props.name); - return [ - `${prefixCls}-submenu-title`, - { - [`${prefixCls}-submenu-active`]: isActive, - [`${prefixCls}-submenu-active-border`]: isActive && level === 0, - [`${prefixCls}-submenu-collapse`]: unref(getCollapse) && level === 0, - }, - ]; - }); - - function getEvents(deep: boolean) { - if (!unref(getCollapse)) { - return {}; - } - return { - onMouseenter: handleMouseenter, - onMouseleave: () => handleMouseleave(deep), - }; - } - - function handleClick() { - const { disabled } = props; - if (disabled || unref(getCollapse)) return; - const opened = state.opened; - - if (unref(getAccordion)) { - const { uidList } = getParentList(); - rootMenuEmitter.emit('on-update-opened', { - opend: false, - parent: instance?.parent, - uidList: uidList, - }); - } else { - rootMenuEmitter.emit('open-name-change', { - name: props.name, - opened: !opened, - }); - } - state.opened = !opened; - } - - function handleMouseenter() { - const disabled = props.disabled; - if (disabled) return; - - subMenuEmitter.emit('submenu:mouse-enter-child'); - - const index = parentGetOpenNames().findIndex((item) => item === props.name); - - sliceIndex(index); - - const isRoot = level === 0 && parentGetOpenNames().length === 2; - if (isRoot) { + subMenuEmitter.emit('submenu:mouse-leave-child'); + if (data.timeout) { + clearTimeout(data.timeout!); + data.timeout = setTimeout(() => { + if (isRemoveAllPopup.value) { parentRemoveAll(); + } else if (!data.mouseInChild) { + parentRemoveSubmenu(props.name); } - data.isChild = parentGetOpenNames().includes(props.name); - clearTimeout(data.timeout!); - data.timeout = setTimeout(() => { - parentAddSubmenu(props.name); - }, DELAY); + }, DELAY); + } + if (deepDispatch) { + if (getParentSubMenu.value) { + parentHandleMouseleave?.(true); } + } + } - function handleMouseleave(deepDispatch = false) { - const parentName = getParentMenu.value?.props.name; - if (!parentName) { - isRemoveAllPopup.value = true; - } + onBeforeMount(() => { + subMenuEmitter.on('submenu:mouse-enter-child', () => { + data.mouseInChild = true; + isRemoveAllPopup.value = false; + clearTimeout(data.timeout!); + }); + subMenuEmitter.on('submenu:mouse-leave-child', () => { + if (data.isChild) return; + data.mouseInChild = false; + clearTimeout(data.timeout!); + }); - if (parentGetOpenNames().slice(-1)[0] === props.name) { - data.isChild = false; + rootMenuEmitter.on( + 'on-update-opened', + (data: boolean | (string | number)[] | Recordable<any>) => { + if (unref(getCollapse)) return; + if (isBoolean(data)) { + state.opened = data; + return; } - - subMenuEmitter.emit('submenu:mouse-leave-child'); - if (data.timeout) { - clearTimeout(data.timeout!); - data.timeout = setTimeout(() => { - if (isRemoveAllPopup.value) { - parentRemoveAll(); - } else if (!data.mouseInChild) { - parentRemoveSubmenu(props.name); - } - }, DELAY); - } - if (deepDispatch) { - if (getParentSubMenu.value) { - parentHandleMouseleave?.(true); + if (isObject(data) && rootProps.accordion) { + const { opend, parent, uidList } = data as Recordable<any>; + if (parent === instance?.parent) { + state.opened = opend; + } else if (!uidList.includes(instance?.uid)) { + state.opened = false; } + return; } + + if (props.name && Array.isArray(data)) { + state.opened = (data as (string | number)[]).includes(props.name); + } + }, + ); + + rootMenuEmitter.on('on-update-active-name:submenu', (data: number[]) => { + if (instance?.uid) { + state.active = data.includes(instance?.uid); } + }); + }); - onBeforeMount(() => { - subMenuEmitter.on('submenu:mouse-enter-child', () => { - data.mouseInChild = true; - isRemoveAllPopup.value = false; - clearTimeout(data.timeout!); - }); - subMenuEmitter.on('submenu:mouse-leave-child', () => { - if (data.isChild) return; - data.mouseInChild = false; - clearTimeout(data.timeout!); - }); + function handleVisibleChange(visible: boolean) { + state.opened = visible; + } - rootMenuEmitter.on( - 'on-update-opened', - (data: boolean | (string | number)[] | Recordable<any>) => { - if (unref(getCollapse)) return; - if (isBoolean(data)) { - state.opened = data; - return; - } - if (isObject(data) && rootProps.accordion) { - const { opend, parent, uidList } = data as Recordable<any>; - if (parent === instance?.parent) { - state.opened = opend; - } else if (!uidList.includes(instance?.uid)) { - state.opened = false; - } - return; - } - - if (props.name && Array.isArray(data)) { - state.opened = (data as (string | number)[]).includes(props.name); - } - }, - ); - - rootMenuEmitter.on('on-update-active-name:submenu', (data: number[]) => { - if (instance?.uid) { - state.active = data.includes(instance?.uid); - } - }); - }); - - function handleVisibleChange(visible: boolean) { - state.opened = visible; - } - - // provide - provide<SubMenuProvider>(`subMenu:${instance?.uid}`, { - addSubMenu: parentAddSubmenu, - removeSubMenu: parentRemoveSubmenu, - getOpenNames: parentGetOpenNames, - removeAll: parentRemoveAll, - isRemoveAllPopup, - sliceIndex, - level: level + 1, - handleMouseleave, - props: rootProps, - }); - - return { - getClass, - prefixCls, - getCollapse, - getItemStyle, - handleClick, - handleVisibleChange, - getParentSubMenu, - getOverlayStyle, - getTheme, - getIsOpend, - getEvents, - getSubClass, - ...toRefs(state), - ...toRefs(data), - }; - }, + // provide + provide<SubMenuProvider>(`subMenu:${instance?.uid}`, { + addSubMenu: parentAddSubmenu, + removeSubMenu: parentRemoveSubmenu, + getOpenNames: parentGetOpenNames, + removeAll: parentRemoveAll, + isRemoveAllPopup, + sliceIndex, + level: level + 1, + handleMouseleave, + props: rootProps, }); </script> -- Gitblit v1.9.3