多平台统一管理软件接口,如何实现多平台统一管理软件接口
174
2024-02-02
本文针对中大型的后台项目的接口模块优化,在不影响项目正常运行的前提下,增量更新。
接口文件写法简化(接口模块半自动化生成)任务调度、Loading调度(接口层面的防抖兜底,多个接口共用一个loading,防止闪烁)接口提示自由化(提示消息可由前端控制,也可以由后端控制)对于一些中后台模块的接口,基本上都是增删改查以及审核流的一些功能(其他特殊接口暂且不谈)。如果后端接口足够规范的话,大概就是下面这个情形
import request from "@/utils/request"; // 销售退货列表 export function getSalesReturnList(data) { return request({ url: "/sales_return/list", method: "post", data, }); } // 保存销售退货 export function saveSalesReturn(data) { return request({ url: "/sales_return/save", method: "post", data, }); } // 根据Id获取销售退货 export function getSalesReturn(query) { return request({ url: "/sales_return/get", method: "get", params: query, }); } // 根据Id删除销售退货 export function deleteSalesReturn(data) { return request({ url: "/sales_return/delete", method: "post", data, }); } // 提交销售退货审核 export function submitSalesReturn(data) { return request({ url: "/sales_return/submit", method: "post", data, }); } // 审核销售退货 export function auditSalesReturn(data) { return request({ url: "/sales_return/audit", method: "post", data, }); } // 撤审销售退货 export function revokeAuditSalesReturn(data) { return request({ url: "/sales_return/withdraw", method: "post", data, }); } // 审核拒绝销售退货 export function rejectSalesReturn(data) { return request({ url: "/sales_return/reject", method: "post", data, }); } // 作废销售退货 export function discardSalesReturn(data) { return request({ url: "/sales_return/discard", method: "post", data, }); }我觉得这个也太重复了,而且接口函数命名太麻烦了,要让团队规范起来比较困难。能不能自动生成了,命名也帮忙处理了,这样这种接口文件岂不是更加规范。
接下来想想办法
假设如上,一个单据模块的通常来说有九个接口方法,增删改查,提交、作废、审核、撤审、拒绝。他们的 url,前面的 sales_return 拼接是固定的,不同的就是后面标识功能的路径标识。另外就是,method 分为 post 和 get 方法。
我们把这九个接口,看成是一个 9 位二进制上的 9 个位,1 代表存在,0 代表不存在。
我们可以创建一个 map 文件来做构建准备(如下)
export const apiEnum = { // 查列表 2^0 1: { name: "list",//接口名称 type: "post",//接口方式 }, // 查详情 2^1 2: { name: "get", type: "get", loading: true,//是否需要loading调度、防抖 }, // 删列表 2^2 4: { name: "delete", type: "post", }, // 保存 或者 保存且提交 2^3 8: { name: "save", type: "post", loading: true, }, // 提交 2^4 16: { name: "submit", type: "post", loading: true, }, // 审核 2^5 32: { name: "audit", type: "post", }, // 撤审 2^6 64: { name: "withdraw", type: "post", }, // 拒绝 2^7 128: { name: "reject", type: "post", }, // 作废 2^7 256: { name: "discard", type: "post", }, }; export const apiFuncModule = { // 全部 COMMON: 511, // 增删改查 CURD: 15, };当我传 1 的时候,九位为000000001,代表只有一个查接口。当我传 15 的时候,九位为000001111,代表拥有增删改查四个接口。以此类推。
接下就是完成处理函数,完成上面的功能(如下)
import request from "@/utils/request"; import { apiEnum, apiFuncModule } from "@/enum/baseModule/apiEnum"; function useApi(moduleName, code = 511) { let apiMap = {}; for (let key in apiEnum) { if ((key & code) == key) { let obj = apiEnum[key]; //可以按自己习惯来对接口函数命名 let apiName = "api_" + obj.name; apiMap[apiName] = (data) => { return request({ url: `/${moduleName}/${obj.name}`, method: obj.type, [obj.type == "get" ? "params" : "data"]: data, loading: obj.loading, }); }; } } return apiMap; } export { useApi, apiFuncModule as apiType };完成以上步骤,我们的接口文件就可以这样写了,这样九个接口就写完了。而且一目了然,如需修改,只需要调整传参就行了。
import { useApi } from "@/utils/system/apiGenPlugin"; //code可以不传 ,默认为511 export const API = useApi("sales_return"); //若有其他特殊接口 兼容原始写法 互不影响 export function xxxxx(data) { ... }使用方式
//API集中管理 import { API as SalesReturn } from "@/api/workApi/sale/return"; const {api_save,api_delete,api_get,api_list,api_audit,api_withdraw,api_discard,api_submit,api_reject} = SalesReturn //单独使用 import { useApi } from "@/utils/system/apiGenPlugin"; const {api_save,api_delete,api_get,api_list,api_audit,api_withdraw,api_discard,api_submit,api_reject} = useApi(sales_return) 增 SalesReturn.api_save删 SalesReturn.api_delete改 SalesReturn.api_get查 SalesReturn.api_list审核 SalesReturn.api_audit撤审 SalesReturn.api_withdraw作废 SalesReturn.api_discard提交 SalesReturn.api_submit拒绝 SalesReturn.api_reject实际开发中,我们可能会有对接口调用做一些处理
对提交事件进行防抖处理,防止重复提交。加载某些重要资源的时候,希望有个loading效果,来优化用户体验。让多个需要loading效果的接口,共用同一个loading,防止页面闪烁。这些功能单独处理起来就显得很麻烦了,而且每个人的写法不一样,后期维护成本就更难。
废话不多说,直接贴代码
接口调度类
import { Loading } from "element-ui"; class RequestLock { // Loading 实例 L_instance = null; // 接口map reqMap = new Map(); // 最近一次调用接口时间戳 timestamp = 0; constructor(timeout = 500) { // 过渡时间 this.timeout = timeout; } // 创建任务 put = (id) => { if (this.reqMap.has(id)) return false; this._put(id); return true; }; _put = (id) => { this.timestamp = new Date().getTime(); this.reqMap.set(id, true); //开启loading this.L_instance = Loading.service({ fullscreen: true, background: "rgba(255, 255, 255, 0.1)", lock: true, }); }; // 移除任务 del = (id) => { if (this.reqMap.has(id)) { this.reqMap.delete(id); if (this.reqMap.size == 0) { this._closeLoading(); } } }; // 清空所有的任务 clearTask = () => { this.reqMap.clear(); this.L_instance.close(); }; //平滑关闭loading _closeLoading = () => { let _timestamp = new Date().getTime(); let settime = _timestamp - this.timestamp; if (settime > this.timeout) { this.L_instance?.close(); } else { setTimeout(() => { this.L_instance?.close(); }, this.timeout - settime); } }; } export default RequestLock;在axios里的使用
这个是增量优化,在不影响以前代码的条件下,添加功能
import { RequestLock } from "@/class/lock"; let loadLock = new RequestLock(500); //请求拦截 service.interceptors.request.use( (config) => { ... //如果配置中有loading 开启调度 if (config.loading) { if (!loadLock.put(config.url)) { return Promise.reject(new Error("repeat request!")); } } ... return config; }, (error) => { ... //如果有错误请求,中止当前调度任务,并清空 loadLock.clearTask(); ... return Promise.reject(error); } ); //响应拦截 service.interceptors.response.use( (response) => { ... //检查 response.config.loading && loadLock.del(response.config.url); ... }, (error) => { loadLock.clearTask(); return Promise.reject(error); } );接口文件书写
// 根据Id获取销售退货 export function getSalesReturn(query) { return request({ url: "/sales_return/get", method: "get", params: query, //在这里配置loading为true,开启 loading:true }); }有时候当我删除一条数据,需要有个弹框提示删除是否成功。通常我们会在接口成功回调的时候加上这个功能。需要判断状态,来显示提示框的描述和颜色。另一方面,有时候删除一条数据,业务需求提示不单单是简单的“删除成功!”,还可能需要其他的附加提示。比如“删除单据xxx成功,请及时处理xxxx!”。这种需求没什么难度,但是有沟通成本和维护成本。业务有一些变化就需要修改。
另一方面,后端对系统的业务逻辑更加贴近,提示功能交给后端更加合理。当然,前端也需要保留这个功能,去兼容某些需求。
import { Message } from "element-ui"; export function responseMsgHandle(res) { //这里需要后端响应数据格式的配合,MsgType表示提示状态,Msg表示提示描述 let { MsgType, Msg } = res; if (["success", "warning", "error"].includes(MsgType)) { Message({ message: Msg, type: MsgType, duration: 5 * 1000, }); } }使用
import { responseMsgHandle } from "@/utils"; //响应拦截 service.interceptors.response.use( (response) => { ... const res = response.data; responseMsgHandle(res); ... }, (error) => { ... responseMsgHandle({ MsgType:"error", Msg:error.message, }); ... return Promise.reject(error); } );基本上能解决很大一部分的重复劳动,还能减少维护成本。
前言本篇来学习下直角坐标系的常用配置 直角坐标系直角坐标系的图表指的是带有x轴和y轴的图表, 常见的直角坐标系的图表有: 柱状图 折线图 散点图 常用配置网格gri ...
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~