前端如何优雅的解决后端枚举字段

593 阅读2分钟
  • 背景:
  1. 前端在业务开发过程中,服务端返回一些枚举字段
eg: orderStatu: 1 表示已完成 2 表示已取消 3表示已发货
  • 需求点:
  1. 前端在新增时 【orderStatu】 字段 需要上传【1、2、3】
  2. 前端在详情时 【orderStatu】需要展示成【已完成、已取消、已发货】
  • 解决方案
  1. 方案1:通过过滤器实现回显枚举

    1. 生成枚举映射中心
    const codeSet = {
        orderStatusMap: [
            {
              id: 1,
              name: "已完成"
            },
            {
              id: 2,
              name: "已取消"
            },
            {
              id: 3,
              name: "已发货"
            }
          ]
        };
    export default codeSet;
    
    1. 业务代码中使用
    <template>
      <div>
        <!-- 在select中使用 -->
        <el-select v-model="ruleForm.orderStatus" placeholder="请选择订单状态">
          <el-option v-for="item in orderStatusList" :key="item.id" :value="item.name" :label="item.id"></el-option>
        </el-select>
    
        <!-- 通过过滤器回显映射 -->
        <span>{{ details.orderStatus | orderStatusStr }}</span>
      </div>
    </template>
    
    <script>
    import codeSet from "./data/codeSet";
    import { arrayFind } from "@/utils/index";
    
    export default {
      name: "test",
      filters: {
        // 订单状态过滤器
        orderStatusStr: function(val) {
          let result = arrayFind(orderStatusList, "id", val);
    
          if (result) {
            return result.name;
          } else {
            return "";
          }
        }
      },
      data() {
        return {
          orderStatusList: codeSet.orderStatusMap
        };
      },
    };
    </script>
    
    

    辅助函数

     /** 根据数组对象某个属性的值筛选出符合要求的数组元素
         * @param {Array}  arr      过滤的数组
         * @param {String} attr     数组对象中的某个属性
         * @param {String} val      具体某个属性的值
         * @result {Object} 返回找到的对象元素
         *
         * */
        export function arrayFind(arr, attr, val) {
          if (!(arr instanceof Array)) {
            return [];
          }
          let result = arr.find(value => value[attr] === val);
    
          if (result) {
            return result;
          } else {
            return "";
          }
        }
    
  2. 方案2:通过计算属性实现回显枚举

    1. 生成枚举映射中心
    const codeSet = {
      authStatusMap: [
        {
          id: 0,
          name: "未实名"
        },
        {
          id: 1,
          name: "已实名"
        }
      ]
    };
    
    export default codeSet;
    
    1. 业务代码中使用
    <template>
      <div>
        <!-- 在select中使用 -->
        <el-select v-model="ruleForm.orderStatus" placeholder="请选择订单状态">
          <el-option v-for="item in orderStatusList" :key="item.id" :value="item.name" :label="item.id"></el-option>
        </el-select>
    
        <!-- 计算属性回显映射 -->
        <span>{{ orderStatusStr }}</span>
      </div>
    </template>
    
    <script>
    import codeSet from "./data/codeSet";
    
    export default {
      name: "test",
      data() {
        return {
          orderStatusList: codeSet.orderStatusMap
        };
      },
      computed: {
        orderStatusStr() {
          return function(val) {
            const { orderStatusList } = codeSet;
            const curItem = orderStatusList.find(x => x.id == val);
    
            return curItem.name;
          };
        }
      },
    };
    </script>
    
  3. 方案3:通过混入实现回显枚举

    1. 生成枚举映射中心
    const codeSet = {
      orderStatusList: [
        {
          id: 1,
          name: "已完成"
        },
        {
          id: 2,
          name: "已取消"
        },
        {
          id: 3,
          name: "已发货"
        }
      ]
    };
    
    export default codeSet;
    
    1. 业务代码中使用
    <template>
      <div>
        <!-- 在select中使用 -->
        <el-select v-model="ruleForm.orderStatus" placeholder="请选择订单状态">
          <el-option v-for="item in orderStatusList" :key="item.id" :value="item.name" :label="item.id"></el-option>
        </el-select>
    
        <!-- 混入全局函数回显映射 -->
        <span>{{ getEnumValueName("orderStatusList", scope.row.authStatus) }}</span>
      </div>
    </template>
    
    <script>
    import codeSet from "./data/codeSet";
    import enumMixin from "@/mixins/enum.js";
    
    export default {
      name: "test",
      props: {},
      components: {},
      mixins: [enumMixin],
      data() {
        return {
          orderStatusList: codeSet.orderStatusMap
        };
      },
      computed: {},
      watch: {},
      methods: {},
      created() {},
      mounted() {}
    };
    </script>
    
    <style lang="scss" scoped></style>
    
    

    辅助函数

    // 导入数据
    import csrCodeSets from '@/data/codeSet'
    export default {
        data() {
            return {
                enumData: {
                    ...csrCodeSets
                }
            }
        },
        methods: {
            getEnumValueName(key, id) {
                if (!key) return false
                const currentItemList = this.enumData[key]
                const findItem = currentItemList.find(x => x.id == id)
                if (findItem) {
                    return findItem.name || ''
                }
                return ''
            }
        }
    }
    
  4. 方案4:通过工厂设计模式实现回显枚举

    1. 通过工厂函数生成枚举映射中心
    import enumFactory from "@/utils/enumFactory";
    
    export const orderStatusEnum = enumFactory("订单状态枚举", [
      { value: "1", label: "已完成" },
      { value: "2", label: "已取消" },
      { value: "3", label: "已发货" }
    ]);
    
    1. 业务代码中使用
    <template>
      <div>
        <!-- 在select中使用 -->
        <el-select v-model="ruleForm.orderStatus" placeholder="请选择订单状态">
          <el-option v-for="item in orderStatusList.enums" :key="item.id" :value="item.name" :label="item.id"></el-option>
        </el-select>
    
        <!-- 通过工厂函数回显映射 -->
        <span>{{ orderStatusList.getLabelByValue(detailRes.orderStatus) }}</span>
      </div>
    </template>
    
    <script>
    import orderStatusList from "./data/codeSet";
    
    export default {
      name: "test",
      props: {},
      components: {},
      data() {
        return {
          orderStatusList
        };
      }
    };
    </script>
    
    

    工厂函数

    /*
     * 枚举工厂函数
     * @param { name } 语意化枚举名称,根据需要可以不要
     * @param { enums } 枚举对象
     */
    function enumFactory(name, enums) {
      const labels = enums.map(item => item.label);
      const values = enums.map(item => item.value);
      const colors = enums.map(item => item.color);
    
      return {
        name,
        labels,
        values,
        enums,
        getValueByLabel(label) {
          return values[labels.indexOf(label)];
        },
        getLabelByValue(value) {
          return labels[values.indexOf(value)];
        },
        getColorByValue(value) {
          return colors[values.indexOf(value)];
        },
        getItemByValueOrLabel(valueOrLabel) {
          let index = values.indexOf(valueOrLabel);
    
          if (index < 0) {
            index = labels.indexOf(valueOrLabel);
          }
          return enums[index];
        },
        canOperation(valueOrLabel, operation) {
          const operations = this.getItemByValueOrLabel(valueOrLabel)?.operations;
    
          return operations && operations.indexOf(operation) !== -1;
        }
      };
    }
    
    export default enumFactory;
    
    /*
    
    const orderStatusEnum = enumFactory('orderStatus', [
      { value: 0, label: '已完成', color: green,
        operations: ['add', 'detail'] },
      { value: 1, label: '待支付', color: orange,
        operations: ['edit', 'cancel'] },
      { value: 2, label: '已取消', color: gray,
        operations: ['detail'] }
    ]);
    
    */
    
    
  • 总结
  1. 以上几种解决方案都能解决业务需求,个人倾向第四种。对业务类入侵小,相对优雅