Bootstrap

✅✅✅【Vue.js】sd.js基于jQuery Ajax最新原生完整版for凯哥API版本

 api.js

//封装ajax方法
import $g from "../sg";//vue项目使用
import $ from 'jquery';//(提示:原生开发页面请前往https://jquery.com下载最新版jQuery)
import { Message } from "element-ui";//element项目使用
// import axios from "axios";

// 不同环境的api配置项________________________________________________________________________________
let APIs = [
    { label: '师大外网服务器', origins: ['mzjyzyk.ynnu.edu.cn'], API_ROOT_URL: `https://mzjyzyk.ynnu.edu.cn/api`, isProduct: true, },
    { label: '师大内网服务器', origins: ['10.10.4.87'], API_ROOT_URL: `http://10.10.4.87:30107/api`, },
    { label: '维度云线上测试服务器运行', origins: ['wedoyun.cn'], API_ROOT_URL: `//sr.wedoyun.cn:82/api`, },
    {
        label: '本地个人开发环境', origins: ["localhost", "127.0.0.1", "192.168.", "172.20.", "0.0.0.0",], isLocal: true,
        API_ROOT_URL: "//192.168.1.108:9088/sr", //鲁宝键的IP
        // API_ROOT_URL = "//192.168.7.95:9088/sr", //无忌的IP
        API_ROOT_URL: "//sr.wedoyun.cn:82/api", //测试服务器的IP
    },
];
let API = APIs.find(({ origins }) => origins.some(v => location.href.includes(v))) || APIs[0];
let API_ROOT_URL = API.API_ROOT_URL;


//一些前提条件和方法________________________________________________________________________________
localStorage.sgAPI && (API_ROOT_URL = localStorage.sgAPI);//如果localStorage.sgAPI存在,优先用这个前缀路径接口访问所有接口
//跳转到登录页面
let jumpLoginPage = ({ url, } = {}) => {
    url && console.log(`导致跳转到登录页面的接口路径`, url);
    $g.cookie.clearAll();//清空所有缓存(需要引入sg.js)
    Object.keys(localStorage).forEach(k => delete localStorage[k]);//清空对象键、值
    location.hash.includes('/login') || location.replace(`${$g.getWebURLBeforeHash()}#/login?url=${encodeURIComponent(location.href)}`);
}
// 普通报错提示
let sgAlert = (d = `很抱歉,服务太火爆了,请您稍后再试!`) => Message ? Message({ message: d, dangerouslyUseHTMLString: true, showClose: true, }) : alert(d.replace(/\<br\>/g, `\n\r`));
// 接口报错提示
let errAlert = (url, d) => sgAlert(`💡可能是后端报错:<br>接口地址👉${url}<br>返回报文👉${JSON.stringify(d, null, 4)}`);
// 本地接口提示
$g.getUrlParam('isLocal') && (API.isLocal = true);//手动添加开发者开发模式
API.isLocal && sgAlert(`温馨提示:当前处于开发模式,接口访问地址为<b  style="color: #67C23A;" >${API_ROOT_URL}</b>`);

