前端开发Vue项目实战:电商后台管理系统(二)-- 登录退出功能 --主页界面

项目地址 https://gitee.com/zy4018/vue_zy

1. 登录/退出功能

1.1 登录概述

登录业务流程

  • 在登录页面输入用户名和密码
  • 调用后台接口进行验证
  • 通过验证之后,根据后台的响应状态跳转到项目主页

登录业务的相关技术点

  • http是无状态的
  • 通过 cookie 在客户端记录状态(无跨域)
  • 通过 session 在服务器端记录状态(无跨域)
  • 通过 token 方式维持状态(跨域)

1.2 token 原理分析

在这里插入图片描述

1.3 登录功能实现

登录页面的布局
通过 Element-UI 组件实现布局

  • el-form
  • el-form-item
  • el-input
  • el-button
  • 字体图标
1.3.1 Git 创建分支

这里是打开project里面的vue_shop,在终端中运行以下命令

// 创建分支
git checkout -b login
// 查看分支,带星号表示当前分支
git branch

在这里插入图片描述

1.3.2 渲染Login组件并实现路由重定向
  • 清空 App.vue模板 和 router.js 里面不必要的内容
  • 删除 ./views 和 ./components/HelloWorld.vue
  • 创建组件 ./components/login.vue
  • 添加路由规则 router.js 设置默认页面重定向到login
import Login from './components/Login.vue'
const routes = [
  { path: '/', redirect: '/login' },
  { path: '/login', component: Login },
]
  • App.vue 添加路由占位符
<template>
  <div id="app">
    App根组件
    <!-- 路由占位符 -->
    <router-view></router-view>
  </div>
</template>
1.3.3 设置背景颜色并在屏幕中央绘制登录盒子
  1. 在可视化工具中安装 开发依赖 less 和 less-loader
  2. 安装过依赖后要重新编译
    在这里插入图片描述

在这里插入图片描述

  1. 设置全局样式 ./assets/css/global.css
  2. 入口文件导入全局css文件 ./main.js
// global.css文件
/* 全局样式表 */
html,
body,
#app {
    height: 100%;
    margin: 0;
    padding: 0;
}

// main.js
//导入全局样式表
import './assets/css/global.css'
  1. 设置背景颜色并在屏幕中央绘制登录盒子 Login.vue
// login.vue文件内容
  <div class="login_container">
      <div class="login_box">
      </div>
  </div>


<style lang="less" scoped>
.login_container {
    background-color: #2b4b6b;
    height: 100%;
}
.login_box {
    width: 450px;
    height: 300px;
    background-color: #fff;
    border-radius:5px ;
    position:absolute;
    left:50%;
    top: 50%;
    transform: translate(-50%,-50%);
}
</style>
1.3.4 绘制默认头像
// login.vue文件内容
<!--  头像区域 -->
<div class="avatar_box">
    <img src="../assets/logo.png" alt="">
</div>

.avatar_box {
        height: 130px;
        width: 130px;
        border: 1px solid #eee;
        border-radius: 50%;
        padding: 10px;
        box-shadow: 0 0 10px #ddd;
        position: absolute;
        left: 50%;
        transform: translate(-50%, -50%);
        background-color: #fff;
        //less的嵌套语法
        img {
            width: 100%;
            height: 100%;
            border-radius: 50%;
            background-color: #eee;
        }
    }
1.3.5 绘制登录表单区域

Element-UI官网
https://element.eleme.cn/#/zh-CN

  1. 按需导入用到的element组件 ./plugins/element.js
import Vue from 'vue'
import { Button,Form, FormItem,Input } from 'element-ui'

Vue.use(Button)
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Input)
  1. 页面使用引入的组件标签
// Login.vue文件内容
   <!-- 登录表单区域 -->
   <el-form label-width="0px" class="login_form">
       <!--  用户名 -->
       <el-form-item >
           <el-input></el-input>
       </el-form-item>
       <!--  密码 -->
       <el-form-item >
           <el-input></el-input>
       </el-form-item>
      <!--  按钮区域 -->
      <el-form-item class="btns">
            <el-button type="primary">登录</el-button>
            <el-button type="info">重置</el-button>
       </el-form-item>
   </el-form>


.login_form {
    width: 100%;
    position: absolute;
    bottom: 0;
    padding: 0 20px;
    //默认form的box-sizing:content-box
    box-sizing:border-box ;
}
.btns {
    display: flex;
    justify-content: flex-end;
}
1.3.6 绘制带icon的input输入框

https://element.eleme.cn/#/zh-CN/component/input
https://element.eleme.cn/#/zh-CN/component/icon

    <!--  用户名 -->
    <el-form-item >
         <el-input prefix-icon="el-icon-s-custom"></el-input>
     </el-form-item>
     <!--  密码 -->
     <el-form-item >
         <el-input prefix-icon="el-icon-lock"></el-input>
     </el-form-item>

