
static/js/sgConfig.js在index.html的head/head里面要加入script srcstatic/js/sgConfig.js/scriptlet sgConfig { /* api说明 ---------------------------------------- label 接口描述 origins 被访问的域名 API_ROOT_URL 接口公共路径 isProduct 生产环境 isLocal 本地测试环境 ---------------------------------------- */ api: [ { label: **外网服务器, origins: [***.edu.cn], API_ROOT_URL: https://***.edu.cn/api, isProduct: true }, { label: **内网服务器, origins: [10.10.4.*], API_ROOT_URL: http://10.10.4.*:30107/api }, { label: **线上测试服务器运行, origins: [***.cn], API_ROOT_URL: //sr.***.cn:82/api }, { label: 本地个人开发环境, origins: [localhost, 127.0.0.1, 192.168., 172.20., 0.0.0.0], isLocal: true, API_ROOT_URL: //sr.***.cn:82/api, } ] };src/js/api/api.js//封装ajax方法 import $g from ../sg;//vue项目使用 import $d from ./sd;//vue项目使用 import $ from jquery; //(提示原生开发页面请前往https://jquery.com下载最新版jQuery) import { Message } from element-ui;//element项目使用 // import axios from axios; let config sgConfig;//外部动态可配置文件 //一些前提条件和方法________________________________________________________________________________ //跳转到登录页面 let jumpLoginPage ({ url, } {}) { if (window.dontJumpLoginPage) return;//全局屏蔽token失效跳转到登录页 url console.warn(导致跳转到登录页面的接口${url}%c提示请确保是否加入了noToken:true, box-sizing: border-box;padding: 5px 10px;border-radius: 88px;background-color: #E6A23C33;margin: 5px;); $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)}); // 一些共享出去的方法 window.jumpLoginPage jumpLoginPage; //主体请求________________________________________________________________________________ let doApi ({ config, url, data, r, otherConfig {}, }) { // 不同环境的api配置项---------------------------------------- let APIs config.api; 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存在优先用这个前缀路径接口访问所有接口 // 本地接口提示---------------------------------------- $g.getUrlParam(isLocal) (API.isLocal true);//手动添加开发者开发模式 // 共享变量、方法---------------------------------------- // 一些共享出去的变量 $d.isLocal API.isLocal;//是否本地环境 $d.isProduct API.isProduct;//是否生产环境 $d.API_ROOT_URL API_ROOT_URL;//接口根路径 $d.API API;//当前接口配置文件 window.API API;//当前接口配置文件全局变量 $d.config config;//全量配置文件 // 开始请求---------------------------------------- $g.showCopryRightConsole();//在控制台打印酷炫的版权所有 if (!r) return sgAlert(注意检查接口${url}的传参“r”是否写成了“doing”); localStorage.deleteSgLog delete (data || {}).sgLog;//清除调试状态下的接口标记说明字段 let type otherConfig.type || POST;//请求方式默认POST otherConfig.retainNullUndefined || Object.keys(data || {}).forEach(k data[k] null delete data[k]);//去掉值为null或undefined的对象属性 //noTokentrue代表该接口无需保持登录状态就可以获取信息譬如一些公共对外开放的接口 let token localStorage.token; //获取token if (!otherConfig.noToken !token) return jumpLoginPage({ url });//判断如果本地没有token否者直接跳转到登录页面 // 建议一般情况就不要设置headers了ajax会根据data结构自动判断类型 let headers { // withCredentials: true ,// 这里可以设置withCredentials // Content-Type: application/x-www-form-urlencoded; charsetUTF-8,//FormData方式提交数据 // Content-Type: application/json;charsetUTF-8,//报跨域错误 ...otherConfig.headers, }; //判断是否需要令牌字符串 if (!otherConfig.noToken) { token (headers[kkToken] || (headers[kkToken] token)); let roleID $g.getUrlParam(roleID); roleID (headers[kkRoleIds] roleID);//切换角色2024.12.11 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... 的分隔请求体 } else { let hasFile Object.keys(data || {}).some(k data[k] instanceof File); if (hasFile) return sgAlert(请确认接口${url}的“isUpload”是设置为true); } // 序列化设置是否将数据转换为查询字符串的形式发送给服务器默认情况下processData的值为true let processData true; (data instanceof FormData) (processData false); (typeof otherConfig.processData ! undefined) (processData otherConfig.processData); // 判断是否正确定义了请求进度、下载进度函数 if ([data.p, data.progress].some(v typeof v function)) return sgAlert(请确认接口${url}是否把请求进度、下载进度函数p或progress写到了data里面); // 判断是否正确定义了报错函数 if ([data.f, data.fail].some(v typeof v function)) return sgAlert(请确认接口${url}是否把报错函数fail或f写到了data里面); // 判断是否正确定义了回调函数 if ([data.cb, r.s].every(v typeof v function)) return sgAlert(请确认接口${url}是否把回调函数s写成了cb); // 精简别名---------------------------------------- r.p (r.progress r.p); //响应请求进度、下载进度 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()); //加载 // 全局加载方法---------------------------------------- let cursorTimeout; let showBodyLoadingCursor () { clearTimeout(cursorTimeout); cursorTimeout setTimeout(() { document.body.style.cursor wait; }, 300); };//全局加载 let closeBodyLoadingCursor () { clearTimeout(cursorTimeout); document.body.style.cursor ; }//关闭全局加载 window.disabledBodyLoadingCursor || showBodyLoadingCursor(); // ---------------------------------------- $.ajax({ timeout: 24 * 60 * 60 * 1000, //设置默认超时时间24小时 type, url: otherConfig.isFullURL ? url : ${API_ROOT_URL}${url},//使用完整接口路径 data,//发送到服务器的数据。将自动转换为请求字符串格式。GET 请求中将附加在 URL 后。查看 processData 选项说明以禁止此自动转换。必须为 Key/Value 格式。如果为数组jQuery 将自动为不同值对应同一个名称。如 {foo:[bar1, bar2]} 转换为 foobar1foobar2。 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 请求。 */ }, // 监听请求进度、下载进度 xhr() { let xhr new XMLHttpRequest(); xhr.onprogress e { if (e.lengthComputable) { let percent e.loaded / e.total * 100; r.progress r.progress({ e, percent, percentText: ${percent.toFixed(2)}%, }); } }; return xhr; }, 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) { let errMsg 请联系后端开发人员给接口${url}添加RequestMapping(value...producesapplication/json); $g.copy(errMsg); return sgAlert(errMsg); }; // typeof d string (d JSON.parse(d));//如果是字符串返回值则转换为JSON格式如果用这句话就会纵容后端开发导致以后复杂的返回报文也用字符串会有问题 r.loading r.loading.close r.loading.close(); //关闭加载 closeBodyLoadingCursor();//关闭全局加载 if (otherConfig.isDownload) return r.success(d); //如果是下载直接返回文档流 switch (d.success) { case true: //登录成功 (data.hasOwnProperty(start) data.hasOwnProperty(limit) !d.hasOwnProperty(totalCount)) console.warn(翻页接口${url}返回结果缺少totalCount字段); r.success r.success((otherConfig.isGetAllData || d.hasOwnProperty(totalCount)) ? d : (d.hasOwnProperty(data) ? d.data : d)); break; case false: //登录失败当token密码失效的时候 return 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 (otherConfig.isDownload d.responseText [object ArrayBuffer]) return sgAlert(请确认${url}是否为下载数据流接口br如果不是请去掉“isDownload:true”配置); //请求报错 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(); //关闭加载 closeBodyLoadingCursor();//关闭全局加载 r.error ? r.error(d) : errAlert(url, d); //console.log(【报错】${JSON.stringify(d, null, 2)},d); }, complete(xhr, status) { //请求完成时运行的函数在请求成功或失败之后均调用即在 success 和 error 函数之后。 } }); // ---------------------------------------- }; export default { do(url, data, r, otherConfig {},) { try { doApi({ config, url, data, r, otherConfig, }); } catch (err) { ((err || {}).message || ).includes(Illegal invocation) ? sgAlert(请确认接口${url}的“isUpload”是设置为true) : console.error([错误信息], err); } }, }src/js/api/sd.js//公共接口定义2024.11.22---------------------------------------- import $api from ./api; import $g from ./sg;//vue项目使用 export default { //【通用接口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 }); }, upload({ data, r }) { $api.do(${API_ROOT_URL}/post.do, data, r, { isUpload: true }); }, }