最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 可以少写if...else ,列表页操作项过多该怎么组织代码

    正文概述 掘金(Canzone)   2021-03-12   359

    记录,苦中作乐 切磋琢磨,渺小的思考填满了一生.jpg; 很少有文章关于怎么写业务的,为啥;虽然具体业务大多不通用,但具体例子或者说故事,也是理解概念性知识的方法之一,看的人会从中看到不一样的东西;

    背景

    业务场景是订单列表页,每个订单都有子单,子单上有多个状态字段,针对订单的操作有十个左右,每个操作对数据的要求都不同,一个操作对应一个接口,以下是一种简便的处理方法,也许对你有些帮助。

    思路

    首先冒出来的念头是,要统一这些操作的处理流程。肯定不是用if-else switch case这类,从看过一些处理大量if-else的文章里汲取营养,链接放在末尾。 统一的处理流程,一个入口函数,一个操作对应一个接口,收集不同接口的参数,判断被勾选的数据是否符合操作的要求,这些处理都必须是适用所有接口的。

    三步走,描述接口(操作),定义筛选规则,定义执行判断的函数

    desc.js文件描述各个接口的信息,以及提供一些数据给页面index.vue文件使用;

    rule.js 集中定义了各个接口对数据的要求,提供给desc.js中 接口描述的rules配置使用;

    hight.js 定义loopRules函数,每条勾选的数据都会执行接口rules里的规则,判断数据是否满足当前操作

    index.vue param保存着所有操作的参数描述对象,key是接口的urlvalueparam,actionStatus是当前操作的url,当前操作的参数保存在currentParamcheckedList数组保存着勾选的子单数据,{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中的actionListindex.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数据是两层的,大部分操作是有弹出输入或备注字段输入的地址

    可以少写if...else ,列表页操作项过多该怎么组织代码

    横向扩展新的操作,十分便捷,只是更深入的扩展具体操作的处理流程,不确定是否可以顶住;

    写完知道了,业务中有很多与技术无关的干扰信息,写出来像流水账,缺乏深度,只能抽离一个简单场作为例子讲解

    参考

    • JavaScript 复杂判断的更优雅写法

    • 还有一篇文章更加深入具体业务,只是我还没看明白,涉及状态机,深耕业务 ---- 探索复杂前端业务的开发与设计


    起源地下载网 » 可以少写if...else ,列表页操作项过多该怎么组织代码

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元