ABP实践(5)-abp前端vue框架之IView实现三级菜单(博友需要特此分享)
为响应博友想要知道三级菜单怎么实现本篇文章先介绍三级菜单的实现,后续再分享其他部分内容
1 修改菜单组件sidebarMenu.vue
- 图为原代码和修改后代码比对

- 修改前后的源码如下
<style lang="less">
@import "../styles/menu.less";
</style>
<template>
<Menu
ref="sideMenu"
:active-name="$route.name"
:open-names="openNames"
:theme="menuTheme"
width="auto"
@on-select="changeMenu"
>
<template v-for="item in menuList">
<MenuItem v-if="item.children.length<=0" :name="item.children[0].name" :key="item.name">
<!-- <Icon :type="item.icon" :size="iconSize"></Icon> -->
<span class="iconfont">{{item.icon}}</span>
<span>{{ itemTitle(item) }}</span>
</MenuItem>
<!-- <Submenu v-if="item.children.length > 0" :name="item.name" :key="item.name">
<template slot="title">
<i class="iconfont" v-html="item.icon"></i>
<span>{{ itemTitle(item) }}</span>
</template>
<template v-for="child in item.children">
<MenuItem :name="child.name" :key="child.name">
<i class="iconfont" v-html="child.icon"></i>
<span>{{ L(child.meta.title) }}</span>
</MenuItem>
</template>
</Submenu>-->
<Submenu v-if="item.children.length > 0" :name="item.name" :key="item.name">
<template slot="title">
<i class="iconfont" v-html="item.icon"></i>
<span>{{ itemTitle(item) }}</span>
</template>
<template v-for="child in item.children">
<MenuItem v-if="!isChild(child.children)" :name="child.name" :key="child.name">
<i class="iconfont" v-html="child.icon"></i>
<span>{{ L(child.meta.title) }}</span>
</MenuItem>
<Submenu v-else :name="child.name" :key="child.name">
<template slot="title">
<i class="iconfont" v-html="child.icon"></i>
<span>{{ itemTitle(child) }}</span>
</template>
<template v-for="child in child.children">
<MenuItem v-if="!isChild(child.children)" :name="child.name" :key="child.name">
<i class="iconfont" v-html="child.icon"></i>
<span>{{ L(child.meta.title) }}</span>
</MenuItem>
</template>
</Submenu>
</template>
</Submenu>
</template>
</Menu>
</template>
<script lang="ts">
import { Component, Vue, Inject, Prop, Emit } from "vue-property-decorator";
import AbpBase from "../../../lib/abpbase";
@Component({})
export default class SidebarMenu extends AbpBase {
name: string = "sidebarMenu";
@Prop({ type: Array }) menuList: Array<any>;
@Prop({ type: Number }) iconSize: number;
@Prop({ type: String, default: "dark" }) menuTheme: string;
@Prop({ type: Array }) openNames: Array<string>;
itemTitle(item: any): string {
return this.L(item.meta.title);
}
@Emit("on-change")
changeMenu(active: string) {}
updated() {
this.$nextTick(() => {
if (this.$refs.sideMenu) {
(this.$refs.sideMenu as any).updateActiveName();
}
});
}
isChild(item) {
if (item && item.length > 0) {
return true;
} else {
return false;
}
}
}
</script>
2 修改路由菜单配置文件router.ts
- 在新增的内容里有注释(第三级菜单),全部代码如下
declare global {
interface RouterMeta {
title: string;
}
interface Router {
path: string;
name: string;
icon?: string;
permission?: string;
meta?: RouterMeta;
component: any;
children?: Array<Router>;
}
interface System {
import(request: string): Promise<any>
}
var System: System
}
import login from '../views/login.vue'
import home from '../views/home/home.vue'
import main from '../views/main.vue'
export const locking = {
path: '/locking',
name: 'locking',
component: () => import('../components/lockscreen/components/locking-page.vue')
};
export const loginRouter: Router = {
path: '/',
name: 'login',
meta: {
title: 'LogIn'
},
component: () => import('../views/login.vue')
};
export const otherRouters: Router = {
path: '/main',
name: 'main',
permission: '',
meta: { title: 'ManageMenu' },
component: main,
children: [
{ path: 'home', meta: { title: 'HomePage' }, name: 'home', component: () => import('../views/home/home.vue') }
]
}
export const appRouters: Array<Router> = [{
path: '/setting',
name: 'setting',
permission: '',
meta: { title: 'ManageMenu' },
icon: '',
component: main,
children: [
{ path: 'user', permission: 'Pages.Users', meta: { title: 'Users' }, name: 'user', component: () => import('../views/setting/user/user.vue') },
{ path: 'role', permission: 'Pages.Roles', meta: { title: 'Roles' }, name: 'role', component: () => import('../views/setting/role/role.vue') },
{ path: 'tenant', permission: 'Pages.Tenants', meta: { title: 'Tenants' }, name: 'tenant', component: () => import('../views/setting/tenant/tenant.vue') },
{ path: 'goods', permission: 'Pages.Goods', meta: { title: 'Goods' }, name: 'goods', component: () => import('../views/setting/goods/goods.vue') },
//第三级菜单
{
path: '', permission: 'Pages.Goods', meta: { title: 'GoodsManage' }, name: 'goodsManage', component:()=> import('../views/setting/goods/goods.vue'),
children: [
{ path: 'goods', permission: 'Pages.Goods', meta: { title: 'Goods' }, name: 'goods', component: () => import('../views/setting/goods/goods.vue') }
]
}
]
}]
export const routers = [
loginRouter,
locking,
...appRouters,
otherRouters
];

