| | |
| | | <template> |
| | | <Menu |
| | | v-bind="getBindValues" |
| | | :activeName="activeName" |
| | | :activeName="menuState.activeName" |
| | | :openNames="getOpenKeys" |
| | | :class="prefixCls" |
| | | :activeSubMenuNames="activeSubMenuNames" |
| | | :activeSubMenuNames="menuState.activeSubMenuNames" |
| | | @select="handleSelect" |
| | | > |
| | | <template v-for="item in items" :key="item.path"> |
| | |
| | | </template> |
| | | </Menu> |
| | | </template> |
| | | <script lang="ts"> |
| | | <script lang="ts" setup> |
| | | import type { MenuState } from './types'; |
| | | import type { Menu as MenuType } from '/@/router/types'; |
| | | import type { Menu as MenuType } from '@/router/types'; |
| | | import type { RouteLocationNormalizedLoaded } from 'vue-router'; |
| | | import { defineComponent, computed, ref, unref, reactive, toRefs, watch, PropType } from 'vue'; |
| | | import { useDesign } from '/@/hooks/web/useDesign'; |
| | | import { computed, ref, unref, reactive, toRefs, watch, PropType, useAttrs } from 'vue'; |
| | | import { useDesign } from '@/hooks/web/useDesign'; |
| | | import Menu from './components/Menu.vue'; |
| | | import SimpleSubMenu from './SimpleSubMenu.vue'; |
| | | import { listenerRouteChange } from '/@/logics/mitt/routeChange'; |
| | | import { propTypes } from '/@/utils/propTypes'; |
| | | import { REDIRECT_NAME } from '/@/router/constant'; |
| | | import { listenerRouteChange } from '@/logics/mitt/routeChange'; |
| | | import { propTypes } from '@/utils/propTypes'; |
| | | import { REDIRECT_NAME } from '@/router/constant'; |
| | | import { useRouter } from 'vue-router'; |
| | | import { isFunction, isUrl } from '/@/utils/is'; |
| | | import { openWindow } from '/@/utils'; |
| | | |
| | | import { isFunction, isHttpUrl } from '@/utils/is'; |
| | | import { openWindow } from '@/utils'; |
| | | import { useOpenKeys } from './useOpenKeys'; |
| | | |
| | | export default defineComponent({ |
| | | name: 'SimpleMenu', |
| | | components: { |
| | | Menu, |
| | | SimpleSubMenu, |
| | | defineOptions({ name: 'SimpleMenu', inheritAttrs: false }); |
| | | |
| | | const props = defineProps({ |
| | | items: { |
| | | type: Array as PropType<MenuType[]>, |
| | | default: () => [], |
| | | }, |
| | | inheritAttrs: false, |
| | | props: { |
| | | items: { |
| | | type: Array as PropType<MenuType[]>, |
| | | default: () => [], |
| | | }, |
| | | collapse: propTypes.bool, |
| | | mixSider: propTypes.bool, |
| | | theme: propTypes.string, |
| | | accordion: propTypes.bool.def(true), |
| | | collapsedShowTitle: propTypes.bool, |
| | | beforeClickFn: { |
| | | type: Function as PropType<(key: string) => Promise<boolean>>, |
| | | }, |
| | | isSplitMenu: propTypes.bool, |
| | | collapse: propTypes.bool, |
| | | mixSider: propTypes.bool, |
| | | theme: propTypes.string, |
| | | accordion: propTypes.bool.def(true), |
| | | collapsedShowTitle: propTypes.bool, |
| | | beforeClickFn: { |
| | | type: Function as PropType<(key: string) => Promise<boolean>>, |
| | | }, |
| | | emits: ['menuClick'], |
| | | setup(props, { attrs, emit }) { |
| | | const currentActiveMenu = ref(''); |
| | | const isClickGo = ref(false); |
| | | |
| | | const menuState = reactive<MenuState>({ |
| | | activeName: '', |
| | | openNames: [], |
| | | activeSubMenuNames: [], |
| | | }); |
| | | |
| | | const { currentRoute } = useRouter(); |
| | | const { prefixCls } = useDesign('simple-menu'); |
| | | const { items, accordion, mixSider, collapse } = toRefs(props); |
| | | |
| | | const { setOpenKeys, getOpenKeys } = useOpenKeys( |
| | | menuState, |
| | | items, |
| | | accordion, |
| | | mixSider as any, |
| | | collapse as any, |
| | | ); |
| | | |
| | | const getBindValues = computed(() => ({ ...attrs, ...props })); |
| | | |
| | | watch( |
| | | () => props.collapse, |
| | | (collapse) => { |
| | | if (collapse) { |
| | | menuState.openNames = []; |
| | | } else { |
| | | setOpenKeys(currentRoute.value.path); |
| | | } |
| | | }, |
| | | { immediate: true }, |
| | | ); |
| | | |
| | | watch( |
| | | () => props.items, |
| | | () => { |
| | | if (!props.isSplitMenu) { |
| | | return; |
| | | } |
| | | setOpenKeys(currentRoute.value.path); |
| | | }, |
| | | { flush: 'post' }, |
| | | ); |
| | | |
| | | listenerRouteChange((route) => { |
| | | if (route.name === REDIRECT_NAME) return; |
| | | |
| | | currentActiveMenu.value = route.meta?.currentActiveMenu as string; |
| | | handleMenuChange(route); |
| | | |
| | | if (unref(currentActiveMenu)) { |
| | | menuState.activeName = unref(currentActiveMenu); |
| | | setOpenKeys(unref(currentActiveMenu)); |
| | | } |
| | | }); |
| | | |
| | | async function handleMenuChange(route?: RouteLocationNormalizedLoaded) { |
| | | if (unref(isClickGo)) { |
| | | isClickGo.value = false; |
| | | return; |
| | | } |
| | | const path = (route || unref(currentRoute)).path; |
| | | |
| | | menuState.activeName = path; |
| | | |
| | | setOpenKeys(path); |
| | | } |
| | | |
| | | async function handleSelect(key: string) { |
| | | if (isUrl(key)) { |
| | | openWindow(key); |
| | | return; |
| | | } |
| | | const { beforeClickFn } = props; |
| | | if (beforeClickFn && isFunction(beforeClickFn)) { |
| | | const flag = await beforeClickFn(key); |
| | | if (!flag) return; |
| | | } |
| | | |
| | | emit('menuClick', key); |
| | | |
| | | isClickGo.value = true; |
| | | setOpenKeys(key); |
| | | menuState.activeName = key; |
| | | } |
| | | |
| | | return { |
| | | prefixCls, |
| | | getBindValues, |
| | | handleSelect, |
| | | getOpenKeys, |
| | | ...toRefs(menuState), |
| | | }; |
| | | }, |
| | | isSplitMenu: propTypes.bool, |
| | | }); |
| | | |
| | | const emit = defineEmits(['menuClick']); |
| | | |
| | | const attrs = useAttrs(); |
| | | |
| | | const currentActiveMenu = ref(''); |
| | | const isClickGo = ref(false); |
| | | |
| | | const menuState = reactive<MenuState>({ |
| | | activeName: '', |
| | | openNames: [], |
| | | activeSubMenuNames: [], |
| | | }); |
| | | |
| | | const { currentRoute } = useRouter(); |
| | | const { prefixCls } = useDesign('simple-menu'); |
| | | const { items, accordion, mixSider, collapse } = toRefs(props); |
| | | |
| | | const { setOpenKeys, getOpenKeys } = useOpenKeys( |
| | | menuState, |
| | | items, |
| | | accordion, |
| | | mixSider as any, |
| | | collapse as any, |
| | | ); |
| | | |
| | | const getBindValues = computed(() => ({ ...attrs, ...props })); |
| | | |
| | | watch( |
| | | () => props.collapse, |
| | | (collapse) => { |
| | | if (collapse) { |
| | | menuState.openNames = []; |
| | | } else { |
| | | setOpenKeys(currentRoute.value.path); |
| | | } |
| | | }, |
| | | { immediate: true }, |
| | | ); |
| | | |
| | | watch( |
| | | () => props.items, |
| | | () => { |
| | | if (!props.isSplitMenu) { |
| | | return; |
| | | } |
| | | setOpenKeys(currentRoute.value.path); |
| | | }, |
| | | { flush: 'post' }, |
| | | ); |
| | | |
| | | listenerRouteChange((route) => { |
| | | if (route.name === REDIRECT_NAME) return; |
| | | |
| | | currentActiveMenu.value = route.meta?.currentActiveMenu as string; |
| | | handleMenuChange(route); |
| | | |
| | | if (unref(currentActiveMenu)) { |
| | | menuState.activeName = unref(currentActiveMenu); |
| | | setOpenKeys(unref(currentActiveMenu)); |
| | | } |
| | | }); |
| | | |
| | | async function handleMenuChange(route?: RouteLocationNormalizedLoaded) { |
| | | if (unref(isClickGo)) { |
| | | isClickGo.value = false; |
| | | return; |
| | | } |
| | | const path = (route || unref(currentRoute)).path; |
| | | |
| | | menuState.activeName = path; |
| | | |
| | | setOpenKeys(path); |
| | | } |
| | | |
| | | async function handleSelect(key: string) { |
| | | if (isHttpUrl(key)) { |
| | | openWindow(key); |
| | | return; |
| | | } |
| | | const { beforeClickFn } = props; |
| | | if (beforeClickFn && isFunction(beforeClickFn)) { |
| | | const flag = await beforeClickFn(key); |
| | | if (!flag) return; |
| | | } |
| | | |
| | | emit('menuClick', key); |
| | | |
| | | isClickGo.value = true; |
| | | setOpenKeys(key); |
| | | menuState.activeName = key; |
| | | } |
| | | </script> |
| | | <style lang="less"> |
| | | @import url('./index.less'); |