1,效果如下
2,思路
页面使用了 border 布局
west 就是 菜单所在的区域 使用 fit 布局 这样就只会显示第一个子元素
其实有两个 子元素
第二个 子元素 是一个 默认的 Ext.tree.Panel 给它设置一个 autoLoad 的 store 获取菜单的数据
west 的第一个组件 也就是 给用户看的这个组件 我叫它镜像菜单
当 treePanel 获取到数据之后 将 镜像菜单的 viewModel 中的默认数据更新 成 treePanel 的数据 然后 让镜像菜单重新渲染即可
接下来 基本就和treePanel 没什么关系了
在 镜像菜单的 initComponent 方法中 获取自己的 viewModel 的数据 渲染成菜单的样子即可 具体怎么渲染就是一下布局 一些事件了
通过 监听 mouseover 事件 触发滑动
调用 menushow 方法 显示 子菜单面板
子菜单面板的点击事件 触发路由 加上想要的参数 就完成跳转了 然后在 路由事件中做一下对应的操作 比如生成新的 tab 页面等等 我的是跳到指定的 tab 页面
3,主要代码
Ext.define('app.view.main.Main', { extend: 'Ext.Container', xtype: 'app-main', requires: [ 'Ext.window.MessageBox', 'app.view.main.MainController', 'app.view.main.MainModel', 'app.view.main.CloneMenuListView', 'app.view.main.TrueMenuListView', 'app.view.main.CenterContainer' ], layout: { type: 'border' }, controller: 'main', viewModel: 'main', items: [ { region: 'north', height: 60, style: { border: 0, borderBottom: '1px solid #ddd' }, layout: { type: 'fit' }, items: [ { xtype: 'button', text: '自定义镜像菜单', handler: 'headerBtnClick' } ] }, { region: 'west', xtype: 'panel', title: '自定义菜单', style: { borderRight: '1px solid #eee' }, reference: 'west', width: 150, layout: { type: 'fit' }, items: [ //{ // xtype:'trueMenuListView' //}, { xtype: 'cloneMenuListView' }, { xtype: 'trueMenuListView' } ] }, { region: 'center', xtype: 'center-container' } ] });
/** * Created by Sukla on 2017/11/24. */ Ext.define('app.view.main.TrueMenuListView', { extend: 'Ext.tree.Panel', alias: 'widget.trueMenuListView', reference: 'trueMenuListView', requires: [ 'Ext.data.TreeStore' ], rootVisible: false, useArrows: true, width: 150, store: Ext.create('app.store.TrueMenuListStore'), viewConfig: { listeners: { render: 'trueMenuRender' }, getRowClass: function (record, rowindex, rowParams, store) { debugger; this.getRefOwner().getRefOwner().down('cloneMenuListView').getViewModel().set('arr',store.data.items); this.getRefOwner().getRefOwner().down('cloneMenuListView').initComponent(); } } })
/** * Created by Sukla on 2017/11/24. */ Ext.define('app.view.main.CloneMenuListView', { extend: 'Ext.Container', alias: 'widget.cloneMenuListView', reference: 'cloneMenuListView', width: 150, layout: { type: 'vbox' }, cls: 'my-menu-contanier', defaults: { xtype: 'button', cls: 'myself-menu', overCls: 'myself-over-menu', listeners: { mouseover: 'myselfMenuOver' }, width: 150, height: 34, style: { border: 0, margin: 0, padding: 0 }, menuAlign: 'tr' }, viewModel: { data: { arr: [ //{ // data:{ // allowDrag: true, // allowDrop: true, // checked: null, // children: null, // cls: "", // depth: 2, // expandable: true, // expanded: false, // glyph: "", // href: "", // hrefTarget: "", // icon: "", // iconCls: "", // id: "demo", // index: 0, // isFirst: true, // isLast: false, // leaf: true, // loaded: false, // loading: false, // parentId: "parent-tree-a", // qshowDelay: 0, // qtip: "", // qtitle: "", // root: false, // text: "子菜单A", // visible: true // } //} ] } }, initComponent: function () { debugger; var arr = this.getViewModel().get('arr'); this.items = []; for (var i = 0; i < arr.length; i++) { var item = {}; var menuItems = []; if (arr[i].data.children) { var children = arr[i].data.children; for (var j = 0; j < children.length; j++) { var menuItem={ items:[] } //判断二级菜单是否叶子节点 从而准备不同的渲染组件 if(children[j].leaf){ }else{ var leafItems=[]; if(children[j].children){ var leafChild=children[j].children; for(var k=0;k<leafChild.length;k++){ leafItems.push({ itemId: leafChild[k].id, text:leafChild[k].text }) } } menuItem.items.push({ width: 100, html: children[j].text }); menuItem.items.push( { flex: 1, layout: { type: 'table', columns: 5 }, defaults: { xtype: 'button', width: 70, height: 40, margin: 5, text: '子菜单', handler: "onMyMenuSelecte" }, items: leafItems }) } menuItems.push(menuItem) } } var menus = [ { xtype: 'panel', padding: 0, margin: 0, border: '1px solid #00f', width: 500, minHeight: 50, layout: { type: 'vbox', align: 'stretch' }, defaults: { layout: { type: 'hbox', align: 'stretch' } }, items: menuItems } ]; //一级菜单 item.text = arr[i].data.text; //二级菜单 item.menu = menus; this.items.push(item) } //this.items = [ // { // text: '交易管理', // menu: [ // { // xtype: 'panel', // padding: 0, // margin: 0, // border: '1px solid #00f', // width: 500, // minHeight: 50, // layout: { // type: 'vbox', // align: 'stretch' // }, // defaults: { // layout: { // type: 'hbox', // align: 'stretch' // } // }, // items: [ // { // items: [ // { // width: 100, // html: '二级菜单' // }, // { // flex: 1, // layout: { // type: 'table', // columns: 3 // }, // defaults: { // xtype: 'button', // width: 70, // height: 40, // margin: 5, // text: '子菜单', // handler: "onMyMenuSelecte" // }, // items: [ // { // itemId: 'menu-001' // } // ] // } // ] // }, // { // items: [ // { // width: 100, // html: '二级菜单' // }, // { // flex: 1, // layout: { // type: 'table', // columns: 3 // }, // defaults: { // xtype: 'button', // width: 70, // height: 40, // margin: 5, // text: '子菜单', // handler: "onMyMenuSelecte" // }, // items: [ // { // itemId: 'menu-0011' // }, // { // itemId: 'menu-0011' // }, // { // itemId: 'menu-0012' // }, // { // itemId: 'menu-0013' // }, // { // itemId: 'menu-0014' // }, // { // itemId: 'menu-0015' // }, // { // itemId: 'menu-0016' // }, // { // itemId: 'menu-0017' // }, // { // itemId: 'menu-0018' // } // ] // } // ] // } // ] // } // ] // } //]; this.callParent(); } })
Ext.define('app.view.main.MainController', { extend: 'Ext.app.ViewController', alias: 'controller.main', headerBtnClick: function () { debugger; }, trueMenuRender:function(){ }, myselfMenuOver:function(btn){ btn.showMenu(); }, routes: { ':id': { action: 'handleRoute',//执行跳转 before: 'beforeHandleRoute'//路由跳转前操作 } }, /** * 路由跳转前的事件 * @param id * @param action */ beforeHandleRoute: function (id, action) { // var me = this, // treeNav = me.lookupReference('lefttreelistnav'), // store = treeNav.getStore(), // treeNavItem = store.getNodeById(id); // if (treeNavItem) { action.resume(); // } else if (store.getCount() === 0) { // // 在store load事件中判断节点,避免store数据未加载情况 // store.on('load', function () { // var rootNode = treeNav.getRootNode(); // if (rootNode.hasChildNodes()) { // rootNode.expand(); // } // node = store.getNodeById(id); // if (node) { // action.resume(); // } else { // //Ext.Msg.alert('路由跳转失败1', '找不到id为' + id + ' 的组件'); // action.stop(); // } // }); // store.load(); // } else { Ext.Msg.alert('路由跳转失败21', store.getCount()); Ext.Msg.alert('路由跳转失败2', '找不到id为' + id + ' 的组件'); // action.stop(); // } }, /** * 执行路由 * @param id */ handleRoute: function (id) { var me = this, // mainView = me.getView(), // treeNav = me.lookupReference('lefttreelistnav'), centerContainer = me.lookupReference('centerContainer'); // store = treeNav.getStore(), // treeNavItem = store.getNodeById(id), // className, cmp, ViewClass; // 响应路由,左侧树定位到相应节点 var parentNode = treeNavItem.parentNode; treeNav.getSelectionModel().select(treeNavItem); treeNav.getView().expand(parentNode); treeNav.getView().focusNode(treeNavItem); // if (treeNavItem.isLeaf()&&treeNavItem.get('resLevel')==2) { //if (treeNavItem.isLeaf()) { me.addTabPanel(centerContainer, id, 'centerContainer'); //} }, /** * 根据点击的菜单显示相应内容页 * @param targetPanel * @param treeNavItem * @param targetPanelStr */ addTabPanel: function (targetPanel, treeNavItem, targetPanelStr) { var newTab = targetPanel.items.findBy(function (tab) { return tab.title === treeNavItem; }); if (!newTab) { var tabObject = { xtype: 'panel', closable: true, title: treeNavItem //className: treeNavItem.get('id'), //resParams: treeNavItem.get('id'), //resId: treeNavItem.get('id') }; //if (treeNavItem.get('resUri') === 'reportquery') {// 报表查询特殊处理 // tabObject.itemId = treeNavItem.get('id'); // tabObject.targetPanel = 'mainpanel'; //} newTab = targetPanel.add(tabObject); } targetPanel.setActiveTab(newTab); }, onMyMenuSelecte: function (selecteMenu) { this.redirectTo(selecteMenu.itemId); } });
4,css
.my-menu-contanier,.myself-menu{ background:#7b7b7b!important; } .myself-menu{ border-bottom:1px solid #9b9b9b!important; font-size: 16px!important; } .myself-over-menu{ background:rgba(0, 130, 223, 1)!important; }
5,store
/** * Created by Sukla on 2017/11/24. */ Ext.define('app.store.TrueMenuListStore', { extend: 'Ext.data.TreeStore', alias: 'store.trueMenuListStore', autoLoad:true, proxy: { type: 'ajax', url: 'resources/data/trueMenuListJson.json' }, root: { text: 'Ext JS', id: 'src', expanded: true }, folderSort: true, sorters: [{ property: 'text', direction: 'ASC' }] });6,json
[ { id:'jygl', text:"权限管理", expanded:false, iconCls: 'x-fa fa-home', children:[ { id:'xhzy', text:"管理员", children:[ { id:'spfb', text:"一级管理员", leaf:true } ] }, { id:'xsdd', text:"维护", children:[ { id:'xjdd', text:"A类", leaf:true }, { id:'ddgl', text:"B类", leaf:true }, { id:'ddgz', text:"C类", leaf:true }, { id:'ddsp', text:"D类", leaf:true } ] }, { id:'xsht', text:"Vip", children:[ { id:'htgl', text:"Vip001", leaf:true }, { id:'htgz', text:"Vip002", leaf:true }, { id:'htsp', text:"Vip003", leaf:true }, { id:'htqz', text:"Vip004", leaf:true }, { id:'bgxygl', text:"Vip005", leaf:true } ] } ] }, { id:'khgl', text:"角色管理", expanded:false, iconCls: 'x-fa fa-home', children:[ { id:'khglc', text:"老师", children:[ { id:'wdkk', text:"其它老师", leaf:true }, { id:'khda', text:"体育老师", leaf:true } ] }, { id:'lfgl', text:"学生", children:[ { id:'lfsq', text:"小学生", leaf:true }, { id:'lfkjsp', text:"中学生", leaf:true }, { id:'lfbjsp', text:"大学生", leaf:true }, { id:'lfbglr', text:"研究生", leaf:true }, { id:'lfcx', text:"博士生", leaf:true } ] } ] } ]