Vue + Element UI 实现权限管理系统 前端篇(四):优化登录流程
继续笔记:在这里基本是大框架搭出来了。
完善登入流程
1.丰富登入界面
1.1 Element 参考 搭建
<template> <el-form :model="loginForm" ref="loginForm" :rules="fieldRules" label-position="left" label-width="0px" class="demo-ruleForm login-container" > <h3 class="title">登入系统</h3> <el-form-item prop="account"> <el-input type="text" v-model="loginForm.account" auto-complete="off" placeholder="账号"></el-input> </el-form-item> <el-form-item prop="password"> <el-input type="password" v-model="loginForm.password" auto-complete="off" placeholder="密码"></el-input> </el-form-item> <!-- <el-checkbox v-model="checked" checked class="remember">记住密码</el-checkbox> --> <el-form-item style="width: 100%;"> <el-button type="primary" style="width: 48%;" @click.native.prevent="reset">重 置</el-button> <el-button type="primary" style="width: 48%;" @click.native.prevent="login" :loading="logining">登 入</el-button> </el-form-item> </el-form> </template>
1.2添加页面组件显示规则和操作响应,登入成功后将登入信息存储到本地会话,用于配置路由跳转
<script> import mock from '@/mock/index.js'; import Cookies from 'js-cookie'; export default { name: 'Login', data() { return { logining: false, loginForm: { // 账号,密码 account: 'admin', password: 'admin', }, fieldRules: { // 输入框规则 rules:表单验证规则,即 async-validator 所使用的校验规则,类型为 Object。 account: [ { required: true, message: '请输入账号', trigger: 'blur' }, // 'blur'是鼠标失去焦点的时候会触发验证 required 是否必填,如果不设置,则会根据校验规则自动生成 ], password: [ { required: true, message: '请输入密码', trigger: 'blur' }, ], }, // checked: true } }, methods: { login() { let userInfo = { account: this.loginForm.account, password: this.loginForm.password }; // 提取本地账号密码 this.$api.login(JSON.stringify(userInfo)).then((res) => { // 传递给后台本地账号密码返回 token ,现在是没有验证账号密码过程的 alert(res.data.token) Cookies.set('token', res.data.token); // 放置token到Cookie sessionStorage.setItem('user', userInfo.account); // 保存用户到本地会话 this.$router.push('/'); // 登入成功,转跳到主页 }).catch((res) => { alert(res); }) }, reset() { this.$refs.loginForm.resetFields(); } } } </script>
1.3优化样式
<style lang="scss" scoped> .login-container { width: 400px; background: #fff; background-clip: padding-box; margin: 180px auto; padding: 35px 35px 15px; box-sizing: border-box; border: 1px solid #eaeaea; border-radius: 5px; box-shadow: 0 0 30px rgba(0, 0, 0 , 0.1); .title { text-align: center; color: #505458; margin: 0 auto 40px; } // .remember { // margin: 0 0 25px; // } }
1.4最后效果
2.修改接口 post 请求
修改 http/interface.js,把请求类型改为 post,并传入 data 参数。
3.修改对应的 mock 接口
修改 mock/modules/logins.js,把请求类型改为 post
// 登入接口 export function login () { return { // isOpen: false, url: 'http://localhost:8080/login', type: 'post', data: { 'msg': 'success', 'code': 0, 'data': { 'token': '4344323121398' // 其它数据 } } } }
4.添加导航守卫(只有登入后才能访问相应权限界面)
// 导航守卫 router.beforeEach((to, from, next) => { // 登入界面成功之后,会把用户信息保存在会话 // 存在时间为会话生命周期,页面关闭既失效 let user = sessionStorage.getItem('user'); if(to.path == '/login') { // 访问登入界面,如果用户会话信息存在,代表已登入过,转跳到主页 if(user) { next({ path: '/' }) }else { next() } }else { // 访问非登入界面,且用户信息不存在,代表未登入,则转跳到登入界面 if(!user) { next({ path: '/login' }) }else { next() } } })
5.完善主页界面
5.1 构建主界面
<template> <div class="container"> <!-- row: 行概念 col: 列概念, col组件的 :span属性的布局调整 栅格占据的列数,默认值 24 --> <el-row class="header"> <!-- 大标题 class 的切换 看 col 是否失效 lapse:失效 --> <el-col :span="5" class="logo" :class="isCollapse ? 'logo-collapse-width' : 'logo-width'"> <img :src="this.logo" /> {{ isCollapse ? sysName : sysName }} </el-col> <!-- icon @click.prevent: 阻止默认行为 --> <el-col :span="1"> <div class="tools" @click.prevent="collapse"> <i class="el-icon-menu"></i> </div> </el-col> <!-- 顶部导航栏, 默认是垂直模式,通过 mode="horizontal" 设置成水平模式 horizontal: 水平的 default-openeds 当前打开的 sub-menu 的 index 的数组 --> <el-col :span="13"> <div class="hearNavBar"> <!-- default-active 它的说明内容为:当前激活菜单的 index。(即当前哪一项被设置为高亮) 是为了浏览器刷新后,仍然可以定位到之前选中的路由。 mode 属性可以使导航菜单变更为水平模式 --> <el-menu :default-active="activeIndex" class="el-menu-demo" background-color="#4b5f6e" text-color="#fff" active-text-color="#ffd04b" mode="horizontal" @select="handleSelectHearNavBar" > <!-- index 唯一标志 默认值 null --> <el-menu-item index="1">首页</el-menu-item> <el-menu-item index="2">消息中心</el-menu-item> <el-menu-item index="3">订单管理</el-menu-item> </el-menu> </div> </el-col> <el-col :span="5" class="userinfo"> <el-dropdown trigger="hover"> <span class="el-dropdown-link userinfo-inner"> <img :src="this.userAvatar" /> {{ username }} </span> <el-dropdown-menu slot="dropdown"> <el-dropdown-item>我的消息</el-dropdown-item> <el-dropdown-item>设置</el-dropdown-item> <!-- 自定义组件 @click + .native 后才能触发 --> <el-dropdown-item divided @click.native="logout">退出登入</el-dropdown-item> </el-dropdown-menu> </el-dropdown> </el-col> </el-row> <el-row class="main"> <aside class="aside"> <!-- 导航菜单 --> <el-menu default-active="1-2" class="el-menu-vertical-demo" :collapse="isCollapse" @open="handleopen" @close="handleclose" @select="handleselect" > <!-- 这里的两个 slot 不懂 --> <el-submenu index="1"> <template slot="title"> <i class="el-icon-location"></i> <span slot="title">系统管理</span> </template> <el-menu-item index="1-1" @click="$router.push('user')">用户管理</el-menu-item> <el-menu-item index="1-2" @click="$router.push('menu')">菜单管理</el-menu-item> </el-submenu> <el-submenu index="2"> <template slot="title"> <i class="el-icon-location"></i> <span slot="title">系统监控</span> </template> <el-menu-item index="2-1" @click="$router.push('user')">服务监控</el-menu-item> <el-menu-item index="2-2" @click="$router.push('menu')">任务监控</el-menu-item> </el-submenu> <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> </aside> <!-- section元素表示一个包含在HTML文档中的独立部分 content:内容 container:容器 --> <section class="content-container"> <div class="grid-content bg-purple-light"> <!-- 面包屑 由当前路由动态显示--> <el-col :span="24" class="breadcrumb-container"> <el-breadcrumb separator="/" class="breadcrumb-inner"> <el-breadcrumb-item v-for="item in $route.matched" :key="item.path"> {{ item.name }} </el-breadcrumb-item> </el-breadcrumb> </el-col> <!-- 子路由展示地方,transition过渡效果--> <el-col :span="24" class="content-wrapper"> <transition name="fade" mode="out-in"> <router-view></router-view> </transition> </el-col> </div> </section> </el-row> </div> </template>
5.2 处理页面事件和页面数据显示,主要是两个事件和在 mounted 函数内获取页面数据。
<script> import mock from '@/mock/index.js'; export default { name: 'Home', data() { return { isCollapse: false, sysName: "kitty", username: "louis", userAvater: "", logo: "", activeIndex: '1' } }, methods: { handleopen() { console.log('handleopen'); }, handleclose() { console.log('handleclose'); }, handleselect(a, b) { console.log('handleselect'); }, handleSelectHearNavBar(key, keyPath) { console.log(key, keyPath) }, // 折叠导航栏 collapse: function() { this.isCollapse = !this.isCollapse; }, // 退出登入 logout: function() { // 绑定 this let _this = this; this.$confirm("确认退出吗?", "提示", { type: "warning" }) .then(() => { sessionStorage.removeItem("user"); this.$router.push("/login"); }) .catch(() => {}); } }, // 页面属性初始化 mounted() { this.sysName = "I like Kitty"; this.logo = require("@/assets/user/logo.png"); let user = sessionStorage.getItem("user"); if (user) { this.userName = user; this.userAvatar = require("@/assets/user/user.png") } } } </script>
5.3 修饰调整 css 样式 调整完效果如下图所示。
<style lang="scss" scoped> // 后续用 flex 布局重构下 .container { position: absolute; top: 0; bottom: 0; left: 0; right: 0; width: 100%; .header { height: 60px; line-height: 60px; background: #4b5f6e; color:#fff; .userinfo { text-align: right; padding-right: 30px; float: right; .userinfo-inner { font-size: 20px; cursor: pointer; color: #fff; img { width: 40px; height: 40px; border-radius: 10px; margin: 10px 0px 10px 10px; float: right; } } } .logo { height: 60px; font-size: 22px; border-right-width: 1px; border-right-style: solid; border-color: rgba(238, 241, 146, 0.5); text-align: left; img { width: 40px; height: 40px; margin: 10px; float: left; } .txt { color: #fff; } } // 切换左侧标签栏状态时变化 .logo-width { width: 230px; } .logo-collapse-width { width: 65px; } // 切换图标 .tools { width: 40px; padding: 0 10px; // 水平方向上居中 text-align: center; cursor: pointer; } .hearNavBar { width: 100%; height: 60px; background: #4b5f6e; line-height: 60px; cursor: pointer; } } .main { width: 100%; display: flex; position: absolute; top: 60px; bottom: 0px; overflow: hidden; aside{ flex: 0 0 230px; width: 230px; .el-menu { height: 100%; text-align: left; } } .content-container { // 占据剩下的空间 flex:1; padding: 0px; .breadcrumb-container { height: 28px; background: rgba(99, 138, 161, 0.2); border: 1px solid rgba(38, 80, 114, 0.2); .breadcrumb-inner { padding: 5px 0px 5px 5px ; text-align: left; font-size: 18px; width: 100%; height: 100%; float: left; } } .content-wrapper { background: #fff; box-sizing: border-box; } } } } </style>
6.嵌套路由(主界面的 main)
6.1 在 views 目录下新建 Main、User、Menu 页面,用于菜单路由,内容随便显示点什么就可以
6.2 在 router/index.js 文件中添加子路由,分别指向子页面
6.3 在 views/Home.vue 页面对应的导航菜单中添加点击事件,路由到对应的子页面
6.4 登录之后,点击用户管理,路由到用户管理界面