1. 写一个统一的url 配置文件
import { baseUrl } from './global'
export default {
method: 'get',
// 基础url前缀
baseUrl: baseUrl,
// 请求头信息
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Access-Control-Allow-Origin':'*',
},
// 参数
data: {},
// 设置超时时间
timeout: 10000,
// 携带凭证
withCredentials: false,
// 返回数据类型
responseType: 'json',
//跨域
changeOrigin:true
}
2. 在前后端分离的情况下,在项目测试部署的时经常会修改文件的后端的url路径,所以我们需要写个请求后端的url配置文件,每次修改的时候只需要修改这个配置文件就可以了
/**
* 全局常量、方法封装模块
* 通过原型挂载到Vue属性
* 通过 this.Global 调用
*/
// 后台管理系统服务器地址
export const baseUrl = 'http://localhost:9001'
// 系统数据备份还原服务器地址
export const backupBaseUrl = 'http://localhost:9001'
export default {
baseUrl,
backupBaseUrl
}
3..封装axios模块 ,可以统一调用 ,利用Promise 先实例化,Promise其实是一个构造函数,它有resolve,reject,race等静态方法;它的原型(prototype)上有then,catch方法,因此只要作为Promise的实例,都可以共享并调用Promise.prototype上面的方法(then,catch),并将错误信息添加到reject 中,成功的添加到resolve
import axios from 'axios';
import config from './config';
import Cookies from "js-cookie";
import router from '@/router'
export default function $axios(options) {
return new Promise((resolve, reject) => {
const instance = axios.create({
baseURL: config.baseUrl,
headers: config.headers,
timeout: config.timeout,
changeOrigin: config.changeOrigin,
withCredentials: config.withCredentials
})
// request 拦截器
instance.interceptors.request.use(
config => {
let token = Cookies.get('token')
// 1. 请求开始的时候可以结合 vuex 开启全屏 loading 动画
// console.log(store.state.loading)
// console.log('准备发送请求...')
// 2. 带上token
if (token) {
config.headers.token = token
} else {
// 重定向到登录页面
router.push('/login')
}
// 3. 根据请求方法,序列化传来的参数,根据后端需求是否序列化
if (config.method === 'post') {
// if (config.data.__proto__ === FormData.prototype
// || config.url.endsWith('path')
// || config.url.endsWith('mark')
// || config.url.endsWith('patchs')
// ) {
// } else {
// config.data = qs.stringify(config.data)
// }
}
return config
},
error => {
// 请求错误时
console.log('request:', error)
// 1. 判断请求超时
if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1) {
console.log('timeout请求超时')
// return service.request(originalRequest);// 再重复请求一次
}
// 2. 需要重定向到错误页面
const errorInfo = error.response
console.log(errorInfo)
if (errorInfo) {
error = errorInfo.data // 页面那边catch的时候就能拿到详细的错误信息,看最下边的Promise.reject
const errorStatus = errorInfo.status; // 404 403 500 ...
router.push({
path: `/error`
})
}
return Promise.reject(error) // 在调用的那边可以拿到(catch)你想返回的错误信息
}
)
// response 拦截器
instance.interceptors.response.use(
response => {
let data;
// IE9时response.data是undefined,因此需要使用response.request.responseText(Stringify后的字符串)
if (response.data == undefined) {
data = JSON.parse(response.request.responseText)
} else {
data = response.data
}
// 根据返回的code值来做不同的处理
switch (data.rc) {
case 1:
console.log(data.desc)
break;
case 0:
store.commit('changeState')
// console.log('登录成功')
default:
}
// 若不是正确的返回code,且已经登录,就抛出错误
// const err = new Error(data.desc)
// err.data = data
// err.response = response
// throw err
return data
},
err => {
if (err && err.response) {
switch (err.response.status) {
case 400:
err.message = '请求错误'
break
case 401:
err.message = '未授权,请登录'
break
case 403:
err.message = '拒绝访问'
break
case 404:
err.message = `请求地址出错: ${err.response.config.url}`
break
case 408:
err.message = '请求超时'
break
case 500:
err.message = '服务器内部错误'
break
case 501:
err.message = '服务未实现'
break
case 502:
err.message = '网关错误'
break
case 503:
err.message = '服务不可用'
break
case 504:
err.message = '网关超时'
break
case 505:
err.message = 'HTTP版本不受支持'
break
default:
}
}
console.error(err)
return Promise.reject(err) // 返回接口返回的错误信息
}
)
// 请求处理
instance(options).then(res => {
resolve(res)
return false
}).catch(error => {
reject(error)
})
})
}
4.封装完成以后 我们只需在每个接口的时候调用就可以了,以登录接口为例
import axios from "@/http/axios";
import qs from 'qs';
let login = {};
login.subInfo = function (parms) {
return axios({
url: '/login',
method: 'post',
data:qs.stringify(parms)
})
}
login.validateCode = function () {
return axios({
url: '/kaptcha/render',
method: "get",
})
}
export default login;
5.并随着接口的增多,我们需要写一个文件统一调用接口
import login from '@/Api/rbac/login.js'
import user from '@/Api/rbac/user.js'
import role from '@/Api/rbac/role.js'
import menu from '@/Api/rbac/menu.js'
import dict from '@/Api/rbac/dict.js'
import dept from '@/Api/rbac/dept.js'
import log from '@/Api/rbac/log.js'
let api = {
login,
user,
role,
menu,
dict,
dept,
log
}
export default api;
5.然后我们把所有结构挂载在 Vue 原型的 $api 对象
// 导入所有接口
import api from "../api/Api";
const install = Vue => {
if (install.installed)
return;
install.installed = true;
Object.defineProperties(Vue.prototype, {
// 注意,此处挂载在 Vue 原型的 $api 对象上
$api: {
get() {
return api
}
}
})
}
export default install
6. 最后在main.js中添加,然后就可以全局引用了
import api from './http' import global from '@/http/global'
Vue.use(api) Vue.prototype.global = global
7.写一个登录页面,我的把js,scss,vue分开写了
<template>
<div class="login-container">
<el-form :model="loginInfo" :rules="myrule"
status-icon
ref="loginInfo"
label-position="left"
label-width="0px"
class="demo-ruleForm login-page">
<h3 class="title">面朝大海 春暖花开</h3>
<el-form-item prop="username">
<el-input prefix-icon="el-icon-user-solid" v-model="loginInfo.username"
auto-complete="off"/>
</el-form-item>
<el-form-item prop="password">
<el-input prefix-icon="el-icon-lock" type="password" v-model="loginInfo.password"
auto-complete="off"/>
</el-form-item>
<el-form-item prop="code" class="code">
<el-input prefix-icon="el-icon-lock" v-model="loginInfo.verifyCode" placeholder="验证码"></el-input>
<img :src="loginInfo.src" @click="clickCapatch"/>
</el-form-item>
<el-form-item>
<el-checkbox v-model="checked" class="rememberme" style="float:left;">记住密码</el-checkbox>
<a class="login-form-forgot" @click="froget" style="float:right;" href="javascript:;">忘记密码 </a>
</el-form-item>
<el-form-item style="width:100%;">
<el-button type="primary" style="width:100%;" @click="loginafter" :loading="loading">登录</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script src="@/views/login/login.js"></script>
<style scoped src="@/views/login/login.scss"></style>
import url from "@/http/global";
import Cookies from "js-cookie";
export default {
name: 'login',
data() {
return {
loading: false,
loginInfo: {
username: 'root',
password: '123456',
verifyCode: "",
src: url.baseUrl + "/kaptcha/render"
},
myrule: {
username: [{required: true, message: 'please enter your account', trigger: 'blur'}],
password: [{required: true, message: 'enter your password', trigger: 'blur'}]
},
checked: false
}
},
methods: {
clickCapatch: function () {
this.loginInfo.src = url.baseUrl + '/kaptcha/render?time='+new Date();
},
froget() {
},
loginafter() {
let that = this
let params = {
username: that.loginInfo.username,
password: that.loginInfo.password,
validateCodeText: that.loginInfo.verifyCode,
captcha: Cookies.get("captcha")
}
this.$api.login.subInfo(params).then(res => {
try {
var dateStatus = res.status
if (203 == dateStatus) {
Cookies.set('token', res.jwtToken)
this.$router.push({path: '/home',});
} else {
this.$router.push({path: '/error',});
}
} catch (e) {
this.$router.push({path: '/error',});
}
})
}
}
};
.login-container {
width: 100%;
height: 100%;
background : #f0f2f5 url('../../assets/background.jpg') no-repeat;
}
.login-page {
-webkit-border-radius: 5px;
border-radius: 5px;
margin: 180px auto;
width: 350px;
padding: 35px 35px 15px;
background: #fff;
border: 1px solid #eaeaea;
box-shadow: 0 0 25px #cac6c6;
}
.rememberme .el-checkbox {
margin: 0px 0px 15px;
text-align: left;
}
.code {
position: relative;
margin-top: 20px;
display: block;
width: 60%;
}
img {
position: absolute;
height: 35px;
margin-left: 16px;
}
最后将登录页面添加到路由中
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'login',
component: () => import('@/views/login/login.vue'),
},
{
path: '/home',
name: 'home',
component: () => import('@/views/home/home.vue'),
},
{
path: '/error',
name: 'error',
component: () => import('@/views/error.vue'),
}
]
})
启动 查看页面效果