Ben Lin
2024-06-18 9dfa701454d6a94690bad39dbb0e38f2a0b31489
src/components/SimpleMenu/src/components/Menu.vue
@@ -4,11 +4,10 @@
  </ul>
</template>
<script lang="ts">
<script lang="ts" setup>
  import type { PropType } from 'vue';
  import type { SubMenuProvider } from './types';
  import {
    defineComponent,
    ref,
    computed,
    onMounted,
@@ -18,140 +17,136 @@
    getCurrentInstance,
    provide,
  } from 'vue';
  import { useDesign } from '@/hooks/web/useDesign';
  import { propTypes } from '@/utils/propTypes';
  import { createSimpleRootMenuContext, type MenuEmitterEvents } from './useSimpleMenuContext';
  import { mitt } from '@/utils/mitt';
  import { useDesign } from '/@/hooks/web/useDesign';
  import { propTypes } from '/@/utils/propTypes';
  import { createSimpleRootMenuContext } from './useSimpleMenuContext';
  import { mitt } from '/@/utils/mitt';
  defineOptions({ name: 'Menu' });
  export default defineComponent({
    name: 'Menu',
    props: {
      theme: propTypes.oneOf(['light', 'dark']).def('light'),
      activeName: propTypes.oneOfType([propTypes.string, propTypes.number]),
      openNames: {
        type: Array as PropType<string[]>,
        default: () => [],
      },
      accordion: propTypes.bool.def(true),
      width: propTypes.string.def('100%'),
      collapsedWidth: propTypes.string.def('48px'),
      indentSize: propTypes.number.def(16),
      collapse: propTypes.bool.def(true),
      activeSubMenuNames: {
        type: Array as PropType<(string | number)[]>,
        default: () => [],
      },
  const props = defineProps({
    theme: propTypes.oneOf(['light', 'dark']).def('light'),
    activeName: propTypes.oneOfType([propTypes.string, propTypes.number]),
    openNames: {
      type: Array as PropType<string[]>,
      default: () => [],
    },
    emits: ['select', 'open-change'],
    setup(props, { emit }) {
      const rootMenuEmitter = mitt();
      const instance = getCurrentInstance();
    accordion: propTypes.bool.def(true),
    width: propTypes.string.def('100%'),
    collapsedWidth: propTypes.string.def('48px'),
    indentSize: propTypes.number.def(16),
    collapse: propTypes.bool.def(true),
    activeSubMenuNames: {
      type: Array as PropType<(string | number)[]>,
      default: () => [],
    },
  });
      const currentActiveName = ref<string | number>('');
      const openedNames = ref<string[]>([]);
  const emit = defineEmits(['select', 'open-change']);
      const { prefixCls } = useDesign('menu');
  const rootMenuEmitter = mitt<MenuEmitterEvents>();
  const instance = getCurrentInstance();
      const isRemoveAllPopup = ref(false);
  const currentActiveName = ref<string | number>('');
  const openedNames = ref<(string | number)[]>([]);
      createSimpleRootMenuContext({
        rootMenuEmitter: rootMenuEmitter,
        activeName: currentActiveName,
  const { prefixCls } = useDesign('menu');
  const isRemoveAllPopup = ref(false);
  createSimpleRootMenuContext({
    rootMenuEmitter: rootMenuEmitter,
    activeName: currentActiveName,
  });
  const getClass = computed(() => {
    const { theme } = props;
    return [
      prefixCls,
      `${prefixCls}-${theme}`,
      `${prefixCls}-vertical`,
      {
        [`${prefixCls}-collapse`]: props.collapse,
      },
    ];
  });
  watchEffect(() => {
    openedNames.value = props.openNames;
  });
  watchEffect(() => {
    if (props.activeName) {
      currentActiveName.value = props.activeName;
    }
  });
  watch(
    () => props.openNames,
    () => {
      nextTick(() => {
        updateOpened();
      });
    },
  );
      const getClass = computed(() => {
        const { theme } = props;
        return [
          prefixCls,
          `${prefixCls}-${theme}`,
          `${prefixCls}-vertical`,
          {
            [`${prefixCls}-collapse`]: props.collapse,
          },
        ];
  function updateOpened() {
    rootMenuEmitter.emit('on-update-opened', openedNames.value);
  }
  function addSubMenu(name: string | number) {
    if (openedNames.value.includes(name)) return;
    openedNames.value.push(name);
    updateOpened();
  }
  function removeSubMenu(name: string | number) {
    openedNames.value = openedNames.value.filter((item) => item !== name);
    updateOpened();
  }
  function removeAll() {
    openedNames.value = [];
    updateOpened();
  }
  function sliceIndex(index: number) {
    if (index === -1) return;
    openedNames.value = openedNames.value.slice(0, index + 1);
    updateOpened();
  }
  provide<SubMenuProvider>(`subMenu:${instance?.uid}`, {
    addSubMenu,
    removeSubMenu,
    getOpenNames: () => openedNames.value,
    removeAll,
    isRemoveAllPopup,
    sliceIndex,
    level: 0,
    props: props as any,
  });
  onMounted(() => {
    openedNames.value = !props.collapse ? [...props.openNames] : [];
    updateOpened();
    rootMenuEmitter.on('on-menu-item-select', (name: string | number) => {
      currentActiveName.value = name;
      nextTick(() => {
        props.collapse && removeAll();
      });
      emit('select', name);
    });
      watchEffect(() => {
        openedNames.value = props.openNames;
      });
      watchEffect(() => {
        if (props.activeName) {
          currentActiveName.value = props.activeName;
        }
      });
      watch(
        () => props.openNames,
        () => {
          nextTick(() => {
            updateOpened();
          });
        },
      );
      function updateOpened() {
        rootMenuEmitter.emit('on-update-opened', openedNames.value);
      }
      function addSubMenu(name: string) {
        if (openedNames.value.includes(name)) return;
    rootMenuEmitter.on('open-name-change', ({ name, opened }) => {
      if (opened && !openedNames.value.includes(name)) {
        openedNames.value.push(name);
        updateOpened();
      } else if (!opened) {
        const index = openedNames.value.findIndex((item) => item === name);
        index !== -1 && openedNames.value.splice(index, 1);
      }
      function removeSubMenu(name: string) {
        openedNames.value = openedNames.value.filter((item) => item !== name);
        updateOpened();
      }
      function removeAll() {
        openedNames.value = [];
        updateOpened();
      }
      function sliceIndex(index: number) {
        if (index === -1) return;
        openedNames.value = openedNames.value.slice(0, index + 1);
        updateOpened();
      }
      provide<SubMenuProvider>(`subMenu:${instance?.uid}`, {
        addSubMenu,
        removeSubMenu,
        getOpenNames: () => openedNames.value,
        removeAll,
        isRemoveAllPopup,
        sliceIndex,
        level: 0,
        props: props as any,
      });
      onMounted(() => {
        openedNames.value = !props.collapse ? [...props.openNames] : [];
        updateOpened();
        rootMenuEmitter.on('on-menu-item-select', (name: string) => {
          currentActiveName.value = name;
          nextTick(() => {
            props.collapse && removeAll();
          });
          emit('select', name);
        });
        rootMenuEmitter.on('open-name-change', ({ name, opened }) => {
          if (opened && !openedNames.value.includes(name)) {
            openedNames.value.push(name);
          } else if (!opened) {
            const index = openedNames.value.findIndex((item) => item === name);
            index !== -1 && openedNames.value.splice(index, 1);
          }
        });
      });
      return { getClass, openedNames };
    },
    });
  });
</script>
<style lang="less">