阿里字体图标库的使用

  • 下载选好的阿里字体图标
  • demo_fontclass.html查看图标名称
  • fonts文件放入assets文件夹中
  • main.js 中引入iconfont.css样式表 import ‘./assets/fonts/iconfont.css’
  • 标签添加类名 class=“iconfont icon-xxx”
1.3.7 实现表单的数据绑定

希望把用户名和密码对应的值可以自动绑定到数据源上

// 1. 为el-form添加:model属性绑定,指向一个数据对象
// 2. 为每一个表单项通过v-model绑定到数据对象中对应的属性
<el-form :model="loginForm">
	<el-input v-model="loginForm.username"></el-input>
	<el-input v-model="loginForm.password" type="password"></el-input>
</el-form>


export default {
  data () {
    return {
      // 登录表单的数据绑定对象
      loginForm: {
        username: 'admin',
        password: '123456'
      }
    }
  }
}
1.3.8 实现表单的数据验证

https://element.eleme.cn/#/zh-CN/component/form

在这里插入图片描述

//1.为el-form属性绑定一个校验规则对象loginFormRules
//2.在校验规则对象之中定义属性username 和 password
//3.在表单item项中通过prop指定校验规则属性
export default {
    data() {
        return {
            // 表单验证规则对象
            loginFormRules: {
                //验证用户名是否合法
                username: [
                    { required: true, message: '请输入登录名称', trigger: 'blur' },
                    { min: 3, max: 8, message: '长度在 3 到 8 个字符', trigger: 'blur' },
                ],
                // 验证密码是否合法
                password: [
                    { required: true, message: '请输入登录密码', trigger: 'blur' },
                    { min: 6, max: 15, message: '长度在 6 到 15 个字符', trigger: 'blur' },
                ],
            },
        }
    },
}
1.3.9 实现表单的重置功能
  1. 添加引用ref获取表单实例对象(通过 ref 标注 DOM 元素)
  2. 重置按钮绑定单击事件
  3. 定义重置方法(通过 $refs 获取 DOM 元素)

在这里插入图片描述

methods: {
  // 点击重置按钮,重置登录表单
  resetLoginForm () {
    // console.log(this) // VueComponent
    this.$refs.loginFormRef.resetFields()
  }
}
1.3.10 实现登录前表单数据的预验证,配置axios发起登录请求
  • 在发起请求之前运行mySql数据库
  • 运行 app.js
  • 导入axios包main.js,配置axios
import axios from 'axios'
// 配置请求的根路径
axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/'
Vue.prototype.$http = axios
  1. 登录按钮添加点击事件
<el-button type="primary" @click="login">登录</el-button>
  1. 导入弹框提示组件element.js

https://element.eleme.cn/#/zh-CN/component/message

import { Message } from 'element-ui'
Vue.prototype.$message = Message // 弹框提示组件挂在在Vue原型上
  1. 编写login方法
login() {
            // valid是一个布尔值,如果表单格式验证合法就为true
            //先判断valid是否为true,如果不等于true就直接返回,等于true发起请求
            this.$refs.loginFormRef.validate(async (valid) => {
                if (!valid) return
                // 发起请求,方式是post,并且携带参数
                // data属性结构赋值,重命名为res
                const { data: res } = await this.$http.post('login', this.loginForm)
                if (res.meta.status != 200) return this.$message.error('登陆失败')
                this.$message.success('登陆成功')
                // 将登录成功之后的token保存到客户端的sessionStorage中(会话期间的存储机制)(所以不放在localStorage)
                // 项目中除了登录之外的其他API接口,必须在登录之后才能访问
                window.sessionStorage.setItem('token',res.data.token)
                // 通过编程式导航跳转到后台主页,路由地址是/home
                this.$router.push('/home')
            })
        },
  1. router.js 并导入路由规则
import Home from './components/Home.vue'
const routes = [  
  { path: '/home', component: Home, }
]
1.3.11 路由导航守卫控制页面访问权限

如果用户没有登录,但是直接通过URL访问有特定权限的页面,需要重新导航到登录页面

// 挂载路由导航守卫
router.beforeEach((to, from, next) => {
  // to 将要访问的路径
  // from 代表从哪个路径跳转而来
  // next 是一个函数, 表示放行   next()放行   next('/login')强制跳转
  if (to.path === '/login') return next() // 用户访问登录页,直接放行
  // 获取token
  const tokenStr = window.sessionStorage.getItem('token')
  if (!tokenStr) return next('/login') // 没有token, 强制跳转到登录页
  next() // 否则(有token)直接放行
})

登录效果展示
在这里插入图片描述

1.4 退出功能实现

