开发项目必然少不了通过接口获取获取,这就用到了axios
axios官网
一、安装依赖
npm install axios
二、封装通用请求工具
src/utils/request.ts
import axios from 'axios'
import { ElNotification , ElMessageBox, ElMessage, ElLoading } from 'element-plus'
import { getToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode'
import { tansParams, blobValidate } from '@/utils/mis'
import cache from '@/plugins/cache'
import { saveAs } from 'file-saver'
import useUserStore from '@/store/modules/user'
let downloadLoadingInstance;
// 是否显示重新登录
export let isRelogin = { show: false };
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
baseURL: import.meta.env.VITE_APP_BASE_API,
// 超时
timeout: 10000
})
// request拦截器
service.interceptors.request.use(config => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
// 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
if(config.Headers){
config.headers['Content-Type'] = config.Headers['Content-Type']
}
// get请求映射params参数
if (config.method === 'get' && config.params) {
let url = config.url + '?' + tansParams(config.params);
url = url.slice(0, -1);
config.params = {};
config.url = url;
}
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put'|| config.method === 'delete')) {
const requestObj = {
url: config.url,
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
time: new Date().getTime()
}
const requestSize = Object.keys(JSON.stringify(requestObj)).length; // 请求数据大小
const limitSize = 5 * 1024 * 1024; // 限制存放数据5M
if (requestSize >= limitSize) {
console.warn(`[${config.url}]: ` + '请求数据大小超出允许的5M限制,无法进行防重复提交验证。')
return config;
}
// const sessionObj = cache.session.getJSON('sessionObj')
cache.session.setJSON('sessionObj', requestObj)
// if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
// cache.session.setJSON('sessionObj', requestObj)
// } else {
// const s_url = sessionObj.url; // 请求地址
// const s_data = sessionObj.data; // 请求数据
// const s_time = sessionObj.time; // 请求时间
// const interval = 100; // 间隔时间(ms),小于此时间视为重复提交
// // && requestObj.time - s_time < interval
// if (s_data === requestObj.data && s_url === requestObj.url) {
// const message = '数据正在处理,请勿重复提交';
// console.warn(`[${s_url}]: ` + message)
// return Promise.reject(new Error(message))
// } else {
// cache.session.setJSON('sessionObj', requestObj)
// }
// }
}
return config
}, error => {
console.log(error)
Promise.reject(error)
})
// 响应拦截器
service.interceptors.response.use(res => {
// 未设置状态码则默认成功状态
const code = res.data.code || 200;
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default']
// 二进制数据则直接返回
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
return res.data
}
if (code === 401) {
if (!isRelogin.show) {
isRelogin.show = true;
ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
isRelogin.show = false;
useUserStore().logOut().then(() => {
location.href = '/index';
})
}).catch(() => {
isRelogin.show = false;
});
}
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) {
ElMessage({ message: msg, type: 'error' })
return Promise.reject(new Error(msg))
} else if (code === 601) {
ElMessage({ message: msg, type: 'warning' })
return Promise.reject(new Error(msg))
} else if (code !== 200&& code !== 400) {
ElNotification.error({ title: msg })
return Promise.reject('error')
} else {
return Promise.resolve(res.data)
}
},
error => {
console.log('err' + error)
let { message } = error;
if (message == "Network Error") {
message = "后端接口连接异常";
} else if (message.includes("timeout")) {
message = "系统接口请求超时";
} else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
ElMessage({ message: message, type: 'error', duration: 5 * 1000 })
return Promise.reject(error)
}
)
// 通用下载方法
export function download(url, params, filename, config) {
downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", })
return service.post(url, params, {
transformRequest: [(params) => { return tansParams(params) }],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'blob',
...config
}).then(async (data) => {
const isBlob = blobValidate(data);
if (isBlob) {
const blob = new Blob([data])
saveAs(blob, filename)
} else {
const resText = await data.text();
const rspObj = JSON.parse(resText);
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
ElMessage.error(errMsg);
}
downloadLoadingInstance.close();
}).catch((r) => {
console.error(r)
ElMessage.error('下载文件出现错误,请联系管理员!')
downloadLoadingInstance.close();
})
}
export default service
三、api请求模块化管理
在src下创建api文件夹,新建文件user.ts用来管理登录等用户相关的接口
import request from '@/utils/request'
export const login = (data:object) => {
return request({
url: '/api/user/login',
method: 'post',
data
})
}
四、新建login.vue/调用login接口
1、新建login.vue页面
<template>
<div class="login">
<div class="loginMain">
<div class="login-right">
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
<h3 class="title">登录</h3>
<span class="title-btm"></span>
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
type="text"
size="large"
auto-complete="off"
placeholder="账号"
>
<template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
type="password"
size="large"
auto-complete="off"
placeholder="密码"
@keyup.enter="handleLogin"
>
<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
</el-input>
</el-form-item>
<el-form-item class="loginActBtn">
<el-button
:loading="loading"
size="large"
type="primary"
style="width:100%;"
@click.prevent="handleLogin"
>
<span >登 录</span>
</el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
</template>
<script setup>
import { ElMessage } from 'element-plus'
import Cookies from "js-cookie";
import { encrypt, decrypt } from "@/utils/jsencrypt";
import useUserStore from '@/store/modules/user'
const userStore = useUserStore()
const route = useRoute();
const router = useRouter();
const changeTabStatus = ref(true);
const loginForm = ref({
username: "",
password: "",
});
const loginRules = ref({
username: [{ required: true, trigger: "blur", message: "请输入您的账号" }],
password: [{ required: true, trigger: "blur", message: "请输入您的密码" }],
});
const codeUrl = ref("");
const loading = ref(false);
// 注册开关
const register = ref(false);
const redirect = ref(undefined);
const loginRef = ref(null)
const disabled = ref(false)
const time = ref(localStorage.getItem('time')||0)
const timer = () => {
if (time.value > 0) {
time.value--
localStorage.setItem('time', time.value)
setTimeout(timer, 1000)
} else {
disabled.value = false
}
}
if(time.value>0){
disabled.value = true
timer()
}
const text = computed(() => {
return time.value > 0 ? `${time.value}s 后重获取` : '获取验证码'
})
watch(route, (newRoute) => {
redirect.value = newRoute.query && newRoute.query.redirect;
}, { immediate: true });
function handleLogin() {
loginRef.value.validate(valid => {
if (valid) {
loading.value = true;
Cookies.remove("username");
Cookies.remove("password");
const params = JSON.parse(JSON.stringify(loginForm.value));
// 调用action的登录方法
userStore.login(params).then((res) => {
const query = route.query;
const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
if (cur !== "redirect") {
acc[cur] = query[cur];
}
return acc;
}, {});
router.push({ path: redirect.value || "/", query: otherQueryParams });
}).catch((err) => {
ElMessage({ message: err, type: 'warning' })
loading.value = false;
});
}
});
}
function getCookie() {
const username = Cookies.get("username");
const password = Cookies.get("password");
loginForm.value = {
username: username === undefined ? loginForm.value.username : username,
password: password === undefined ? loginForm.value.password : decrypt(password),
};
}
getCookie();
</script>
<style lang='scss' scoped>
.login {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
background: linear-gradient(139deg, #F3F8FF 0%, #E7F1FF 100%);
background-size: cover;
width: 100%;
padding:4.6875rem 7.75rem;
.loginMain{
width: 100%;
height: 100%;
position: relative;
.sendSmsBtn {
height: 40px;
line-height: 40px;
background: transparent;
border: none;
padding: 0 6px;
color: #cccccc;
display: inline-block;
min-width: 92px;
cursor: pointer;
}
.loginActBtn,.loginSmsBtn{
width: 100%;
}
.loginSmsBtn{
margin-top: 2rem;
}
.login-left{
width: 43%;
height: 100%;
position: absolute;
img{
position: absolute;
display: block;
}
.loginBg{
z-index: 999;
width: auto;
height: 100%;
top: 0;
left: 0;
}
.loginText{
z-index: 2900;
margin-top:7.0625rem;
width: auto;
margin-left: 7vh;
height: 5.5vh;
}
}
.login-right{
background: #fff;
box-shadow: 0px 11 24px 0px rgba(77,142,255,0.19);
border-radius:1.75rem;
width:100%;
height: 100%;
}
}
}
.loginNav{
display: flex;
width: 100%;
padding: 0;
align-items: flex-end;
}
.loginNav .item{
flex: 1;
height: 2.8125rem;
background: #EDF3FF;
color: #6984C3;
position: relative;
list-style: none;
text-align: center;
line-height: 2.8125rem;
cursor: pointer;
}
.loginNav .item.active{
background: #4F6EF7;
color: #fff;
z-index: 10;
}
.loginNav .item:before,.loginNav .item:after{
content: '';
width: 30px;
height: 100%;
position: absolute;
background: #EDF3FF;
}
.loginNav #itemOne:before{
right: -15px;
transform: skew(-20deg);
}
.loginNav #itemTwo:before{
left: -15px;
transform: skew(-20deg);
}
.loginNav .item.active:before,.loginNav .item.active:after{
background: #4F6EF7;
z-index: 10;
}
.loginNav .item.active:before{
border-left: none;
}
.loginNav .item.active:after{
border-right: none;
}
.title {
margin: 0px auto 30px auto;
text-align: left;
font-size: 2.2rem;
font-weight: bold;
color: #333;
margin-bottom: 13px;
}
.title-btm{
width: 36px;
height: 6px;
background: #4F6EF7;
border-radius: 3px;
display: block;
margin-bottom: 2.125rem;
}
.login-form {
position: absolute;
top: 10vh;
border-radius: 6px;
background: #ffffff;
width: 100%;
padding: 25px 25px 5px 25px;
.el-input {
height: 40px;
input {
height: 40px;
}
}
.input-icon {
height: 39px;
width: 14px;
margin-left: 0px;
}
}
.login-tip {
font-size: 13px;
text-align: center;
color: #bfbfbf;
}
.login-code {
width: 33%;
height: 40px;
float: right;
img {
cursor: pointer;
vertical-align: middle;
}
}
.el-login-footer {
height: 40px;
line-height: 40px;
position: fixed;
bottom: 0;
width: 100%;
text-align: center;
color: #fff;
font-family: Arial;
font-size: 12px;
letter-spacing: 1px;
}
.login-code-img {
height: 40px;
padding-left: 12px;
}
</style>
<style>
.login{
.el-checkbox__label{
color: #333333!important;
font-family: SourceHanSansSC-Regular;
}
.el-input-group__append{
background-color: transparent;
cursor: pointer;
}
.el-input__inner{
background-color: transparent!important;
}
}
</style>
点击登录调用全局状态管理内容不的userStore.login方法,modules/user.ts内容如下:
import { login, logout, getInfo,setCustomList,getCustomList } from '@/api/user'
import { getToken, setToken, removeToken } from '@/utils/auth'
import auth from '@/plugins/auth'
import defAva from '@/assets/img/logo.png'
const useUserStore = defineStore(
'user',
{
state: () => ({
token: getToken(),
id: '',
name: '',
avatar: '',
roles: [],
permissions: [],
customList:[],
customer:{}
}),
actions: {
// 登录
login(userInfo:any) {
userInfo.username = userInfo.username.trim()
return new Promise((resolve, reject) => {
login(userInfo).then(res => {
if(res.code === 200){
setToken(res.token)
this.token = res.token
resolve()
}else{
reject(res.msg)
}
}).catch(error => {
reject(error)
})
})
},
getCustomListData(userId){
const params = {
cacheKey:'customList_cache:card_'+userId
}
return []
},
setCustomListData(cumList){
const params = {
cacheKey:'customList_cache:card_'+this.id,
cacheValue:JSON.stringify(cumList)
}
// setCustomList(params).then(res => {
// if(res.code==200){
// this.customList = cumList
// }
// })
},
// 获取用户信息
getInfo() {
var res = {
"msg": "操作成功",
"code": 200,
"data": null,
"permissions": [
"*:*:*"
],
"roles": [
"admin"
],
"user": {
"createBy": "admin",
"createTime": "2023-05-05 23:39:43",
"updateBy": null,
"updateTime": null,
"remark": "管理员",
"userId": 1,
"deptId": 100,
"userName": "admin",
"nickName": "超级管理",
"email": "mis@163.com",
"phonenumber": "15001044531",
"sex": "0",
"avatar": "http://8.140.57.214:9000/mis-oss/2023/12/04/blob_20231204022503A129.png",
"password": "$2a$10$ONeCUxG3wrWrjsIzRI9l1eG.9TvqXC9w4/MTaOZC9jHBZE9R.m4FK",
"status": "0",
"delFlag": "0",
"loginIp": "127.0.0.1",
"loginDate": "2023-05-05T23:39:43.000+08:00",
"dept": {
"createBy": null,
"createTime": null,
"updateBy": null,
"updateTime": null,
"remark": null,
"deptId": 100,
"parentId": 0,
"ancestors": "0",
"deptName": "众诚科技",
"orderNum": 0,
"leader": "众诚",
"phone": null,
"email": null,
"status": "0",
"delFlag": null,
"parentName": null,
"children": []
},
"roles": [
{
"createBy": null,
"createTime": null,
"updateBy": null,
"updateTime": null,
"remark": null,
"roleId": 1,
"roleName": "超级管理员",
"roleKey": "admin",
"roleSort": 1,
"dataScope": "1",
"menuCheckStrictly": false,
"deptCheckStrictly": false,
"status": "0",
"delFlag": null,
"flag": false,
"menuIds": null,
"deptIds": null,
"permissions": null,
"admin": true
}
],
"roleIds": null,
"postIds": null,
"roleId": null,
"admin": true
},
"customer": {
"customerId": 48,
"customerName": "管理员账户",
"customerCheckStatus": null,
"checkResultDescription": null,
"customerType": 1,
"cancelState": false,
"withdrawalFeeRatio": 0.06
}
}
const user = res.user
const avatar = (user.avatar == "" || user.avatar == null) ? defAva : user.avatar;
if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
this.roles = res.roles
this.permissions = res.permissions
} else {
this.roles = ['ROLE_DEFAULT']
}
this.customer = res.customer
this.id = user.userId
this.name = user.userName
this.avatar = avatar
this.getCustomListData(user.userId)
// return new Promise((resolve, reject) => {
// getInfo().then(res => {
// const user = res.user
// const avatar = (user.avatar == "" || user.avatar == null) ? defAva : user.avatar;
// if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
// this.roles = res.roles
// this.permissions = res.permissions
// } else {
// this.roles = ['ROLE_DEFAULT']
// }
// this.customer = res.customer
// this.id = user.userId
// this.name = user.userName
// this.avatar = avatar
// this.getCustomListData(user.userId)
// resolve(res)
// }).catch(error => {
// reject(error)
// })
// })
},
// 退出系统
logOut() {
this.token = ''
this.roles = []
this.permissions = []
removeToken()
// return new Promise((resolve, reject) => {
// logout(this.token).then(() => {
// this.token = ''
// this.roles = []
// this.permissions = []
// removeToken()
// resolve()
// }).catch(error => {
// reject(error)
// })
// })
}
}
})
export default useUserStore
登录成功,跳转首页