el-menu 二次封装

element-plus —— el-menu 进行二次封装

本组件通过 render 函数进行封装,在 setup 中直接返回一个函数。

通过数组数据自动生成菜单,数据类型请看 接口定义部分 和 数据模型部分

<script lang="ts">
import { ref, reactive, defineComponent, h, getCurrentInstance, resolveComponent } from "vue";
import { useRouter, onBeforeRouteUpdate } from "vue-router";
import { getImageUrl } from "../utils";
interface MenuProps { // 接口定义
  name: string;
  path?: string;
  icon?: string | undefined;
  children?: [];
}
export default defineComponent({
  props: {
    menus: {
      type: Array,
      default: () => [ // 数据模型
        {
          name: "系统服务",
          icon: "../assets/icons/xinxianqian_icon@2x.png",
          children: [
            {
              name: "服务调用情况分析",
              children: [
                {
                  name: "服务调用记录查询",
                  path: "/serviceCallRecordView",
                },
                {
                  name: "服务覆盖情况报告",
                  path: "/serviceCoverageReport",
                },
                {
                  name: "服务调用情况报告",
                  path: "/serviceCallReport",
                },
              ],
            },
          ],
        },
        {
          name: "信息安全",
          icon: "../assets/icons/ipu_icons@2x.png",
        },
        {
          name: "业务领域",
          icon: "../assets/icons/业务领域_icosn@2x.png",
        },
      ],
    },
  },

  setup(props) {
    const router = useRouter();
	//因为使用公司自己的组件库,由于特殊原因才这样使用,在这里需要获取组件实例,如果你使用官方的  element-plus 可以直接通过组件名进行使用或者通过 import 在文件顶部引入 ElMenu、ElMenuItem、ElSubMenu 组件
    const YtoMenu = resolveComponent("YtoMenu");
    const YtoMenuItem = resolveComponent("YtoMenuItem");
    const YtoSubmenu: any = resolveComponent("YtoSubmenu");
    const internalInstance = getCurrentInstance(); //
    const isCollapse = ref(false);
    const defaultOpeneds = reactive([]);
    const defaultActive = ref();
    const menuList = [ // 当前路由激活时,回显临时使用
      { name: "服务调用记录查询", path: "/serviceCallRecordView" },
      { name: "服务覆盖情况报告", path: "/serviceCoverageReport" },
      { name: "服务调用情况报告", path: "/serviceCallReport" },
    ];
    const open = () => {
      internalInstance?.appContext.config.globalProperties.$message({
        type: "error",
        message: "无效路径!",
      });
    };
    const onMenu = (item: MenuProps) => { // 点击事件
      item.path && router.push({ path: item.path });
      !item.path && open();
    };
    const handleOpen = (key: string, keyPath: string) => { // 菜单展开时回调
      console.log(key, keyPath);
    };
    const handleClose = (key: string, keyPath: string) => { // 菜单关闭时回调
      console.log(key, keyPath);
    };
    const titleRender = (item: MenuProps) => {
      const arr = [h("span", null, item.name)];
      item.icon &&
        arr.unshift(
          h(
            "i",
            {
              class: "my-icon",
            },
            [
              h("img", {
                src: getImageUrl(item.icon),
                alt: "",
                class: "mn-icon",
              }),
            ]
          )
        );

      return arr;
    };
    const renderChildrenNode = (menus: any) => {
      return menus.map((item: any, index: number) => {
        if (!item.children || !item.children.length) {
          return h(
            YtoMenuItem,
            {
              onClick: () => {
                onMenu(item);
              },
              arrts: {
                index: item.path || item.name,
              },
              index: item.path || item.name,
            },
            [h("div", { class: "my-menu-item-title" }, titleRender(item))]
          );
        } else {
          return h(
            YtoSubmenu,
            {
              index: item.name,
            },
            {
              default: renderChildrenNode(item.children),
              title: titleRender(item),
            }
          );
        }
      });
    };
    const beForeRoute: any = ref({});
    const setMenuActive = (_menus: any) => {
      const toPath = beForeRoute.value.path;
      try {
        _menus.forEach((item: any) => {
          if (item.path && item.path === toPath) {
            defaultActive.value = toPath;
            throw new Error("菜单已匹配...");
          } else {
            defaultActive.value = "";
          }
          if (item.children && item.children.length) {
            setMenuActive(item.children);
          }
        });
      } catch (error) {
        console.log(error);
      }
    };
    // onBeforeRouteUpdate((to) => {
    //   beForeRoute.value = to;
    //   setMenuActive(props.menus);
    // });
    onBeforeRouteUpdate((to) => {
      const bool = menuList.some((item) => item.path === to.path);
      if (!bool) {
        defaultActive.value = "";
      } else {
        defaultActive.value = to.path;
      }
    });
    return () => {
      return h(
        "div",
        {
          class: "yt-menu-w",
        },
        [
          h(
            YtoMenu,
            {
              class: "el-menu-vertical-demo",
              onOpen: (key: string, keyPath: string) => {
                console.log(key);
                handleOpen(key, keyPath);
              },
              onClose: (key: string, keyPath: string) => {
                handleClose(key, keyPath);
              },
              collapse: isCollapse.value,
              defaultOpeneds: defaultOpeneds,
              defaultActive: defaultActive.value,
            },
            [renderChildrenNode(props.menus)]
          ),
          h("div", { class: "collapse-btn" }, [
            h("i", {
              class: "yto-icon-s-fold",
              size: 22,
              onClick: () => (isCollapse.value = !isCollapse.value),
            }),
          ]),
        ]
      );
    };
  },
});
</script>
<style lang="scss" scoped>
.yt-menu-w {
  height: 100%;
  background: #ffffff;
  @extend .df;
  @extend .fdc;
}
.el-menu {
  flex: 1;
  border: none;
  box-shadow: 2px 0px 6px 0px rgb(0 21 41 / 12%);
  padding: 8px 0;
}
:deep(.el-submenu__title) {
  height: 40px;
}
.el-menu-item {
  height: 40px;
  line-height: 40px;
}
.mn-icon {
  height: 14px;
  width: 14px;
}
.my-icon {
  @extend .df;
  @extend .aic;
  @extend .jcc;
}
.collapse-btn {
  width: 100%;
  height: 48px;
  @extend .df;
  @extend .aic;
  border-top: 1px solid #f0f0f0;
  padding-left: 16px;
  i {
    cursor: pointer;
  }
}
.el-menu-item {
  position: relative;
}
.el-sub-menu-collapse {
  width: auto !important;
}
:deep(.el-menu-vertical-demo:not(.el-menu--collapse)) {
  width: 208px;
}
:deep(.is-opened .el-submenu__title) {
  color: #2c3cd8 !important;
}
:deep(.el-submenu__title) {
  @extend .df;
  @extend .aic;
  i {
    padding-right: 10px;
  }
}
:deep(.el-menu-item.is-active) {
  background: rgba(44, 60, 216, 0.1);
}
.is-active-after {
  position: absolute;
  top: 0;
  right: 0;
  display: block;
  content: "";
  width: 2px;
  height: 100%;
  background: #444cff;
}
:deep(.el-menu-item.is-active)::after {
  @extend .is-active-after;
}
.my-menu-item-title {
  @extend .df;
  & > i {
    padding-right: 10px;
  }
}
.el-menu-item:hover::after {
  @extend .is-active-after;
}
</style>

posted @ 2022-02-08 15:16  从此世间无星辰  阅读(731)  评论(0)    收藏  举报