bunny笔记| VUE2前端实现后台管理系统的要点理解

一、理解前后端权限的区别

后端:后端权限可以控制某个用户是否能够查询数据,是否能够修改数据等操作
前端:前端仅视图层的展示权限的核心是在于服务器中的数据变化
所以后端才是权限的关键

简单了解后端吧

1.后端如何知道该请求是哪个用户发过来的

cookie
session
token

2.后端的权限设计使用RBAC

用户
角色
权限工

二、前端的权限的意义

1.降低非法操作的可能性
2.尽可能排除不必要请求减轻服务器压力
3.提高用户体验

三、前端的权限控制常见的分类

菜单的控制
界面的控制
按钮的控制
请求和响应的控制

四、vue的权限控制实现前的准备工作

推荐下载vue-admin-template模板 打开文件,如下图所示:

image.png

目录结构说明

├── build # 构建相关
├── mock # 项目mock 模拟数据
├── plop-templates # 基本模板
├── public # 静态资源
│ │── favicon.ico # favicon图标
│ └── index.html # html模板
├── src # 源代码
│ ├── api # 所有请求
│ ├── assets # 主题 字体等静态资源
│ ├── components # 全局公用组件
│ ├── directive # 全局指令
│ ├── filters # 全局 filter
│ ├── icons # 项目所有 svg icons
│ ├── lang # 国际化 language
│ ├── layout # 全局 layout
│ ├── router # 路由
│ ├── store # 全局 store管理
│ ├── styles # 全局样式
│ ├── utils # 全局公用方法
│ ├── vendor # 公用vendor
│ ├── views # views 所有页面
│ ├── App.vue # 入口页面
│ ├── main.js # 入口文件 加载组件 初始化等
│ └── permission.js # 权限管理
├── tests # 测试
├── .env.xxx # 环境变量配置
├── .eslintrc.js # eslint 配置项
├── .babelrc # babel-loader 配置
├── .travis.yml # 自动化CI配置
├── vue.config.js # vue-cli 配置
├── postcss.config.js # postcss 配置
└── package.json # package.json

跨域配置vue.config.js 中配置,如图:

image.png

说明:默认的模板中,使用的是mockjs,进一步了解mockjs官方对mockjs的解释有以下4点:

1.前后端分离
2.不需要修改既有代码,就可以拦截 Ajax 请求,返回模拟的响应数据
3.数据类型丰富
4.通过随机数据,模拟各种场景

看一个项目首先要从入口文件开始看,即:main.js和App.vue

main.js程序入口文件 初始化vue实例 并引入使用需要的插件和各种公共组件
[ new Vue()是新创建的实例 el是为实例提供挂载元素 ]

App.vue项目的主组件/页面入口文件 ,所有页面都在App.vue下进行切换,app.vue负责构建定义及页面组件归集。

这里就不做过多解释了

五、理解permission.js文件

import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['/login'] // no redirect whitelist
router.beforeEach(async(to, from, next) => {
  // start progress bar
  NProgress.start()

  // set page title
  document.title = getPageTitle(to.meta.title)

  // determine whether the user has logged in
  const hasToken = getToken()

  if (hasToken) {
    if (to.path === '/login') {
      // if is logged in, redirect to the home page
      next({ path: '/' })
      NProgress.done()
    } else {
      const hasGetUserInfo = store.getters.user
      if (hasGetUserInfo) {
        next()
      } else {
        try {
          // get user infob
          await store.dispatch('user/getInfo').then(res => {                        //触发获取用户信息到接口
            store.dispatch('permission/generateRoutes', res).then(() => {           //触发获取路由表的接口
              router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
              next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
            })
          })

          next()
        } catch (error) {
          // remove token and go to login page to re-login
          await store.dispatch('user/resetToken')  // 触发vuex中  resetToken => src/store/user.js 的 resetToken函数 清除token
          Message.error(error.message || 'Has Error') // 弹出异常
          next(`/login?redirect=${to.path}`)
          // 然后就执行这里 跳转到 login  redirect把从哪个页面出错的 做重定向的 => view/login/index.js的handleLogin函数
          // =>src/store/user.js 的login函数
          NProgress.done()
        }
      }
    }
  } else {
    /* has no token*/

    if (whiteList.indexOf(to.path) !== -1) {
      // in the free login whitelist, go directly
      next()
    } else {
      // other pages that do not have permission to access are redirected to the login page.
      //其他没有访问权限的页面将重定向到登录页面
      next(`/login?redirect=${to.path}`)
      NProgress.done()
    }
  }
})

router.afterEach(() => {
  // finish progress bar
  NProgress.done()
})

六、理解request.js文件,理解axios的拦截请求/响应原理

import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
// 创建axios实例
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
  // withCredentials: true, // 跨域请求时发送cookie
  timeout: 5000 // 请求超时
})
// 请求拦截器
service.interceptors.request.use(
  config => {
    // do something before request is sent
    console.log(config)
    if (store.getters.token) {
      // 让每个请求都带有token
      // ['AdminToken'] is a custom headers key
      // 根据实际情况自行修改
      config.headers['AdminToken'] = getToken()
    }
    return config
  },
  error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)
