5.23-vue-admin-template笔记

本文详细记录了vue-admin-template的使用,包括vue-element-admin的结构、安装过程、登录表单验证,特别是如何存储token并实现登录。还介绍了主页的token拦截处理流程、Axios拦截器的工作原理及应用,以及vue-element-admin与vue-admin-template的区别。同时,文章探讨了RBAC权限系统,Element UI的引入方式,和Vue全家桶的概念,以及如何自定义指令。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

vue-admin-template

vue-element-admin结构

├── build # 构建相关
├── mock # 项目mock 模拟数据
├── public # 静态资源
│ │── favicon.ico # favicon图标
│ └── index.html # html模板
├── src # 源代码
│ ├── api # 所有请求
│ ├── assets # 主题 字体等静态资源
│ ├── components # 全局公用组件
│ ├── icons # 项目所有 svg icons
│ ├── layout # 全局 layout
│ ├── router # 路由
│ ├── store # 全局 store管理
│ ├── styles # 全局样式
│ ├── utils # 全局公用方法
│ ├── vendor # 公用vendor
│ ├── views # views 所有页面
│ ├── App.vue # 入口页面
│ ├── main.js # 入口文件 加载组件 初始化等
│ └── permission.js # 权限管理
│ └── settings.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

安装

# 进入项目目录
cd vue-admin-template

# 安装依赖
npm install

# 本地开发 启动项目
npm run dev

登录表单验证

login页面的html部分

<template>
  <div class="login-container">
    <el-form
      ref="loginForm"
      :model="loginForm"
      :rules="loginRules"
      class="login-form"
      auto-complete="on"
      label-position="left"
    >
      <div class="title-container">
        <h3 class="title">小优后台电商管理系统</h3>
      </div>

      <el-form-item prop="username">
        <span class="svg-container">
          <svg-icon icon-class="user" />
        </span>
        <el-input
          ref="username"
          v-model="loginForm.username"
          v-focus
          placeholder="Username"
          name="username"
          type="text"
          tabindex="1"
          auto-complete="on"
          autofocus
        />
      </el-form-item>

      <el-form-item prop="password">
        <span class="svg-container">
          <svg-icon icon-class="password" />
        </span>
        <el-input
          :key="passwordType"
          ref="password"
          v-model="loginForm.password"
          :type="passwordType"
          placeholder="Password"
          name="password"
          tabindex="2"
          auto-complete="on"
          @keyup.enter.native="handleLogin"
        />
        <!-- 按键修饰符 enter:表示摁下回车键的时候 -->
        <span class="show-pwd" @click="showPwd">
          <svg-icon
            :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'"
          />
        </span>
      </el-form-item>

      <el-button
        :loading="loading"
        type="primary"
        style="width: 100%; margin-bottom: 30px"
        @click.native.prevent="handleLogin"
        >登录</el-button
      >
      <!-- 事件修饰符 prevent:阻止事件的默认行为 -->

      <div class="tips">
        <span style="margin-right: 20px">username: admin</span>
        <span> password: any</span>
      </div>
    </el-form>
  </div>
</template>

<script>
import { validUsername } from '@/utils/validate'

export default {
  name: 'Login',
  data () {
    const validateUsername = (rule, value, callback) => {
      if (!validUsername(value)) {
        callback(new Error('请输入正确的用户名'))
      } else {
        callback()
      }
    }
    return {
      loginForm: {
        username: 'admin',
        password: '123456'
      },
      loginRules: {
        username: [{ required: true, trigger: 'blur', validator: validateUsername }],
        password: [
          { required: true, trigger: 'blur' },
          { min: 6, max: 12, trigger: 'blur', message: '密码长度应该在6-12位之间' }
        ]
      },
      loading: false,
      passwordType: 'password',
      redirect: undefined
    }
  },
  methods: {
    // eslint-disable-next-line space-before-function-paren
    showPwd () {
      if (this.passwordType === 'password') {
        this.passwordType = ''
      } else {
        this.passwordType = 'password'
      }
      this.$nextTick(() => {
        this.$refs.password.focus()
      })
    },
    // eslint-disable-next-line space-before-function-paren
    async handleLogin () {
      try {
        await this.$refs.loginForm.validate()
        this.loading = true
        // dispatch 调用 action
        await this.$store.dispatch('user/login', this.loginForm)
        console.log('login组件继续执行')
        // 登陆成功之后跳转到
        this.$router.push({ path: '/' })
        this.loading = false
      } catch (err) {
        console.log(err)
        this.loading = false
        return false
      }
    }
  }
}
</script>

