- 安装依赖
"dependencies": {
"qs": "^6.10.1",
"axios": "^0.21.1"
},
- 统一管理配置,创建src/config/net.config.ts
type NetConfigSuccessCode = 200 | '200' | '000000'
export const baseURL: string = import.meta.env.MODE === 'development'
? '/xz-risk'
: `${import.meta.env.VITE_RES_URL}/xz-risk`
export const contentType: string = 'application/json;charset=UTF-8'
export const requestTimeout: number = 10000
export const timeoutNum: number = 3
export const intervalTime: number = 1000
export const successCode: NetConfigSuccessCode[] = [200, '200', '000000']
export const statusName: string = 'code'
export const messageName: string = 'msg'
- 封装api模块,用于管理请求路径 src/api/index.ts
import common from '@/api/common'
interface UrlDict {
[key: string]: {
[key: string]: string
}
}
const urlDict: UrlDict = {
common
}
const getUrl = (url: string): string => {
try {
if (url === '') throw new Error('请求路径为空')
const [modelName, urlName] = url.split('.')
if (!Object.keys(urlDict).includes(modelName)) throw new Error('未获取到请求模块')
const reqUrl = urlDict[modelName][urlName]
if (!reqUrl) throw new Error('未获取到请求所需url')
return reqUrl
} catch (e) {
console.error(e)
return ''
}
}
export default getUrl
export default {
token: '/common/token'
}
- 创建请求所需类型
declare namespace MyRequest {
interface response {
code: number | string,
msg: string,
data: any
}
class request {
public post(url: string, data?: any, config?: object): Promise<response>
public get(url: string, params?: any, config?: object): Promise<response>
}
}
- 封装请求
import axios, { AxiosResponse, AxiosRequestConfig, CancelTokenStatic, AxiosInstance } from 'axios'
import {
baseURL,
successCode,
contentType,
requestTimeout,
statusName,
messageName
} from '@/config/net.config'
import qs from 'qs'
import getUrl from '@/api'
const CODE_MESSAGE: any = {
200: '服务器成功返回请求数据',
201: '新建或修改数据成功',
202: '一个请求已经进入后台排队(异步任务)',
204: '删除数据成功',
400: '发出信息有误',
401: '用户没有权限(令牌失效、用户名、密码错误、登录过期)',
402: '前端无痛刷新token',
403: '用户得到授权,但是访问是被禁止的',
404: '访问资源不存在',
406: '请求格式不可得',
410: '请求资源被永久删除,且不会被看到',
500: '服务器发生错误',
502: '网关错误',
503: '服务不可用,服务器暂时过载或维护',
504: '网关超时'
}
class MyRequest {
protected service: AxiosInstance = axios
protected pending: Array<{ url: string, cancel: Function }> = []
protected CancelToken: CancelTokenStatic = axios.CancelToken
protected axiosRequestConfig: AxiosRequestConfig = {}
private static _instance: MyRequest;
constructor () {
this.requestConfig()
this.service = axios.create(this.axiosRequestConfig)
this.interceptorsRequest()
this.interceptorsResponse()
}
protected requestConfig (): void {
this.axiosRequestConfig = {
baseURL: baseURL,
headers: {
timestamp: new Date().getTime(),
'Content-Type': contentType
},
transformResponse: [function (data: AxiosResponse) {
return data
}],
paramsSerializer: function (params: any) {
return qs.stringify(params, { arrayFormat: 'brackets' })
},
timeout: requestTimeout,
withCredentials: false,
responseType: 'json',
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxRedirects: 5,
maxContentLength: 2000,
validateStatus: function (status: number) {
return status >= 200 && status < 500
}
}
}
protected interceptorsRequest (): void {
this.service.interceptors.request.use(
(config: AxiosRequestConfig) => {
const keyOfRequest = this.getKeyOfRequest(config)
this.removePending(keyOfRequest, true)
config.cancelToken = new this.CancelToken((c: any) => {
this.pending.push({
url: keyOfRequest,
cancel: c
})
})
this.requestLog(config)
return config
},
(error) => {
return Promise.reject(error)
}
)
}
protected interceptorsResponse (): void {
this.service.interceptors.response.use((response: AxiosResponse) => {
return this.handleResponse(response)
}, error => {
const { response } = error
if (response === undefined) {
return Promise.reject(new Error(error))
} else {
return this.handleResponse(response)
}
})
}
protected handleResponse (response: AxiosResponse): Promise<AxiosResponse<any>> {
this.responseLog(response)
this.removePending(this.getKeyOfRequest(response.config))
const { data, status, config, statusText } = response
let code = data && data[statusName]
? data[statusName]
: status
if (successCode.indexOf(data[statusName]) + 1) code = 200
switch (code) {
case 200:
return Promise.resolve(response)
case 401:
break
case 403:
break
}
const errMsg = data && data[messageName]
? data[messageName]
: CODE_MESSAGE[code]
? CODE_MESSAGE[code]
: statusText
return Promise.reject(errMsg)
}
protected removePending (key: string, request: boolean = false): void {
this.pending.some((item, index) => {
if (item.url === key) {
if (request) console.log('===== 取消重复请求 =====', item)
item.cancel()
this.pending.splice(index, 1)
return true
}
return false
})
}
protected getKeyOfRequest (config: AxiosRequestConfig): string {
let key = config.url
if (config.params) key += JSON.stringify(config.params)
if (config.data) key += JSON.stringify(config.data)
key += `&request_type=${config.method}`
return key as string
}
protected requestLog (config: any): void {
}
protected responseLog (response: AxiosResponse) {
if (import.meta.env.MODE === 'development') {
const randomColor = `rgba(${Math.round(Math.random() * 255)},${Math.round(
Math.random() * 255
)},${Math.round(Math.random() * 255)})`
console.log(
'%c┍------------------------------------------------------------------┑',
`color:${randomColor};`
)
console.log('| 请求地址:', response.config.url)
console.log('| 请求参数:', qs.parse(response.config.data))
console.log('| 返回数据:', response.data)
console.log(
'%c┕------------------------------------------------------------------┙',
`color:${randomColor};`
)
}
}
public post (url: string, data: any = {}, config: object = {}): Promise<MyRequest.response> {
return new Promise((resolve, reject) => {
this.service.post(getUrl(url), data, config).then(result => {
resolve({
msg: result.data.msg,
data: result.data.data,
code: result.data.code
})
}, reject)
})
}
public get (url: string, params: any = {}, config: object = {}): Promise<MyRequest.response> {
return new Promise((resolve, reject) => {
this.service.get(`${getUrl(url)}?${qs.stringify(params)}`, config).then(result => {
resolve({
msg: result.data.msg,
data: result.data.data,
code: result.data.code
})
}, reject)
})
}
public static getInstance (): MyRequest {
this._instance || (this._instance = new MyRequest())
return this._instance
}
}
export default MyRequest.getInstance()
- 全局注册,这里是挂载到globalProperties
import { App } from 'vue'
import request from '@/utils/request'
import router from '@/router'
import { store } from '@/store'
const baseTableHeight = (formType: number = 1): number => {
const mainInfo = store.getters.layoutMainInfo
return (mainInfo.height - 130) * formType
}
const formatImage = (src: string): string => {
if (!src) return ''
if (src.includes('http')) return src
return `${import.meta.env.VITE_RES_URL}${src}`
}
const install = (app: App) => {
app.config.globalProperties.$request = request
app.config.globalProperties.$baseTableHeight = baseTableHeight
app.config.globalProperties.$image = formatImage
}
export default install
- 全局声明,如果不声明,在组件中使用 this.没有代码提示
export {}
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$request: MyRequest.request,
$baseTableHeight: (formType?: number) => number,
$image: (src: string) => void
}
}
- 使用
this.$request.post('common.token', {})
this.$request.get('common.token', {})
import request from '@/utils/request'
request.post('common.token', {})
request.get('common.token', {})
- 注意新增api模块后,要在 api/index.ts中导入,比如新增一个用户模块
export default {
list: '/user/list'
}
import common from '@/api/common'
import user from '@/api/user'
interface UrlDict {
[key: string]: {
[key: string]: string
}
}
const urlDict: UrlDict = {
common,
user
}
this.$request.get('user.list', {})