Skip to content
On this page

接口模块

api使用方法原理

🐾🐾基于axios封装的请求库

install(app: App) {  //安装方法
    app.provide(QAPI_INJECT_KEY, this)
    app.config.globalProperties.$http = this.$http
    const module: Map = this._options.module ? { ...this._options.module, ...BuiltModuleActons } : { ...BuiltModuleActons }
    instllBuiltModuleActons(this, module)
  }
  
export function createApi(options: AxiosRequestCustomConfig) { //实例方法
  return new Request(options)
}

export function useHttp<T>(key: InjectionKey<T> | string | null = null) { //使用方法
  return inject(key !== null ? key : QAPI_INJECT_KEY) as Request
}
//对象TS
import { AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios'

//定义扩展接口类型
export interface requestInterceptors {
  //// 请求拦截
  requestInterceptors?: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig
  requestInterceptorsCatch?: (err: Map) => Map
  // // 响应拦截
  responseInterceptors?: (config: AxiosResponse) => AxiosResponse
  responseInterceptorsCatch?: (err: Map) => Map
}

//继承axiso请求配置,加上模块和白名单
export interface AxiosRequestCustomConfig extends AxiosRequestConfig {
  module?: object //接口模块
  interceptors?: requestInterceptors
  responseInterceptorsCatchMsgFun?: (err: Map) => boolean //响应错误
  requestInterceptorsCatchMsgFun?: (err: Map) => boolean //请求错误
  isOpenShowErrorOneTip?: boolean
  responseInterceptorsAtfterCatchMsgFun?: (err: Map) => boolean //响应成功,但是接口code5xx或者4xx提示
}

class Request{ //实例对象ts
....
}

通过在app事后使用use安装该包,并且export使用方法和实例方法

api配置

main.ts

js
...
import qApiPlugin from './utils/qApiPlugin'  //在utils工具库实例化api包
  ...
app.use(qApiPlugin)

qApiPlugin.ts

qApiPlugin

api使用展示

1.0)在V6环境下使用
js
//在setup组件函数下使用
<script setup lang="ts">
    ...
   import qqtApi from '@qqt-product/api'
    ...
   const qhttp: qqtApi.Request = qqtApi.useHttp()
   ...
   //method:get/post/delete..
   //config ==axois conifg  //config可以是axios里面的配置包括参考axios官方文档
   qhttp.[method]({
      [...config]
       url:xxxx,
       data/parmas
   })
<script>
       
//在setup()使用
<script  lang="ts">    
    ...
   import qqtApi from '@qqt-product/api'
    ...
   export default defineComponent({
     ...
    const qhttp: qqtApi.Request = qqtApi.useHttp()
     ...
    return {
        qhttp
    }
   })
    
<script>
1.1)在V6环境下使用registerModuleAction
api包对比v5来说,多了一个核心api接口封装
基于v5整理了一些核心模块的接口统一放到了module里面包括:
login--登录模块
permission--用户权限模块
roles--角色模块
schedule--日历模块
system--系统信息模块
user--用户模块

接口模块

使用

//在setup组件函数下使用(仅仅展示在steup使用,其他方式参考上面)
<script setup lang="ts">
    ...
   import qqtApi from '@qqt-product/api'
    ...
   const qhttp: qqtApi.Request = qqtApi.useHttp()
   ...
     //模块/名称
     qhttp.registerModuleAction('login/getEncryptedKey')
     //名称-  里面已经做了处理,如果直接使用接口名称必须要存在不存在报错
     qhttp.registerModuleAction('getEncryptedKey')
     qhttp.registerModuleAction('login', {
     data: {
      loginType: null,
      elsAccount: '',
      subAccount: '',
      password: '',
      keyId: '',
    },
      })
    })
   
<script>
2.0)其他包用,基于引入到V6

仅仅展示setup组件函数

<script lang="ts" setup>
//TS
export interface RequestConfig extends MapObjectNoneType {
  baseURL?: string
  headers?: object
  timeout?: number
  url?: string
  params?: object
  data?: object
}

export interface ResponseData extends MapObjectNoneType {
  code?: number
  message?: string
  result?: any
  success?: boolean
}

export interface HttpClient {
  get(config: RequestConfig): Promise<ResponseData>
  post(config: RequestConfig): Promise<ResponseData>
}

 import {inject } from 'vue'    
 const qHttp = inject('http') as HttpClient
     
<script>
3.0)下载需要调整或者需要配置更多请求项目
qHttp[acType]({
      url: exportXlsUrl,
      params: params,
      data: params,
      responseType: 'blob',
    })
      .then((data) => {
        // responseType为blob的请求,统一获取错误信息
        if (data.type === 'application/json') {
          const fileReader = new FileReader()
          fileReader.onloadend = () => {
            const jsonData = JSON.parse(fileReader.result as string)
            message.error(jsonData.message)
          }
          fileReader.readAsText(data as Blob)
          return
        }
        if (!data) {
          message.error('文件下载失败')
          return
        }
        // if ('msSaveBlob' in window.navigator) {
        //   window.navigator.msSaveBlob(new Blob([data as Blob]), fileName + '.xls')
        // } else {
        const url = window.URL.createObjectURL(new Blob([data as Blob]))
        const link = document.createElement('a')
        link.style.display = 'none'
        link.href = url
        link.setAttribute('download', `${fileName}.${type}`)
        document.body.appendChild(link)
        link.click()
        document.body.removeChild(link) //下载完成移除元素
        window.URL.revokeObjectURL(url) //释放掉blob对象
        // }
      })
      .finally(() => {
        // confirmLoading.value = false
        afterCallback && afterCallback()
      })