<style lang="scss">
/* 修复input 背景不协调 和光标变色 */
/* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */

$bg: #283443;
$light_gray: #fff;
$cursor: #fff;

@supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
  .login-container .el-input input {
    color: $cursor;
  }
}

/* reset element-ui css */
.login-container {
  .el-input {
    display: inline-block;
    height: 47px;
    width: 85%;

    input {
      background: transparent;
      border: 0px;
      -webkit-appearance: none;
      border-radius: 0px;
      padding: 12px 5px 12px 15px;
      color: $light_gray;
      height: 47px;
      caret-color: $cursor;

      &:-webkit-autofill {
        box-shadow: 0 0 0px 1000px $bg inset !important;
        -webkit-text-fill-color: $cursor !important;
      }
    }
  }

  .el-form-item {
    border: 1px solid rgba(255, 255, 255, 0.1);
    background: rgba(0, 0, 0, 0.1);
    border-radius: 5px;
    color: #454545;
  }
}
</style>

<style lang="scss" scoped>
$bg: #2d3a4b;
$dark_gray: #889aa4;
$light_gray: #eee;

.login-container {
  min-height: 100%;
  width: 100%;
  background-color: $bg;
  overflow: hidden;

  .login-form {
    position: relative;
    width: 520px;
    max-width: 100%;
    padding: 160px 35px 0;
    margin: 0 auto;
    overflow: hidden;
  }

  .tips {
    font-size: 14px;
    color: #fff;
    margin-bottom: 10px;

    span {
      &:first-of-type {
        margin-right: 16px;
      }
    }
  }

  .svg-container {
    padding: 6px 5px 6px 15px;
    color: $dark_gray;
    vertical-align: middle;
    width: 30px;
    display: inline-block;
  }

  .title-container {
    position: relative;

    .title {
      font-size: 26px;
      color: $light_gray;
      margin: 0px auto 40px auto;
      text-align: center;
      font-weight: bold;
    }
  }

  .show-pwd {
    position: absolute;
    right: 10px;
    top: 7px;
    font-size: 16px;
    color: $dark_gray;
    cursor: pointer;
    user-select: none;
  }
}
</style>

el-form 的属性  :*rules*="rules"  后面这个rules在data里面定义各种规则,然后把规则再给el-form-item的prop属性对应上

然后验证的时候 this.$refs.form.**validate**(),form指的是表单上的ref属性绑定的

表单验证部分

在这里插入图片描述

在这里插入图片描述

最后点击登录进行表单验证,存储token,实现登录

在这里插入图片描述

api调用

在这里插入图片描述

在这里插入图片描述

主页的token拦截处理

根据token处理主页的访问权限问题

流程图

在这里插入图片描述

代码:

import router from './router'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { getToken } from '@/utils/auth'
import getPageTitle from '@/utils/get-page-title'
import store from './store'

NProgress.configure({ showSpinner: false }) // NProgress Configuration

const whiteList = ['/login'] // no redirect whitelist

