一,前言

上一篇,完善了Header组件,添加了用户信息的显示和退出功能
用户信息(昵称+头像)都是写死的,线上应用肯定是通过登录获取到用户信息这一篇,制作一个简单的登录页面,模拟接口异步调用(用户信息写死json对象)
将用户信息暂时保存到本地存储localStorge中,Header组件读取用户信息显示
登出时,清空localStorge本地存储,登出状态不能通过URL直接进入主界面
在路由守卫中,判断用户登录状态控制跳转,
退出登录后,改用刷新当前页面方式,由路由控制跳回登录页对于一个完善登录组件,除了最基本的用户名,密码输入框和登录按钮及各种表单验证
还需要具备很多其他功能,如:记住密码,自动登录,找回密码,各种第三方登录,
发送短信验证码,拼图验证,验证码验证等,目前先实现一个简单的登录页面

二,登录页面制作-外层背景布局UserLayout.vue

在之前的路由配置中,登录页面由两部分组成:外层背景布局+登录功能组件
即layouts/UserLayout.vue和views/User/Login.vue
首先,制作外层背景布局:
计划将登录功能组件(包含用户名,密码输入框+登录按钮)放置预页面中心位置
为外层背景布局设置背景铺满全屏,页面内容包含
标题:XX商城后台管理系统,较大字体加粗显示
描述:XX商城后台管理系统-V1.0.0,较小字体,偏灰色,采用黑色+透明
登录功能组件替换位置<router-view>(登录注册页面公用UserLayout.vue布局)
页脚footer:置于底部位置,一般做版权声明,较小字体,偏灰色,采用黑色+透明UserLayout.vue只做页面布局和背景设置,作为容器,无实际逻辑
<template><div id="user-layout-wapper"><!-- container-background --><div class="container"><!-- top --><div class="top"><div class="header"><span class="title">XX商城后台管理系统</span></div><div class="desc">XX商城后台管理系统-V1.0.0</div></div><!-- login --><router-view></router-view><!-- footer --><div class="footer"><div class="copyright">Copyright &copy;2019 XX商城后台管理系统 By Brave</div></div></div></div>
</template><script></script><style lang="less" scoped>
#user-layout-wapper {height: 100%;.container {width: 100%;height: 100%;background: #f0f2f5 url(~@/assets/background.svg) no-repeat 50%;background-size: 100%;padding: 220px 0;.top {text-align: center;.header {height: 44px;line-height: 44px;.title {font-size: 34px;color: #000000;font-family: Avenir, "Helvetica Neue", Arial, Helvetica, sans-serif;font-weight: 600;}}.desc {font-size: 14px;color: rgba(0, 0, 0, 0.5);margin-top: 14px;margin-bottom: 50px;}}.footer {width: 100%;text-align: center;position: absolute;bottom: 0;margin-bottom: 20px;.copyright {font-size: 14px;color: rgba(0, 0, 0, 0.5);}}}
}
</style>
为了占满全屏,需设置App.vue高度为100%
<template><div id="app"><router-view /></div>
</template><style lang="less">
#app {height: 100%;
}
</style>
为Login.vue设置一个高度和边框,测试效果:


备注:暂不支持响应式


三,登录页面制作-登录功能组件Login.vue