退出功能实现原理
基于token的方式实现退出比较简单,只需要销毁本地的token即可。
这样,后续的请求就不会携带token,必须重新登录生成一个新的token之后才可以访问页面

//清空token
window.sessionStorage.clear()
//跳转到登录页
this.$router.push('/login')

具体操作 Home.vue

<template>
  <div>
      <el-button type="info" @click="goout">退出</el-button>
  </div>
</template>


export default {
  methods:{
    goout(){
      window.sessionStorage.clear()
      this.$router.push('/login')
    }
  }
}

1.5 将本地代码提交到码云Gitee中


git status //查看文件状态

git add .   //代码添加到缓存区

git commit -m "完成了登录功能"   //提交到本地库

git branch   //查看分支

git checkout master  //切换分支到主分支上

git merge login  //在主分支上合并login分支的代码

git push  //推送到码云中

// 将login分支也推送到码云中
git checkout login //先切换到login分支
git push -u origin login //推送

如果推送失败,没反应,可以先把仓库和项目再关联一下

git remote add origin https://gitee.com/****/vue_zy.git   

2. 主页布局

2.1 整体布局

https://element.eleme.cn/#/zh-CN/component/container

要先在 .plugins/element.js中引入样式

import { Container,Header,Aside,Main } from 'element-ui'
Vue.use(Container)
Vue.use(Header)
Vue.use(Aside)
Vue.use(Main)

Home.vue内容

<template>
    <el-container class="home-container">
        //头部区域 
        //每一个element-UI中提供的组件名称就是类名,即el-header就是类名 
        <el-header>Header <el-button type="info" @click="goout">退出</el-button>
        </el-header>
        //页面主体区域
        <el-container>
           // 侧边栏 
            <el-aside width="200px">Aside</el-aside>
            // 右侧内容主体 
            <el-main>Main</el-main>
        </el-container>
    </el-container>
</template>


<style lang="less" scoped>
.home-container {
  height: 100%;
}
.el-header {
  background-color: #373D41;
}
.el-aside {
  background-color: #333744;
}
.el-main {
  background-color: #eaedf1;
}
</style>

2.2 主页Header区域


```html

```cpp

