书接上回,接着填充。。。。

此篇包含Ajax请求及router路由填充

准备工作,在src文件夹下创建文件夹:api axios router

axios填充

规范一点,我们使用interface规范数据,创建一个types文件

import type {
  InternalAxiosRequestConfig,
  AxiosResponse,
  AxiosRequestConfig,
  AxiosInstance,
  AxiosRequestHeaders,
  AxiosError,
} from "axios";

interface RequestInterceptors<T> {
  // 请求拦截
  requestInterceptors?: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig;
  requestInterceptorsCatch?: (err: any) => any;
  // 响应拦截
  responseInterceptors?: (config: T) => T;
  responseInterceptorsCatch?: (err: any) => any;
}

interface RequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
  interceptors?: RequestInterceptors<T>;
}

export {
  AxiosResponse,
  RequestInterceptors,
  RequestConfig,
  AxiosInstance,
  InternalAxiosRequestConfig,
  AxiosRequestHeaders,
  AxiosError,
};

在asios文件夹下创建 service.ts 、index.ts、 config.ts

service.ts

import axios from "axios";
import { defaultRequestInterceptors, defaultResponseInterceptors } from "./config";
import { AxiosInstance, InternalAxiosRequestConfig, RequestConfig, AxiosResponse } from "./types";
import { message } from "ant-design-vue";
import { useUserStoreWithOut } from "../pinia/user";
import Spin from "@/components/SpinLoader/loading";
// 获取配置的请求路径baseURL
export const PATH_URL = import.meta.env.VITE_API_BASE_PATH;

const abortControllerMap: Map<string, AbortController> = new Map();

const axiosInstance: AxiosInstance = axios.create({
  timeout: 60000,
  baseURL: PATH_URL,
});

axiosInstance.interceptors.request.use((res: InternalAxiosRequestConfig) => {
  Spin.show(); // 后续文章中具体写全局loading设置
  const controller = new AbortController();
  const url = res.url || "";
  res.signal = controller.signal;
  abortControllerMap.set(url, controller);
  return res;
});

axiosInstance.interceptors.response.use(
  (res: AxiosResponse) => {
    Spin.hide();
    const url = res.config.url || "";
    abortControllerMap.delete(url);
    // 这里不能做任何处理,否则后面的 interceptors 拿不到完整的上下文了
    return res;
  },
  (error: any) => {
    Spin.hide();
    // 根据后端返回的值进行区分
    console.log("err: " + error); // for debug
    if (error?.response?.data?.status === 401) {
      const userStore = useUserStoreWithOut();
      userStore.logout();// 后续更新公共方法及全局方法
      message.error("登录失效,请重新登录");
    } else if (error?.response?.data?.status === 400) {
      message.error("数据有误,请确认正确后重新操作");
    } else {
      message.error(error.message);
    }
    return Promise.reject(error);
  }
);

axiosInstance.interceptors.request.use(defaultRequestInterceptors);
axiosInstance.interceptors.response.use(defaultResponseInterceptors);

const service = {
  request: (config: RequestConfig) => {
    return new Promise((resolve, reject) => {
      if (config.interceptors?.requestInterceptors) {
        config = config.interceptors.requestInterceptors(config as any);
      }

      axiosInstance
        .request(config)
        .then((res) => {
          resolve(res);
        })
        .catch((err: any) => {
          reject(err);
        });
    });
  },
  cancelRequest: (url: string | string[]) => {
    const urlList = Array.isArray(url) ? url : [url];
    for (const _url of urlList) {
      abortControllerMap.get(_url)?.abort();
      abortControllerMap.delete(_url);
    }
  },
  cancelAllRequest() {
    for (const [_, controller] of abortControllerMap) {
      controller.abort();
    }
    abortControllerMap.clear();
  },
};

export default service;

index.ts

import service from "./service";
import { useUserStoreWithOut } from "../pinia/user";
import { RawAxiosRequestHeaders } from "axios";

type AxiosMethod = "get" | "post" | "delete" | "put";

type AxiosResponseType = "arraybuffer" | "blob" | "document" | "json" | "text" | "stream";
interface AxiosConfig {
  params?: any;
  data?: any;
  url?: string;
  method?: AxiosMethod;
  headers?: RawAxiosRequestHeaders;
  responseType?: AxiosResponseType;
}
interface IResponse<T = any> {
  code: number;
  data: T extends any ? T : T & any;
}
// userStore.getToken在后续文章中介绍Pinia中更新获取token的方法
const request = (option: AxiosConfig) => {
  const { url, method, params, data, headers, responseType } = option;
  const userStore = useUserStoreWithOut();
  return service.request({
    url: url,
    method,
    params,
    data,
    responseType: responseType,
    headers: {
      "Content-Type": "application/json;charset=UTF-8",
      [userStore.getTokenKey ?? "Authorization"]: userStore.getToken ?? "",
      token: userStore.getToken ?? "",
      ...headers,
    },
  });
};

