记录,苦中作乐 切磋琢磨,渺小的思考填满了一生.jpg; 很少有文章关于怎么写业务的,为啥;虽然具体业务大多不通用,但具体例子或者说故事,也是理解概念性知识的方法之一,看的人会从中看到不一样的东西;
背景
业务场景是订单列表页,每个订单都有子单,子单上有多个状态字段,针对订单的操作有十个左右,每个操作对数据的要求都不同,一个操作对应一个接口,以下是一种简便的处理方法,也许对你有些帮助。
思路
首先冒出来的念头是,要统一这些操作的处理流程。肯定不是用if-else switch case
这类,从看过一些处理大量if-else
的文章里汲取营养,链接放在末尾。
统一的处理流程,一个入口函数,一个操作对应一个接口,收集不同接口的参数,判断被勾选的数据是否符合操作的要求,这些处理都必须是适用所有接口的。
三步走,描述接口(操作),定义筛选规则,定义执行判断的函数
desc.js文件描述各个接口的信息,以及提供一些数据给页面index.vue
文件使用;
rule.js 集中定义了各个接口对数据的要求,提供给desc.js
中 接口描述的rules
配置使用;
hight.js 定义loopRules
函数,每条勾选的数据都会执行接口rules
里的规则,判断数据是否满足当前操作
index.vue param
保存着所有操作的参数描述对象,key
是接口的url
,value
是param
,actionStatus
是当前操作的url
,当前操作的参数保存在currentParam
,
checkedList
数组保存着勾选的子单数据,{child: {…}, parent: {…}}
, child
是子单,parent
是父级订单
desc.js 描述接口
操作一一对应接口,这里url
作为唯一性标识符(可以改进)
// desc.js
import { Rules } from './rules'
export const actionList = [
{
url: "checkOrder",
title: "审核",
desc: ""
},
...
// 编辑、取消、锁定、解锁...
export const checkOrder_api = {
url: "checkOrder",
title: "审核",
method: "post",
param: { // 参数
outSideCodes: [] // 参数,取自table里选中的数据,数组表示多选,字段名为outSide
},
info: '1、勾选发货单,可多选。2、发货单状态只能为"待审核"且子订单未锁定,3、同一个发货单必须同时审核。',
rulesName: ["_isParentChecked", "_isParentStatusOne", "_noLockedChild"], // 该操作对数据的要求
rules: Rules.filter(item => ["_isParentChecked", "_isParentStatusOne", "_noLockedChild"].includes(item.name)),
tips: '确认框的提示'
};
...
// 为了方便理解, 接口描述对象里的param 中有值,但是使用时需要清空;
export function collectApiParam(Desc) {
let param = {};
for (let item of actionList) {
let item_api = actionToApi(item.url);
param[item_api] = deepCloneWithoutValue(Desc[item_api].param, true);
}
return param;
}
//生成一个对象 url为key param为value { "chkorders_api": { "outstkCodes": [] }, "cancorders_api": { "outstkCodes": [], "ids": [] }, ....}
rules.js
export const Rules = [
{
name: "_isParentChecked",
desc: "该操作仅针同一发货单号下所有商品",
expression: ({ child, parent }) => {
// 返回false 命中
let is = true;
if (child.checked && !parent.checked) {
// 不自动取消勾选
// for (let item of parent.send_items) {
// item.checked = false;
// }
// parent.checked = false;
is = false;
}
return is;
},
message: (title) => {
return `${title}:操作仅针同一发货单号下所有商品`;
},
// 下面三个是提示框的三个配置字段
title: "",
type: "",
duration: 3000,
// beforeNotify: () => {} 返回 boolean 值
// beforeNotify, we can stop Notify by changing isCheck, so the process can keep on after rule
},
{
name: "_isParentStatusOne",
desc: "只能处理待审核的发货单,有一个子单是待审核,整个发货单就是待审核",
expression: ({ child, parent }, checkedList) => {
// 返回false 命中
let is = false;
for (let item of parent.send_items)
if (item.outstkFlag === 1) {
is = true;
}
return is;
},
message: (title) => {
return `${title}:只能处理待审核的发货单`;
},
title: "",
type: "",
duration: 3000,
},
{
name: "_noLockedChild",
desc: "发货单子单是否有锁定的",
expression: ({ child, parent }) => {
// 返回false 命中
let is = true;
console.log(" child.lockFlag", child.lockFlag);
if (child.lockFlag === "T") {
is = false;
child.checked = false;
parent.checked = false;
}
return is;
},
message: (title) => {
return `${title}:不能执行,发货单子单有锁定的`;
},
title: "",
type: "",
duration: 3000,
},
...
]
high.js loopRules函数
import Vue from "vue";
const vue = new Vue();
// 每条勾选的数据处理某个操作的某条规则;
/**
* checkList中的每条数据都将执行一遍规则
* @param {*} desc 接口描述对象
* @param {*} rule 接口上的规则
* @param {*} checkedList 已经勾选的数据
*/
function baseFun(desc, rule, checkedList) {
let isCheck = true;
for (let item of checkedList) {
// 真实的checkedList数据格式,每条勾选子单都保存了父级 {child: xx, parent: XX}
if (!rule.expression(item, checkedList)) {
// 冲突数据 取消勾选
isCheck = false;
}
}
// beforeNotify, we can stop Notify by changing isCheck, so the process can keep on after rule
if (rule.beforeNotify) {
isCheck = rule.beforeNotify(isCheck, rule, desc);
}
if (!isCheck) {
vue.$notify({
title: rule.title ? rule.title : "提示",
message: rule.message(desc.title),
type: rule.type ? rule.type : "warnning",
duration: rule.duration ? rule.duration : 3000,
});
console.log("notify");
return false; // 命中的规则,退出对loopRules的循环执行;
}
return true;
}
/**
*
* @param {*} desc 接口描述 使用了title和rules字段
* @param {*} checkedList 选中数据
*/
export function loopRules(desc, checkedList) {
// debugger;
for (let rule of desc.rules) {
if (!baseFun(desc, rule, checkedList)) {
// 命中规则 停止执行后续规则
return false;
}
}
return true;
}
index.vue
desc.js中的actionList
给index.vue
操作栏的select
组件使用,
index.vue
里的 this.param
对象记录了所有接口的参数
// index.vue 收集收集所有操作的参数描述对象,
import * as Desc from "./desc";
data() {
return {
tableData: [],
Dec,
actionList: Desc.actionList, // 操作列表
actionStatus: "", // 当前选中的操作项,即url
param: {}, // 所有接口的参数
}
},
created() {
this.initParam();
this.getTableData();
},
methods: {
handleAction() {
// 判断是有 actionStatus和数据,否则什么操作都执行不了
if (!this._needActionStatusAndData()) return;
let desc = Desc[Desc.actionToApi(this.actionStatus)];
// 命中规则不再执行后续
if (!loopRules(desc, this.checkedList)) return;
...
this.submit();
},
// 只是正好 这里的两个接口的备注字段都叫 lockMark
// 可以在接口描述对象上设置是有备注的标志位,
submit() {
let actionApi = Desc.actionToApi(this.actionStatus);
let submitData = JSON.parse(JSON.stringify(this.currentParam));
let { data, err } = await axiosApi(Desc[actionApi], submitData);
},
// 获取列表数据
async getTableData() {
let { data, err } = await this.axiosApi(Desc.orderLists_api, this.queryPrdlists);
this.tableData = data.data.data.data; // 这数据层级有点离谱ba
this.addProperyForTableDataItem();
},
// 初始化所有接口的参数
initParam() {
this.param = Desc.collectApiParam(Desc);
},
changeActionStatus() {
this._collectParam();
},
// 接口映射请求字段,收集当前接口的参数
_collectParam() {
if (!this.actionStatus) return;
this.initParam();
let actionStatusApi = Desc.actionToApi(this.actionStatus);
let currentParam = this.param[actionStatusApi]; // currentParam 是响应式数据,对他的修改,就是对 this.currentParam的修改
for (let { child, parent } of this.checkedList) {
for (let key in currentParam) {
let endOfS = Array.from(key).slice(-1)[0] === "s" ? true : false;
if (!child[key] && endOfS) {
// outskCodes 与 child.outsktCode 和 stkCodes 与child.stkCode
// 其实约定多选字段为数组类型就行了,后端给多选的字段加了“s”,只好拧巴一下
let keyWithoutS = Array.from(key)
.slice(0, key.length - 1)
.join("");
if (child[keyWithoutS] && currentParam[key] && currentParam[key].constructor === Array) {
currentParam[key].push(child[keyWithoutS]);
} else if (!child[key] && child[keyWithoutS] && !currentParam[key]) {
currentParam[key] = child[keyWithoutS];
}
} else {
currentParam[key] = child[key] ? child[key] : undefined;
}
}
}
},
},
}
基本处理流程就是这样三步,描述接口(操作),定义筛选规则,定义执行判断的函数。
本质是将if-else
的判断做了拆分,使用了“策略模式”做逻辑判断。
完整版:table数据是两层的,大部分操作是有弹出输入或备注字段输入的地址
横向扩展新的操作,十分便捷,只是更深入的扩展具体操作的处理流程,不确定是否可以顶住;
写完知道了,业务中有很多与技术无关的干扰信息,写出来像流水账,缺乏深度,只能抽离一个简单场作为例子讲解
参考
-
JavaScript 复杂判断的更优雅写法
-
还有一篇文章更加深入具体业务,只是我还没看明白,涉及状态机,深耕业务 ---- 探索复杂前端业务的开发与设计
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!