vue电商项目(2)登录模块与主页模块具体实施
黑马vue2后台电商管理系统,登录模块与主页布局实现与讲解
·
黑马vue电商项目
项目实施
1. login登录模块
views文件夹存储路由控制的组件,components文件夹存储非路由控制的组件
1.1 前期准备
- 通过在终端中输入
git checkout -b login
语句,创建一个新的分支,用于记录对login模块的操作,我们在login分支中进行开发 - 在views文件夹下创建login登录组件,并在路由文件router>index.js中声明路由规则
// 路由规则
const routes = [
{ path: '/', redirect: '/login' }, //路由重定向,将主页重定向到login页面中
{ path: '/login', component: Login }, //路由规则
]
- 并在App跟组件中的template便签合适位置添加路由占位符
<router-view></router-view>
- 创建完毕后,我们在谷歌浏览器调试工具中,会看到页面存在边距,这是组件和页面默认给定的边距,因此我们需要去掉这些内外边距,使得组件可以占满整个屏幕。在assests文件夹中创建css>global.css文件,并在main.js文件中添加
import '@/assets/css/global.css'
引入该css文件
/* 全局css样式 */
html,
body,
#app {
height: 100%;
margin: 0;
padding: 0;
}
1.2 正式开始
1. 对页面样式进行编写,话不多说上代码
<template>
<!-- 背景大盒子 -->
<div class="login-container">
<!-- 登录内容盒子 -->
<div class="login-box">
<!-- 头像区域 -->
<div class="avatar-box">
<img src="@/assets/head.jpg" alt="头像" />
</div>
<!-- 文本框内容区域 -->
<el-form class="login-form" :model="loginForm" :rules="loginFormRules" ref="loginFormRef">
<el-form-item prop="username">
<el-input prefix-icon="el-icon-user" v-model="loginForm.username"></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input prefix-icon="el-icon-lock" v-model="loginForm.password"></el-input>
</el-form-item>
<el-form-item class="btns">
<el-button type="primary" @click="login">登录</el-button>
<el-button type="info" @click="resetLoginForm">重置</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
// 行为区域内容在后续讲解 ...
</script>
<style lang="less" scoped>
// 为大盒子设置背景颜色,占满全屏`在这里插入代码片`
.login-container {
background-color: #2b5b6b;
height: 100%;
}
// 为登录内容盒子设置样式
.login-box {
width: 450px;
height: 300px;
background-color: #fff;
border-radius: 10px;
// 盒子居中显示
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
// 头像框区域样式
.avatar-box {
width: 130px;
height: 130px;
border-radius: 50%; //圆形盒子
border: 1px solid #eee;
box-shadow: 0 0 10px #ddd;
padding: 10px;
background-color: #fff;
// 设置头像的位置,与父盒子居中偏上位置
position: absolute;
left: 50%;
transform: translate(-50%, -50%);
img {
width: 100%;
height: 100%;
background-color: #eee;
border-radius: 50%;
}
}
.login-form {
position: absolute;
bottom: 0;
width: 100%;
padding: 0 20px;
box-sizing: border-box; //设置盒子属性为内减模式,防止设置内边距后右侧突出现象
.btns {
display: flex;
justify-content: flex-end;
}
}
</style>
样式中form表单添加内边距产生问题:
- 当给form表单定位到父元素底部时,发现表单宽度变小,不是占满整个父元素,需要给form表单添加
width : 100%;
占满整个父元素; - 当添加内边距padding时,左侧没有问题,右侧会超出父元素,这是因为form的box-sizing是默认content,因此需要添加
box-sizing : border-box;
2. 登录操作逻辑实现
<script>
export default {
name: 'Login',
data() {
return {
//* 登录表单数据对象
loginForm: {
username: 'admin',
password: '123456',
},
//* 登录表单的验证规则对象
loginFormRules: {
// 用户名验证规则数组
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 10, message: '长度在 3 到 10 个字符', trigger: 'blur' },
],
// 密码验证规则数组
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, max: 15, message: '长度在 6 到 15 个字符', trigger: 'blur' },
],
},
}
},
methods: {
//* 点击重置按钮重置表单内容与表单验证
resetLoginForm() {
this.$refs.loginFormRef.resetFields()
},
//* 点击登录按钮先判断表单验证结果,再向服务器发起登录请求
login() {
this.$refs.loginFormRef.validate(async valid => {
if (!valid) return
// todo 通过axios发送请求
const { data: res } = await this.$http.post('login', this.loginForm)
if (res.meta.status !== 200) return this.$message.error('登录失败')
this.$message.success('登录成功')
//todo 将token保存到本地sessionStorage中
window.sessionStorage.setItem('token', res.data.token)
this.$router.push('/home')
})
},
},
}
</script>
- data函数返回的对象是页面上用到的数据,loginForm是登录表单所绑定的数据源对象,loginFormRules是表单验证对象所绑定的数据源对象。
- method对象定义了方法,重置按钮绑定resetLoginForm()方法,登录按钮绑定了login()方法。
- 在
<el-form>
对象上定义了属性ref=loginFormRef
,可以通过this.$refs
点出所绑定的表单dom元素,根据element-ui中表单元素的方法.resetFields()重置表单内容,.validate((valid)=>{ })对表单验证的结果进行判断。 - 当表单验证通过后,通过axios向服务器发送post请求,返回的结果是一个Promise对象,可利用await/async简化异步操作拿到返回的数据data并重新命名为res。
- 判断res的meta.status的值,与接口文档对比,若不等于200则说明请求失败,返回提示消息,否则将res.data.token的值存入sessionStorage中并通过this.$router.push(‘/home’)进入home页面
1.3 重点内容讲解
添加表单验证步骤:
- 给
<el-form>
添加属性:rules="rules"
,其中rules是表单验证规则对象,在script中定义 - 在script中定义表单验证规则
//* 定义表单验证规则对象
rules: {
//* 帐号验证规则
username: [
{ required: true, message: '请输入帐号', trigger: 'blur' },
{ min: 3, max: 10, message: '长度在 3 到 10 个字符', trigger: 'blur' },
],
}
- 通过
<el-form-item>
的prop属性设置验证规则<el-form-item prop="username">
导入axios以发送ajax请求:
- 在main.js中
import axios from 'axios'
导入axios - 给axios配置默认根路径
axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/'
- 将axios挂载到vue构造函数的原型上
Vue.prototype.$http = axios
,命名为$http
在各个vue实例中都可以通过this.$http
调用axios
配置element弹窗提示:
- element的Message组件的引用方法与其他组件有些许不同,import导入后,需要通过
Vue.prototype.$message = Message
方法将Message挂载到Vue构造函数的原型对象上 - 这样就可以在全部vue实例上通过
this.$message
调用弹窗提示 - 错误提示:
this.$message.error('登录失败')
正确提示:this.$message.success('登录成功')
配置前置路由守卫,实现路由跳转的判定:
- 通过
router.beforeEach
来配置全局的前置路由守卫,每当页面发生跳转之前都要触发该路由守卫 - 先对要访问的路径地址进行判断,访问的是否是登录页面,若访问登录页面则直接放行,否则判断是否sessionStorage存在token
- 若不存在token,则说明还未登录,没有访问其他页面的权限,next方法自动将页面跳转到login页面
- 否则token存在,说明已登录,可直接放行
//* 设置前置路由守卫,对访问的url地址进行权限判定
router.beforeEach((to, from, next) => {
//* to表示要访问的路径
//* from表示从哪个页面跳转进来
//* next是一个方法,表示放行 next()放行 next('/login')跳转到login页面
// *当要访问的路径是/login则直接放行
if (to.path === '/login') return next()
// *否则需要对sessuonStorage中的token进行验证
const token = window.sessionStorage.getItem('token')
// *当token为空,则直接跳转到登录页面
if (!token) return next('/login')
// *否则说明token存在,直接放行
next()
})
1.4 后续工作
至此登录功能已经全部完成,需要将代码全部提交到gitee存储,vscode安装git插件能够对git操作进行可视化操作,非常方便,但处于学习目的,还是用老办法通过git命令行实现代码的提交
git add .
将修改过的文件全部保存到暂存区git commit -m '完成login登录模块全出操作'
将暂存区中的文件提交到本地仓库git status
查看当前暂存区是否干净git push -u origin login
将本地仓库中的login分支提交到远程仓库中git checkout master
回到主分支上git branch
查看当前分支git merge login
将login分支上的内容合并到当前所在的master分支上git push
将当前分支master推送到远程仓库中保存
至此所有git操作已经完成,刷新码云,可以看到远程仓库中添加了最新的内容和消息。
2. 主页模块
2.1 前期准备
- 在上一模块结束后,我们处于master主分支上。通过在终端中输入
git checkout -b home
语句,创建一个新的分支,用于记录对主页模块的操作,我们在home分支中进行开发 - 实现退出登陆功能。在页面中添加退出登录按钮,并为其绑定点击事件。退出登录的逻辑是:点击按钮,先清除sessionStorage中的token,再将页面跳转回login登录页面
//* 点击退出登陆按钮,退出登录
logout() {
//* 1. 先清除sessionStorage中的token
window.sessionStorage.clear()
//* 2. 页面跳转回login
this.$router.replace('/login')
},
- 为防止用户在未登录的情况下在地址栏中输入后续页面的url地址直接进入网页,因此需要为axios配置请求拦截器interceptors,在每次发起请求之前都要为该请求的Authorization添加token。在main。js文件中调用
axios.interceptors.request.use
表示每次发起请求之前都要调用use中的回调函数,形参config表示请求对象
//* 在挂载到Vue构造函数之前,先对axios配置请求拦截器
//* axios.interceptors.request 表示在每次请求之前都要调用use回调函数
axios.interceptors.request.use(config => {
//* 在每次请求的请求头中挂载Authorization = token,服务器就能根据是饭后接收到token进行登录判断
config.headers.Authorization = window.sessionStorage.getItem('token')
return config //固定写法
})
- 在views文件夹下创建home.vue文件,并在router>index.js路由规则中声明home组件。
{ path : '/home', component : 'Home'}
2.2 正式开始
- 使用element ui中的页面布局容器组件,将当前页面划分成头部标题栏,左侧导航栏和右侧内容栏
<el-container>
<el-header>Header</el-header>
<el-container>
<el-aside width="200px">Aside</el-aside>
<el-main>Main</el-main>
</el-container>
</el-container>
- 其中头部标题栏功能单一,由左侧logo和title与右侧退出登陆按钮组成
<!-- 头部区域 -->
<el-header>
<!-- 左侧标题 -->
<div>
<img src="@/assets/heima.png" alt="logo" />
<span>后台电商管理平台</span>
</div>
<!-- 右侧按钮 -->
<el-button type="info" @click="logout">退出登陆</el-button>
</el-header>
<!-- 样式区域 -->
<style lang="less" scoped>
.home-container {
height: 100%;
}
.el-header {
background-color: #373d41;
display: flex;
justify-content: space-between;
align-items: center;
div {
display: flex;
align-items: center;
span {
font-size: 20px;
margin-left: 20px;
color: #fff;
}
}
}
</style>
- 获取侧边栏数据,通过axios.get向服务器请求侧边栏内容列表
//* 获取左侧导航栏列表数据
async getMenuList() {
const { data: res } = await this.$http.get('menus')
if (res.meta.status !== 200) return this.$message.error('菜单列表请求失败')
this.menuList = res.data
// console.log(this.menuList)
},
- 通过element ui中的Menu组件,通过双重v-for循环渲染左侧侧边栏
<!-- 左侧菜单导航栏 -->
<el-menu
background-color="#333744"
text-color="#fff"
active-text-color="#409eff"
unique-opened
:collapse="isCollapse"
:collapse-transition="false"
router
:default-active="this.$route.path"
>
<el-submenu :index="item.id + ''" v-for="item in menuList" :key="item.id">
<template slot="title">
<i :class="iconObj[item.id]"></i>
<span>{{ item.authName }}</span>
</template>
<el-menu-item
:index="'/' + subItem.path"
v-for="subItem in item.children"
:key="subItem.id"
>
<template slot="title">
<i class="el-icon-menu"></i>
<span>{{ subItem.authName }}</span>
</template>
</el-menu-item>
</el-submenu>
</el-menu>
<el-menu>
属性 | 作用 |
---|---|
background-color | 设置导航栏背景颜色 |
text-color | 未选中的文本颜色 |
active-text-color | 当前选中文本颜色 |
unique-opened | 是否只保持一个子菜单的展开 |
collapse | 是否折叠 |
collapse-transition | 是否开启折叠动画 |
router | 是否使用 vue-router 的模式,启用该模式会在激活导航时以 index 作为 path 进行路由跳转 |
default-active | 当前激活菜单的 index |
<el-submenu>
是可折叠的标题栏,通过v-for循环从服务器获取到的左侧菜单栏数据数组menuList,其中每一循环项item的authName就是当前折叠列表的标题。属性index表示当前折叠标题栏的唯一标识符,用item.id来指代。<el-menu-item>
是不可折叠的标题栏,及当前目录下的子标题,通过v-for循环当前折叠标题栏下的子标题及item.children数组。因为<el-menu>
启用了router路由模式,因此每次点击当前<el-menu-item>
都会跳转到index指代的页面,因此这里的index不用subItem.id指代而是用subItem.path指代。- 最终效果
- 添加折叠按钮,在
<el-menu>
上方添加一个div,并绑定点击事件切换<el-menu>
的collapse的值
<!-- 侧边栏 -->
<el-aside :width="isCollapse ? '64px' : '200px'">
<!-- 切换折叠按钮 -->
<div class="toggle-button" @click="changeCollapse">|||</div>
<el-menu
:collapse="isCollapse"
:collapse-transition="false"
...
>
...
<script>
data() {
return {
//* 控制侧边栏折叠与展开
isCollapse: false,
}
},
methods: {
//* 点击切换按钮折叠展开侧边栏
changeCollapse() {
this.isCollapse = !this.isCollapse
},
},
</script>
- 为div绑定了点击事件,isCollapse的值默认为false,每次点击都将isCollapse的值都取反
<el-menu>
中根据isCollapse决定是否折叠<el-aside>
的宽度width根据isCollapse决定
- 右侧主体部分放置路由占位符
<!-- 右侧内容主体 -->
<el-main>
<router-view></router-view>
</el-main>
2.3 页面代码
<template>
<el-container class="home-container">
<!-- 头部区域 -->
<el-header>
<!-- 左侧标题 -->
<div>
<img src="@/assets/heima.png" alt="logo" />
<span>后台电商管理平台</span>
</div>
<!-- 右侧按钮 -->
<el-button type="info" @click="logout">退出登陆</el-button>
</el-header>
<!-- 下方内容 -->
<el-container>
<!-- 侧边栏 -->
<el-aside :width="isCollapse ? '64px' : '200px'">
<!-- 切换折叠按钮 -->
<div class="toggle-button" @click="changeCollapse">|||</div>
<!-- 左侧菜单导航栏 -->
<el-menu
background-color="#333744"
text-color="#fff"
active-text-color="#409eff"
unique-opened
:collapse="isCollapse"
:collapse-transition="false"
router
:default-active="this.$route.path"
>
<el-submenu :index="item.id + ''" v-for="item in menuList" :key="item.id">
<template slot="title">
<i :class="iconObj[item.id]"></i>
<span>{{ item.authName }}</span>
</template>
<el-menu-item
:index="'/' + subItem.path"
v-for="subItem in item.children"
:key="subItem.id"
>
<template slot="title">
<i class="el-icon-menu"></i>
<span>{{ subItem.authName }}</span>
</template>
</el-menu-item>
</el-submenu>
</el-menu>
</el-aside>
<!-- 右侧内容主体 -->
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</template>
<style lang="less" scoped>
.home-container {
height: 100%;
}
.el-header {
background-color: #373d41;
display: flex;
justify-content: space-between;
align-items: center;
div {
display: flex;
align-items: center;
span {
font-size: 20px;
margin-left: 20px;
color: #fff;
}
}
}
.el-aside {
background-color: #333744;
}
.el-main {
background-color: #eaedf1;
}
.iconfont {
margin-right: 10px;
}
.toggle-button {
background-color: #4a5064;
color: #fff;
text-align: center;
line-height: 24px;
font-size: 10px;
letter-spacing: 0.2em; //字间距
cursor: pointer; //鼠标经过变小手
}
.el-menu {
border-right: none;
}
</style>
<script>
export default {
name: 'Home',
data() {
return {
//* 左侧数据列表
menuList: [],
//* 字体图标与id对应关系对象
iconObj: {
125: 'iconfont icon-users',
103: 'iconfont icon-tijikongjian',
101: 'iconfont icon-shangpin',
102: 'iconfont icon-danju',
145: 'iconfont icon-baobiao',
},
//* 控制侧边栏折叠与展开
isCollapse: false,
}
},
methods: {
//* 退出登陆
logout() {
// 1.清除sessionStorage中的token
window.sessionStorage.removeItem('token')
// 2.跳转到login页面
this.$router.replace('/login')
},
//* 获取左侧导航栏列表数据
async getMenuList() {
const { data: res } = await this.$http.get('menus')
if (res.meta.status !== 200) return this.$message.error('菜单列表请求失败')
this.menuList = res.data
// console.log(this.menuList)
},
//* 点击切换按钮折叠展开侧边栏
changeCollapse() {
this.isCollapse = !this.isCollapse
},
},
created() {
this.getMenuList()
},
}
</script>
2.4 后续工作
至此home组件以完成,向gitee仓库中提交代码
git add .
将修改过的文件全部保存到暂存区git commit -m '完成home主页模块全出操作'
将暂存区中的文件提交到本地仓库git status
查看当前暂存区是否干净git push -u origin home
将本地仓库中的home分支提交到远程仓库中git checkout master
回到主分支上git branch
查看当前分支git merge home
将home分支上的内容合并到当前所在的master分支上git push
将当前分支master推送到远程仓库中保存
因篇幅有限将后续功能写在下一文档中
更多推荐
已为社区贡献1条内容
所有评论(0)