export default {
  get: <T = any>(option: AxiosConfig) => {
    return request({ method: "get", ...option }) as Promise<IResponse<T>>;
  },
  post: <T = any>(option: AxiosConfig) => {
    return request({ method: "post", ...option }) as Promise<IResponse<T>>;
  },
  delete: <T = any>(option: AxiosConfig) => {
    return request({ method: "delete", ...option }) as Promise<IResponse<T>>;
  },
  put: <T = any>(option: AxiosConfig) => {
    return request({ method: "put", ...option }) as Promise<IResponse<T>>;
  },
  cancelRequest: (url: string | string[]) => {
    return service.cancelRequest(url);
  },
  cancelAllRequest: () => {
    return service.cancelAllRequest();
  },
};

config.ts

import { AxiosResponse, InternalAxiosRequestConfig } from "./types";
import { message } from "ant-design-vue";
import qs from "qs";
import { useUserStoreWithOut } from "../pinia/user";

const defaultRequestInterceptors = (config: InternalAxiosRequestConfig) => {
  if (
    config.method === "post" &&
    config.headers["Content-Type"] === "application/x-www-form-urlencoded"
  ) {
    config.data = qs.stringify(config.data);
  }
  if (config.method === "get" && config.params) {
    let url = config.url as string;
    url += "?";
    const keys = Object.keys(config.params);
    for (const key of keys) {
      if (config.params[key] !== void 0 && config.params[key] !== null) {
        url += `${key}=${encodeURIComponent(config.params[key])}&`;
      }
    }
    url = url.substring(0, url.length - 1);
    config.params = {};
    config.url = url;
  }
  return config;
};

const defaultResponseInterceptors = (response: AxiosResponse) => {
  if (response?.config?.responseType === "blob") {
    // 如果是文件流,直接过
    return response;
  } else if (response.data.code === 0) {
    return response.data;
  } else {
    message.error(response?.data?.message);
    if (response?.data?.code === 401) {
      const userStore = useUserStoreWithOut();
      userStore.logout();
    }
  }
};

export { defaultResponseInterceptors, defaultRequestInterceptors };

router填充

index.ts

import { createRouter, createWebHashHistory } from "vue-router";
import type { RouteRecordRaw } from "vue-router";
import type { App } from "vue";
import Layout from "@/views/Layout/index.vue";
const constantRouterMap = [
  {
    path: "/",
    name: "Login",
    component: () => import("@/views/Login/index.vue"),
    meta: {
      title: "登录",
      keepAlive: true,
      hideInMenu: true,
    },
  },
  {
    path: "/401",
    name: "error_401",
    meta: {
      hideInMenu: true,
    },
    component: () => import("@/views/ErrorPage/401.vue"),
  },
  {
    path: "/500",
    name: "error_500",
    meta: {
      hideInMenu: true,
    },
    component: () => import("@/views/ErrorPage/500.vue"),
  },
  {
    path: "/AAA",
    name: "AAA",
    redirect: "/AAA/BBB",
    component: Layout,
    meta: {
      title: "目录",
    },
    children: [
      {
        path: "BBB",
        name: "BBB",
        component: () => import("@/views/AAA/BBB/index.vue"),
        meta: {
          title: "目录1",
          keepAlive: true,
        },
      },
      {
        path: "CCC",
        component: () => import("@/views/AAA/CCC/index.vue"),
        name: "ChangeRecord",
        meta: {
          title: "目录2",
          keepAlive: true,
        },
      },
      {
        path: "DDD",
        component: () => import("@/views/AAA/DDD/index.vue"),
        name: "UploadManage",
        meta: {
          title: "目录3",
          keepAlive: true,
        },
      },
    ],
  },
];
const router = createRouter({
  history: createWebHashHistory(),
  strict: true,
  routes: constantRouterMap as RouteRecordRaw[],
  scrollBehavior: () => ({ left: 0, top: 0 }),
}) as any;

export const setupRouter = (app: App<Element>) => {
  app.use(router);
};

export default router;

本篇结束,后面文章继续填充:Pinia + Utils公共方法

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部