生成菜單方法createMenu.jsimport navMenu from "./navMenu";
/** * 菜單組件 * @module widgets/my-menu * @example * * // 使用說明 */ export default { name: "createMenu", mixins: [navMenu], /** * 屬性參數 * @property {Array} [data = []] data 構成菜單內容的數組 * @property {Object} [props = { id: 'id', text: 'text', icon: 'icon', children: 'children', group: 'group', route: 'route' }] props 指引菜單組件根據data數組中元素的鍵名作為菜單中對應的顯示內容。例如:數組內元素為 {g_id:1, str:'第一組'}, 可以通過設置"props={id: 'g_id', text: 'str'}"來將數組的g_id對應為id,str對應為text * @property {String} [mode = 'verticle'] mode 默認為垂直模式,橫向 ‘horizontal’ * @property {Boolean} [collapse = false] 是否水平折疊收起菜單(僅在 mode 為 vertical 時可用) * @property {String} [backgroundColor] backgroundColor='#FFFFFF' 菜單的背景色(僅支持 hex 格式) * @property {String} [textColor] 菜單的文字顏色(僅支持 hex 格式) * @property {String} [activeTextColor] 當前激活菜單的文字顏色(僅支持 hex 格式) * @property {String} [defaultActive] 當前激活菜單的 index。 defaultActive = '1' * @property {Array} [defaultOpeneds] 當前打開的sub-menu的 key 數組。 defaultOpeneds = ['2', '4'] * @property {Boolean} [uniqueOpened = false] 是否只保持一個子菜單的展開 * @property {String} [menuTrigger = 'click'] 子菜單打開的觸發方式(只在 mode 為 horizontal 時有效) * @property {Boolean} [router = false] 是否使用 vue-router 的模式,啟用該模式會在激活導航時以 index 作為 path 進行路由跳轉 */ props: { // 菜單數據 data: { type: Array, default() { return []; } }, // 數據字段名稱映射 props: { type: Object, default() { return { id: "id", text: "text", icon: "icon", children: "children", group: "group", route: "route" }; } }, // 模式 horizontal / vertical mode: String, // 是否水平折疊收起菜單(僅在 mode 為 vertical 時可用) collapse: Boolean, // 菜單的背景色(僅支持 hex 格式) backgroundColor: String, // 菜單的文字顏色(僅支持 hex 格式) textColor: String, // 當前激活菜單的文字顏色(僅支持 hex 格式) activeTextColor: String, // 當前激活菜單的 index defaultActive: String, // 當前打開的sub-menu的 key 數組 defaultOpeneds: Array, // 是否只保持一個子菜單的展開 uniqueOpened: Boolean, // 子菜單打開的觸發方式(只在 mode 為 horizontal 時有效) menuTrigger: String, // 是否使用 vue-router 的模式,啟用該模式會在激活導航時以 index 作為 path 進行路由跳轉 router: Boolean, // 彈出菜單的自定義類名 popperClass: String }, methods: { grouping(array) { let groups = { default: [] }; let props = this.props; array.forEach(n => { let key = n[props.group]; if (key) { groups[key] = groups[key] || []; groups[key].push(n); } else { groups["default"].push(n); } }); return groups; }, createTitle(h, item) { return [ h("i", { class: item[this.props.icon] }), h( "span", { slot: "title" }, item[this.props.text] ) ]; }, createItem(h, item) { const key = (item[this.props.id] || "").toString(); return h( "el-menu-item", { props: { index: key, route: item[this.props.route], name: item[this.props.text] }, key: key, on: { // eslint-disable-next-line no-undef
//綁定點擊事件 點擊事件來源navMenu.js
click: (index, indexPath) => this.clickMenu(index, indexPath, item[this.props.text]) } }, this.createTitle(h, item) ); }, createSubmenu(h, item) { const key = (item[this.props.id] || "").toString(); return h( "el-submenu", { props: { index: (item[this.props.id] || "").toString(), popperClass: this.popperClass }, key: key }, [ h( "template", { slot: "title" }, this.createTitle(h, item) ), this.createNodes(h, item[this.props.children]) ] ); }, createGroup(h, title, items) { let nodes = []; let props = this.props; items.forEach(item => { if (item[props.children] && item[props.children].length > 0) { nodes.push(this.createSubmenu(h, item)); } else { nodes.push(this.createItem(h, item)); } }); nodes.unshift( h( "template", { slot: "title" }, title ) ); return h("el-menu-item-group", null, nodes); }, createNodes(h, array) { let nodes = [], groups = this.grouping(array); let props = this.props; for (let key in groups) { let items = groups[key] || []; if (key === "default") { items.forEach(item => { if (item[props.children] && item[props.children].length > 0) { nodes.push(this.createSubmenu(h, item)); } else { nodes.push(this.createItem(h, item)); } }); } else { nodes.push(this.createGroup(h, key, items)); } } return nodes; }, open(index) { console.log(index); this.$refs.menu.open(index); }, close(index) { this.$refs.menu.close(index); } }, render(h) { return h( "el-menu", { props: { ...this.$props }, class: "my-menu", on: { /** * 觸發選中事件 * @event select * @param index {String} 選中列表id * @param indexPath {Array} 選中列表對應的路徑 如:['item1', 'item1-1'] */ select: (index, indexPath) => this.$emit("select", index, indexPath), /** * 觸發列表組(子菜單)展開事件 * @event open * @param index {String} 選中列表id * @param indexPath {Array} 選中列表對應的路徑 如:['item1', 'item1-1'] */ open: (index, indexPath) => this.$emit("open", index, indexPath), /** * 觸發列表組(子菜單)收起事件 * @event close * @param index {String} 選中列表id * @param indexPath {Array} 選中列表對應的路徑 如:['item1', 'item1-1'] */ close: (index, indexPath) => this.$emit("close", index, indexPath) }, ref: "menu" }, this.createNodes(h, this.data) ); } };
綁定點擊事件js navMenus.js
export default { name: "navMenu", methods: { clickMenu(index, indexPath, text) { // console.log(index, indexPath, text); console.log("左邊", text); let componentName = index.$options.propsData.route; this.openedTab = this.$store.state.openedTab; // tabNum 為當前點擊的列表項在openedTab中的index,若不存在則為-1 let tabNum = this.openedTab.indexOf(componentName); if (tabNum === -1) { // 該標簽當前沒有打開 // 將componentName加入到已打開標簽頁state.openedTab數組中 this.$store.commit("addTab", { name: text, componentName }); } else { // 該標簽是已經打開過的,需要激活此標簽頁 this.$store.commit("changeTab", componentName); } } }, data() { return { openedTab: [] }; } };
菜單數組配置JS menu-config.js
module.exports = [ { id: "Index", text: "首頁", icon: "el-icon-star-on", route: "BasicIndex" }, { id: "log", text: "日志", icon: "el-icon-s-order", route: "BasicLayout" }, { id: "basic", text: "個人中心", icon: "el-icon-s-custom", children: [ { id: 21, text: "選項一" }, { id: 22, text: "選項二" }, { id: 23, text: "選項三" } ] } ];
生成菜單 navMenu.vue
<!--本頁為左側下拉菜單--> <template> <el-row class="tac" style="width: 200px;"> <el-col :span="24"> <createMenu :data="menu" backgroundColor="#545c64" textColor="#FFFFFF" ></createMenu> </el-col> </el-row> </template> <script> import menu from "@/config/menu-config.js"; import createMenu from "./createMenu"; export default { components: { createMenu }, name: "navMenu", data() { return { menu: menu }; } }; </script> <style scoped> .over-hide { overflow-x: hidden; } .el-submenu__title { border-bottom: 1px solid #8d98a2; } </style>
渲染組件頁面navMain.vue
<!--本頁為tab標簽--> <template> <div> <el-tabs v-model="editableTabsValue" type="card" closable @tab-remove="removeTab" @tab-click="handleClickTab($event.name)" > <el-tab-pane :key="item.name" v-for="item in editableTabs" :label="item.title" :name="item.name" > </el-tab-pane> </el-tabs> <!--動態組件 ,更具is的值來確定渲染哪個組件 --> <component :is="componentId"></component> </div> </template> <script> export default { name: "navMain", data() { return { componentId: () => import("@/components/navMain/mainComponents/BasicIndex.vue"), editableTabsValue: "BasicIndex", editableTabs: [ { title: "首頁", name: "BasicIndex" } ], tabIndex: 1, openedTab: ["BasicIndex"] }; }, methods: { handleClickTab(route) { this.$store.commit("changeTab", route); this.IsShowComponents(route); // this.$router.push(route); }, removeTab(targetName) { // 首頁不允許被關閉(為了防止el-tabs欄中一個tab都沒有) if (targetName === "BasicIndex") { return false; } let tabs = this.editableTabs; let activeName = this.editableTabsValue; if (activeName === targetName) { tabs.forEach((tab, index) => { if (tab.name === targetName) { let nextTab = tabs[index + 1] || tabs[index - 1]; if (nextTab) { activeName = nextTab.name; } } }); } this.$store.commit("deductTab", targetName); let deductIndex = this.openedTab.indexOf(targetName); this.openedTab.splice(deductIndex, 1); this.IsShowComponents(activeName); this.editableTabsValue = activeName; this.editableTabs = tabs.filter(tab => tab.name !== targetName); if (this.openedTab.length === 0) { this.$store.commit("addTab", "BasicIndex"); this.IsShowComponents("BasicIndex"); // this.$router.push("BasicIndex"); } }, IsShowComponents(newTab) { this.componentId = () => import("@/components/navMain/mainComponents/" + newTab + ".vue"); } }, computed: { getOpenedTab() { return this.$store.state.openedTab; }, changeTab() { return this.$store.state.activeTab; } }, watch: { getOpenedTab(val) { if (val.length > this.openedTab.length) { // 添加了新的tab頁 // 導致$store.state中的openedTab長度 // 大於 // 標簽頁中的openedTab長度 // 因此需要新增標簽頁 let newTitle = this.$store.state.openedTitle[val.length - 1]; let newTab = val[val.length - 1]; // 新增的肯定在數組最后一個 this.IsShowComponents(newTab); ++this.tabIndex; this.editableTabs.push({ title: newTitle, name: newTab, content: "" }); this.editableTabsValue = newTab; this.openedTab.push(newTab); } }, changeTab(val) { // 監聽activetab以實現點擊左側欄時激活已存在的標簽 if (val !== this.editableTabsValue) { this.editableTabsValue = val; this.IsShowComponents(val); } } }, created() { // 刷新頁面時(F11) // 因為<router-view>的<keep-alive>,會保留刷新時所在的router // 但是tab標簽頁因為刷新而被重構了,tab沒有了 // 因此需要將router回到index this.$router.push("Home"); } }; </script> <style scoped></style>
效果: