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 登录之后,点击用户管理,路由到用户管理界面


浙公网安备 33010602011771号