//主体请求________________________________________________________________________________
export default {
    // 共享变量、方法
    share: {
        // 一些共享出去的变量
        $g,
        isLocal: API.isLocal,//是否本地环境
        isProduct: API.isProduct,//是否生产环境
        API_ROOT_URL,//接口根路径
        imgPrePath: `${API_ROOT_URL}/`,//图片前缀

        // 一些共享出去的方法
        jumpLoginPage,
    },

    //API FUNCTION ________________________________________________________________________________
    do(url, data, r, otherConfig = {}) {
        if (!r) return sgAlert(`注意检查接口${url}的传参“r”是否写成了“doing”`);

        // delete (data || {}).sgLog;//清除调试状态下的接口标记说明字段
        
        let type = otherConfig.type;//请求方式,默认POST
        type || (type = 'POST');

        //noToken=true代表该接口无需保持登录状态就可以获取信息,譬如一些公共对外开放的接口
        let token = localStorage.token; //获取token
        if (!otherConfig.noToken) {
            //判断如果本地没有token,否者直接跳转到登录页面
            if (!token) return jumpLoginPage({ url });
        }

        // 建议:一般情况就不要设置headers了,ajax会根据data结构自动判断类型
        let headers = {
            // withCredentials: true ,// 这里可以设置withCredentials
            // 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',//FormData方式提交数据
            // 'Content-Type': 'application/json;charset=UTF-8',//报跨域错误
            ...otherConfig.headers,
        };

        //判断是否需要令牌字符串
        if (!otherConfig.noToken) {
            token && (headers["kkToken"] || (headers["kkToken"] = token));
            data || (data = {});
        }

        // 判断是否为下载接口,防止下载导出文件乱码
        let xhrFields = otherConfig.isDownload ? { xhrFields: { responseType: "arraybuffer" } } : null;

        // 判断是否为上传接口
        let contentType = {};
        if (otherConfig.isUpload) {
            /*
【警告!千万不要设置请求头格式!!! 用<form>表单上传的时候, 请求自动会设置为multipart/form-data; boundary=----WebKitFormBoundary****************】 
如果是用的ajax请求上传File文件必须设置最外层contentType:false,且删除headers中的“Content-Type”,
否则就会导致提示跨域或传参格式没有序列化变成类似:
------WebKitFormBoundaryxWRglNw6MXxmBjr2
Content-Disposition: form-data; name: "字段名1"
EBE29C03DA9E4FB4A050F4D820A834BC
------WebKitFormBoundaryxWRglNw6MXxmBjr2
Content-Disposition: form-data; name="字段名2"
这样的格式。
ajax正确参数如下:
$.ajax({
...
  headers:{不要有“Content-Type”},
  processData:false,//必须是false
  contentType:false,//必须是false
...
})
               */
            data = $g.convertObejct2FormData(data);//转换为multipart/form-data数据格式
            contentType = { contentType: false };//如果不设置就会导致提示跨域或传参格式没有序列化变成类似 ------WebKitFormBoundary... 的分隔请求体
        }

        // 序列化设置(是否将数据转换为查询字符串的形式发送给服务器,默认情况下,processData的值为true)
        let processData = true;
        (data instanceof FormData) && (processData = false);
        (typeof otherConfig.processData !== 'undefined') && (processData = otherConfig.processData);

        // 精简别名
        r.s && (r.success = r.s); //响应请求成功
        r.f && (r.fail = r.f); //响应请求失败
        r.e && (r.error = r.e); //响应请求报错
        r.l && (r.loading = r.l, r.loading.show && r.loading.show()); //加载
        $.ajax({
            timeout: 24 * 60 * 60 * 1000, //设置默认超时时间24小时
            type,
            url: otherConfig.isCompleteURL ? url : `${API_ROOT_URL}/${url}`,//使用完整接口路径
            data,//发送到服务器的数据。将自动转换为请求字符串格式。GET 请求中将附加在 URL 后。查看 processData 选项说明以禁止此自动转换。必须为 Key/Value 格式。如果为数组,jQuery 将自动为不同值对应同一个名称。如 {foo:["bar1", "bar2"]} 转换为 '&foo=bar1&foo=bar2'。
            headers,//请求头
            ...xhrFields,//设置XMLHttpRequest对象的属性
            ...contentType,//默认值: "application/x-www-form-urlencoded"。发送信息至服务器时内容编码类型。 默认值适合大多数情况。如果你明确地传递了一个 content-type 给 $.ajax() 那么它必定会发送给服务器(即使没有数据要发送)。
            processData,//默认值: true。默认情况下,通过data选项传递进来的数据,如果是一个对象(技术上讲只要不是字符串),都会处理转化成一个查询字符串,以配合默认内容类型 "application/x-www-form-urlencoded"。如果要发送 DOM 树信息或其它不希望转换的信息,请设置为 false。

            beforeSend: xhr => {
                /* 发送请求前运行的函数。
                发送请求前可修改 XMLHttpRequest 对象的函数,如添加自定义 HTTP 头。
                XMLHttpRequest 对象是唯一的参数。
                这是一个 Ajax 事件。如果返回 false 可以取消本次 ajax 请求。
                */
            },

            success: d => {
                if (url.toLocaleLowerCase().includes(`download`) && !otherConfig.hasOwnProperty('isDownload') && typeof d === 'string') return sgAlert(`注意检查接口${url}的“otherConfig”参数是否加入了“isDownload:true”配置"`);
                if (!otherConfig.isDownload && typeof d === 'string') return sgAlert(`请联系后端开发人员给接口${url}添加@RequestMapping(value=...produces="application/json)"`);

                // typeof d === 'string' && (d = JSON.parse(d));//如果是字符串返回值,则转换为JSON格式(如果用这句话就会纵容后端开发,导致以后复杂的返回报文也用字符串,会有问题!)

                r.loading && r.loading.close && r.loading.close(); //关闭加载
                if (otherConfig.isDownload) return r.success(d); //如果是下载,直接返回文档流
                switch (d.success) {
                    case true: //登录成功
                        r.success && r.success((otherConfig.isGetAllData || d.hasOwnProperty('totalCount')) ? d : (d.hasOwnProperty('data') ? d.data : d));
                        break;
                    case false: //登录失败(当token密码失效的时候)
                        r.fail ? r.fail(d) : sgAlert(d.msg);
                        break;
                }
                switch (d.code) {
                    case 403: //登录失败(当token密码失效的时候)
                    case 404: //登录失败(当token密码失效的时候)
                        jumpLoginPage(); r.fail ? r.fail(d) : sgAlert(d.msg);
                        break;
                    case -2: //扫码失败
                    case -1: //请求失败
                        r.fail ? r.fail(d) : sgAlert(d.msg);
                        break;
                }
            },

            error: d => {
                //请求报错
                if (d.status == 403) { jumpLoginPage(); return r.error ? r.error(d) : sgAlert(JSON.parse(d.responseText).msg); }
                r.loading && r.loading.close && r.loading.close(); //关闭加载
                r.error ? r.error(d) : errAlert(url, d);
                // console.log("【报错】" + JSON.stringify(d, null, 4),d);
            },

            complete: (xhr, status) => {
                //请求完成时运行的函数(在请求成功或失败之后均调用,即在 success 和 error 函数之后)。
            }
        });
    },
}

sd.js

//公共接口定义2024.11.22----------------------------------------
import $api from "./api";
import $g from "./sg";//vue项目使用
export default {

    ...$api.share,

 
    //【通用接口demo】________________________________________________________________________________
    API_NAME({ data, r, config }) { $api.do(`${API_ROOT_URL}/xxx/xxx/xxx`, data, r, config); },
 
    //提交用例________________________________________
    get({ r }) { $api.do(`${API_ROOT_URL}/get.do`, null, r, { isGetAllData: true, type:`get`, }); },
 
    post({ data, r }) { $api.do(`${API_ROOT_URL}/post.do`, data, r); },
 
    login({ data, r }) { $api.do(`${API_ROOT_URL}/post.do`, data, r, { noToken: true }); },
 
    download({ data, r }) { $api.do(`${API_ROOT_URL}/post.do`, data, r, { isDownload: true }); },

}

;