36

vue权限控台
这个项目基于手摸手,带你用vue撸后台 的学习自己动手实现的一个简易demo

技术栈

  • vue-axios
  • vue-cli
  • vue-router
  • element-ui

目前实现功能

  • 登录/退出
  • 面包屑导航
  • 全局树形导航

主要页面如下

  • 登录

clipboard.png

  • 主页 (页面的结构基本和主页相同。左侧导航栏,右侧上方面包屑导航和用户头像,右侧下方大块空白区域对应的是各路由)

clipboard.png

  • 项目结构

clipboard.png

views:各业务组件
components:公共组件(面包屑,导航开关等等)
api: 请求的js文件
icons: svg和相关配置文件(此项目的图标是使用svg-sprite-loader来实现的, 之前写过的相关文章链接
permission.js 检查权限
其他地方与别的项目结构一致,我就不再赘述了。

接下来我会以 实现页面的结构、面包屑导航、左侧树形导航、页面跳转这四个基础模块去讲解。因为篇幅有限,在这篇文章只放功能相关的部分代码,github源码链接在文章最后。

1.实现页面的结构。

(因为除了login等特殊页面外,其他的结构都是一致的。所以可以用Layout组件来承载页面的结构)
@/views/Layout/的目录结构如下

clipboard.png

对应到Layout的各个区域如下图。

clipboard.png

  • @/router/index.js 路由器。

当点击切换路由时,sidebar和navbar不会改变,只有app-main组件的内容会改变。
实现思路:左侧导航栏的路由的component为Layout,再通过router的redirect属性控制显示app-main区域的路由。部分代码如下

@/Layout/Layout.vue

<template>
    <div class="app-wrapper" :class="classObj">
        <!--导航 -->
        <sidebar/>
        <!--面包屑-->
        <div class="main-container">
            <!-- 头部导航 -->
            <navbar/>
            <!-- 二级路由 -->
            <app-main/>
        </div>
    </div>
</template>
<script>
import Sidebar from './components/Sidebar'
import Navbar from './components/navbar'
import AppMain from './components/AppMain'
</script>

@/Layou/components/AppMain.vue

<template>
    <section class="app-main">
        <transition name="fade-transform" mode="out-in">
            <!-- 在这里映射不同的路由到Layout中的app-main -->
            <router-view/>
        </transition>
    </section>
</template>

2.左侧导航

左侧的导航是通过element-ui的 el-menu组件和循环路由表来实现的。具体效果如下图
图片描述

如果没有子路由,点击直接跳转。有则出现下拉弹框。

3.面包屑导航

使用element-ui里的el-breadcrumb组件,循环this.$router.matched数组渲染el-breadcrumb。
this.$router.matched数组包含当前路由的所有嵌套路径片段的路由记录(包括它的子路由)。
部分代码如下@/components/breadcrumb/index

<template>
    <el-breadcrumb class="app-breadcrumd" separator="/">
        <transition-group name="breadcrumb">
            <el-breadcrumb-item v-for="(item, index) in levelList" v-if="item.meta.title" :key="item.path">
                <!--当前路由,点击不做跳转 -->
                <span v-if="item.redirect==='noredirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span>
                <a v-else @click.prevent="handleLink(item)">{{item.meta.title}}</a>
            </el-breadcrumb-item>
        </transition-group>
    </el-breadcrumb>
</template>

4.登录

该项目的处理逻辑为:
a.通过账号密码请求登录api获取token
b.将token保存到cookie和store中
c.通过判断token是否存在和导航守卫来控制路由
下面我将以上abc三个步骤分开来写


a:表单验证和发送请求

@/views/login/index 登录页,表单使用elementui的el-form。

<!-- :rules 表单校验规则的对象 {username:[],password:[]}-->
<!-- loginForm 整个表单需要提交的字段 {username:'',password:''} -->
<el-form :rules="loginRules" :model="loginForm">
    <!-- username是必填项 prop对应要验证的字段username -->
    <el-form-item prop="username">
       <el-input
            v-model="loginForm.username"
            name="username" type="text"
            auto-complete="on"
            placeholder="username" />
    </el-form-item>
    <!-- 密码 -->
    <el-form-item prop="password">
        <el-input
            v-model="loginForm.username"
            type="password"
            name="password"
            auto-complete="on" />
    </el-form-item>
</el-form>
data() {
   //username的校验规则
   const validateUsername = (rule, value, callback) => {
     // rule 对应的为loginRules.username的值
     if (!isvalidUsername(value)) {
       callback(new Error('请输入正确的用户名'))
     } else {
       callback()
     }
   }
   // password的校验规则
   const validatePass = (rule, value, callback) => {
     if (value.length < 5) {
       callback(new Error('密码不能小于5位'))
     } else {
       callback()
     }
   }
   return {
       // 表单校验规则
       loginRules: {
           //required 是否必须 trigger 触发条件 validator 该字段的校验规则
           username: [{required: true, trigger: 'blur', validator: validateUsername}],
           password: [{required: true, trigger: 'blur', validator: validatePass}]
         },
       // 表单提交对象
       loginForm: {
          username:
       }
   }
}

发送请求
(因为没有后台的相关api,所以该项目的登录、获取用户信息都是在 https://easy-mock.com/mock/59...
该登录接口下的账号是 admin、editor。密码不能小于5位)

在开发环境下配置这个请求接口的域名
config/dev.env.js

module.exports = merge(prodEnv, {
  NODE_ENV: '"development"',
  BASE_API: '"https://easy-mock.com/mock/5950a2419adc231f356a6636/vue-admin"' //直接通过process.env.BASE_API 来获取
})

@/utils/request 封装ajax请求相关
@/api/login 封装登录,获取用户信息相关的接口


b
请求成功获取token后保存到cookie和store中。这一步不再仔细,详细代码在以下几个文件
处理token @/utils/auth
登录 @/api/login
封装axios @/utils/request


C 通过判断token是否存在和导航守卫来控制路由
@/permission.js
处理逻辑:

(1)token存在,根据beforeRouter的to属性判断.
    前往login,则通过next({path:'/'})改变to.path,因为不需要再登录。
    前往其他页面,先判断store里的roles数组是否为空(roles用来控制权限),如果为空则获取用户信息,再next()或者直接next()
(2)token不存在,判断to.path是否存在白名单路由数组
    存在,直接next()
    不存在,前往login将to.path作为参数携带在url中,/login?redirect=${to.path},
    登录成功后再跳转到to.path

项目源码

如果喜欢请给个star吧!
https://github.com/lazyChan29...


someone
218 声望7 粉丝

前端工程师