到此菜单已经可以出来,但是头部面包屑展示有问题需要修改lib下的util.ts改后代码如下其中有注释面包屑字眼的为修改代码
import Vue from 'vue';
import appconst from './appconst'
class Util{
abp:any=window.abp;
loadScript(url:string){
var script=document.createElement('script');
script.type="text/javascript";
script.src=url;
document.body.appendChild(script);
}
title(title:string){
let appname=this.abp.localization.localize('AppName',appconst.localization.defaultLocalizationSourceName);
let page=this.abp.localization.localize(title,appconst.localization.defaultLocalizationSourceName);
window.document.title = appname+'--'+page;
}
inOf(arr:Array<any>, targetArr:any) {
let res = true;
arr.forEach(item => {
if (targetArr.indexOf(item) < 0) {
res = false;
}
});
return res;
}
oneOf(ele:any, targetArr:Array<any>) {
if (targetArr.indexOf(ele) >= 0) {
return true;
} else {
return false;
}
}
showThisRoute (itAccess:any, currentAccess:any) {
if (typeof itAccess === 'object' && Array.isArray(itAccess)) {
return this.oneOf(currentAccess, itAccess);
} else {
return itAccess === currentAccess;
}
}
getRouterObjByName (routers:Array<any>, name?:string):any {
if (!name || !routers || !routers.length) {
return null;
}
let routerObj = null;
for (let item of routers) {
if (item.name === name) {
return item;
}
routerObj = this.getRouterObjByName(item.children, name);
if (routerObj) {
return routerObj;
}
}
return null;
}
toDefaultPage (routers:Array<any>, name:string|undefined, route:any, next:any) {
let len = routers.length;
let i = 0;
let notHandle = true;
while (i < len) {
if (routers[i].name === name && routers[i].children && routers[i].redirect === undefined) {
route.replace({
name: routers[i].children[0].name
});
notHandle = false;
next();
break;
}
i++;
}
if (notHandle) {
next();
}
}
handleTitle (vm:any, item:any) {
if (typeof item.meta.title === 'object') {
return vm.$t(item.title.i18n);
} else {
return item.meta.title;
}
}
setCurrentPath (vm:Vue, name?:string) {
let title = '';
let isOtherRouter = false;
vm.$store.state.app.routers.forEach((item:any) => {
if (item.children.length === 1) {
if (item.children[0].name === name) {
title = this.handleTitle(vm, item);
if (item.name === 'otherRouter') {
isOtherRouter = true;
}
}
} else {
item.children.forEach((child:any) => {
if (child.name === name) {
title = this.handleTitle(vm, child);
if (item.name === 'otherRouter') {
isOtherRouter = true;
}
}
});
}
});
let currentPathArr = [];
if (name === 'home') {
currentPathArr = [
{
meta:{title: this.handleTitle(vm, this.getRouterObjByName(vm.$store.state.app.routers, 'home'))},
path: 'main/home',
name: 'home'
}
];
} else if (((name as string).indexOf('index') >= 0 || isOtherRouter) && name !== 'home') {
currentPathArr = [
{
meta:{title: this.handleTitle(vm, this.getRouterObjByName(vm.$store.state.app.routers, 'home'))},
path: 'main/home',
name: 'home'
},
{
meta:{title: title},
path: '',
name: name
}
];
} else {
let currentPathObj = vm.$store.state.app.routers.filter((item:any) => {
if (item.children.length <= 1) {
return item.children[0].name === name||item.name===name;
} else {
let i = 0;
let childArr = item.children;
let len = childArr.length;
while (i < len) {
//第三级菜单面包屑
if(childArr[i].children&&childArr[i].children.length>0){
for(let children of childArr[i].children){
if(children.name===name){
return true;
}
}
}
//-------------
if (childArr[i].name === name) {
return true;
}
i++;
}
return false;
}
})[0];
if (currentPathObj.children&¤tPathObj.children.length <= 1 && currentPathObj.name === 'home') {
currentPathArr = [
{
meta:{title: 'HomePage'},
path: 'main/home',
name: 'home'
}
];
} else if (currentPathObj.children&¤tPathObj.children.length <= 1 && currentPathObj.name !== 'home') {
currentPathArr = [
{
meta:{title: 'HomePage'},
path: 'main/home',
name: 'home'
},
{
meta:{title: currentPathObj.meta.title},
path: '',
name: name
}
];
} else {
let childObj = currentPathObj.children.filter((child:any) => {
//第三级菜单实现
if(child.children&&child.children.length>0){
return true;
}
//--------------------
return child.name === name;
})[0];
currentPathArr = [
{
meta:{title: 'HomePage'},
path: 'main/home',
name: 'home'
},
{
meta:{title: currentPathObj.meta.title},
path: '',
name: ''
},
{//-----第三级菜单面包屑
meta:{title: childObj.meta.title},
path: '',
name: childObj.name
}
];
if(childObj.children&&childObj.children.length>0){
let tChildObj = childObj.children.filter((child:any) => {
return child.name === name;
})[0];
if(tChildObj){
let tr={
meta:{title: tChildObj.meta.title},
path: currentPathObj.path + '/' +tChildObj.path+'/'+ tChildObj.path,
name: name
}
currentPathArr.push(tr)
}
}
//--------------------
}
}
vm.$store.commit('app/setCurrentPath', currentPathArr);
return currentPathArr;
}
openNewPage (vm:Vue, name:string|undefined, argu?:any, query?:any) {
let pageOpenedList = vm.$store.state.app.pageOpenedList;
let openedPageLen = pageOpenedList.length;
let i = 0;
let tagHasOpened = false;
while (i < openedPageLen) {
if (name === pageOpenedList[i].name) { // 页面已经打开
vm.$store.commit('app/pageOpenedList', {
index: i,
argu: argu,
query: query
});
tagHasOpened = true;
break;
}
i++;
}
if (!tagHasOpened) {
let tag = vm.$store.state.app.tagsList.filter((item:any) => {
if (item.children) {
return name === item.children[0].name;
} else {
return name === item.name;
}
});
tag = tag[0];
if (tag) {
tag = tag.children ? tag.children[0] : tag;
if (argu) {
tag.argu = argu;
}
if (query) {
tag.query = query;
}
vm.$store.commit('app/increateTag', tag);
}
}
vm.$store.commit('app/setCurrentPageName', name);
}
fullscreenEvent (vm:Vue) {
vm.$store.commit('app/initCachepage');
// 权限菜单过滤相关
vm.$store.commit('app/updateMenulist');
// 全屏相关
}
extend(...args:any[]) {
let options, name, src, srcType, copy, copyType, copyIsArray, clone,
target = args[0] || {},
i = 1,
length = args.length,
deep = false;
if ( typeof target === 'boolean') {
deep = target;
target = args[i] || {};
i++;
}
if ( typeof target !== 'object' && typeof target !== 'function') {
target = {};
}
if ( i === length) {
target = this;
i--;
}
for ( ; i < length; i++ ) {
if ( (options = args[i]) !== null ) {
for ( name in options ) {
src = target[name];
copy = options[name];
if ( target === copy ) {
continue;
}
srcType = Array.isArray(src) ? 'array': typeof src;
if ( deep && copy && ((copyIsArray = Array.isArray(copy)) || typeof copy === 'object')) {
if ( copyIsArray ) {
copyIsArray = false;
clone = src && srcType === 'array' ? src : [];
} else {
clone = src && srcType === 'object' ? src: {};
}
target[name] = this.extend(deep, clone, copy);
} else if ( copy !== undefined ) {
target[name] = copy;
}
}
}
}
return target;
}
}
const util=new Util();
export default util;
知道如何实现三级菜单可以考虑一下怎么实现无限级菜单(以后有时间再分享)
ABP实践(5)-abp前端vue框架之IView实现三级菜单(博友需要特此分享)的更多相关文章
- ABP实践(4)-abp前端vue框架之简单商品增删改查(帮助刚入门的新手快速了解怎么才能加入自己的功能并运行起来)
提示:如有不明白的地方请先查看前3篇ABP实践系列的文章 1,下载及启动abp项目前后端分离(netcore+vue) 2,修改abp数据库为mysql 3,商品系列api接口(本文主要依赖在这个商品 ...
- 前端vue框架 脚手架
1.安装node.js最新版本2.cmd下输入 1.node -v得到版本号检测是否安装成功 版本号要在6.9以上 2.npm -v 版本号要在3.10以上3.安装脚手架 1.npm install ...
- vue的递归组件以及三级菜单的制作
js里面有递归算法,同时,我们也可以利用props来实现vue模板的递归调用,但是前提是组件拥有 name 属性 父组件:slotDemo.vue: <template> <div& ...
- 前端--vue框架
1.下载 查看已安装好的版本 -------渐进式的JS框架--------- vue是什么 Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的 渐进式框架.与其他重量级框架 ...
- 前端VUE框架
一.什么是VUE? 它是一个构建用户界面的JAVASCRIPt框架 vue不关心你页面上的是什么标签,它操作的是变量或属性 为什么要使用VUE? 在前后端分离的时候,后端只返回json数据,再没有 ...
- 前端vue框架上手记录
---恢复内容开始--- Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,提供: 通过 @vue/cli 搭建交互式的项目脚手架. 通过 @vue/cli + @vue/cli-se ...
- 前端vue框架 路由的安装及使用
安装: 1.cmd下输入: npm install vue-router --save //安装路由 2.npm run dev //重新启动 使用: 1.在mian.js下引入路由 import V ...
- 前端Vue框架-vuex状态管理详解
新人报道!多多关照-多提宝贵意见 谢谢- vuex理解 采用集中式存储管理模式.用来管理组件的状态,并以自定义规则去观测实时监听值得变化. 状态模式管理理解 属性 理解 state 驱动应用的数据源 ...
- 前端vue框架 父组件与子组件之间的相互调用
子组件调用父组件东西: 1.在父组件与子组件契合的标签的的template模板中绑定 v-bind:自定义一个名字=“要调用的名字” 2.在子组件的script中props:["自定义的名字 ...
- 前端VUE框架-es6
EMCAScript 6 又叫 es2015 1.常量和变量 常量: const a = "hello" 常量不能修改和重复定义 变量: let:定义一个块级作用域的变量 需要先定 ...
随机推荐
- 个头小却很能“打”!合合信息扫描全能王推出A4便携式打印机
个头小却很能"打"!合合信息扫描全能王推出A4便携式打印机 过去,为了打印一份清晰工整的材料,人们往往需要到专门的打印店或办公室.处理文件.对于销售.物流人员.工程师.医生.媒 ...
- Identity – User Login, Forgot Password, Reset Password, Logout
前言 这篇来聊聊常见操作. 会讲到: Create Account Login Logout Change Password Reset Password (by email) External Lo ...
- 搭建高效攻防靶场vulfocus与Docker仓库管理实战:从听说到入门系列
搭建高效攻防靶场vulfocus与Docker仓库管理实战:从听说到入门系列 vulfocus 简介 vulfocus,作为一款前沿的漏洞集成平台,它巧妙地将多种最新的CVE漏洞环境封装于Docker ...
- ChatGPT转发工具-springboot
背景 国内服务器无法访问openAI接口,我想过有两种实现方式 代理工具类似 tinyproxy.nginx 开发一个转发客户端(java.python都可以实现),提供一个api接口 源码 gith ...
- 【赵渝强老师】Redis的RDB持久化
Redis 提供了多种不同级别的持久化方式: RDB 持久化可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot). AOF (Append-only file) ...
- Fio工具详解【强大的IO性能压测工具】
Fio压测工具操作 fio -name=iouring_test -filename=/mnt/vdd/testfile -iodepth=128 -thread -rw=randread -ioen ...
- C# Webapi Filter 过滤器 - 生命周期钩子函数 - Action Filter 基础
ACTION Filter IAsyncACtionFilter 接口 : 1.注入ActionFilter // 注册过滤器 builder.Services.Configure<MvcOpt ...
- JDBC 和 Mybatis
使用JDBC连接操作数据库 Mybatis是JDBC的二次封装 使用更加简单了
- Kali && Debain 防火墙规则
Kali && Debain 防火墙规则 查看防火墙规则 iptables -L -n -v iptables -L -n -v 增加防火墙规则:开放指定的端口 iptables -A ...
- 开源的口袋妖怪自走棋「GitHub 热点速览」
作为一名 90 后,我对口袋妖怪(宝可梦)游戏有着特殊的感情,满满的都是回忆.如果你也喜欢宝可梦主题的游戏,这款开源的宝可梦自走棋游戏 pokemonAutoChess 一定要试试,它采用战棋(自走棋 ...