其他配置

1)配置自定义接口模块(示例)

//创建一个customModule文件放自定义的接口模块

//列子-custom.ts 文件
import type { ApiModuleStyle } from '../types/index'
import qqtApi from '../../lib/index'
export default <ApiModuleStyle>{
  name: 'LoginModule',
  actions: {
    getEncryptedKey: (http: qqtApi.Request, data: unknown) => {
     ...
    },
    login: (http: qqtApi.Request, data: unknown) => {
     ...
    },
    loginByToken: (http: qqtApi.Request, params: unknown) => {
       ...
    },
    phoneLogin: (http: qqtApi.Request, data: unknown) => {
       ...
    },
    getSmsCaptcha: (http: qqtApi.Request, data: unknown) => {
    ...
    },
    getInfo: (http: qqtApi.Request, params: unknown) => {
       ...
    },
    getUserInfo: (http: qqtApi.Request, params: unknown) => {
       ...
    },
  },
}

//utils下的qApiPlugin.ts引入该文件
//import custom from './custom'
qqtApi.createApi({
   module:{custom}

})

2)多个错误只提示一个弹窗

//继承axiso请求配置
export interface AxiosRequestCustomConfig extends AxiosRequestConfig {
  module?: object //接口模块
  interceptors?: requestInterceptors
  responseInterceptorsCatchMsgFun?: (err: unknown) => boolean //响应错误
  requestInterceptorsCatchMsgFun?: (err: unknown) => boolean //请求错误
  isOpenShowErrorOneTip?: boolean
  responseInterceptorsAtfterCatchMsgFun?: (err: unknown) => boolean //响应成功,但是接口code5xx或者4xx提示
}

isOpenShowErrorOneTip:true的时候必须配置responseInterceptorsCatchMsgFun,requestInterceptorsCatchMsgFun,responseInterceptorsAtfterCatchMsgFun

建议响应拦截和请求拦截的报错放到对应的这个3个函数里面函数并返回Boolen类型

qqtApi.createApi({
 isOpenShowErrorOneTip: true,
 responseInterceptorsCatchMsgFun: (err) => {return Funerr(err)},
 requestInterceptorsCatchMsgFun: (err)=>{
 return true
 }
 responseInterceptorsAtfterCatchMsgFun:(result: any) =>{
 return resErr(result.data )
 }

})

3)520或者530报错后,取消后面的接口请求(最新V7)

api包

request.ts 优化

export class Request {
  $http: AxiosInstance
  source = axios.CancelToken.source()
  interceptors: requestInterceptors | undefined
  _options: AxiosRequestCustomConfig
  _actions: Map = Object.create(null)
  apiSets: Map = Object.create(null)
  ....
  
    //再次封装request方法
  request<Map>(config: AxiosRequestConfig): Promise<Map> {
    if (this.interceptors?.requestInterceptors) {
      config = this.interceptors.requestInterceptors({
        ...config,
        cancelToken: this.source.token,//(新增)
        headers: config.headers as AxiosRequestHeaders,
      })
    }
    return this.$http.request<any, Map>(config)
  }

}

SMR

qApiPlugin.ts调整

function Funerr(error: MapObject) {
  const token = getUserStoreToken()
  if (error.response) {
    switch (error.response.status) {
      case 403:
        message.warning(srmI18n('拒绝访问'))
        break
      case 520:
        if (token) {
          clearUserStore()
          api.source && api.source.cancel()//(新增)
          message.warning(
            {
              content: srmI18n('很抱歉,登录已过期,请重新登录!'),
            },
            2000,
          )
        }
        break
      case 404:
        message.warning(srmI18n('很抱歉,资源未找到!'))
        break
      case 504:
        message.warning(srmI18n('网络超时'))
        break
      case 401:
        message.warning(srmI18n('网络超时'))
        break
      default:
        message.warning(srmI18n('网络超时'))
        break
    }
  }
  return Promise.reject(error)
}

function loginAgainByCode(code: number) {
  const tips = code === 520 ? srmI18n('很抱歉,登录已过期,请重新登录!') : srmI18n('当前用户已被登出,请重新登录!')
  checkedWhileListAddress().then((res) => {
    if (!res) {
      const { setLogout } = useUserStore()
      setLogout().then(() => {
        api.source && api.source.cancel()//(新增)
      })

      if (RESPONSE_520_OR_530) {
        Modal.info({
          title: srmI18n('系统提示'),
          content: tips,
          okText: srmI18n('关闭'),
          onOk() {
            window.location.reload()
          },
        })
        RESPONSE_520_OR_530 = false
      }
    }
  })
}