```css
<!-- 头部区域 -->
        <el-header>
          <div>
            <img src="../assets/heima.png" alt="">
            <span>电商后台管理系统</span>
          </div>
          <el-button type="info" @click="goout">退出</el-button>
        </el-header>



.el-header {
  background-color: #373D41;
  display: flex;
  //左右贴边对齐
  justify-content: space-between;
  //上下居中
  align-items: center;
  
  //字体
  color:#fff;
  font-size: 20px;
  //嵌套子选择器
  > div {
    display: flex;
    align-items: center;
    span {
      margin-left: 15px;
    }
  }

2.2 主页Header区域

 //头部区域 
 <el-header>
   <div>
     <img src="../assets/heima.png" alt="">
     <span>电商后台管理系统</span>
   </div>
   <el-button type="info" @click="goout">退出</el-button>
 </el-header>


.el-header {
  background-color: #373D41;
  display: flex;
  //flex布局左右贴边对齐
  justify-content: space-between;
  //flex布局上下居中
  align-items: center;
  //字体
  color:#fff;
  font-size: 20px;
  //嵌套子选择器
  > div {
    display: flex;
    align-items: center;
    span {
      margin-left: 15px;
    }
  }
}

2.3 左侧菜单布局

https://element.eleme.cn/#/zh-CN/component/menu

// 侧边栏菜单区域 
<el-menu background-color="#333744" text-color="#fff" active-text-color="#ffd04b">
    //一级菜单
    <el-submenu index="1">
        //一级菜单的模板区域
        <template slot="title">
            //图标
            <i class="el-icon-location"></i>
            //文本
            <span>一级菜单</span>
        </template>
        //二级菜单
        <el-menu-item index="1-4-1">
          <template slot="title">                          
            <i class="el-icon-location"></i>                          
            <span>二级菜单</span>
        </template>
        </el-menu-item>
    </el-submenu>
</el-menu>

2.4 通过接口获取菜单数据

通过axios请求拦截器添加token验证,保证拥有获取数据的权限;
需要授权的 API ,必须在请求头中使用 Authorization字段提供 token 令牌;
后台除了登录接口之外,都需要token权限验证,我们可以通过添加axios请求拦截器来添加token,以保证拥有获取数据的权限;
在main.js中添加代码,在将axios挂载到vue原型之前添加下面的代码

在这里插入图片描述

2.5 获取左侧菜单数据

在这里插入图片描述
上面是console.log(result)的左侧菜单的数据,可以看到打印出来是一个对象;
meta显示获取菜单列表成功;data是所有数据,总共5个一级菜单,第一个一级菜单通过children属性嵌套了自己的二级菜单

 data() {
     return {
         //拿到数据之后,为了在页面中渲染出来,应该把获取到的数据立即挂载到data里面
         //左侧菜单数据
         menulist: [],
        }
    },
//页面加载之前就要获取数据,定义生命周期函数
created() {
    this.getMenuList()
},
methods: {
        //获取所有菜单
        async getMenuList() {
            const { data: result } = await this.$http.get('menus')
            //如果获取数据失败,就弹出错误消息,并且错误消息是保存在result.meta.msg里面的
            if (result.meta.status !== 200) return this.$message.error(result.meta.msg)
            //如果成功就赋值
            this.menulist = result.data
            console.log(result)
        },
    },

2.6 通过双层for循环渲染左侧菜单

//一级菜单 
<el-submenu :index="item.id + ''" v-for="item in menulist" :key="item.id">
    //一级菜单的模板区域
    <template slot="title">
        <i class="el-icon-location"></i>
        <span>{{item.authName}}</span>
    </template> 
    <!-- 二级菜单 -->
    <el-menu-item :index="subItem.id + '' "  v-for="subItem in item.children" :key="subItem.id">
        <template slot="title">
            <i class="el-icon-location"></i>
            <span>{{subItem.authName}}</span>
        </template>
    </el-menu-item>
</el-submenu>

2.7 为选中项设置字体颜色并添加分类图标

通过更改el-menu的active-text-color属性可以设置侧边栏菜单中点击的激活项的文字颜色

<el-menu background-color="#333744" text-color="#fff" active-text-color="#409EFF">

在data中添加一个iconsObj,然后将图标类名进行双向数据绑定

iconsObj: {
       125: 'el-icon-s-custom',
       103: 'el-icon-s-check',
       101: 'el-icon-s-goods',
       102: 'el-icon-s-order',
       145: 'el-icon-s-marketing',
            },

一级菜单图标绑定iconsObj中的数据:

<i :class="iconsObj[item.id]"></i>

2.8 每次只能打开一个菜单项并解决边框问题

在这里插入图片描述
为了保持左侧菜单每次只能打开一个,显示其中的子菜单,我们可以在el-menu中添加一个属性unique-opened

<el-menu unique-opened>

或者也可以数据绑定进行设置(此时true认为是一个bool值,而不是字符串)
:unique-opened=“true”

解决边框不对齐问题

.el-menu {
  border-right: none;
}

2.9 实现侧边栏的折叠与展开效果

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

//三元运算,当isCollapse 变成true,折叠起来的时候,侧边栏宽度变成60px
<el-aside :width="isCollapse ? '60px':'200px'">
    <div class="toggle-button" @click ="toggleCollapse"> ||| </div>
    // 侧边栏菜单区域
    // collapse	是否水平折叠收起菜单,默认是false,不折叠
    // collapse-transition 是否开启折叠动画,默认是true开启,要关闭
    <el-menu  :collapse="isCollapse" :collapse-transition='false'>


//点击按钮,切换菜单的折叠与展开
data() {
    return {
       isCollapse:false //不折叠
       }
}methods: {
  toggleCollapse(){
     this.isCollapse=!this.isCollapse //取反
   }
   }


.toggle-button {
  background-color: #4A5064;
  font-size: 10px;
  line-height: 24px;
  color: #fff;
  text-align: center;
  letter-spacing: 0.2em;
  cursor: pointer;
}

2.10 首页路由重定向

  1. 新增子级路由组件 ./components/Welcome.vue
  2. 在router.js中导入子级路由组件,并设置路由规则以及子级路由的默认重定向
import Welcome from '../components/Welcome.vue'
const routes = [
  {
    path: '/home',
    component: Home,
    redirect: '/welcome',
    children: [{ path: '/welcome', component: Welcome }]
  }
]
  1. 打开Home.vue,在main的主体结构中添加一个路由占位符
<!--  右侧内容主体 -->
      <el-main>
         <router-view></router-view>
       </el-main>

2.11 左侧菜单改造为路由链接

在这里插入图片描述

需要将所有的侧边栏二级菜单都改造成子级路由链接,我们只需要将el-menu的router属性设置为true就可以了,此时当我们点击二级菜单的时候,就会根据菜单的index属性进行路由跳转,如: /110

 <!-- 侧边栏菜单区域 -->
 <el-menu background-color="#333744" text-color="#fff" active-text-color="#409EFF"  unique-opened   
         :collapse="isCollapse" :collapse-transition='false'  :router="true">

使用index id来作为跳转路径不合适,我们可以重新绑定index的值为 :index=“’/’+subItem.path”

<!-- 二级菜单 -->
 <el-menu-item :index=" '/'+ subItem.path " v-for="subItem in item.children" :key="subItem.id">
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值