zoukankan      html  css  js  c++  java
  • vue-element-admin学习笔记--权限加载及自定义布局(2)

    接着昨天的笔记,整个布局和权限的结合基本搞定。后续开始看下Navbar等其他布局的设计。

    根据element-vue-admin中的说明,这里的Sidebar是基于element-ui的NavMenu来实现的。于是先看了下element-ui的NavMenu。在官网找了个例子,主要代码如下:

    <div id="app">
    <el-row class="tac">
      <el-col :span="12">
        <h5>默认颜色</h5>
        <el-menu
          default-active="2"
          class="el-menu-vertical-demo"
          @open="handleOpen"
          @close="handleClose">
          <el-submenu index="1">
            <template slot="title">
              <i class="el-icon-location"></i>
              <span>导航一</span>
            </template>
            <el-menu-item-group>
              <template slot="title">分组一</template>
              <el-menu-item index="1-1">选项1</el-menu-item>
              <el-menu-item index="1-2">选项2</el-menu-item>
            </el-menu-item-group>
            <el-menu-item-group title="分组2">
              <el-menu-item index="1-3">选项3</el-menu-item>
            </el-menu-item-group>
            <el-submenu index="1-4">
              <template slot="title">选项4</template>
              <el-menu-item index="1-4-1">选项1</el-menu-item>
            </el-submenu>
          </el-submenu>
          <el-menu-item index="2">
            <i class="el-icon-menu"></i>
            <span slot="title">导航二</span>
          </el-menu-item>
          <el-menu-item index="3" disabled>
            <i class="el-icon-document"></i>
            <span slot="title">导航三</span>
          </el-menu-item>
          <el-menu-item index="4">
            <i class="el-icon-setting"></i>
            <span slot="title">导航四</span>
          </el-menu-item>
        </el-menu>
      </el-col>
     
    </el-row>
    </div>
    

    层级结构主要为el-row>el-col>el-menu>el-submenu>el-menu-item。如果是多层级菜单使用了el-menu-item-group。其他属性可以参看element-ui的API。然后看下目前项目中的Sidebar。

    这里Sidebar使用了index.vue/SidebarItem.vue/Item.vue/Link.vue

    index.vue

    在此组件中主要初始化菜单的el-menu,同时将用户的路由传到子组件SidebarItem.vue中进行判断逻辑。这里没有使用,而是使用了,这个是在官方文档中没有但是在element-ui的源码中的。其余的一些参数配置暂时默认为了false。代码如下:

    <template>
      <div :class="{ 'has-logo': showLogo }">
        <logo v-if="showLogo" :collapse="isCollapse" />
        <el-scrollbar wrap-class="scrollbar-wrapper">
          <el-menu
            :default-active="activeMenu"
            :collapse="isCollapse"
            :background-color="variables.menuBg"
            :text-color="variables.menuText"
            :unique-opened="false"
            :active-text-color="variables.menuActiveText"
            :collapse-transition="false"
            mode="vertical"
          >
            <sidebar-item
              v-for="route in permission_routes"
              :key="route.path"
              :item="route"
              :base-path="route.path"
            />
          </el-menu>
        </el-scrollbar>
      </div>
    </template>
    
    <script>
    import { mapGetters } from "vuex";
    // import Logo from "./Logo";
    import SidebarItem from "./SidebarItem";
    import variables from "@/styles/variables.scss";
    
    export default {
      components: { SidebarItem },
      computed: {
        ...mapGetters(["permission_routes"]),
        activeMenu() {
          const route = this.$route;
          const { meta, path } = route;
          // if set path, the sidebar will highlight the path you set
          if (meta.activeMenu) {
            return meta.activeMenu;
          }
          return path;
        },
        showLogo() {
          return false;
        },
        variables() {
          return variables;
        },
        isCollapse() {
          return false;
        }
      }
    };
    </script>
    
    
    SidebarItem.vue/Item.vue/Link.vue

    SidebarItem主要是路由的递归,然后加载相应的菜单。这里的Item.vue采用了函数式组件的方式,并通过render函数返回创建的节点。Link.vue则是判断是否外链。在SidebarItem中还有个逻辑,如果一个路由只有一级子节点,在没有配置alwaysShow属性时,默认就会合并为一个菜单。其它的逻辑如:判断菜单(router)是否是隐藏的,每个菜单都有icon、path、name等。

    SidebarItem.vue

    <template>
      <div v-if="!item.hidden">
        <template
          v-if="
            hasOneShowingChild(item.children, item) &&
              (!onlyOneChild.children || onlyOneChild.noShowingChildren) &&
              !item.alwaysShow
          "
        >
          <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
            <el-menu-item
              :index="resolvePath(onlyOneChild.path)"
              :class="{ 'submenu-title-noDropdown': !isNest }"
            >
              <item
                :icon="onlyOneChild.meta.icon || (item.meta && item.meta.icon)"
                :title="item.meta.title"
              />
            </el-menu-item>
          </app-link>
        </template>
    
        <el-submenu
          v-else
          ref="subMenu"
          :index="resolvePath(item.path)"
          popper-append-to-body
        >
          <template slot="title">
            <item
              v-if="item.meta"
              :icon="item.meta && item.meta.icon"
              :title="item.meta.title"
            />
          </template>
          <sidebar-item
            v-for="child in item.children"
            :key="child.path"
            :is-nest="true"
            :item="child"
            :base-path="resolvePath(child.path)"
            class="nest-menu"
          />
        </el-submenu>
      </div>
    </template>
    
    <script>
    import path from "path";
    import Item from "./Item";
    import AppLink from "./Link";
    import { isExternal } from "@/utils/validate";
    export default {
      name: "SidebarItem",
      components: { Item, AppLink },
      props: {
        // route object
        item: {
          type: Object,
          required: true
        },
        isNest: {
          type: Boolean,
          default: false
        },
        basePath: {
          type: String,
          default: ""
        }
      },
      data() {
        // To fix https://github.com/PanJiaChen/vue-admin-template/issues/237
        // TODO: refactor with render function
        this.onlyOneChild = null;
        return {};
      },
      methods: {
        hasOneShowingChild(children = [], parent) {
          const showingChildren = children.filter(item => {
            if (item.hidden) {
              return false;
            } else {
              // Temp set(will be used if only has one showing child)
              this.onlyOneChild = item;
              return true;
            }
          });
    
          // When there is only one child router, the child router is displayed by default
          if (showingChildren.length === 1) {
            return true;
          }
    
          // Show parent if there are no child router to display
          if (showingChildren.length === 0) {
            this.onlyOneChild = { ...parent, path: "", noShowingChildren: true };
            return true;
          }
    
          return false;
        },
        resolvePath(routePath) {
          if (isExternal(routePath)) {
            return routePath;
          }
          if (isExternal(this.basePath)) {
            return this.basePath;
          }
          return path.resolve(this.basePath, routePath);
        }
      }
    };
    </script>
    
    <style scoped>
    .svg-icon {
      margin-right: 16px;
    }
    </style>
    
    

    Item.vue

    <script>
    export default {
      name: "MenuItem",
      functional: true,
      props: {
        icon: {
          type: String,
          default: ""
        },
        title: {
          type: String,
          default: ""
        }
      },
      render(h, context) {
        const { icon, title } = context.props;
        const vnodes = [];
    
        if (icon) {
          vnodes.push(<svg-icon icon-class={icon} />);
        }
    
        if (title) {
          vnodes.push(<span slot="title">{title}</span>);
        }
        return vnodes;
      }
    };
    </script>
    
    

    Link.vue

    <template>
      <!-- eslint-disable vue/require-component-is -->
      <component v-bind="linkProps(to)">
        <slot />
      </component>
    </template>
    
    <script>
    import { isExternal } from "@/utils/validate";
    
    export default {
      props: {
        to: {
          type: String,
          required: true
        }
      },
      methods: {
        linkProps(url) {
          if (isExternal(url)) {
            return {
              is: "a",
              href: url,
              target: "_blank",
              rel: "noopener"
            };
          }
          return {
            is: "router-link",
            to: url
          };
        }
      }
    };
    </script>
    
    

    初次登陆不加载菜单

    最后测试的时候发现第一次登录后不加载菜单,必须刷新页面后才加载。排查了下,发现是当初测试时,登陆后将用户的role存放到了store中,所以导致权限逻辑加载时逻辑路径错误,没有去动态的加载菜单。修改用户登录中的submitLogin方法,将返回值中的SET_ROLE去掉即可。如下所示:

      submitlogin({ commit }, { payload }) {
        const { username, password } = payload;
        return new Promise((resolve, reject) => {
          login(username.trim(), password)
            .then(response => {
              if (response.data.userInfo.token != "error") {
                //commit("SET_ROLES", response.data.userInfo.roles);//去掉此行
                commit("SET_NAME", response.data.userInfo.name);
                commit("SET_TOKEN", response.data.userInfo.token);
                setToken(response.data.userInfo.token);
                resolve();
                router.push("/");
              } else {
                console.log(response.data.userInfo.token);
                resolve();
                router.push("/404");
              }
            })
            .catch(error => {
              reject(error);
            });
        });
      }
    
  • 相关阅读:
    L317 电子烟
    L316 波音737Max 危机
    2.19.3月 专业综合错题
    L314 单音节词读音规则(二)-元音字母发音规则
    L313 珊瑚裸鼠灭绝
    L312 难看懂的
    Pycharm写代码中文输入法不跟随
    Windows下Python多版本共存
    Python之批处理字符串(打开文件)
    Python Url请求代码
  • 原文地址:https://www.cnblogs.com/GYoungBean/p/12470033.html
Copyright © 2011-2022 走看看