|
@@ -0,0 +1,385 @@
|
|
|
+import axios, {
|
|
|
+ AxiosError,
|
|
|
+ AxiosInstance,
|
|
|
+ AxiosRequestHeaders,
|
|
|
+ AxiosResponse,
|
|
|
+ InternalAxiosRequestConfig,
|
|
|
+} from "axios";
|
|
|
+import qs from "qs";
|
|
|
+import JSONbig from "json-bigint";
|
|
|
+// import Vue from "vue";
|
|
|
+import { merge } from "lodash-es";
|
|
|
+
|
|
|
+import { encryptBase64, encryptWithAes, generateAesKey, decryptWithAes, decryptBase64 } from "./crypto";
|
|
|
+import { encrypt, decrypt } from "./jsencrypt";
|
|
|
+// mock 不需要加密请求
|
|
|
+const encryptHeader = process.env.VUE_APP_MOCK ? "" : "cprams-encrypt-key"; //
|
|
|
+JSONbig({ storeAsString: true }); //所有超出安全范围的整数以字符串的形式存储
|
|
|
+// import Logger from "@/utils/Logger";
|
|
|
+// import errorCode from "./errorCode";
|
|
|
+// 需要忽略的提示。忽略后,自动 Promise.reject('error')
|
|
|
+const ignoreMsgs = [
|
|
|
+ "无效的刷新令牌", // 刷新令牌被删除时,不用提示
|
|
|
+ "刷新令牌已过期", // 使用刷新令牌,刷新获取新的访问令牌时,结果因为过期失败,此时需要忽略。否则,会导致继续 401,无法跳转到登出界面
|
|
|
+];
|
|
|
+// 默认超时时间
|
|
|
+axios.defaults.timeout = process.env.NODE_ENV === "development" ? 60 * 1000 : 20 * 1000;
|
|
|
+// 相对路径设置
|
|
|
+axios.defaults.baseURL = process.env.VUE_APP_API_PREFIX;
|
|
|
+
|
|
|
+//数据未加密时生效
|
|
|
+axios.defaults.transformResponse = [
|
|
|
+ function (data) {
|
|
|
+ try {
|
|
|
+ // 如果转换成功则返回转换的数据结果
|
|
|
+ return JSONbig.parse(data);
|
|
|
+ } catch (err) {
|
|
|
+ // 如果转换失败,则包装为统一数据格式并返回
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+ },
|
|
|
+];
|
|
|
+
|
|
|
+axios.defaults.transformRequest = [
|
|
|
+ function (data) {
|
|
|
+ return JSONbig.stringify(data);
|
|
|
+ },
|
|
|
+];
|
|
|
+//数据未加密时生效end
|
|
|
+
|
|
|
+// http request 拦截器
|
|
|
+axios.interceptors.request.use(
|
|
|
+ async (config) => {
|
|
|
+ // 设置参数
|
|
|
+ if (!config.headers["Content-Type"]) {
|
|
|
+ config.headers["Content-Type"] = "application/json";
|
|
|
+ }
|
|
|
+
|
|
|
+ const params = config.params || {};
|
|
|
+ const data = config.data || false;
|
|
|
+
|
|
|
+ // 是否需要加密
|
|
|
+ // Logger.log("请求拦截器: config", config);
|
|
|
+
|
|
|
+ let isEncrypt = (config.headers || {})?.isEncrypt === "true";
|
|
|
+ // JSONbig.stringify(data);
|
|
|
+
|
|
|
+ if (
|
|
|
+ config.method?.toUpperCase() === "POST" &&
|
|
|
+ (config.headers as AxiosRequestHeaders)["Content-Type"] === "application/x-www-form-urlencoded"
|
|
|
+ ) {
|
|
|
+ config.data = qs.stringify(data);
|
|
|
+ }
|
|
|
+ // get参数编码
|
|
|
+ if (config.method?.toUpperCase() === "GET" && params) {
|
|
|
+ let url = config.url + "?";
|
|
|
+ for (const propName of Object.keys(params)) {
|
|
|
+ const value = params[propName];
|
|
|
+ if (value) {
|
|
|
+ if (typeof value === "object") {
|
|
|
+ for (const val of Object.keys(value)) {
|
|
|
+ const params = propName + "[" + val + "]";
|
|
|
+ const subPart = encodeURIComponent(params) + "=";
|
|
|
+ url += subPart + encodeURIComponent(value[val]) + "&";
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ url += `${propName}=${encodeURIComponent(value)}&`;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 给 get 请求加上时间戳参数,避免从缓存中拿数据
|
|
|
+ // const now = new Date().getTime()
|
|
|
+ // params = params.substring(0, url.length - 1) + `?_t=${now}`
|
|
|
+ url = url.slice(0, -1);
|
|
|
+ config.params = {};
|
|
|
+ config.url = url;
|
|
|
+ }
|
|
|
+ // 当开启参数加密
|
|
|
+ // Logger.log("开启参数加密:isEncrypt:" + isEncrypt);
|
|
|
+ console.log(config.data);
|
|
|
+ if (isEncrypt && encryptHeader && (config.method === "post" || config.method === "put")) {
|
|
|
+ // 生成一个 AES 密钥
|
|
|
+ const aesKey = generateAesKey();
|
|
|
+ const mergeKey = encrypt(encryptBase64(aesKey)) as any;
|
|
|
+ config.headers[encryptHeader] = mergeKey;
|
|
|
+ config.data =
|
|
|
+ typeof config.data === "object"
|
|
|
+ ? encryptWithAes(JSONbig.stringify(config.data), aesKey)
|
|
|
+ : encryptWithAes(config.data, aesKey);
|
|
|
+ }
|
|
|
+ Reflect.deleteProperty(config.headers, "isEncrypt");
|
|
|
+
|
|
|
+ return config;
|
|
|
+ },
|
|
|
+ (err) => {
|
|
|
+ return Promise.reject(err);
|
|
|
+ }
|
|
|
+);
|
|
|
+// response 拦截器
|
|
|
+axios.interceptors.response.use(
|
|
|
+ async (response: AxiosResponse<any>) => {
|
|
|
+ const { data, status } = response;
|
|
|
+ // 加密后的 AES 秘钥
|
|
|
+ const keyStr = response.headers[encryptHeader];
|
|
|
+ // 加密
|
|
|
+ if (keyStr) {
|
|
|
+ const data = response.data;
|
|
|
+ // 请求体 AES 解密
|
|
|
+ const base64Str = decrypt(keyStr);
|
|
|
+ // base64 解码 得到请求头的 AES 秘钥
|
|
|
+ const aesKey = decryptBase64(base64Str.toString());
|
|
|
+ // console.log('---aesKey', base64Str,data, aesKey)
|
|
|
+ // aesKey 解码 data
|
|
|
+ const decryptData = decryptWithAes(data, aesKey);
|
|
|
+ // console.log('---decryptData', decryptData)
|
|
|
+ // 将结果 (得到的是 JSON 字符串) 转为 JSON
|
|
|
+ //转繁体
|
|
|
+ response.data = JSONbig.parse(decryptData);
|
|
|
+ console.log(response.config.url, response.data);
|
|
|
+ }
|
|
|
+ if (!data) {
|
|
|
+ // 返回“[HTTP]请求没有返回值”;
|
|
|
+ console.log("!data");
|
|
|
+ throw new Error();
|
|
|
+ }
|
|
|
+ // const { t } = useI18n()
|
|
|
+ // 未设置状态码则默认成功状态
|
|
|
+ const code = data.status || 200;
|
|
|
+ // 二进制数据则直接返回
|
|
|
+ if (response.request.responseType === "blob" || response.request.responseType === "arraybuffer") {
|
|
|
+ return response.data;
|
|
|
+ }
|
|
|
+ // 获取错误信息
|
|
|
+ const msg = data.msg
|
|
|
+ // || errorCode[code] || errorCode["default"];
|
|
|
+ if (ignoreMsgs.indexOf(msg) !== -1) {
|
|
|
+ // 如果是忽略的错误码,直接返回 msg 异常
|
|
|
+ return Promise.reject(msg);
|
|
|
+ } else if (code === 401) {
|
|
|
+ // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
|
|
|
+ // Vue.prototype.$message({
|
|
|
+ // message: msg || "令牌过期",
|
|
|
+ // type: "error",
|
|
|
+ // });
|
|
|
+ } else if (code === 500) {
|
|
|
+ // ElMessage.error(t('sys.api.errMsg500'))
|
|
|
+ // Vue.prototype.$message({
|
|
|
+ // message: msg || "接口异常",
|
|
|
+ // type: "error",
|
|
|
+ // });
|
|
|
+ return Promise.reject(new Error(msg));
|
|
|
+ } else if (code === -2) {
|
|
|
+ return Promise.reject(new Error(code));
|
|
|
+ } else {
|
|
|
+ return response;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ (error: any) => {
|
|
|
+ // 中断请求不提示
|
|
|
+ if (axios.isCancel(error)) {
|
|
|
+ return new Promise(() => {});
|
|
|
+ }
|
|
|
+ if (error.name === "AxiosError") {
|
|
|
+ console.error(error.message);
|
|
|
+ }
|
|
|
+ return Promise.reject({ status: 500 });
|
|
|
+
|
|
|
+ if (error.response) {
|
|
|
+ switch (error.response.status) {
|
|
|
+ case 401:
|
|
|
+ // router.replace({
|
|
|
+ // path: publicPath + "login",
|
|
|
+ // query: { redirect: router.currentRoute.fullPath }
|
|
|
+ // });
|
|
|
+ // window.localStorage.clear();
|
|
|
+ break;
|
|
|
+ case 500:
|
|
|
+ // Vue.prototype.$message({
|
|
|
+ // message: error.response.data.resp_msg || "服务器异常",
|
|
|
+ // type: "error",
|
|
|
+ // });
|
|
|
+ break;
|
|
|
+ case 502:
|
|
|
+ // Vue.prototype.$message({
|
|
|
+ // message: error.response.data.resp_msg || "接口异常",
|
|
|
+ // type: "error",
|
|
|
+ // });
|
|
|
+ break;
|
|
|
+ case 400:
|
|
|
+ // Vue.prototype.$message({
|
|
|
+ // message: error.response.data.resp_msg || "请求失败",
|
|
|
+ // type: "error",
|
|
|
+ // timeout: 1300, // 显示毫秒数
|
|
|
+ // });
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Promise.reject(error);
|
|
|
+ }
|
|
|
+);
|
|
|
+export function put(url, data = {}, config = {}) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ axios.put(url, data, config).then(
|
|
|
+ (response) => {
|
|
|
+ if (response.status === 200) {
|
|
|
+ resolve(response.data);
|
|
|
+ } else {
|
|
|
+ // Vue.prototype.$message({
|
|
|
+ // message: response.statusText || "接口异常",
|
|
|
+ // type: "error",
|
|
|
+ // });
|
|
|
+ // Vue.prototype.$message.error(response.statusText)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ (err) => {
|
|
|
+ return reject(err);
|
|
|
+ }
|
|
|
+ );
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function initConfig({ config, reqType }) {
|
|
|
+ let contentType = ``;
|
|
|
+ switch (reqType) {
|
|
|
+ case "json":
|
|
|
+ contentType = "application/json";
|
|
|
+ break;
|
|
|
+ case "file":
|
|
|
+ contentType = "multipart/form-data";
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ contentType = "application/x-www-form-urlencoded;charset=utf-8";
|
|
|
+ }
|
|
|
+
|
|
|
+ config = merge(
|
|
|
+ {
|
|
|
+ headers: {
|
|
|
+ "Content-Type": contentType,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ config
|
|
|
+ );
|
|
|
+ return config;
|
|
|
+}
|
|
|
+
|
|
|
+export function post(url, data = {}, config = {}, reqType) {
|
|
|
+ config = initConfig({ reqType, config });
|
|
|
+
|
|
|
+ if (data instanceof FormData === false) {
|
|
|
+ data = reqType === "json" ? JSONbig.stringify(data) : qs.stringify({ ...data });
|
|
|
+ }
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ axios.post(url, data, config).then(
|
|
|
+ (response) => {
|
|
|
+ if (response.status === 200) {
|
|
|
+ resolve(response.data);
|
|
|
+ } else {
|
|
|
+ // Vue.prototype.$message({
|
|
|
+ // message: response.statusText || "接口异常",
|
|
|
+ // type: "error",
|
|
|
+ // });
|
|
|
+ reject(response);
|
|
|
+ // Vue.prototype.$message.error(response.statusText)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ (err) => {
|
|
|
+ reject(err);
|
|
|
+ }
|
|
|
+ );
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+export function postRaw(url, data = {}, config: any = {}, reqType = "json") {
|
|
|
+ config = initConfig({ reqType, config });
|
|
|
+ // JSONbig.stringify(data)
|
|
|
+ data = JSONbig.stringify(data);
|
|
|
+
|
|
|
+ return axios.post(url, data, config).then(
|
|
|
+ (response) => {
|
|
|
+ if (response.status === 200) {
|
|
|
+ return response.data;
|
|
|
+ } else {
|
|
|
+ // Vue.prototype.$message({
|
|
|
+ // message: response.statusText || "接口异常",
|
|
|
+ // type: "error",
|
|
|
+ // });
|
|
|
+ // Vue.prototype.$message.error(response.statusText)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ (err) => {
|
|
|
+ // return Promise.reject(err);
|
|
|
+ }
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+export function get(url, data = {}, config = {}) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ axios.get(url + "?" + qs.stringify(data?.data || {}), config).then(
|
|
|
+ (response) => {
|
|
|
+ if (response.status === 200) {
|
|
|
+ resolve(response.data);
|
|
|
+ } else {
|
|
|
+ // Vue.prototype.$message({
|
|
|
+ // message: response.statusText || "接口异常",
|
|
|
+ // type: "error",
|
|
|
+ // });
|
|
|
+ // Vue.prototype.$message.error(response.statusText)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ (err) => {
|
|
|
+ reject(err);
|
|
|
+ }
|
|
|
+ );
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+export function del(url, data = {}, config = {}) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ axios.delete(url, { data, ...config }).then(
|
|
|
+ (response) => {
|
|
|
+ if (response.status === 200) {
|
|
|
+ resolve(response.data);
|
|
|
+ } else {
|
|
|
+ // Vue.prototype.$message({
|
|
|
+ // message: response.statusText || "接口异常",
|
|
|
+ // type: "error",
|
|
|
+ // });
|
|
|
+ // Vue.prototype.$message.error(response.statusText)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ (err) => {
|
|
|
+ reject(err);
|
|
|
+ }
|
|
|
+ );
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+export function form(url: string, formData: object = {}, config: object = {}) {
|
|
|
+ let allConfig = Object.assign(
|
|
|
+ {
|
|
|
+ headers: {
|
|
|
+ "Content-Type": "multipart/form-data",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ config
|
|
|
+ );
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ axios.post(url, formData, allConfig).then(
|
|
|
+ (response) => {
|
|
|
+ if (response.status === 200) {
|
|
|
+ resolve(response.data);
|
|
|
+ } else {
|
|
|
+ // Vue.prototype.$message({
|
|
|
+ // message: response.statusText || "接口异常",
|
|
|
+ // type: "error",
|
|
|
+ // });
|
|
|
+ // Vue.prototype.$message.error(response.statusText)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ (err) => {
|
|
|
+ return Promise.reject(err);
|
|
|
+ }
|
|
|
+ );
|
|
|
+ });
|
|
|
+}
|