zoukankan      html  css  js  c++  java
  • 第四节:菜单权限、动态路由注册、菜单搭建、面包屑、按钮权限 剖析

    一. 菜单权限-动态路由注册

     1. 核心思路

     (1). 首先我们知道不同账号登录,会拥有不同的菜单权限,在前端仅仅需要把有权限的路由注册进去即可,而不是把所有路由都注册了,这就是所谓的动态路由注册

     (2). 前端先把所有的路由都建立出来,然后接口中返回有权限的菜单,这里接口中返回的菜单数据 需要 能和前端建立出来的路由进行关联比对,我们采用路径的方式,即接口中返回一个路径(如:/main/system/user),这个路径恰好是前端路由中的path字段

     (3). 最终把接口中返回的菜单,且这些菜单在和前端构建的所有路由通过path相等比对,即能匹配上的路由,进行注册,添加到vue-router中。

    补充:

     上述步骤只是为了将路由动态注册到vue-router里,与菜单显示无关,菜单显示需要借助menu组件,通过递归数据进行显示,详见下面的(二. 菜单搭建 )

    2. 实现步骤

    (1). 准备 基础路由 和 前端所有的菜单路由。

    A. 基础路由代码

    const routes: Array<RouteRecordRaw> = [
        {
            path: '/',
            redirect: '/login',
        },
        {
            path: '/login',
            name: 'login',
            component: () => import('@/views/login/login.vue'),
        },
        {
            path: '/main',
            name: 'main',
            component: () => import('@/views/main/main.vue'),
        },
        {
            path: '/:pathMatch(.*)*',
            name: 'notFound',
            component: () => import('@/views/not-found/not-found.vue'),
        },
    ];
    
    const router = createRouter({
        history: createWebHistory(),
        routes,
    });
    View Code

    B. 所有的菜单路由代码,一个菜单对应一个路由文件,如下图:(未来这些菜单路由都是添加为 main 的子路由 )

     user.ts代码如下:

    const user = () => import('@/views/main/system/user/user.vue');
    export default {
        path: '/main/system/user',
        name: 'user',
        component: user,
        children: [],
    };

    (2). 请求接口获得具有权限的菜单数据,存入vuex中

    接口数据如下:

    [{
        "id": 38,
        "name": "系统总览",
        "type": 1,
        "url": "/main/analysis",
        "icon": "el-icon-monitor",
        "sort": 1,
        "children": [{
            "id": 39,
            "url": "/main/analysis/overview",
            "name": "核心技术",
            "sort": 106,
            "type": 2,
            "children": null,
            "parentId": 38
        }, {
            "id": 40,
            "url": "/main/analysis/dashboard",
            "name": "商品统计",
            "sort": 107,
            "type": 2,
            "children": null,
            "parentId": 38
        }]
    }, {
        "id": 1,
        "name": "系统管理",
        "type": 1,
        "url": "/main/system",
        "icon": "el-icon-setting",
        "sort": 2,
        "children": [{
            "id": 2,
            "url": "/main/system/user",
            "name": "用户管理",
            "sort": 100,
            "type": 2,
            "children": [{
                "id": 5,
                "url": null,
                "name": "创建用户",
                "sort": null,
                "type": 3,
                "parentId": 2,
                "permission": "system:users:create"
            }, {
                "id": 6,
                "url": null,
                "name": "删除用户",
                "sort": null,
                "type": 3,
                "parentId": 2,
                "permission": "system:users:delete"
            }, {
                "id": 7,
                "url": null,
                "name": "修改用户",
                "sort": null,
                "type": 3,
                "parentId": 2,
                "permission": "system:users:update"
            }, {
                "id": 8,
                "url": null,
                "name": "查询用户",
                "sort": null,
                "type": 3,
                "parentId": 2,
                "permission": "system:users:query"
            }],
            "parentId": 1
        }, {
            "id": 3,
            "url": "/main/system/department",
            "name": "部门管理",
            "sort": 101,
            "type": 2,
            "children": [{
                "id": 17,
                "url": null,
                "name": "创建部门",
                "sort": null,
                "type": 3,
                "parentId": 3,
                "permission": "system:department:create"
            }, {
                "id": 18,
                "url": null,
                "name": "删除部门",
                "sort": null,
                "type": 3,
                "parentId": 3,
                "permission": "system:department:delete"
            }, {
                "id": 19,
                "url": null,
                "name": "修改部门",
                "sort": null,
                "type": 3,
                "parentId": 3,
                "permission": "system:department:update"
            }, {
                "id": 20,
                "url": null,
                "name": "查询部门",
                "sort": null,
                "type": 3,
                "parentId": 3,
                "permission": "system:department:query"
            }],
            "parentId": 1
        }, {
            "id": 4,
            "url": "/main/system/menu",
            "name": "菜单管理",
            "sort": 103,
            "type": 2,
            "children": [{
                "id": 21,
                "url": null,
                "name": "创建菜单",
                "sort": null,
                "type": 3,
                "parentId": 4,
                "permission": "system:menu:create"
            }, {
                "id": 22,
                "url": null,
                "name": "删除菜单",
                "sort": null,
                "type": 3,
                "parentId": 4,
                "permission": "system:menu:delete"
            }, {
                "id": 23,
                "url": null,
                "name": "修改菜单",
                "sort": null,
                "type": 3,
                "parentId": 4,
                "permission": "system:menu:update"
            }, {
                "id": 24,
                "url": null,
                "name": "查询菜单",
                "sort": null,
                "type": 3,
                "parentId": 4,
                "permission": "system:menu:query"
            }],
            "parentId": 1
        }, {
            "id": 25,
            "url": "/main/system/role",
            "name": "角色管理",
            "sort": 102,
            "type": 2,
            "children": [{
                "id": 26,
                "url": null,
                "name": "创建角色",
                "sort": null,
                "type": 3,
                "parentId": 25,
                "permission": "system:role:create"
            }, {
                "id": 27,
                "url": null,
                "name": "删除角色",
                "sort": null,
                "type": 3,
                "parentId": 25,
                "permission": "system:role:delete"
            }, {
                "id": 28,
                "url": null,
                "name": "修改角色",
                "sort": null,
                "type": 3,
                "parentId": 25,
                "permission": "system:role:update"
            }, {
                "id": 29,
                "url": null,
                "name": "查询角色",
                "sort": null,
                "type": 3,
                "parentId": 25,
                "permission": "system:role:query"
            }],
            "parentId": 1
        }]
    }, {
        "id": 9,
        "name": "商品中心",
        "type": 1,
        "url": "/main/product",
        "icon": "el-icon-goods",
        "sort": 3,
        "children": [{
            "id": 15,
            "url": "/main/product/category",
            "name": "商品类别",
            "sort": 104,
            "type": 2,
            "children": [{
                "id": 30,
                "url": null,
                "name": "创建类别",
                "sort": null,
                "type": 3,
                "parentId": 15,
                "permission": "system:category:create"
            }, {
                "id": 31,
                "url": null,
                "name": "删除类别",
                "sort": null,
                "type": 3,
                "parentId": 15,
                "permission": "system:category:delete"
            }, {
                "id": 32,
                "url": null,
                "name": "修改类别",
                "sort": null,
                "type": 3,
                "parentId": 15,
                "permission": "system:category:update"
            }, {
                "id": 33,
                "url": null,
                "name": "查询类别",
                "sort": null,
                "type": 3,
                "parentId": 15,
                "permission": "system:category:query"
            }],
            "parentId": 9
        }, {
            "id": 16,
            "url": "/main/product/goods",
            "name": "商品信息",
            "sort": 105,
            "type": 2,
            "children": [{
                "id": 34,
                "url": null,
                "name": "创建商品",
                "sort": null,
                "type": 3,
                "parentId": 16,
                "permission": "system:goods:create"
            }, {
                "id": 35,
                "url": null,
                "name": "删除商品",
                "sort": null,
                "type": 3,
                "parentId": 16,
                "permission": "system:goods:delete"
            }, {
                "id": 36,
                "url": null,
                "name": "修改商品",
                "sort": null,
                "type": 3,
                "parentId": 16,
                "permission": "system:goods:update"
            }, {
                "id": 37,
                "url": null,
                "name": "查询商品",
                "sort": null,
                "type": 3,
                "parentId": 16,
                "permission": "system:goods:query"
            }],
            "parentId": 9
        }]
    }, {
        "id": 41,
        "name": "随便聊聊",
        "type": 1,
        "url": "/main/story",
        "icon": "el-icon-chat-line-round",
        "sort": 4,
        "children": [{
            "id": 42,
            "url": "/main/story/chat",
            "name": "你的故事",
            "sort": 108,
            "type": 2,
            "children": null,
            "parentId": 41
        }, {
            "id": 43,
            "url": "/main/story/list",
            "name": "故事列表",
            "sort": 109,
            "type": 2,
            "children": [],
            "parentId": 41
        }]
    }]
    View Code

    (3). 封装方法 mapMenusToRoutes,递归获取具有用户权限的路由信息。

    mapMenusToRoutes如下:【重点!!!】
    /**
     * 获取用户权限的路由对象
     * @param userMenus 菜单数据
     * @returns 用户权限的路由对象
     */
    export function mapMenusToRoutes(userMenus: RouteRecordRaw[]) {
        // 具有权限的routes
        const routes: RouteRecordRaw[] = [];
    
        // 1.先去加载本地默认所有的routes
        const allRoutes: RouteRecordRaw[] = [];
        // 1.1 获取所有的路由文件,【 require.context,是webpack的函数(true表示递归)】
        const routeFiles = require.context('../router/main', true, /\.ts/);
    
        // 1.2 获取路由对象
        // 默认获取的是当前main目录下,./analysis/dashboard/dashboard.ts, 所以要拼接切割一下(因为该文件存放在utils文件夹下)
        routeFiles.keys().forEach((key) => {
            // require获取的是一个ESModule对象
            const route = require('../router/main' + key.split('.')[1]);
            // xx.default获取的是的路由对象
            allRoutes.push(route.default);
        });
    
        // 2. 根据菜单获取需要添加的routes(需要递归!! 支持多级菜单)
        // type === 1 -> children -> type === 1 (表示有子菜单)
        // type === 2 -> url -> route
        const _recurseGetRoute = (menus: any[]) => {
            for (const menu of menus) {
                if (menu.type === 2) {
                    const route = allRoutes.find((item) => item.path === menu.url);
                    if (route) {
                        routes.push(route);
                    }
                } else {
                    _recurseGetRoute(menu.children);
                }
            }
        };
        // 调用
        _recurseGetRoute(userMenus);
    
        return routes;
    }
    View Code

    (4). 动态注册路由

             // 2. 注册动态路由,添加映射关系,用户后面的点击跳转
                const routes = mapMenusToRoutes(userMenus);
                // 添加到main下作为二级路由
                routes.forEach((itemRoute) => {
                    // main是一级路由的name值
                    router.addRoute('main', itemRoute);
                });    

    二. 菜单搭建

    1. 封装nav-menu菜单组件

    (1). 剖析

     A. 这里仅仅封装两级菜单,即:①一级菜单直接跳转 ②一级菜单展开后,二级菜单可以跳转。

     B. 最外层是<el-menu>,一级菜单直接跳转是<el-menu-item>标签;一级菜单展开后,二级可以跳转是<el-sub-menu>包裹<el-menu-item>

     C. default-active属性表示默认选中,对应的值是标签上的index属性

     D. collapse 控制展开和折叠

    (2). 核心代码

    <template>
        <div class="nav-menu">
            <div class="logo">
                <img class="img" src="~@/assets/img/logo.svg" alt="logo" />
                <span v-show="!collapse" class="title">Vue3+TS</span>
            </div>
            <!-- <el-menu default-active="1-1" class="el-menu-vertical-demo">
                <el-sub-menu index="1">
                    <template #title>
                        <el-icon><location /></el-icon>
                        <span>Navigator One</span>
                    </template>
                    <el-menu-item index="1-1">item one</el-menu-item>
                    <el-menu-item index="1-2">item two</el-menu-item>
                </el-sub-menu>
                <el-sub-menu index="2">
                    <template #title>
                        <el-icon><location /></el-icon>
                        <span>Navigator Two</span>
                    </template>
                    <el-menu-item index="2-1">item Three</el-menu-item>
                    <el-menu-item index="2-2">item Four</el-menu-item>
                </el-sub-menu>
                <el-menu-item index="3">
                    <el-icon><location /></el-icon>
                    <span>Navigator Three</span>
                </el-menu-item>
                <el-menu-item index="4">
                    <el-icon><location /></el-icon>
                    <span>Navigator Four</span>
                </el-menu-item>
            </el-menu> -->
    
            <el-menu
                class="el-menu-vertical"
                background-color="#0c2135"
                text-color="#b7bdc3"
                active-text-color="#0a60bd"
                unique-opened
                :default-active="defaultValue + ''"
                :collapse="collapse"
            >
                <template v-for="item in userMenus" :key="item.id">
                    <!-- type=1,表示含 二级菜单 -->
                    <template v-if="item.type === 1">
                        <!-- 二级菜单的可以展开的标题 -->
                        <el-sub-menu :index="item.id + ''">
                            <template #title>
                                <el-icon :size="15"><location /></el-icon>
                                <span>{{ item.name }}</span>
                            </template>
                            <!-- 遍历里面的item -->
                            <template v-for="subitem in item.children" :key="subitem.id">
                                <el-menu-item :index="subitem.id + ''" @click="HandleMenuItemClick(subitem)">
                                    <el-icon :size="15"><setting /></el-icon>
                                    <span>{{ subitem.name }}</span>
                                </el-menu-item>
                            </template>
                        </el-sub-menu>
                    </template>
                    <!-- type=2 表示不含二级菜单,存放在最外层直接点击 -->
                    <template v-else-if="item.type === 2">
                        <el-menu-item :index="item.id + ''" @click="HandleMenuItemClick(item)">
                            <el-icon :size="15"><location /></el-icon>
                            <span>{{ item.name }}</span>
                        </el-menu-item>
                    </template>
                </template>
            </el-menu>
        </div>
    </template>
    
    <script lang="ts">
        import { defineComponent, computed, ref } from 'vue';
        import { useStore } from '@/store';
        import { Location, Setting } from '@element-plus/icons';
        import { useRouter, useRoute } from 'vue-router';
        import { pathMapToMenu } from '@/utils/map-menus';
    
        export default defineComponent({
            components: {
                Location,
                Setting,
            },
            props: {
                collapse: {
                    type: Boolean,
                    default: false,
                },
            },
            setup() {
                // 这里的useStore是自己改造的,加上了返回值的类型声明,便于后面state.能点出来
                const store = useStore();
                // 获取vuex中的菜单信息,放到computed中,支持响应式
                const userMenus = computed(() => store.state.login.userMenus);
                // console.log(userMenus.value);
                const router = useRouter();
                const route = useRoute();
                const currentPath = route.path;
                console.log('---------currentPath-----------------', currentPath);
    
                // 根据路由的path去userMenus集合中找到对应的menu
                const menu = pathMapToMenu(userMenus.value, currentPath);
                const defaultValue = ref(menu?.id ?? 15);
    
                // 菜单点击跳转事件
                const HandleMenuItemClick = (item: any) => {
                    // console.log(item.url);
                    router.push({
                        path: item.url ?? '/not-found',
                    });
                };
    
                return {
                    userMenus,
                    HandleMenuItemClick,
                    defaultValue,
                };
            },
        });
    </script>
    View Code

    2. 如何实现点击变色和打开页面?

      点击某个菜单,菜单选中后变色,是menu组件默认实现的功能。

      打开页面:需要给每个可点击的菜单绑定一个事件,事件中  push 跳转到对应的url处。

    3.首次进入默认选中和打开页面如何匹配?

      首先登录成功后,肯定会有一个push,比如push("/system/user"),

      那么default-active属性就需要设置成 “/system/user”标签上的index值。

    4. 刷新页面后仍然记住当前页面的两套方案

    方案一:(推荐!)

      index属性里存放的是跳转的路径(如:/system/user),每次点击的时候,获取点击的url,先给deault-active属性赋值,然后再存放到缓存里。页面加载的时候先从缓存中获取值,赋给deault-active,如果没有的话,再赋一个默认值。

    代码参考:

    <template>
      <el-container class="home-container">
        <!-- 头部区域 -->
        <el-header>
          <div>
            <img src="../assets/heima.png" alt="">
            <span>后台管理系统</span>
          </div>
          <el-button type="info" @click="logout" size="small">退出</el-button>
        </el-header>
        <!-- 页面主体区域 -->
        <el-container>
          <!-- 侧边栏 -->
          <el-aside :width="isCollapse ? '64px' : '200px'">
            <div class="toggle-button" @click="toggleCollapse">|||</div>
            <!-- 侧边栏菜单区域   :default-openeds="['103']"表示默认登录展开项,103对应权限管理的index值 有bug-->
            <!-- :default-active="activePath" 用来处理点击后记录选中的子菜单项 -->
            <el-menu background-color="#333744" text-color="#fff" active-text-color="#409EFF" :collapse="isCollapse"
              :collapse-transition="false"  :default-active="activePath"  router unique-opened>
              <!-- 一级菜单 -->
              <el-submenu :index="item.id + ''" v-for="item in menulist" :key="item.id">
                <!-- 一级菜单的模板区域 -->
                <template slot="title">
                  <!-- 图标 -->
                  <i :class="iconsObj[item.id]"></i>
                  <!-- 文本 -->
                  <span>{{item.authName}}</span>
                </template>
                <!-- 二级菜单 -->
                <el-menu-item :index="'/' + subItem.path" v-for="subItem in item.children" :key="subItem.id"
                  @click="saveNavState('/' + subItem.path)">
                  <template slot="title">
                    <!-- 图标 -->
                    <i class="el-icon-menu"></i>
                    <!-- 文本 -->
                    <span>{{subItem.authName}}</span>
                  </template>
                </el-menu-item>
              </el-submenu>
            </el-menu>
          </el-aside>
          <!-- 右侧内容主体 -->
          <el-main>
            <!-- 路由占位符 -->
            <router-view></router-view>
          </el-main>
        </el-container>
      </el-container>
    </template>
    
    <script>
      export default {
        data() {
          return {
            // 左侧菜单数据
            menulist: [],
            // 图标
            iconsObj: {
              125: 'iconfont icon-user',
              103: 'iconfont icon-tijikongjian',
              101: 'iconfont icon-shangpin',
              102: 'iconfont icon-danju',
              145: 'iconfont icon-baobiao'
            },
            // 是否折叠
            isCollapse: false,
            // 保存当前选中状态
            activePath: '',
          }
        },
        // 实例创建之后执行
        created() {
          // 1.获取左侧所有菜单
          this.getMenuList()
          this.activePath = window.sessionStorage.getItem('activePath')
        },
        methods: {
          // 1.退出
          logout() {
            window.sessionStorage.clear()
            this.$router.push('/login')
          },
          // 2.获取左侧所有菜单
          async getMenuList() {
            const {
              data: res
            } = await this.$http.get('menus')
            if (res.meta.status !== 200) return this.$message.error(res.meta.msg)
            this.menulist = res.data
            console.log(res)
          },
          // 3. 切换左侧菜单的折叠与展开
          toggleCollapse() {
            this.isCollapse = !this.isCollapse
          },
          // 4.保存当前页面的选中状态
          // (比如刷新了一下浏览器,还是进入到刷新前的页面,而不是默认打开页面)
          saveNavState(activePath) {
            window.sessionStorage.setItem('activePath', activePath)
            this.activePath = activePath
          }
        }
      }
    </script>
    
    <style lang="less" scoped>
      .home-container {
        height: 100%;
      }
    
      .el-header {
        background-color: #373d41;
        display: flex;
        justify-content: space-between;
        padding-left: 0;
        align-items: center;
        color: #fff;
        font-size: 20px;
    
        >div {
          display: flex;
          align-items: center;
    
          span {
            margin-left: 15px;
          }
        }
      }
    
      .el-aside {
        background-color: #333744;
    
        .el-menu {
          border-right: none;
        }
      }
    
      .el-main {
        background-color: #eaedf1;
      }
    
      .iconfont {
        margin-right: 10px;
      }
    
      .toggle-button {
        background-color: #4a5064;
        font-size: 10px;
        line-height: 24px;
        color: #fff;
        text-align: center;
        letter-spacing: 0.2em;
        cursor: pointer;
      }
    </style>
    View Code

    方案二:

      利用route.path可以获取上一次点击的路由跳转路径,结合上述方案一,就可以不用缓存了。【推荐】

     下面分享的是通过id来匹配,拿到path后,根据path去菜单中找到对应的menuItem,然后找到id,赋值给default-active.

     代码参考详见上述1

     pathMapToMenu方法如下:

    /**
     *根据path递归找对应的menu
     * @param userMenus 所有菜单
     * @param currentPath 当前path
     * @returns 对应menu
     */
    export function pathMapToMenu(userMenus: any[], currentPath: string): any {
        for (const menu of userMenus) {
            if (menu.type === 1) {
                const findMenu = pathMapToMenu(menu.children ?? [], currentPath);
                if (findMenu) {
                    return findMenu;
                }
            } else if (menu.type === 2 && menu.url === currentPath) {
                return menu;
            }
        }
    }

    三. 菜单-面包屑

    1. 封装Ypf-breadcrumb

     使用el-breadcrumb组件封装即可。

    核心代码如下

    <template>
        <!--     <el-breadcrumb :separator-icon="ArrowRight">
            <el-breadcrumb-item :to="{ path: '/' }">商品中心</el-breadcrumb-item>
            <el-breadcrumb-item :to="{ path: '/' }">商品类别</el-breadcrumb-item>
        </el-breadcrumb> -->
        <el-breadcrumb :separator-icon="ArrowRight">
            <template v-for="item in breadcrumbs" :key="item.name">
                <!-- <el-breadcrumb-item :to="{ path: item.path }">{{ item.name }}</el-breadcrumb-item> -->
                <el-breadcrumb-item>{{ item.name }}</el-breadcrumb-item>
            </template>
        </el-breadcrumb>
    </template>
    
    <script lang="ts">
        import { defineComponent, PropType } from 'vue';
        import { IBreadcrumb } from '../types';
        import { ArrowRight } from '@element-plus/icons';
    
        export default defineComponent({
            components: {
                ArrowRight,
            },
            props: {
                breadcrumbs: {
                    type: Array as PropType<IBreadcrumb[]>,
                    default: () => [],
                },
            },
            // 下面这种数组写法,不存在类型验证,同样可以使用
            // props: ['breadcrumbs'],
            setup() {
                return {
                    ArrowRight,
                };
            },
        });
    </script>
    View Code

    2. 如何获取对应的面包屑数据?【重点】

     (ps: 上述面包屑数据的封装很容易,关键是如何传入对应的值)

       A 先从vuex中获取useMenus菜单数据

         B 然后从route中获取当前的path (灵魂的地方,很关键)

         C 然后再封装个方法,根据userMenus和当前path获取面包屑需要的数据。

    核心代码

                //2. 面包屑相关数据[{name: , path: }]
                const store = useStore();
                const myBreadcrumbs = computed(() => {
                    // 2.1 从vuex中获取当前菜单数据
                    const userMenus = store.state.login.userMenus;
                    const route = useRoute();
                    // 2.2 获取当前path,然后根据当前path获取需要面包屑数组
                    const currentPath = route.path;
                    return pathMapBreadcrumbs(userMenus, currentPath);
                });

    封装方法 

    /**
     *根据path递归找对应的menu
     * @param userMenus 所有菜单
     * @param currentPath 当前path
     * @returns 对应menu
     */
    export function pathMapToMenu(userMenus: any[], currentPath: string): any {
        for (const menu of userMenus) {
            if (menu.type === 1) {
                const findMenu = pathMapToMenu(menu.children ?? [], currentPath);
                if (findMenu) {
                    return findMenu;
                }
            } else if (menu.type === 2 && menu.url === currentPath) {
                return menu;
            }
        }
    }
    
    /**
     * 根据当前的path去用户menus中获取需要的面包屑数组
     * @param userMenus 用户菜单数据
     * @param currentPath 当前路径
     * @returns 返回格式:[{name: , path: },{name: , path: }]
     */
    export function pathMapBreadcrumbs(userMenus: any[], currentPath: string) {
        const breadcrumbs: IBreadcrumb[] = [];
        for (const menu of userMenus) {
            if (menu.type === 1) {
                const findMenu = pathMapToMenu(menu.children ?? [], currentPath);
                if (findMenu) {
                    breadcrumbs.push({ name: menu.name, path: menu.url });
                    breadcrumbs.push({ name: findMenu.name, path: findMenu.url });
                }
            } else if (menu.type === 2 && menu.url === currentPath) {
                return menu;
            }
        }
        return breadcrumbs;
    }
    View Code

    四. 按钮权限

    1. 核心思路

     (1) .从接口中拿到所有按钮权限,存放到vuex里state属性中的一个数组里。 【这里需要约定一个规则 ,如:system:users:create,system:users:delete,最后一项对应按钮的名称】

     (2). 封装一个方法,判断页面上的某个按钮是否在这个数组里,返回一个标记,为 :false or true。

     (3). 每个按钮都添加v-if (或者 v-show)属性,通过上面的flag来判断是否显示。

    2. 实现步骤

    (1). 请求接口,拿到数据 封装一个方法mapMenusToPermissions,处理数据,返回按钮权限数组  存放到vuex里

    接口公用菜单权限接口,数据如下:

    [{
        "id": 38,
        "name": "系统总览",
        "type": 1,
        "url": "/main/analysis",
        "icon": "el-icon-monitor",
        "sort": 1,
        "children": [{
            "id": 39,
            "url": "/main/analysis/overview",
            "name": "核心技术",
            "sort": 106,
            "type": 2,
            "children": null,
            "parentId": 38
        }, {
            "id": 40,
            "url": "/main/analysis/dashboard",
            "name": "商品统计",
            "sort": 107,
            "type": 2,
            "children": null,
            "parentId": 38
        }]
    }, {
        "id": 1,
        "name": "系统管理",
        "type": 1,
        "url": "/main/system",
        "icon": "el-icon-setting",
        "sort": 2,
        "children": [{
            "id": 2,
            "url": "/main/system/user",
            "name": "用户管理",
            "sort": 100,
            "type": 2,
            "children": [{
                "id": 5,
                "url": null,
                "name": "创建用户",
                "sort": null,
                "type": 3,
                "parentId": 2,
                "permission": "system:users:create"
            }, {
                "id": 6,
                "url": null,
                "name": "删除用户",
                "sort": null,
                "type": 3,
                "parentId": 2,
                "permission": "system:users:delete"
            }, {
                "id": 7,
                "url": null,
                "name": "修改用户",
                "sort": null,
                "type": 3,
                "parentId": 2,
                "permission": "system:users:update"
            }, {
                "id": 8,
                "url": null,
                "name": "查询用户",
                "sort": null,
                "type": 3,
                "parentId": 2,
                "permission": "system:users:query"
            }],
            "parentId": 1
        }, {
            "id": 3,
            "url": "/main/system/department",
            "name": "部门管理",
            "sort": 101,
            "type": 2,
            "children": [{
                "id": 17,
                "url": null,
                "name": "创建部门",
                "sort": null,
                "type": 3,
                "parentId": 3,
                "permission": "system:department:create"
            }, {
                "id": 18,
                "url": null,
                "name": "删除部门",
                "sort": null,
                "type": 3,
                "parentId": 3,
                "permission": "system:department:delete"
            }, {
                "id": 19,
                "url": null,
                "name": "修改部门",
                "sort": null,
                "type": 3,
                "parentId": 3,
                "permission": "system:department:update"
            }, {
                "id": 20,
                "url": null,
                "name": "查询部门",
                "sort": null,
                "type": 3,
                "parentId": 3,
                "permission": "system:department:query"
            }],
            "parentId": 1
        }, {
            "id": 4,
            "url": "/main/system/menu",
            "name": "菜单管理",
            "sort": 103,
            "type": 2,
            "children": [{
                "id": 21,
                "url": null,
                "name": "创建菜单",
                "sort": null,
                "type": 3,
                "parentId": 4,
                "permission": "system:menu:create"
            }, {
                "id": 22,
                "url": null,
                "name": "删除菜单",
                "sort": null,
                "type": 3,
                "parentId": 4,
                "permission": "system:menu:delete"
            }, {
                "id": 23,
                "url": null,
                "name": "修改菜单",
                "sort": null,
                "type": 3,
                "parentId": 4,
                "permission": "system:menu:update"
            }, {
                "id": 24,
                "url": null,
                "name": "查询菜单",
                "sort": null,
                "type": 3,
                "parentId": 4,
                "permission": "system:menu:query"
            }],
            "parentId": 1
        }, {
            "id": 25,
            "url": "/main/system/role",
            "name": "角色管理",
            "sort": 102,
            "type": 2,
            "children": [{
                "id": 26,
                "url": null,
                "name": "创建角色",
                "sort": null,
                "type": 3,
                "parentId": 25,
                "permission": "system:role:create"
            }, {
                "id": 27,
                "url": null,
                "name": "删除角色",
                "sort": null,
                "type": 3,
                "parentId": 25,
                "permission": "system:role:delete"
            }, {
                "id": 28,
                "url": null,
                "name": "修改角色",
                "sort": null,
                "type": 3,
                "parentId": 25,
                "permission": "system:role:update"
            }, {
                "id": 29,
                "url": null,
                "name": "查询角色",
                "sort": null,
                "type": 3,
                "parentId": 25,
                "permission": "system:role:query"
            }],
            "parentId": 1
        }]
    }, {
        "id": 9,
        "name": "商品中心",
        "type": 1,
        "url": "/main/product",
        "icon": "el-icon-goods",
        "sort": 3,
        "children": [{
            "id": 15,
            "url": "/main/product/category",
            "name": "商品类别",
            "sort": 104,
            "type": 2,
            "children": [{
                "id": 30,
                "url": null,
                "name": "创建类别",
                "sort": null,
                "type": 3,
                "parentId": 15,
                "permission": "system:category:create"
            }, {
                "id": 31,
                "url": null,
                "name": "删除类别",
                "sort": null,
                "type": 3,
                "parentId": 15,
                "permission": "system:category:delete"
            }, {
                "id": 32,
                "url": null,
                "name": "修改类别",
                "sort": null,
                "type": 3,
                "parentId": 15,
                "permission": "system:category:update"
            }, {
                "id": 33,
                "url": null,
                "name": "查询类别",
                "sort": null,
                "type": 3,
                "parentId": 15,
                "permission": "system:category:query"
            }],
            "parentId": 9
        }, {
            "id": 16,
            "url": "/main/product/goods",
            "name": "商品信息",
            "sort": 105,
            "type": 2,
            "children": [{
                "id": 34,
                "url": null,
                "name": "创建商品",
                "sort": null,
                "type": 3,
                "parentId": 16,
                "permission": "system:goods:create"
            }, {
                "id": 35,
                "url": null,
                "name": "删除商品",
                "sort": null,
                "type": 3,
                "parentId": 16,
                "permission": "system:goods:delete"
            }, {
                "id": 36,
                "url": null,
                "name": "修改商品",
                "sort": null,
                "type": 3,
                "parentId": 16,
                "permission": "system:goods:update"
            }, {
                "id": 37,
                "url": null,
                "name": "查询商品",
                "sort": null,
                "type": 3,
                "parentId": 16,
                "permission": "system:goods:query"
            }],
            "parentId": 9
        }]
    }, {
        "id": 41,
        "name": "随便聊聊",
        "type": 1,
        "url": "/main/story",
        "icon": "el-icon-chat-line-round",
        "sort": 4,
        "children": [{
            "id": 42,
            "url": "/main/story/chat",
            "name": "你的故事",
            "sort": 108,
            "type": 2,
            "children": null,
            "parentId": 41
        }, {
            "id": 43,
            "url": "/main/story/list",
            "name": "故事列表",
            "sort": 109,
            "type": 2,
            "children": [],
            "parentId": 41
        }]
    }]
    View Code

    mapMenusToPermissions递归方法如下:

    // 获取按钮权限
    //  type=3,表示是按钮权限,字段为permission,内容的格式如:"system:users:create"
    // type=1,2 表示还有下一级别菜单
    export function mapMenusToPermissions(userMenus: any[]) {
        const permissonList: string[] = [];
        // 递归函数
        const _getPermissions = (menus: any[]) => {
            for (const item of menus) {
                if (item.type === 2 || item.type === 1) {
                    _getPermissions(item.children ?? []);
                } else if (item.type === 3) {
                    permissonList.push(item.permission);
                }
            }
        };
    
        // 调用
        _getPermissions(userMenus);
    
        return permissonList;
    }

    (2). 封装方法 usePermission,利用find来判断是否存在该权限。

    import { useStore } from '@/store';
    /**
     * 判断是否有这个按钮权限
     * @param pageName 页面名称
     * @param handleName 按钮名称
     * @returns true or false
     */
    export function usePermission(pageName: string, handleName: string) {
        const store = useStore();
        const permissonList = store.state.login.permissions;
        const myPer = `system:${pageName}:${handleName}`;
        //  !name=>false
        //  !!name=>true
        return !!permissonList.find((item: any) => item === myPer);
    }

    (3). 调用usePermission方法,获取结果,然后通过v-if给按钮 或者 查询 赋予权限。

               const isCreate = usePermission(props.pageName as string, 'create');
                const isUpdate = usePermission(props.pageName as string, 'update');
                const isDelete = usePermission(props.pageName as string, 'delete');
                const isQuery = usePermission(props.pageName as string, 'query');

     页面代码:

        <div class="handle-btns">
                        <el-button v-if="isUpdate" :icon="Edit" size="mini" type="text" @click="editHandle(myScope.row1)">
                            编辑
                        </el-button>
                        <el-button v-if="isDelete" :icon="Delete" size="mini" type="text" @click="deleteHandle(myScope.row1)">
                            删除
                        </el-button>
         </div>

     查询方法代码:

        const getPageData = (queryInfo: any = {}) => {
                    if (!isQuery) return;
                    store.dispatch('system/getPageListAction', {
                        pageName: props.pageName,
                        queryInfo: {
                            offset: (pageInfo.value.currentPage - 1) * pageInfo.value.pageSize,
                            size: pageInfo.value.pageSize,
                            ...queryInfo,
                        },
                    });
        };

     

     

    !

    • 作       者 : Yaopengfei(姚鹏飞)
    • 博客地址 : http://www.cnblogs.com/yaopengfei/
    • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
    • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     

     

  • 相关阅读:
    python3.7安装pygame
    Jmeter定时器
    弱网测试
    面试心得123
    如何保证测试项目的质量
    LeetCode448-数组中消失的数字
    LeetCode-两数相加
    JVM-Class文件的结构
    leetcode-222完全二叉树的节点个数
    Java虚拟机常用的性能监控工具
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/15640965.html
Copyright © 2011-2022 走看看