// 响应拦截器
service.interceptors.response.use(
  /**
   * If you want to get http information such as headers or status
   * Please return  response => response
   */

  /**
   * Determine the request status by custom code
   * Here is just an example
   * 根据HTTP状态码来判断code
   */
  response => {
    const res = response.data
    // if the custom code is not 20000, it is judged as an error.
    if (res.code !== 1) {
      Message({
        message: res.msg || 'Error',
        type: 'error',
        duration: 5 * 1000
      })
      // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
      if (res.code == 1101) {
        // to re-login
        MessageBox.confirm('登录已失效,请重新登录!', {
          confirmButtonText: '重新登录',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          store.dispatch('user/resetToken').then(() => {
            location.reload()
            //Location.reload() 方法用来刷新当前页面。该方法只有一个参数,当值为 true 时,将强制浏览器从服务器加载页面资源,
            //当值为 false 或者未传参时,浏览器则可能从缓存中读取页面。
          })
        })
      }
      return Promise.reject(new Error(res.msg || 'Error'))
    } else {
      return res
    }
  },
  error => {
    console.log('err' + error) // for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)
export default service

七、理解并编辑router/index.js的文件 ,动态路由渲染侧边栏

import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
//布局
import Layout from '@/layout'
export const constantRouterMap = [
  {
    path: '/login',
    component: () => import('@/views/login/index'),
    hidden: true
  },

  {
    path: '/404',
    component: () => import('@/views/404'),
    hidden: true
  },
  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    children: [
      {
        path: 'dashboard',
        name: 'Dashboard',
        component: () => import('@/views/dashboard/index'),
        meta: { title: '控制面板', icon: 'dashboard' }
      }
    ]
  }
]

export const asyncRouterMap = [
  {
    path: '/permission',
    component: Layout,
    name: 'permission',
    meta: {
      title: '权限管理',
      permissions: ['roleRead', 'roleEdit', 'adminUserRead', 'adminUserEdit'],
      icon: 'el-icon-s-order'
    },
    children: [
      {
        path: 'role',
        component: () => import('@/views/pemission/role'),
        name: 'role',
        meta: {
          title: '角色管理',
          permissions: ['roleRead', 'roleEdit'],
          icon: 'el-icon-coordinate'
        }
      },
      {
        path: 'index',
        component: () => import('@/views/pemission/admin'),
        name: 'admin',
        meta: {
          title: '管理员管理',
          permissions: ['adminUserRead', 'adminUserEdit'],
          icon: 'el-icon-s-custom'
        }
      }
    ]
  },
  {
    path: '/user',
    component: Layout,
    name: 'user',
    meta: {
      title: '用户管理',
      permissions: ['userRead', 'userEdit'],
      icon: 'el-icon-user-solid'
    },
    children: [
      {
        path: 'index',
        component: () => import('@/views/user/user.vue'),
        name: 'userIndex',
        meta: {
          title: '用户管理',
          permissions: ['userRead', 'userEdit'],
          icon: 'el-icon-user'
        }
      }
    ]
  },
/*动态路由渲染的侧边栏菜单*/
 // 404 page must be placed at the end !!!
  { path: '*', redirect: '/404', hidden: true }
]
const createRouter = () =>
  new Router({
    // mode: 'history', // require service support
    scrollBehavior: () => ({ y: 0 }),
    routes: constantRouterMap
  })
const router = createRouter()
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // 重置路由
}
export default router

八、新建文件,编辑api/admin.js文件

import request from '@/utils/request'
const prefix = '/admin'
// 登陆
export function login(data) {
  return request({
    url: prefix + '/login',
    method: 'post',
    data
  })
}
// 获取登录用户信息
export function getInfo() {
  return request({
    url: prefix + '/getUserInfo',
    method: 'get'
  })
}
// 退出登录
export function logout() {
  return request({
    url: prefix + '/logout',
    method: 'post'
  })
}
// 获取用户列表
export function getList(params) {
  return request({
    url: prefix + '/getUserList',
    method: 'get',
    params
  })
}
// 获取角色列表
export function getRoleList(params) {
  return request({
    url: prefix + '/getRoleList',
    method: 'get',
    params
  })
}
// 增加角色
export function addRoles(params) {
  return request({
    url: prefix + '/addRole',
    method: 'post',
    params
  })
}
// 编辑角色
export function editRoles(params) {
  return request({
    url: prefix + '/editRole',
    method: 'post',
    params
  })
}
// 删除角色
export function deleteRoles(params) {
  return request({
    url: prefix + '/deleteRole',
    method: 'post',
    params
  })
}
// 获取权限列表
export function getPermissionList(params) {
  return request({
    url: prefix + '/getAuthList',
    method: 'get',
    params
  })
}
// 新增用户
export function adminUserCreate(data) {
  return request({
    url: prefix + '/userCreate',
    method: 'post',
    data
  })
}
// 重置用户密码
export function resetAdminPassword(data) {
  return request({
    url: prefix + '/resetUserPassword',
    method: 'post',
    data
  })
}
// 编辑用户
export function editUser(data) {
  return request({
    url: prefix + '/editUser',
    method: 'post',
    data
  })
}
// 编辑用户角色
export function editAdminRoles(data) {
  return request({
    url: prefix + '/editUserRole',
    method: 'post',
    data
  })
}

九、编辑view的vue文件,此处示例 :permission/admin.vue







十、页面展示

image.png

总结

同理,结合ES6和Element-UI可以完成其它管理菜单

1.首先是理解vue_admin_template集成模板自带的主要文件
2.编辑登录页面的权限配置
3.生成侧边栏菜单管理
4.实现菜单管理页面的各个功能,常见的有增/删/改/除/查、禁用状态、上传图片等
5.新页面的实现:router[新增侧边栏菜单]+api[配置接口]+view[编辑菜单管理的功能]

你可能感兴趣的:(bunny笔记| VUE2前端实现后台管理系统的要点理解)