登录功能组件需要使用输入框和表单组件,修改main.js添加注册组件
修改main.js添加注册组件:
主要引入注册Input,  Form组件,
notification用于登录成功后,提示信息
(常用组件notification通知提醒框,绑定到Vue.prototype原型)
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import {Button,Layout,Icon,Breadcrumb,Menu,Dropdown,Avatar,Modal,message,Divider,Input,Form,notification
} from "ant-design-vue";Vue.config.productionTip = false;Vue.use(Button);
Vue.use(Layout);
Vue.use(Icon);
Vue.use(Breadcrumb);
Vue.use(Menu);
Vue.use(Dropdown);
Vue.use(Avatar);
Vue.use(Modal);
Vue.use(Divider);
Vue.use(Form);
Vue.use(Input);
Vue.use(notification);Vue.prototype.$confirm = Modal.confirm;
Vue.prototype.$info = Modal.info;
Vue.prototype.$success = Modal.success;
Vue.prototype.$error = Modal.error;
Vue.prototype.$warning = Modal.warning;
Vue.prototype.$message = message;
Vue.prototype.$notification = notification;new Vue({router,store,render: h => h(App)
}).$mount("#app");
登录功能组件表单的使用
Antd表单文档 : https://vue.ant.design/components/form-cn/
<template><div id="user-login-wrapper"><a-form class="user-layout-login" :form="form" @submit="handleSubmit"><a-form-item><a-inputsize="large"type="text"placeholder="请输入用户名"v-decorator="['username',{rules: [{ required: true, message: '请输入用户名' }],validateTrigger: 'change'}]"><a-iconslot="prefix"type="user":style="{ color: 'rgba(0,0,0,0.25)' }"/></a-input></a-form-item><a-form-item><a-inputsize="large"type="password"autocomplete="false"placeholder="请输入密码"v-decorator="['password',{rules: [{ required: true, message: '请输入密码' }],validateTrigger: 'blur'}]"><a-iconslot="prefix"type="lock":style="{ color: 'rgba(0,0,0,.25)' }"/></a-input></a-form-item><a-form-item style="margin-top:24px"><a-buttonsize="large"type="primary"htmlType="submit"class="login-button":loading="state.loginBtn":disabled="state.loginBtn">登录</a-button></a-form-item></a-form></div>
</template><script>
import { saveUserInfo } from "../../utils/localStorage";export default {data() {return {form: this.$form.createForm(this),state: {loginBtn: false // 登录Loading加载状态}};},methods: {handleSubmit(e) {e.preventDefault();this.state.loginBtn = true;const validateFieldsKey = ["username", "password"];this.form.validateFields(validateFieldsKey,{ force: true },async (err, values) => {if (!err) {return new Promise(resolve => {setTimeout(() => {resolve();}, 2000);}).then(() => {var userInfo = {username: "admin",nickname: "BraveWang"};this.state.loginBtn = false;this.loginSuccess(userInfo);}).catch(err => {this.state.loginBtn = false;this.$message.error({title: "错误",description: err.message});});} else {setTimeout(() => {this.state.loginBtn = false;}, 600);}});},loginSuccess(res) {this.$router.push({ name: "dashboard" });setTimeout(() => {this.$notification.success({message: "欢迎",description: res.nickname + " 欢迎回来"});}, 1000);}}
};
</script><style lang="less" scoped>
#user-login-wrapper {width: 400px;margin: 0 auto;// height: 250px;// border: 1px solid red;.user-layout-login {button.login-button {font-size: 18px;height: 45px;width: 100%;}}
}
</style>
布局比较简单,form中包含用户名,密码两个输入框和登录按钮
设置登录功能组件,整体宽度400px,添加输入框前置图标,登录按钮宽度100%
state.loginBtn控制按钮加载中转框显示和隐藏,加载中按钮不可用

Submit表单提交逻辑:

e.preventDefault();阻止默认行为
this.state.loginBtn = true;设置加载中状态
读取表单中用户名,密码字段值,并进行表单校验,
校验通过后,Promise模拟接口异步调用,获得userInfo用户信息
this.state.loginBtn = false;设置加载完成状态
路由跳转至dashboard,并使用notification提醒信息
功能测试:

登录页面及表单验证:

输入用户名密码,点击登录:

登录成功跳转至后台管理仪表盘页面,显示登录通知:


四,用户信息的保存和使用

目前,登录页面和登录成功(使用Promise模拟接口异步调用)后跳转首页显示通知的功能已经完成
下一步,将获取到的用户信息保存至本地存储localStorge中,登录成功进入首页时,使用用户信息对Header赋值
安装vue-ls:
npm install --save vue-ls
main.js中注册和配置vue-ls:
import VueStorage from "vue-ls";Vue.use(VueStorage, {namespace: "pro__", // key prefixname: "ls", // name variable Vue.[ls] or this.[$ls],storage: "local" // storage name session, local, memory
});
创建utils/localStorage.js工具文件:
localStorage.js提供了用户信息读写功能,
目前暂时只保存username和nickname字段,
Header部分使用nickname值显示,
取username是否存在来判断登录状态
用户退出登录时,将清空用户信息所以,当前的设计不支持登录状态为false时自动保存用户名密码功能
import Vue from "vue";const USER_NAME = "username";
const NICK_NAME = "nickname";export function saveUserInfo(userInfo) {Vue.ls.set(USER_NAME, userInfo.username);Vue.ls.set(NICK_NAME, userInfo.nickname);
}export function getValueByKey(key) {return Vue.ls.get(key);
}export function getUserName() {return Vue.ls.get(USER_NAME);
}export function getNickName() {return Vue.ls.get(NICK_NAME);
}export function removeUserInfo() {Vue.ls.remove("username");Vue.ls.remove("realName");
}
修改Login.vue, 登录成功时,将用户信息保存至localStorge:
<script>
import { saveUserInfo } from "../../utils/localStorage";export default {data() {return {form: this.$form.createForm(this),state: {loginBtn: false // 登录Loading加载状态}};},methods: {handleSubmit(e) {e.preventDefault();this.state.loginBtn = true;const validateFieldsKey = ["username", "password"];this.form.validateFields(validateFieldsKey,{ force: true },async (err, values) => {if (!err) {return new Promise(resolve => {setTimeout(() => {resolve();}, 2000);}).then(() => {var userInfo = {username: "admin",nickname: "BraveWang"};saveUserInfo(userInfo);this.state.loginBtn = false;this.loginSuccess(userInfo);}).catch(err => {this.state.loginBtn = false;this.$message.error({title: "错误",description: err.message});});} else {setTimeout(() => {this.state.loginBtn = false;}, 600);}});},loginSuccess(res) {this.$router.push({ name: "dashboard" });setTimeout(() => {this.$notification.success({message: "欢迎",description: res.nickname + " 欢迎回来"});}, 1000);}}
};
</script>
修改Header.vue, 读取用户昵称显示,退出时,清除用户信息:
<template><div class="header"><div class="left"></div><div class="right"><a-dropdown><span class="user-dropdown-menu"><a-avatarclass="avatar"size="small"alt="avatar"style="backgroundColor:#87d068"icon="user"/><span>{{ nickName }}</span></span><a-menu slot="overlay" class="user-dropdown-menu-wrapper"><a-menu-item key="0"><a-icon type="user" /><span>item1</span></a-menu-item><a-menu-item key="1" disabled><a-icon type="info-circle" /><span>item2</span></a-menu-item><a-menu-item key="2"><a-icon type="setting" /><span>item3</span></a-menu-item><a-menu-divider /><a-menu-item key="3"><a href="javascript:;" @click="handleLogout"><a-icon type="logout" /><span>退出登录</span></a></a-menu-item></a-menu></a-dropdown></div></div>
</template><script>
import { getNickName, removeUserInfo } from "../utils/localStorage";export default {name: "Header",data() {return {nickName: ""};},mounted() {this.nickName = getNickName();},methods: {handleLogout() {const that = this;this.$confirm({title: "提示",content: "确认要注销登录吗 ?",onOk() {return new Promise(resolve => {setTimeout(() => {removeUserInfo()resolve();}, 2000);}).then(() => {window.location.reload()that.$notification.open({message: "登出提醒",description: "您已成功退出登录",onClick: () => {console.log("退出登录!");}});}).catch(err => {that.$message.error({title: "错误",description: err.message});});},onCancel() {that.$message.info("取消登出操作");}});}}
};
</script>

功能测试:

登录完成后,用户信息被保存至localStorge

五,登录状态控制

一般的.当用户未登录时,应无法通过URL直接进入到需登录后才能查看的页面
当用户退出登录后,也不能再通过浏览器返回按钮再次返回到需登录后才能查看的页面这就需要在路由跳转时,根据用户登录状态(或权限)来控制路由跳转,修改如下,
当用户退出登录时,清除用户信息,刷新当前页面,出发路由守卫
在路由前置首位中,判断用户是否登录,如未登录且目标url是需登录后才能查看的页面,重定向至登录页面
修改路由配置router.js,添加登录状态控制:
import { getUserName } from "./utils/localStorage";router.beforeEach((to, from, next) => {if (to.path !== from.path) {NProgress.start();}if (getUserName() !== null && getUserName() !== "") {next();} else {if (to.path === "/user/login") {next();} else {next({ path: "/user/login" });NProgress.done();}}
});router.afterEach(() => {NProgress.done();
});

注意:

当next({ path: "/user/login" });重定向时,不会进入afterEach,需要立即NProgress.done();
修改Header.vue,退出登录时,改为重载当前页面
 使用 window.location.reload()重载当前页面
handleLogout() {const that = this;this.$confirm({title: "提示",content: "确认要注销登录吗 ?",onOk() {return new Promise(resolve => {setTimeout(() => {removeUserInfo()resolve();}, 2000);}).then(() => {window.location.reload()that.$notification.open({message: "登出提醒",description: "您已成功退出登录",onClick: () => {console.log("退出登录!");}});}).catch(err => {that.$message.error({title: "错误",description: err.message});});},onCancel() {that.$message.info("取消登出操作");}});
}

测试功能:

退出登录后,刷新当前页面触发路由守卫,重定向至登录页,点击浏览器返回不能回到首页
未登录状态下直接访问dashboard,触发路由守卫,重定向至登录页


六,会话结束后session失效问题

注:使用后续带token版本,不会出现此问题

在一下小项目的实际使用中,遇到一个问题,记录一下:

用户登录后,与服务器端建立会话,由于此项目未使用token,关闭浏览器后会话消失
重新开启浏览器输入地址会直接进入后台界面(不会登录),而此时和服务端的session已失效在正常情况下,前端会保存cookies,再次进入时,通过cookies服务端建立会话
但在当前项目后端未实现根据cookies创建session的相关接口,
所以此时登录进来是session丢失状态,即无法访问接口API
(之所以这个问题要在前端解决也是由于API没有返回授权失败状态)

解决方法:

1,登录成功时,记录登录状态isLogin到sessionStroge

    loginSuccess(res) {sessionStorage.setItem("isLogin", true);this.$router.push({ name: "sparepartslist" });setTimeout(() => {this.$notification.success({message: "欢迎",description: res.realName + " 欢迎回来"});}, 1000);},

2,退出登录时,删除isLogin

      this.$confirm({title: "提示",content: "确认要注销登录吗 ?",onOk() {return new Promise(resolve => {setTimeout(() => {removeUserInfo();sessionStorage.removeItem("isLogin");resolve();}, 2000);}).then(() => {...}).catch(err => {...});},onCancel() {...}});

3,浏览器关闭时,会话结束sessionStroge中的存储记录会自动清空
4,路由跳转(路由守卫)

router.beforeEach((to, from, next) => {if (to.path !== from.path) {NProgress.start();}if (getUserName() !== null &&getUserName() !== "" &&sessionStorage.key("isLogin") !== null) {next();} else {if (to.path === "/user/login") {next();} else {next({ path: "/user/login" });NProgress.done();}}
});

七,结尾

晚上再写

八,代码下载

传送门:CSDN下载
传送门:GitHub下载-vue-framework-admin-v0.0.8


维护记录:

20190810:
编写文章,上传代码资源
20190812:
更新CSDN资源下载链接
20191011:
当前版本下,会话结束后session失效问题

一步一步实现中后台管理平台模板-08-登录页和用户信息保存相关推荐

  1. 一步一步实现中后台管理平台模板-13-解决IE浏览器兼容性问题

    一,前言 上一篇,介绍了ECharts封装,响应式,刷新问题及vue-echarts的使用,以及IE兼容性问题这一篇,就来着重解决目前项目存在的IE兼容性问题 二,IE兼容性问题 Vue官方文档描述支 ...

  2. 中后台管理信息系统通用原型方案、业务中台管理系统、业务中台架构、管理信息系统、订单管理、客户管理、货源管理、财务管理、客服管理、营销管理、办公申请、协作管理、CMS、OA、CRM、ERP、Axure

    本作品是一套通用型的中后台信息系统原型方案,可以快速扩展并输出标准美观的中后台产品原型,极大的提升输出效率和节省协作成本.方案中提供了几十套不同风格和结构的系统框架,并涵盖了大量的常用组件和通用页面模 ...

  3. vue中后台管理登录后的token管理

    在做后台管理系统的时候,登录后token管理很重要.上代码,有瑕疵,有待改善,见谅. import Vue from 'vue' import Router from 'vue-router' imp ...

  4. 前端开箱即用的中后台管理模版,建议收藏

    开箱即用的中后台管理模版,建议收藏! 今天来推荐几款开箱即用的中后台管理模版! Vue Element Admin vue-element-admin 是一个后台前端解决方案,它基于 vue2 和 e ...

  5. vite打包快几款基于vue3和vite的开箱即用的中后台管理模版

    vite打包快的原因: 冷启动 1.esbuild构建依赖,go语言编写多线程打包. 2.原生的esm方式提供源码,浏览器分担了一部分工作. HMR热更新 1.缓存机制,利用浏览器http头部,源码模 ...

  6. 基于Vite + Vue3 + NaiveUI + TypeScript的中后台管理模版 — Soybean Admin开源啦

    ???基于Vite + Vue3 + NaiveUI + TypeScript的中后台管理模版 - Soybean Admin开源啦??? 简介 Soybean Admin 是一个基于 Vue3.Vi ...

  7. 基于vue2.0 + elementUI 后台管理平台

    Vue-Admin-Demo 这是一个基于vue2.0 + elementUI 后台管理平台 Github: https://github.com/xiahuahua/vue-vux-demo(欢迎S ...

  8. php ci框架后台管理,ci: 羽翼后台管理平台是一个简单的博客系统,后端基于CodeIgniter,前端基于Semantic UI 2.2.10...

    羽翼后台管理平台(博客系统) 羽翼的由来 羽翼的故事是9月20日的晚上,我突然就想到--造翼者小说.我通过自己构思不少的东西.羽毛代表着轻松.纯洁和神圣的:翼代表着升天和梦想,两者在一起的含义:带着一 ...

  9. 1.电子商城后台管理平台项目概况

    一.项目介绍 随着信息化的发展,电商也随着互联网的发展日益状态.为了整理之前所学,这里将做一个电子商城后台管理系统. 二.项目需求 电子商城后台管理平台包含账号管理(保存用户信息).用户登录.退出登录 ...

最新文章

  1. 联想服务器升级微码文件,ThinkPad如何升级硬盘微码程序(适用于SL系列机器)
  2. 项目小白斩获蘑菇街暑期实习offer的面试过程
  3. matlab怎么算2乘2矩阵,【Matlab】2.矩阵的运算
  4. 回溯法(深度优先)剪枝和分支限界法(宽度优先)剪枝对比:01背包问题
  5. SCNCHECKPOINT
  6. 细聊分布式ID生成方法
  7. P4016 负载平衡问题(最小费用最大流)
  8. Underlay网络:如何立住可靠又支持大规模无收敛的“人设”
  9. Linux下Kafka单机安装配置
  10. [转]CG编程概念 ,及CG编译器与VC6.0集成方法
  11. 专科学历 工作机会少的可怜?这个简单方法帮几千人提升3倍工作机会
  12. python装饰器函数执行顺序_python 函数后装饰器怎么加
  13. php 字符编码转换
  14. 20190801H3C无线控制器3510H 版本升级总结
  15. IIR滤波器的FPGA实现
  16. PO BO VO DTO POJO DAO概念及其作用
  17. matplotlib 绘图可视化知识点整理
  18. 贴片电阻各种封装规格及阻值标注方法
  19. 转自【MDCC技术大咖秀】Android内存优化之OOM
  20. Pathon的安装过程

热门文章

  1. 利用python实现软考成绩实时监控+查询提醒
  2. C语言struct用法详解
  3. 【SSLGZ 2811】2017年10月30日提高组T2 摘Galo
  4. 安徽省软考报名时间成绩查询安徽省教育考试院安徽省人事考试网报名入口
  5. MySQL 8——学习笔记03(插入、更新、删除 数据 [DML语句]、查询数据 [DQL语句])
  6. 通过指令获取tsc标签打印机的状态
  7. 重大调整!高速限速标准将不再是120km/h
  8. SAP CRM BOL编程基础,代码+详细注释
  9. 『Batch Normalization』Batch Normalization一文吃透
  10. asp.net 微信jsapi支付