/** * 该插件可根据菜单配置自动生成 ANTD menu组件 * menuOptions示例: * [ * { * name: '菜单名称', * path: '菜单路由', * meta: { * icon: '菜单图标', * invisible: 'boolean, 是否不可见, 默认 false', * }, * children: [子菜单配置] * }, * { * name: '菜单名称', * path: '菜单路由', * meta: { * icon: '菜单图标', * invisible: 'boolean, 是否不可见, 默认 false', * }, * children: [子菜单配置] * } * ] * * i18n: 国际化配置。系统默认会根据 options route配置的 path 和 name 生成英文以及中文的国际化配置,如需自定义或增加其他语言,配置 * 此项即可。如: * i18n: { * messages: { * zh_CN: {dashboard: {name: '监控中心'}} * * } * } **/ import Menu from "ant-design-vue/es/menu"; import Icon from "ant-design-vue/es/icon"; import fastEqual from "fast-deep-equal"; import {getI18nKey} from '@/utils/routerUtil' const { Item, SubMenu } = Menu; export default { name: "IMenu", props: { options: { type: Array, required: true, }, theme: { type: String, required: false, default: "dark", }, mode: { type: String, required: false, default: "inline", }, collapsed: { type: Boolean, required: false, default: false, }, i18n: Object, openKeys: Array, }, data() { return { selectedKeys: [], sOpenKeys: [], cachedOpenKeys: [], }; }, computed: { menuTheme() { return this.theme == "light" ? this.theme : "dark"; }, }, created() { this.updateMenu(); if (this.options.length > 0 && !this.options[0].fullPath) { this.formatOptions(this.options, ""); } // 自定义国际化配置 if (this.i18n && this.i18n.messages) { const messages = this.i18n.messages; Object.keys(messages).forEach((key) => { this.$i18n.mergeLocaleMessage(key, messages[key]); }); } }, watch: { options(val) { if (val.length > 0 && !val[0].fullPath) { this.formatOptions(this.options, ""); } }, i18n(val) { if (val && val.messages) { const messages = this.i18n.messages; Object.keys(messages).forEach((key) => { this.$i18n.mergeLocaleMessage(key, messages[key]); }); } }, collapsed(val) { if (val) { this.cachedOpenKeys = this.sOpenKeys; this.sOpenKeys = []; } else { this.sOpenKeys = this.cachedOpenKeys; } }, $route: function() { this.updateMenu(); }, sOpenKeys(val) { this.$emit("openChange", val); this.$emit("update:openKeys", val); }, }, methods: { renderIcon: function(h, icon, key) { if (this.$scopedSlots.icon && icon && icon !== "none") { const vnodes = this.$scopedSlots.icon({ icon, key }); vnodes.forEach((vnode) => { vnode.data.class = vnode.data.class ? vnode.data.class : []; vnode.data.class.push("anticon"); }); return vnodes; } return !icon || icon == "none" ? null : h(Icon, { props: { type: icon } }); }, renderMenuItem: function(h, menu) { let tag = "router-link"; let config = { props: { to: menu.fullPath }, attrs: { style: "overflow:hidden;white-space:normal;text-overflow:clip;" }, }; if (menu.meta && menu.meta.link) { tag = "a"; config = { attrs: { style: "overflow:hidden;white-space:normal;text-overflow:clip;", href: menu.meta.link, target: "_blank", }, }; } return h(Item, { key: menu.fullPath }, [ h(tag, config, [ this.renderIcon(h, menu.meta ? menu.meta.icon : "none", menu.fullPath), this.$t(getI18nKey(menu.fullPath)), ]), ]); }, renderSubMenu: function(h, menu) { let this_ = this; let subItem = [ h( "span", { slot: "title", attrs: { style: "overflow:hidden;white-space:normal;text-overflow:clip;" } }, [ this.renderIcon(h, menu.meta ? menu.meta.icon : "none", menu.fullPath), this.$t(getI18nKey(menu.fullPath)), ] ), ]; let itemArr = []; menu.children.forEach(function(item) { itemArr.push(this_.renderItem(h, item)); }); return h(SubMenu, { key: menu.fullPath }, subItem.concat(itemArr)); }, renderItem: function(h, menu) { const meta = menu.meta; if (!meta || !meta.invisible) { let renderChildren = false; const children = menu.children; if (children != undefined) { for (let i = 0; i < children.length; i++) { const childMeta = children[i].meta; if (!childMeta || !childMeta.invisible) { renderChildren = true; break; } } } return menu.children && renderChildren ? this.renderSubMenu(h, menu) : this.renderMenuItem(h, menu); } }, renderMenu: function(h, menuTree) { let this_ = this; let menuArr = []; menuTree.forEach(function(menu, i) { menuArr.push(this_.renderItem(h, menu, "0", i)); }); return menuArr; }, formatOptions(options, parentPath) { options.forEach((route) => { let isFullPath = route.path.substring(0, 1) == "/"; route.fullPath = isFullPath ? route.path : parentPath + "/" + route.path; if (route.children) { this.formatOptions(route.children, route.fullPath); } }); }, updateMenu() { const matchedRoutes = this.$route.matched.filter((item) => item.path !== ""); this.selectedKeys = this.getSelectedKey(this.$route); let openKeys = matchedRoutes.map((item) => item.path); openKeys = openKeys.slice(0, openKeys.length - 1); if (!fastEqual(openKeys, this.sOpenKeys)) { this.collapsed || this.mode === "horizontal" ? (this.cachedOpenKeys = openKeys) : (this.sOpenKeys = openKeys); } }, getSelectedKey(route) { return route.matched.map((item) => item.path); }, }, render(h) { return h( Menu, { props: { theme: this.menuTheme, mode: this.$props.mode, selectedKeys: this.selectedKeys, openKeys: this.openKeys ? this.openKeys : this.sOpenKeys, }, on: { "update:openKeys": (val) => { this.sOpenKeys = val; }, click: (obj) => { obj.selectedKeys = [obj.key]; this.$emit("select", obj); }, }, }, this.renderMenu(h, this.options) ); }, };