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>

浙公网安备 33010602011771号