• Vue + Element-UI —— 项目实战(八)(完结)


    系列文章目录

    Vue + Element-UI —— 项目实战(零)(项目概述)

    Vue + Element-UI —— 项目实战(一)

    Vue + Element-UI —— 项目实战(二)

    Vue + Element-UI —— 项目实战(三)

    Vue + Element-UI —— 项目实战(四)

    Vue + Element-UI —— 项目实战(五)

    Vue + Element-UI —— 项目实战(六)

    Vue + Element-UI —— 项目实战(七)

    Vue + Element-UI —— 项目实战(八)(完结)



    八、项目实战八

    Ⅰ、登录界面

    1. 编写 login 页面
    1. ./views/Login/login.vue,编写登录页面
    	<template>
    	  <!--status-icon: 在输入框中显示校验结果反馈图标 -->
    	  <el-form
    	    :model="form"
    	    status-icon
    	    :rules="rules"
    	    ref="form"
    	    label-width="100px"
    	    class="login-container"
    	  >
    	    <h3 class="login_title">系统登录</h3>
    	    <!-- prop:定义在form中对应的字段 -->
    	    <el-form-item
    	        label="用户名"
    	        label-width="80px"
    	        prop="username"
    	        class="username"
    	    >
    	        <!-- autocomplete:表单是否启用自动完成功能。自动完成允许浏览器对字段的输入,是基于之前输入过的值。 -->
    	        <el-input
    	            type="input"
    	            v-model="form.username"
    	            autocomplete="off"
    	            placeholder="请输入账号"
    	        >
    	        </el-input>
    	    </el-form-item>
    	    <el-form-item
    	        label="密码"
    	        label-width="80px"
    	        prop="password"
    	    >
    	        <el-input
    	            type="password"
    	            v-model="form.password"
    	            autocomplete="off"
    	            placeholder="请输入密码"
    	        ></el-input>
    	    </el-form-item>
    	    <el-form-item class="login_submit">
    	        <el-button type="primary" @click="login" class="login_submit">登录</el-button>
    	    </el-form-item>
    	  </el-form>
    	</template>
    	
    	<script>
    	// import Mock from 'mockjs'
    	import {getMenu} from '../../api/data'
    	export default {
    	  name: "Login",
    	  data() {
    	    return {
    	      form: {},
    	      // 表单校验的定义
    	      rules: {
    	        username: [
    	          // 用户名校验:必需、没有输入会有提示、失去焦点触发
    	          { required: true, message: "请输入用户名", trigger: "blur" },
    	          {
    	            min: 3,
    	            message: "用户名长度不能小于3位",
    	            trigger: "blur",
    	          },
    	        ],
    	        // 密码校验:必需、提示、失去焦点触发
    	        password: [{ required: true, message: "请输入密码", trigger: "blur" }],
    	      },
    	    };
    	  },
    	};
    	</script>
    	
    	<style lang="less" scoped>
    	.login-container {
    	    border-radius: 15px;
    	    // 背景裁剪的内边距
    	    background-clip: padding-box;
    	    margin: 180px auto;
    	    width: 350px;
    	    padding: 35px 35px 15px 35px;
    	    background-color: #fff;
    	    border: 1px solid #eaeaea;
    	    box-shadow: 0 0 25px #cac6c6;
    	}
    	.login_title {
    	    margin: 0px auto 40px auto;
    	    text-align: center;
    	    color: #505458;
    	}
    	.login_submit {
    	    margin: 10px auto 0 auto;
    	}
    	</style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93

    登录页面

    在这里插入图片描述

    2. 登录权限 & 导航守卫
    1. 安装缓存插件
    	npm i js-cookie
    
    • 1
    1. 在 store 文件中,创建 user.js 文件,用于缓存输入的内容。
    	import Cookie from "js-cookie";
    	
    	export default {
    	  state: {
    	    token: "",
    	  },
    	  mutations: {
    	    // 设置cookie
    	    setToken(state, val) {
    	      (state.token = val), Cookie.set("token", val); //cookie的名称,传入的值
    	    },
    	    // 清除cookie
    	    clearToken(state) {
    	      (state.token = ""), Cookie.remove("token");
    	    },
    	    // 获取cookie
    	    getToken(state) {
    	      // 如果当前的缓存中有token,直接获取。如果没有,要从state中获取
    	      state.token = Cookie.get("token") || state.token;
    	    },
    	  },
    	};
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    1. 在 ./store/index.js 中导入
    	import Vue from 'vue'
    	import Vuex from 'vuex'
    	import tab from './tab'
    	import user from './user'
    	// 全局使用Vuex
    	Vue.use(Vuex)
    	export default new Vuex.Store({
    	    // 模块化定义
    	    modules:{
    	        tab,
    	        user
    	    }
    	})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    1. main.js 中添加前置路由守卫
    	// 前置路由守卫
    	router.beforeEach((to, from, next) => {
    	  store.commit('getToken') //防止页面刷新后vuex丢失token信息
    	  const token = store.state.user.token
    	  // 如果token不存在,并且当前页不是登录页
    	  if(!token && to.name !== 'login') {
    	    next({name: 'login'}) // 返回登录页
    	  } else if(token && to.name === 'login'){
    	    next({name: 'home'})
    	  } else {
    	    next()
    	  }
    	})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    2. 登录接口逻辑
    1. ./api/mockServerData/permission.js,用于定义接口相关的逻辑
    	// 接口的相关逻辑
    	import Mock from 'mockjs'
    	export default {
    	  // 模拟菜单权限,接收传递进来的参数
    	  getMenu: config => {
    	    console.log(config);
    	    const { username, password } = JSON.parse(config.body)
    	    console.log(JSON.parse(config.body))
    	    // 先判断用户是否存在
    	    // 判断账号和密码是否对应
    	    if (username === 'admin' && password === 'admin') {
    	      return {
    	        code: 20000,
    	        data: {
    	          menu: [
    	            {
    	              path: '/home',
    	              name: 'home',
    	              label: '首页',
    	              icon: 's-home',
    	              url: 'home/index'
    	            },
    	            {
    	              path: '/mall',
    	              name: 'mall',
    	              label: '商品管理',
    	              icon: 'video-play',
    	              url: 'mall/index'
    	            },
    	            {
    	              path: '/user',
    	              name: 'user',
    	              label: '用户管理',
    	              icon: 'user',
    	              url: 'User/index'
    	            },
    	            {
    	              label: '其他',
    	              icon: 'location',
    	              children: [
    	                {
    	                  path: '/page1',
    	                  name: 'page1',
    	                  label: '页面1',
    	                  icon: 'setting',
    	                  url: 'other/pageOne.vue'
    	                },
    	                {
    	                  path: '/page2',
    	                  name: 'page2',
    	                  label: '页面2',
    	                  icon: 'setting',
    	                  url: 'other/pageTwo.vue'
    	                }
    	              ]
    	            }
    	          ],
    	          token: Mock.Random.guid(),
    	          message: '获取成功'
    	        }
    	      }
    	    } else if (username === 'xiaoxiao' && password === 'xiaoxiao') {
    	      return {
    	        code: 20000,
    	        data: {
    	          menu: [
    	            {
    	              path: '/',
    	              name: 'home',
    	              label: '首页',
    	              icon: 's-home',
    	              url: 'home/index'
    	            },
    	            {
    	              path: '/mall',
    	              name: 'mall',
    	              label: '商品管理',
    	              icon: 'video-play',
    	              url: 'mall/index'
    	            }
    	          ],
    	          token: Mock.Random.guid(),
    	          message: '获取成功'
    	        }
    	      }
    	    } else {
    	      return {
    	        code: -999,
    	        data: {
    	          message: '密码错误'
    	        }
    	      }
    	    }
    	  }
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    1. 在 mock.js 中进行接口拦截
    	import permissionApi from './mockServerData/permission'
    	Mock.mock(/permission\/getMenu/, 'post', permissionApi.getMenu)
    
    • 1
    • 2
    3. 菜单权限功能
    1. 动态添加路由,在 tab.js 中定义 menu 空数组
    	menu: []
    
    • 1
    1. 在 tab.js 的 mutations 中添加修改方法
    	setMenu(state, val) {
          state.menu = val
          Cookie.set('menu', JSON.stringify(val))
        },
        clearMenu(state) {
          state.menu = []
          Cookie.remove('menu')
        },
        addMenu(state, router) {
          if(!Cookie.get('menu')) {
            return 
          }
          // 转成对象
          const menu = JSON.parse(Cookie.get('menu'))
          state.menu = menu
          const menuArray = []
          menu.forEach(item => {
            // 有二级菜单的数据
            if(item.children) {
              item.children = item.children.map(item => {
                item.component = () => import(`../views/${item.url}`)
                return item
              })
              menuArray.push(...item.children)
              // 一级菜单
            }else{
              item.component = () => import(`../views/${item.url}`)
              menuArray.push(item)
            }
          })
          // 路由的动态添加
          menuArray.forEach(item => {
            router.addRoute('Main', item)
          })
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    1. login.vue 中添加方法
    	login() {
          getMenu(this.form).then((res) => {
            console.log(res, "res");
            // 接口调用成功
            if (res.code === 20000) {
              // 登录成功后,清除当前路由
              this.$store.commit("clearMenu");
              // 设置路由,传入数据
              this.$store.commit("setMenu", res.data.menu);
              // 设置token,传入接口的数据
              this.$store.commit("setToken", res.data.token);
              // 动态添加路由,传入router 实例
              this.$store.commit("addMenu", this.$router); 
              // 页面跳转
              this.$router.push({ name: "home" });
            } else {
              //失败的提示
              this.$message.warning(res.data.message);
            }
          });
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    这样就可以把 CommonAside.vue 中写死的数据去掉,只留 menu: []。
    .router/index.js 中里面的数据都删掉,只保留 children: []。

    1. 在 CommonAside.vue 中定义 asyncMenu(),用来获取 menu
    	computed: {
    	    noChildren() {
    	      // 过滤出来没有子项目的数据
    	      return this.asyncMenu.filter((item) => !item.children);
    	    },
    	    hasChildren() {
    	      // 过滤出有子项目的数据
    	      return this.asyncMenu.filter((item) => item.children);
    	    },
    	    isCollapse() {
    	      return this.$store.state.tab.isCollapse;
    	    },
    	    asyncMenu() {
    	      // 获取menu
    	      return this.$store.state.tab.menu
    	    }
    	  }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    登录成功

    在这里插入图片描述
    在这里插入图片描述

    Ⅱ、权限管理问题 & 退出登录

    1. 刷新白屏的解决方法
    1. main.js 中,在 vue实例生成前, created 钩子中调用动态路由的方法。
    	created() {
          store.commit('addMenu', router)
        }
    
    • 1
    • 2
    • 3
    2. 权限管理

    已经登录后,不应该还能访问登录页面,而是让它跳转到首页。

    1. 在 main.js 中修改路由守卫
    	router.beforeEach((to, from, next) => {
    	  store.commit('getToken') //防止页面刷新后vuex丢失token信息
    	  const token = store.state.user.token
    	  // 如果token不存在,并且当前页不是登录页
    	  if(!token && to.name !== 'login') {
    	    next({name: 'login'}) // 返回登录页
    	  } else if(token && to.name === 'login'){
    	    next({name: 'home'})
    	  } else {
    	    next()
    	  }
    	})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    3. 退出功能

    在 CommonHeader.vue 中添加退出功能

    	<el-dropdown-menu slot="dropdown">
          <el-dropdown-item>个人中心</el-dropdown-item>
          <el-dropdown-item @click.native="logOut">退出</el-dropdown-item>
        </el-dropdown-menu>
    
    • 1
    • 2
    • 3
    • 4
    	logOut() {
          this.$store.commit("clearToken"); //清除token
          this.$store.commit("clearMenu"); //清除menu
          this.$router.push("/login"); //跳转到登录界面
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    点击退出,返回到登录界面

    在这里插入图片描述

    不积跬步无以至千里,不积小流无以成江海

    点个关注不迷路,持续更新中…

  • 相关阅读:
    理解计算着色器中glsl语言的内置变量
    TS初体验
    【教学类-19-03】20221127《ABBABB式-规律排序-A4竖版2份》(中班)
    tomcat配置jdk环境
    自编码器(Autoencoder)基本原理与模型实现
    黑马程序员Java Web--14.综合案例--修改功能实现
    python面试题常用语句
    IPV4地址、VLSM以及CIDR
    NR CSI(二) the workflow of CSI report
    Leecode刷题 383.赎金信——哈希表
  • 原文地址:https://blog.csdn.net/qq_45902692/article/details/125336362