// eslint-disable-next-line space-before-function-paren
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 token = getToken()
  // console.log(token)

  if (token) {
    if (to.path === '/login') {
      // if is logged in, redirect to the home page
      next({ path: '/' })
      NProgress.done()
    } else {
      /**
       * 静态路由有五个
       * 如果 <5,则里面只有静态路由,就要根据用户的权限生成动态路由
       * 然后加入到静态路由中
       */
      if (store.state.permission.routes.length <= 5) {
        // 验证用户是否有访问某个路由的权限
        const routes = await store.dispatch('permission/filterRoutes', store.state.user.menus)
        // console.log(routes)
        router.addRoutes(routes)
        /**
         *  相当于跳到对应的地址  相当于多做一次跳转 为什么要多做一次跳转
         */
        next(to.path)
      } else {
        next()
      }
    }
  } else {
    /* import router from './router'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { getToken } from '@/utils/auth'
import getPageTitle from '@/utils/get-page-title'
import store from './store'

NProgress.configure({ showSpinner: false }) // NProgress Configuration

const whiteList = ['/login'] // no redirect whitelist

// eslint-disable-next-line space-before-function-paren
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 token = getToken()
  // console.log(token)

  if (token) {
    if (to.path === '/login') {
      // if is logged in, redirect to the home page
      next({ path: '/' })
      NProgress.done()
    } else {
      /**
       * 静态路由有五个
       * 如果 <5,则里面只有静态路由,就要根据用户的权限生成动态路由
       * 然后加入到静态路由中
       */
      if (store.state.permission.routes.length <= 5) {
        // 验证用户是否有访问某个路由的权限
        const routes = await store.dispatch('permission/filterRoutes', store.state.user.menus)
        // console.log(routes)
        router.addRoutes(routes)
        /**
         *  相当于跳到对应的地址  相当于多做一次跳转 为什么要多做一次跳转
         */
        next(to.path)
      } else {
        next()
      }
    }
  } 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`)
      NProgress.done()
    }
  }
})

router.afterEach(() => {
  // finish progress bar
  NProgress.done()
})
import router from './router'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { getToken } from '@/utils/auth'
import getPageTitle from '@/utils/get-page-title'
import store from './store'

NProgress.configure({ showSpinner: false }) // NProgress Configuration

const whiteList = ['/login'] // 白名单

router.beforeEach(async (to, from, next) => {
  NProgress.start()

  document.title = getPageTitle(to.meta.title)

  const token = getToken()
  // console.log(token)

  if (token) {
  	// 如果有 token,判断是否请求的是登录页
    if (to.path === '/login') {
      // 如果请求的是登录页,因为具有 token ,表明已经登录了,就直接跳转主页即可
      next({ path: '/' })
      NProgress.done()
    } else {
      /**
       * 静态路由有五个
       * 如果 <5,则里面只有静态路由,就要根据用户的权限生成动态路由
       * 然后加入到静态路由中
       */
      if (store.state.permission.routes.length <= 5) {
        // 验证用户是否有访问某个路由的权限
        const routes = await store.dispatch('permission/filterRoutes', store.state.user.menus)
        // console.log(routes)
        router.addRoutes(routes)
        /**
         *  相当于跳到对应的地址  相当于多做一次跳转 为什么要多做一次跳转
         */
        next(to.path)
      } else {
        next()
      }
    }
  } else {
    /* 如果没有 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`)
      NProgress.done()
    }
  }
})

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

    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`)
      NProgress.done()
    }
  }
})

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

Axios拦截器

axios作为网络请求的第三方工具, 可以进行请求和响应的拦截

拦截器的原理

在这里插入图片描述

请求拦截器和响应拦截器

import axios from 'axios'
import { Message } from 'element-ui'
import store from '@/store'
import router from '../router'
import { getToken, getTimeSteamp } from '@/utils/auth'

// 定义 token 超时的时间
const timeOut = 3600 * 24 * 7

// axios.defaults.baseURL=
// 创建一个axios实例
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
  // withCredentials: true, // send cookies when cross-domain requests
  timeout: 5000 // request timeout
})

// request interceptor 注册的请求的拦截器
service.interceptors.request.use(
  config => {
    // do something before request is sent
    // 获取token值且有 token 值怎么做
    if (store.getters.token) {
      // 获取 token 设置的时间
      const tokenTIme = getTimeSteamp()
      // 获取当前的时间
      const currentTime = Date.now()
      // 判断当前 token 的时间戳是否过期
      if ((currentTime - tokenTIme) / 1000 > timeOut) {
        // store.dispatch('user/logout')
        // // 跳转到登录页
        // router.push('/login')
        // return Promise.reject(new Error('token超时了'))
        console.log((currentTime - tokenTIme) / 1000)
      }
      config.headers['Authorization'] = getToken()
    }
    return config
  },
  error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)

// response interceptor  响应拦截器
service.interceptors.response.use(
  response => {
    const { meta: { msg, status }, data } = response.data
    // console.log(res)
    // if the custom code is not 20000, it is judged as an error.
    if (status !== 200 && status !== 201) {
      // 处理 token 过期问题
      if (status === 400 && msg === '无效token') {
        store.dispatch('user/logout')
        router.push('/login')
      }
      Message({
        message: msg || 'Error',
        type: 'error',
        duration: 5 * 1000
      })
      return Promise.reject(new Error(msg || 'Error'))
    } else {
      return data
    }
  },
  error => {
    console.log('err' + error) // for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

export default service

里面auth里面的方法

在这里插入图片描述

vue-element-admin 和 vue-admin-template 有什么区别?

而vue-element-admin是基于element-ui 的一套后台管理系统集成方案。
vue-admin-template是基于vue-element-admin的一套后台管理系统基础模板(最少精简版),可作为模板进行二次开发。
vue-element-admin 是一个后台的集成方案,它囊括了很多的功能和组件,并不适合作为基础模板来进行二次开发。 vue-admin-template 则是一个后台的基础模板脚手架,适合在它的基础上进行二次开发。

RBAC权限系统

在RBAC模型里面,有3个基础组成部分,分别是:用户、角色和权限。
RBAC通过定义角色的权限,并对用户授予某个角色从而来控制用户的权限,实现了用户和权限的逻辑分离(区别于ACL模型),极大地方便了权限的管理

权限验证:
在前后端分离的项目中,权限验证分为三个部分
1)页面级别的权限验证,不同用户登录后,看弹道的左侧菜单是不一样的,又或者在地址栏输入没有的地址,会提示404页面找不到,主要依靠对路由对象的操作实现
2)功能级别的权限验证,根据角色判断你是否有这个功能,有则显示,无则提示权限不够
3)对于后台的API的请求权限,主要由后台完成

User(用户):每个用户都有唯一的UID识别,并被授予不同的角色
Role(角色):不同角色具有不同的权限
Permission(权限):访问权限
用户-角色映射:用户和角色之间的映射关系
角色-权限映射:角色和权限之间的映射

在这里插入图片描述

elementElement-UI全局引入和按需引入

网址:
https://element.eleme.cn/#/zh-CN/component/quickstart

完整引入:
	在 main.js 中写入以下内容:
        import Vue from 'vue';
        import ElementUI from 'element-ui';
        import 'element-ui/lib/theme-chalk/index.css';
        import App from './App.vue';

        Vue.use(ElementUI);

        new Vue({
          el: '#app',
          render: h => h(App)
        });
以上代码便完成了 Element 的引入。需要注意的是,样式文件需要单独引入。

按需引入:
	借助 babel-plugin-component,我们可以只引入需要的组件,以达到减小项目体积的目的。
	首先,安装 babel-plugin-component:
	npm install babel-plugin-component -D
	然后,将 .babelrc 修改为:
	{
      "presets": [["es2015", { "modules": false }]],
      "plugins": [
        [
          "component",
          {
            "libraryName": "element-ui",
            "styleLibraryName": "theme-chalk"
          }
        ]
      ]
    }
    接下来,如果你只希望引入部分组件,比如 Button 和 Select,那么需要在 main.js 中写入以下内容:
    import Vue from 'vue';
    import { Button, Select } from 'element-ui';
    import App from './App.vue';
    Vue.use(Button)
 	Vue.use(Select)
 	new Vue({
      el: '#app',
      render: h => h(App)
    });

vue全家桶指的是?

vue(整体架构,Vue 是一套用于构建用户界面的渐进式框架)
vuex(状态管理,Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式,适合开发大型单页应用 
vue-router(路由)
vue_resource || axios(ajax请求)

如何自定义指令

Vue.directive 自定义指令
filter  过滤器
computed  计算属性
注册一个自定义指令(有全局注册与局部注册)
全局注册注册主要是用Vue.directive方法进行注册
Vue.directive第一个参数是指令的名字(不需要写上v-前缀),第二个参数可以是对象数据,也可以是一个指令函数
el:指令所绑定的元素,可以用来直接操作 DOM
示例代码:
	// 注册一个全局自定义指令 `v-focus`
    Vue.directive('focus', {
      // 当被绑定的元素插入到 DOM 中时……
      inserted: function (el) {
        // 聚焦元素
        el.focus()  // 页面加载完成之后自动让输入框获取到焦点的小功能
      }
    })
    
局部注册通过在组件options选项中设置directive属性
    directives: {
      focus: {
        // 指令的定义
        inserted: function (el) {
          el.focus() // 页面加载完成之后自动让输入框获取到焦点的小功能
        }
      }
    }
然后你可以在模板中任何元素上使用新的 v-focus property,如下:
	<input v-focus />
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值