一、前后端进行对接
1、前端安装axios
npm install axios
2、新建utils/api.js
- utils / api.js
import axios from 'axios'
/* 单独引入 此时调用方法为 Message(options)。我们也为每个 type 定义了各自的方法,如 Message.success(options)。
并且可以调用 Message.closeAll() 手动关闭所有实例。*/
import { Message } from 'element-ui'
//处理响应信息的响应拦截器
/*上面已经导入了axios的对象了,这个对象里面有个响应的拦截器axios.interceptors.response.use(),use里面的data是服务端响应给你的数据,
该拦截器有两个回调函数,一个是success,一个是error,和jQuery里面的ajax一样请求数据的时候也有两个回调函数,一个success,一个error,
可以简单的理解为http的响应码是200的,它会进入到success方法中来,400以上的会进入到error来
*/
axios.interceptors.response.use(success => {
//success.status: http的响应码
//success.data.status == 500: 返回json的status
if(success.status && success.status == 200 && success.data.status == 500 ){
//把后台的出错消息写出来
Message.error({message: success.data.msg})
return;
}
if(success.data.msg){
//把操作成功的信息也弹出来
Message.success({message: success.data.msg})
}
return success.data
},error =>{ //失败的处理
if(error.response.status == 504 || error.response.status == 404){
Message.error({message: "服务器被吃了哦!"})
}else if(error.response.status == 403){
Message.error({message: "权限不足,请联系管理员"})
}else if(error.response.status == 401){
Message.error({message: "尚未登录"})
}else{
//服务器返回的错误信息
if(error.response.data.msg){
Message.error({message: error.response.data.msg})
}else{
Message.error({message: "未知错误"})
}
}
return;
})
//封装请求
let base = ''; //避免前缀要一个一个去改,太麻烦
//
export const postKeyValueRequest=(url,params)=>{
return axios({
method: 'post',
url: `${base}${url}`, //特别注意这里是``引用变量的方式,而不是''
data: params, //直接这样以json的形式传给服务器,这是不支持的,所有还需要定义下面的transformRequest
transformRequest: [function (data){
let ret = '';
for(let i in data){
//往ret上面追加变量
ret+=encodeURIComponent(i) + '=' + encodeURIComponent(data[i]) + '&'
}
console.log(ret);
return ret;
}],
//定义header
headers:{
'Content-Type':'application/x-www-form-urlencoded '
}
})
}
3、在Login.vue中使用
先导入
import {postKeyValueRequest} from "@/utils/api"
点击按钮时发起请求:
submitLogin(){
this.$refs.loginForm.validate((valid) =>{ // Element自带的校验
if(valid){
//这是在api.js封装的请求
postKeyValueRequest('/doLogin',this.loginForm).then(resp=>{
if(resp){
alert(JSON.stringify(resp))
}
})
}else {
this.$message.error("请输入所有字段");
return false;
}
})
}
4、配置请求转发的代理对象(解决跨域)
新建vue.config.js
- vue.config.js
//配置请求转发的代理
//定义代理的对象
let proxyObj = {};
//拦截http请求
proxyObj['/'] = {
ws: false, //关掉websocket
target: 'http://localhost:8081', //目标转发的地址
changeOrigin: true,
pathRewrite:{ //请求地址重写
'^/': '' //拦截到的地址不去修改它
}
}
//把上面的导出来
module.exports = {
devServer:{ //配置开发环境
host: 'localhost', //端口号
port: 8080,
proxy: proxyObj //代理对象
}
}
5、测试
- 密码输入错误时
- 账号错误时
- 账号密码都正确时
6、在api.js封装其它登录请求
以后想要引入这些封装好的登录方法直接在methods方法里面使用this.
方法名就行
export const postRequest=(url, params)=>{
return axios({
method: 'post',
url: `${base}${url}`,
data: params //这是用json来传递的,所以不用加transformRequest转换了
})
}
export const putRequest=(url, params)=>{
return axios({
method: 'put',
url: `${base}${url}`,
data: params //这是用json来传递的,所以不用加transformRequest转换了
})
}
export const getRequest=(url, params)=>{
return axios({
method: 'get',
url: `${base}${url}`,
data: params //这是用json来传递的,所以不用加transformRequest转换了
})
}
export const deleteRequest=(url, params)=>{
return axios({
method: 'delete',
url: `${base}${url}`,
data: params //这是用json来传递的,所以不用加transformRequest转换了
})
}
7、制作vue插件
在main.js中引入刚封装好的请求
import {postKeyValueRequest} from "./utils/api";
import {postRequest} from "./utils/api";
import {putRequest} from "./utils/api";
import {getRequest} from "./utils/api";
import {deleteRequest} from "./utils/api";
Vue.prototype.postKeyValueRequest = postKeyValueRequest;
Vue.prototype.postRequest = postRequest;
Vue.prototype.putRequest = putRequest;
Vue.prototype.getRequest = getRequest;
Vue.prototype.deleteRequest = deleteRequest;
8、新建Home.vue组件并在router/index.js配置路由
配置路由
9、登录跳转
submitLogin(){
this.$refs.loginForm.validate((valid) =>{ // Element自带的校验
if(valid){
//这是在api.js封装的请求
this.postKeyValueRequest('/doLogin',this.loginForm).then(resp=>{
if(resp){
//resp:从服务端拿到的数据 用户的数据要保存到哪里? 保存在sessionStorage 关闭浏览器就没了
window.sessionStorage.setItem("user", JSON.stringify(resp.obj));
//页面跳转 replace:替换 用push的话,可以使用后退按钮回到登录页,用replace不可以回到登录页
this.$router.replace('/home')
}
})
}else {
this.$message.error("请输入所有字段");
return false;
}
})
}
10、测试
二、Home页的Title制作
1、使用Element UI中的Container布局容器
https://element.eleme.cn/#/zh-CN/component/container
复制响应布局代码过来并给头部添加背景色
效果:
发现有默认的边距,这时我们需要在public/index.html的body加上样式style="margin:0px; padding:0px;"
即可消除边距
消除后刷新页面:
可以看到已经没有边距了
2、完善Home组件
<template>
<div>
<el-container>
<el-header class="homeHeader">
<div class="title">微人事</div>
<el-dropdown class="userInfo" @command="commandHandler"> <!--@command="commandHandler" 点击菜单项触发的事件回调-->
<span class="el-dropdown-link">
{{user.name}}
<i><img :src="user.userface" alt=""></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="userInfo">个人中心</el-dropdown-item>
<el-dropdown-item command="setting">设置</el-dropdown-item>
<!--disabled:禁用的 divided:有分隔线-->
<el-dropdown-item command="logout" divided>注销登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-container>
<el-aside width="200px">Aside</el-aside>
<el-main>Main</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
export default {
name: "Home",
data(){
return{
user:JSON.parse(window.sessionStorage.getItem("user")) //这样得到的数据是字符串,要用JSON.parse方法吧字符串转换成json数据
}
},
methods:{
commandHandler(cmd){ //该方法有一个参数,cmd
if(cmd=='logout'){
this.$confirm('此操作将注销登录, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.getRequest("/logout");
window.sessionStorage.removeItem("user")
this.$router.replace("/")
}).catch(() => {
this.$message({
type: 'info',
message: '已取消注销'
});
});
}
}
}
}
</script>
<style>
.homeHeader{
background-color:#409eff;
display: flex;
align-items: center; /*竖轴上居中*/
justify-content:space-between; /*空白的地方在中间*/
padding: 0 15px;
box-sizing: border-box;
}
/* 字体样式 */
.title{
font-size: 30px;
font-family: 华文行楷;
color: #ffffff;
}
/*设置鼠标移上去显示为手指*/
.userInfo{
cursor: pointer;
}
/* 头像样式 */
.el-dropdown-link img{
width: 48px;
height: 48px;
border-radius: 24px;
margin-left: 8px;
}
/* 用户名样式 */
.el-dropdown-link{
display: flex;
align-items: center;
}
</style>
- 每个下拉菜单都是一个点击按钮,所以要给下拉菜单添加点击事件,可以使command=" "点击菜单项触发的事件回调
- 然后在标签里面添加@command=“commandHandler” 点击事件
- 再在script标签里面加上method方法
3、效果
注销成功后回到登录页面:
登录成功后:
三、左侧菜单
1、侧栏
https://element.eleme.cn/#/zh-CN/component/menu
修改后:
<el-aside width="200px">
<el-menu>
<el-submenu index="1">
<template slot="title">
<i class="el-icon-location"></i>
<span>导航一</span>
</template>
<el-menu-item-group>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</el-aside>
2、新建两个测试用的组件
3、动态渲染菜单
- Home.vue
<template>
<div>
<el-container>
<el-header class="homeHeader">
<div class="title">微人事</div>
<el-dropdown class="userInfo" @command="commandHandler"> <!--@command="commandHandler" 点击菜单项触发的事件回调-->
<span class="el-dropdown-link">
{{user.name}}
<i><img :src="user.userface" alt=""></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="userInfo">个人中心</el-dropdown-item>
<el-dropdown-item command="setting">设置</el-dropdown-item>
<!--disabled:禁用的 divided:有分隔线-->
<el-dropdown-item command="logout" divided>注销登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-header>
<el-container>
<el-aside width="200px">
<el-menu router>
<!--这个遍历拿到的是index.js里面的routers地址数组 -->
<!-- !item.hidden 是将home和login的路由名隐藏,不需要在左侧菜单渲染出来 -->
<el-submenu index="1" v-for="(item,index) in this.$router.options.routes" v-if="!item.hidden" :key="index">
<template slot="title">
<i class="el-icon-location"></i>
<span>{{item.name}}</span>
</template>
<el-menu-item :index="child.path" v-for="(child,indexj) in item.children" :key="indexj">
{{child.name}}
</el-menu-item>
</el-submenu>
</el-menu>
</el-aside>
<el-main>
<router-view/>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
export default {
name: "Home",
data(){
return{
user:JSON.parse(window.sessionStorage.getItem("user")) //这样得到的数据是字符串,要用JSON.parse方法吧字符串转换成json数据
}
},
methods:{
commandHandler(cmd){ //该方法有一个参数,cmd
if(cmd=='logout'){
this.$confirm('此操作将注销登录, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.getRequest("/logout");
window.sessionStorage.removeItem("user")
this.$router.replace("/")
}).catch(() => {
this.$message({
type: 'info',
message: '已取消注销'
});
});
}
}
}
}
</script>
<style>
.homeHeader{
background-color:#409eff;
display: flex;
align-items: center; /*竖轴上居中*/
justify-content:space-between; /*空白的地方在中间*/
padding: 0 15px;
box-sizing: border-box;
}
/* 字体样式 */
.title{
font-size: 30px;
font-family: 华文行楷;
color: #ffffff;
}
/*设置鼠标移上去显示为手指*/
.userInfo{
cursor: pointer;
}
/* 头像样式 */
.el-dropdown-link img{
width: 48px;
height: 48px;
border-radius: 24px;
margin-left: 8px;
}
/* 用户名样式 */
.el-dropdown-link{
display: flex;
align-items: center;
}
</style>
4、在router/index.js配置
- router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../views/Login.vue'
import Home from '../views/Home.vue'
import Test1 from '../views/Test1'
import Test2 from '../views/Test2'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Login',
component: Login,
hidden: true
},
{
path: '/home',
name: 'Home',
component: Home,
hidden: true //做个标记,然后在Home.vue里面进行判断
},
{
path: '/home',
name: '导航一',
component: Home,
children:[
{
path: '/test1',
name: '选项1',
component: Test1
},
{
path: '/test2',
name: '选项2',
component: Test2
}
]
}
]
const router = new VueRouter({
routes
})
export default router
5、效果