书接上回,接着填充。。。。
此篇包含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公共方法
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » Vue3 + TS + Antd + Pinia 从零搭建后台系统(二) Ajax + Router
发表评论 取消回复