axios.js 7.72 KB
/*
 * @Author: hookehuyr hookehuyr@gmail.com
 * @Date: 2022-05-28 10:17:40
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2025-09-30 22:25:22
 * @FilePath: /data-table/src/utils/axios.js
 * @Description:
 */
import axios from 'axios';
import router from '@/router';
import qs from 'Qs'
import { strExist } from '@/utils/tools'
// import { parseQueryString } from '@/utils/tools'
import { showToast } from 'vant';

axios.defaults.params = {
  f: 'custom_form',
};

//去重缓存数据
const onlyDataCache = {
  // 缓存列表
  list: [],
  // 根据请求的信息(请求方式,url,请求get/post数据),产生map的key
  getRequestKey(config) {
    const fullPath = config.headers["full-path"];
    const { method, url, params, data } = config;
    var key = [method, url].join("&");
    if (fullPath) {
      key = [method, url, JSON.stringify(params), JSON.stringify(data)].join("&");
    }
    return key;
  },
  query() {
    console.log("onlyData=" + JSON.stringify(this.list));
  },
  add(config, cancel) {
    var key = this.getRequestKey(config);
    //console.log("onlyData add key=" + key);
    var index = this.list.findIndex(t => t.key === key);
    if (index > -1) {
      this.list[index].cancel();
      this.list[index].cancel = cancel;
    } else {
      // 添加到缓存列表中
      this.list.push({ key, cancel });
    }
  },
  delete(config) {
    var targetKey = this.getRequestKey(config);
    //console.log("onlyData delete key=" + targetKey);
    if (targetKey) {
      this.list.forEach((item, index) => {
        if (item.key === targetKey) {
          item.cancel();
          this.list.splice(index, 1);
        }
      });
    }
  },
  deleteAll() {
    this.list.forEach((item, index) => {
      item.cancel();
      this.list.splice(index, 1);
    });
  }
};
// 持久化缓存数据
const keepDataCache = {
  // 缓存列表
  list: [],
  // 最大缓存数
  MAX_NUM: 50, // 从100减少到50,减少内存占用
  // 最大缓存时间
  EXPIRED_TIME: 30000, // 从60秒减少到30秒,更频繁地清理过期缓存
  // 根据请求的信息(请求方式,url,请求get/post数据),产生map的key
  getRequestKey(config) {
    const { method, url, params, data } = config;
    const key = [method, url, JSON.stringify(params), JSON.stringify(data)].join("&");
    return key;
  },
  query() {
    console.log("keepData=" + JSON.stringify(this.list));
  },
  // 添加缓存结果
  add({ config, data }) {
    if (config.data) config.data = JSON.parse(config.data);
    var key = this.getRequestKey(config);
    // console.log("keepData add key=" + key);
    var index = this.list.findIndex(t => t.key === key);
    if (index > -1) {
      // 保存请求结果
      this.list[index].data = data;
      this.list[index].time = Date.now(); // 更新时间戳
    } else {
      // 添加到缓存列表中
      this.list.push({ time: Date.now(), key, data });
    }

    // 主动清理过期缓存
    this.cleanExpiredCache();
  },
  // 查找缓存结果
  find(config) {
    // 根据请求信息生成key
    var key = this.getRequestKey(config);
    // console.log("keepData find key=" + key);
    var index = this.list.findIndex(t => t.key === key);
    // 判断缓存当中是否有该请求结果
    if (index > -1) {
      let data = this.list[index];
      // 判断是否超出了最大缓存时间
      if (Date.now() - data.time > this.EXPIRED_TIME) {
        // 清除该缓存
        this.list.splice(index, 1);
      } else {
        // 返回缓存
        return data;
      }
    }
    // 判断是否超出了最大缓存数量
    if (this.list.length >= this.MAX_NUM) {
      // 移除最旧的缓存项
      this.list.shift();
    }
    // 返回undefined,让请求拦截不执行config.adapter
    return undefined;
  },
  // 清理过期缓存的方法
  cleanExpiredCache() {
    const now = Date.now();
    this.list = this.list.filter(item => now - item.time <= this.EXPIRED_TIME);
  },
  // 清空所有缓存
  clear() {
    this.list = [];
  }
};

/**
 * @description 请求拦截器
 */
axios.interceptors.request.use(
  config => {
    // const url_params = parseQueryString(location.href);
    // GET请求默认打上时间戳,避免从缓存中拿数据。
    const timestamp = config.method === 'get' ? (new Date()).valueOf() : '';
    /**
     * POST PHP需要修改数据格式
     * 序列化POST请求时需要屏蔽上传相关接口,上传相关接口序列化后报错
     */
    // config.data = config.method === 'post' && !strExist(['a=upload', 'upload.qiniup.com'], config.url) ? qs.stringify(config.data) : config.data;

    // config.params.headers 合并到 config.headers 里面, 适配去重配置
    if (config.params.headers) {
      config.headers = { ...config.headers, ...config.params.headers };
      delete config.params.headers;
    }
    // 绑定默认请求头
    // config.params = { ...config.params, timestamp };
    // TAG:请求去重和持久化缓存
    if (config.headers) {
      //判断是否需要去重
      const onlyData = config.headers["only-data"];
      if (onlyData) {
        //console.log("删除接口onlyData缓存数据!");
        onlyDataCache.delete(config);

        config.cancelToken = new axios.CancelToken(cancel => {
        //console.log("保存接口onlyData缓存数据!");
          onlyDataCache.add(config, cancel);
        });
      }

      //判断是否需要持久化,并判断是否有缓存数据
      const keepData = config.headers["keep-data"];
      if (keepData) {
        // console.log("加载接口keepData缓存数据!");
        var cacheData = keepDataCache.find(config);
        // 查看缓存当中有没有
        if (cacheData) {
          // console.log("加载成功过,keepData有缓冲数据!");
          // 通过config.adapter,允许自定义处理请求
          config.adapter = function (config) {
            return new Promise(resolve => {
              const res = {
                status: 200,
                statusText: "",
                headers: { "content-type": "text/html; charset=UTF-8" },
                config,
                request: {}
              };
              resolve({ ...res, data: cacheData.data });
            });
          };
        }
      }
    }

    return config;
  },
  error => {
    // 请求错误处理
    return Promise.reject(error);
  });

/**
 * @description 响应拦截器
 */
axios.interceptors.response.use(
  response => {
    if (response.data.code === 402) { // 未授权状态
      // 特殊标识-带此标识报错不显示
      response.data.show = false;
      //
      showToast({
        message: '登录失效!将跳转到登录页面',
        duration: 1500,
        onClose: () => {
          window.location.href = location.origin + '/admin/';
        }
      })
    }

    // TAG:请求持久化缓存
    const config = response.config;
    if (response) {
      const keepData = config.headers["keep-data"];
      if (keepData) {
        if (response.status === 200 && response.data.code === 1) {
          if (
              response.headers["content-type"] &&
              response.headers["content-type"].indexOf("text/html; charset=UTF-8") >=
              0 &&
              response.request.responseType != "blob"
          ) {
            // console.log("保存接口keepData缓存数据!");
            //缓存结果到缓存中
            keepDataCache.add(response);
          }
        }
      }
    }

    return response;
  },
  error => {
    // TAG: 处理请求去重
    if (axios.isCancel(error)) { // 取消请求的情况下,终端Promise调用链
      return new Promise(() => {
        console.error('取消请求响应', error);
        return false;
      });
    } else { // 其他的错误处理
      return Promise.reject(error);
    }
  });

export default axios;