【仰取俯拾】动态表单配置浅谈

86 阅读2分钟

最近在项目开发中遇到一个需求:即前端根据约定好的规则将一段描述匹配成可进行输入操作的表单,并将改变后的表单值按照原约定规则还原成JSON字符串返回给后端存储,以便下次拿到最新的描述。具体业务不多赘述,主要是提供一种开发思路,以下是案例展示与代码实现。

1.案例展示:

image.png image.png image.png

2.代码展示:

html部分(仅展示表单部分):

<template v-for="(item, index) in optionsObj.ruleStrArr">

    <!--    文本输入框      -->
    <template v-if="item.type === 'input'">
      <el-input :key="index" v-model="item.value" />
    </template>
    
    <!--    数字输入框      -->
    <template v-else-if="item.type === 'number'">
      <el-input-number :key="index" v-model="item.value" />
    </template>
    
    <!--    下拉选择框      -->
    <template v-else-if="['select', 'multiSelect'].includes(item.type)">
      <el-select :key="index" v-model="item.value" :multiple="item.multiple">
        <el-option
            v-for="option in item.options"
            :key="option.value"
            :label="option.label"
            :value="option.value"
        />
      </el-select>
    </template>
    
    <!--    其它类型      -->
    <template v-else>{{item.value}}</template>
</template>

基础数据

data() {
  return {
    // 表单类型数组
    formTypeArr: [],
    // 需匹配的表单类型数组
    needConvertFormTypeArr: ['input', 'number', 'select', 'multiSelect'],
    optionsObj: {
      ruleStr: '这是一个数字输入框{number},这是文字里的未知项{unknown},这是一个文本输入框{input},这是一个下拉单选框{select},这是一个下拉多选框{multiSelect}',
      ruleStrArr: [],
      oldFormValue: "[1003, 'unknown', '我是文本', '1', ['1', '2'] ]",
      newFormValue: undefined,
      oldFormOption: "[[], 'unknown', [], [{'value': '1', 'label': '选项一'}, {'value': '2', 'label': '选项二'}, {'value': '3', 'label': '选项三'}]," +
          "[{'value': '1', 'label': '选项一'}, {'value': '2', 'label': '选项二'}, {'value': '3', 'label': '选项三'}]]",
      newFormOption: undefined
    },
    str: '',
    formArr: []
  }
}

整体实现代码

/**
 * 匹配规则数据
 * 返回变量类型
 * @param {Object} obj
 * @param {String} type '1'-匹配字符串,'2'-匹配对象数组
 * @returns {String, Array} 变量类型
 */
formatRuleData(obj, type = '1') {
    // 转为标准JSON字符串,将'替换为"
    obj.newFormValue = obj.oldFormValue.replace(/'/gi, '"')
    obj.newFormOption = obj.oldFormOption.replace(/'/gi, '"')
    // 转为标准JSON对象
    obj.newFormValue = obj.newFormValue ? JSON.parse(obj.newFormValue) : []
    obj.newFormOption = obj.newFormOption ? JSON.parse(obj.newFormOption) : []
    // 用统一字符替换{}
    obj.ruleStrReplace = obj.ruleStr.replace(/{|}/gi, '===')
    // 分割obj.ruleStrReplace形成数组以便遍历插值
    obj.ruleStrArr = obj.ruleStrReplace.split('===')
    // 得到表单组件类型数组
    this.formTypeArr = this.matchStr(obj.ruleStr)
    // 获取匹配后数据
    if (type === '1') {
      this.formatStr(obj)
    } else {
      this.formatObjArr(obj)
    }
    return type === '1' ? obj.ruleStrArr.join('') : obj.ruleStrArr
}

匹配成值方法

// 去除大括号{}
matchStr(str){
  const reg = /{(.*?)}/gi;
  const tmpStr = str.match(reg);
  return tmpStr.map(item => {
    item = item.replace(/{|}/gi, '')
    return item
  })
},

// 匹配完成填充后字符串
formatStr(obj) {
  obj.ruleStrArr = obj.ruleStrArr.map(rule => {
    let content = ''
    switch (rule) {
      // 文本输入、数值输入类型
      case 'input':
      case 'number':
        content = this.formatRichText(obj.newFormValue[0])
        break
        
      // 单选类型
      case 'select':
        content = obj.newFormOption[0].find(item => item.value === obj.newFormValue[0]).label
        content = this.formatRichText(content)
        break
        
      // 多选类型
      case 'multiSelect': {
        let text = ''
        obj.newFormValue[0].forEach(oldFormValue => {
          const value = obj.newFormOption[0].find(item => item.value === oldFormValue).label
          text = text ? `${text}${value}` : value
        })
        content = this.formatRichText(text)
      }
        break
        
      // 其他类型
      default: content = this.formTypeArr.includes(rule) ? `{${rule}}` : rule
    }
    // 删除已匹配的数值项newFormValue与规则项newFormOption,使得匹配时始终从下标为0的获取无需自增下标
    if (this.formTypeArr.includes(rule)) {
      obj.newFormOption.shift()
      obj.newFormValue.shift()
    }
    return content
  })
},

// 匹配富文本
formatRichText(text) {
  return `<span style="color:#3296fa">${text}</span>`
},

匹配成表单项方法

// 匹配对象数组
formatObjArr(obj) {
  obj.ruleStrArr = obj.ruleStrArr.map(rule => {
    let content = ''
    switch (rule) {
      // 数值输入、文本输入类型
      case 'input':
      case 'number':
        content = {
          type: rule,
          value: obj.newFormValue[0]
        }
        break
        
        // 单选、多选类型
      case 'select':
      case 'multiSelect':
        content = {
          type: rule,
          multiple: rule === 'multiSelect',
          value: obj.newFormValue[0],
          options: obj.newFormOption[0]
        }
        break
        
        // 其他类型
      default: {
        content = {
          type: rule,
          value: this.formTypeArr.includes(rule) ? `{${rule}}` : rule
        }
      }
    }
    // 删除已匹配的数值项newFormValue与规则项newFormOption,使得匹配时始终从下标为0的获取无需自增下标
    if (this.formTypeArr.includes(rule)) {
      obj.newFormOption.shift()
      obj.newFormValue.shift()
    }
    return content
  })
}

转换为原始格式

// 将表单改变后重新转换为JSON字符串
convertToStr(obj) {
  let formValue = []
  obj.ruleStrArr.forEach(item => {
    if (this.needConvertFormTypeArr.includes(item.type)) {
      formValue.push(item.value)
    } else if (this.formTypeArr.includes(item.type.replace(/{|}/gi, ''))) {
      formValue.push(item.value)
    }
  })
  return JSON.stringify(formValue)
}

以上内容如有纰漏或更优解,望不吝赐教~