axios+mockjs+vite-plugin-mock实现本地数据mock

599 阅读3分钟

说明:axios负责发送请求,mockjs负责便捷构造响应数据,vite-plugin-mock负责代理axios数据请求到本地

npm i axios mockjs vite-plugin-mock

2.封装axios

import axios from 'axios'
import type {
  AxiosInstance,
  AxiosError,
  AxiosRequestConfig,
  AxiosResponse,
} from 'axios'
import { ElMessage } from 'element-plus'
import { useUserStore } from '@/store/modules/user'
import { ResultEnum } from '@/enums/httpEnums'
import { ResultData } from './type'
import { LOGIN_URL } from '@/config/config'
import { RESEETSTORE } from '../reset'
import router from '@/router'
import { initMock } from '@/mock/mock'
export enum ResultEnum {
  SUCCESS = 200,
  EXPIRE = 203,
  ERROR = -1,
  ERRMESSAGE = '请求失败',
  TIMEOUT = 25000,
  TYPE = 'success',
}
export const service: AxiosInstance = axios.create({
  // 判断环境设置不同的baseURL
  baseURL:
    import.meta.env.NODE_ENV === 'development'
      ? import.meta.env.VITE_APP_BASE_API
      : import.meta.env.VITE_APP_BASE_URL,
  timeout: ResultEnum.TIMEOUT as number,
})
// 根据配置决定是否使用mock
if (import.meta.env?.VITE_APP_OPEN_MOCK?.toString() === 'true') {
  // 初始化mock,axios-mock-adapter
  console.log('初始化mock,axios-mock-adapter')
  initMock(service)
}
​
/**
 * @description: 请求拦截器
 * @returns {*}
 */
service.interceptors.request.use(
  (config) => {
    const userStore = useUserStore()
    const token = userStore.token
    if (token) {
      config.headers.token = token
    }
    return config
  },
  (error: AxiosError) => {
    ElMessage.error(error.message)
    return Promise.reject(error)
  },
)
/**
 * @description: 响应拦截器
 * @returns {*}
 */
service.interceptors.response.use(
  (response: AxiosResponse) => {
    const { data } = response
    // * 登陆失效(code == 203)
    if (data.code === ResultEnum.EXPIRE) {
      RESEETSTORE()
      ElMessage.error(data.message || ResultEnum.ERRMESSAGE)
      router.replace(LOGIN_URL)
      return Promise.reject(data)
    }
​
    if (data.code && data.code !== ResultEnum.SUCCESS) {
      ElMessage.error(data.message || ResultEnum.ERRMESSAGE)
      return Promise.reject(data)
    }
    return data
  },
  (error: AxiosError) => {
    // 处理 HTTP 网络错误
    let message = ''
    // HTTP 状态码
    const status = error.response?.status
    switch (status) {
      case 401:
        message = 'token 失效,请重新登录'
        break
      case 403:
        message = '拒绝访问'
        break
      case 404:
        message = '请求地址错误'
        break
      case 500:
        message = '服务器故障'
        break
      default:
        message = '网络连接故障'
    }
​
    ElMessage.error(message)
    return Promise.reject(error)
  },
)
​
/**
 * @description: 导出封装的请求方法
 * @returns {*}
 */
const http = {
  get<T>(
    url: string,
    params?: object,
    config?: AxiosRequestConfig,
  ): Promise<ResultData<T>> {
    return service.get(url, { params, ...config })
  },
​
  post<T>(
    url: string,
    data?: object,
    config?: AxiosRequestConfig,
  ): Promise<ResultData<T>> {
    return service.post(url, data, config)
  },
​
  put<T>(
    url: string,
    data?: object,
    config?: AxiosRequestConfig,
  ): Promise<ResultData<T>> {
    return service.put(url, data, config)
  },
​
  delete<T>(
    url: string,
    data?: object,
    config?: AxiosRequestConfig,
  ): Promise<ResultData<T>> {
    return service.delete(url, { data, ...config })
  },
}
​
export default http
​

额外封装一个用于mock请求的axios,这样可以很方便针对请求单独进行mock,而不必全局mock,与上面的axios实例唯一不同的就是baseUrl蚕食,这个/mock,与下面的all.ts最后暴露出去的数组前缀相关联,最后只要是使用这个axios的请求的都会从mock请求

图片.png

3.具体实现代码

目录结构:

图片.png

1._utils.ts

/**
 * @description: 响应结果
 * @argument SUCCESS  请求成功
 * @argument EXPIRE   token请求失效或校验失败
 * @argument ERROR    请求错误
 * @argument TIMEOUT  请求超时
 * @argument TYPE     请求类型
 */
export const ResultEnum = {
  SUCCESS: 200,
  EXPIRE: [501, 601, 602],
  ERROR: -1,
  ERRMESSAGE: '请求失败',
  TIMEOUT: 25000,
  TYPE: 'success',
} as const;

/**
 * @description: 错误响应结构
 * @returns {*}
 */
export function resultError(message = 'Request failed', { code = ResultEnum.ERROR, data = null } = {}) {
  return {
   code,
   data,
   msg:message,
   type: 'error',
  };
}

/**
 * @description: 成功响应结构
 * @returns {*}
 */
export function resultSuccess<T>(data: T, { message = 'ok' } = {}) {
  return {
   code: ResultEnum.SUCCESS,
   data,
   msg:message,
   type: 'success',
  };
}

export interface requestParams {
  method: string;
  body: any;
  headers?: { token?: string };
  query: any;
}

/**
 * @description 本函数用于从request数据中获取token,请根据项目的实际情况修改
 * @return token
 */

export function getRequestToken({ headers }: requestParams): string | undefined {
  return headers?.token;
}

2._all.ts

import { resultSuccess } from './_utils';
import Mock from 'mockjs';
import { MockMethod } from 'vite-plugin-mock'

const mockApiList = [
  //#region <视频图像标注>
  // 获取摄像头项目列表
  {
    url: "/admin/cameraProject/cameraList/:projectId",
    method: "get",
    response: (request: any) => {
      const { data } = Mock.mock({
        "data|4-10": [
          {
            "cameraId|+1": 1,
            "cameraName": "@ctitle(5, 10)",

          }
        ]
      });
      return resultSuccess(data);
    }
  },
  // 视频抽取图片
  {
    url: "/admin/cameraDailyVideo/extract",
    method: "post",
    response: (request: any) => {
      return resultSuccess(
        {
          "businessFilesId": 1,
          "url": "@image(1920x1080)",
          "fullUrl": "@image(1920x1080)",
          "pictureName": "@ctitle(5, 10)",
          "pictureTime": "@date(yyyy-MM-dd)",
          "cameraName": "@ctitle(5, 10)",
        }
      );
    }
  },
  // 新增班组影像
  {
    url: "/admin/teamPicture/addTeamPicture",
    method: "post",
    response: (request: any) => {
      return resultSuccess(
        true
      );
    }
  },
  //#endregion
] as MockMethod[];
export default mockApiList.map((item: any) => {
  return {
   ...item,
   url: `/mock${item.url}`,
  };
});

4.配置vite.config

增加配置

// 便于复制
import { viteMockServe } from 'vite-plugin-mock';
viteMockServe(),

图片.png