疫情期间,写的两个场景

落笔前,先期望疫情快快过去,都要生锈了都~

场景一

模拟接口请求,对请求头的参数进行处理,如下图:

嗯,我是用的vue版本的ant design,然后实现之后是这样的:

相关代码:

<template>    <div class="mock-info">      <a-form :form="form">        <!-- 基本信息 -->        <a-divider orientation="left" style="color: #1890ff;">基本信息</a-divider>        <a-form-item          v-bind="formItemLayout"          label="期待名称">          <a-input            v-decorator="[              'name',              {rules: [{ required: true, message: '请输入环境域名'}]}            ]"            placeholder="请输入期待名称"/>        </a-form-item>        <a-form-item          style="margin-bottom: 0;"          v-bind="formItemLayoutWithOutLabel">          <a-switch checkedChildren="JSON" unCheckedChildren="JSON" v-decorator="['is_json', {valuePropName: 'checked', initialValue: false }]" />        </a-form-item>        <div class="paramsArr" v-show="!form.getFieldValue('is_json')"> <!--非json展示输入框-->          <a-form-item            v-for="(k, index) in form.getFieldValue('baseKeys')"            :key="k"            v-bind="index === 0 ? formItemLayout : formItemLayoutWithOutLabel"            :label="index === 0 ? '参数名称' : ''"            :required="false"            style="margin-bottom: 0;">            <a-input              v-decorator="[`paramNames[${k}]`]"              placeholder="参数过滤"              style="width: 40%; margin-right: 8px"/>            <a-input              v-decorator="[`paramValues[${k}]`]"              placeholder="参数值"              style="width: 40%; margin-right: 8px"/>            <a-icon              v-if="form.getFieldValue('baseKeys').length > 1"              class="dynamic-delete-button"              type="minus-circle-o"              :disabled="form.getFieldValue('baseKeys').length === 1"              @click="() => removeParam(k)"/>          </a-form-item>          <a-form-item v-bind="formItemLayoutWithOutLabel">            <a-button type="primary" style="width: 60%" @click="addParam">              <a-icon type="plus" /> 添加参数            </a-button>          </a-form-item>        </div>        <div v-show="form.getFieldValue('is_json')">          <a-form-item            v-bind="formItemLayout"            label="参数名称">            <v-jsoneditor              style="margin-top: 12px;"              :options="options"              v-model="request_params" />          </a-form-item>        </div>          <!-- 响应信息 -->        <a-divider orientation="left" style="color: #1890ff;">响应</a-divider>        <a-form-item          v-bind="formItemLayout"          label="HTTP Code">          <a-select            v-decorator="[              'http_code',              {rules: [{ required: false, message: '请选择'}]}            ]"            showSearch            placeholder="请选择">            <a-select-option v-for="(item, index) in codes" :key="index" :value="item">{{item}}</a-select-option>          </a-select>        </a-form-item>        <a-form-item          v-bind="formItemLayout"          label="延时">          <a-input-number            v-decorator="['delay_time', { initialValue: 0 }]"            :min="0"/>&nbsp;ms        </a-form-item>        <div class="httpArr">          <a-form-item            v-for="(k, index) in form.getFieldValue('httpKeys')"            :key="k"            v-bind="index === 0 ? formItemLayout : formItemLayoutWithOutLabel"            :label="index === 0 ? 'HTTP头' : ''"            :required="false"            style="margin-bottom: 0;">            <a-select              mode="combobox"              v-decorator="[`httpNames[${k}]`]"              showSearch              placeholder="请选择"              style="width: 40%; margin-right: 8px">              <a-select-option v-for="(item, index) in http_headers" :key="index" :value="item">{{item}}</a-select-option>            </a-select>            <a-input              v-decorator="[`httpValues[${k}]`]"              placeholder="参数值"              style="width: 40%; margin-right: 8px"/>            <a-icon              v-if="form.getFieldValue('httpKeys').length > 1"              class="dynamic-delete-button"              type="minus-circle-o"              :disabled="form.getFieldValue('httpKeys').length === 1"              @click="() => removeHttp(k)"/>          </a-form-item>          <a-form-item v-bind="formItemLayoutWithOutLabel">            <a-button type="primary" style="width: 60%" @click="addHttp">              <a-icon type="plus" /> 添加HTTP头            </a-button>          </a-form-item>        </div>        <a-form-item          v-bind="formItemLayout"          label="Body">          <v-jsoneditor            style="margin-top: 12px;"            :options="options"            v-model="response_body" />        </a-form-item>      </a-form>    </div>  </template>  复制代码
<script>  import VJsoneditor from 'v-jsoneditor'  export default {    name: 'mock-info',    components: {      VJsoneditor, // json编辑器    },    props: {      row: Object, // 回填信息    },    data() {      const formItemLayout = {          labelCol: {            span: 5          },          wrapperCol: {            span: 18          },        };      const formItemLayoutWithOutLabel = {          wrapperCol: {            span: 18,            offset: 5          },        };      const options = {          mainMenuBar: false,          mode: 'code'        };      // 可以考虑后端返回,也允许用户自己添加      const codes = [100, 101, 102, 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, 302, 303, 304, 305, 307, 308, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 422, 423, 424, 426, 428, 429, 431, 500, 501, 502, 503, 504, 505, 506, 507, 508, 510, 511]      // 可以考虑后端返回,也允许用户自己添加      const http_headers = ['Accept', 'Accept-Charset', 'Accept-Encoding', 'Accept-Language', 'Accept-Datetime', 'Authorization', 'Cache-Control', 'Connection', 'Cookie', 'Content-Disposition', 'Content-Length', 'Content-MD5', 'Content-Type', 'Date', 'Expect', 'From', 'Host', 'If-Match', 'If-Modified-Since',        'If-None-Match', 'If-Range', 'If-Unmodified-Since', 'Max-Forwards', 'Origin', 'Pragma', 'Proxy-Authorization', 'Range', 'Referer', 'TE', 'User-Agent', 'Upgrade', 'Via', 'Warning', 'X-Requested-With', 'DNT', 'X-Forwarded-For', 'X-Forwarded-Host', 'X-Forwarded-Proto', 'Front-End-Https', 'X-Http-Method-Override',        'X-ATT-DeviceId', 'X-Wap-Profile', 'Proxy-Connection', 'X-UIDH', 'X-Csrf-Token']      return {        formItemLayout,        formItemLayoutWithOutLabel,        options,        baseId: 0, // 基本信息的ID        httpId: 0, // 响应信息得ID        request_params: {}, // 请求参数        response_body: {}, // 响应参数        codes,        http_headers,        itemVal: ''      };    },    beforeCreate() {      // 创建form      this.form = this.$form.createForm(this, { name: 'form' });      this.form.getFieldDecorator('baseKeys', { initialValue: [0], preserve: true });      this.form.getFieldDecorator('httpKeys', { initialValue: [0], preserve: true });    },    mounted() {      let vm = this      // 信息回填      vm.form.setFieldsValue({        name: vm.row.name,        is_json: vm.row.is_json == '1' ? true : false, // 这里考虑'0','1','2'之类的        http_code: vm.row.resp_code,        delay_time: vm.row.delay || 0      })      if(vm.row.id) {        vm.response_body = JSON.parse(vm.row.resp_body)        vm.form.id = vm.row.id        // 回填请求参数        if(vm.row.is_json == '0') { // 非JSON          vm.rollbackKeyValue(JSON.parse(vm.row.request_params), 'paramNames', 'paramValues', 'baseKeys', 'baseId')        }        if(vm.row.is_json == '1') { // JSON格式          vm.request_params = JSON.parse(vm.row.request_params)        }        // 回填响应参数        vm.rollbackKeyValue(JSON.parse(vm.row.resp_header), 'httpNames', 'httpValues', 'httpKeys', 'httpId')      }    },    methods: {      // 回填keyValues值      rollbackKeyValue(objectData, names, values, keys, seq) {        let vm = this        let temp_names = [],            temp_values = [],            temp_keys = [];        let objKeys = Object.keys(objectData)        if(objKeys.length > 0) {          objKeys.map((name, index) => {            temp_names.push(name)            temp_values.push(objectData[name])            temp_keys.push(index)            vm[seq] = index          })          // 回填          vm.form.setFieldsValue({            [keys]: temp_keys, // 这个要先出来,保证UI被渲染出来了          })          vm.$nextTick(() => { // nextTick保证dom被渲染好之后进行下一步操作            vm.form.setFieldsValue({              [names]: temp_names,              [values]: temp_values            })          })        }      },      // 移除参数      removeParam(k) {        const { form } = this;        const baseKeys = form.getFieldValue('baseKeys');        if (baseKeys.length === 1) {          return;        }        form.setFieldsValue({          baseKeys: baseKeys.filter(key => key !== k),        });      },      // 移除http      removeHttp(k) {        const { form } = this;        const httpKeys = form.getFieldValue('httpKeys');        if (httpKeys.length === 1) {          return;        }        form.setFieldsValue({          httpKeys: httpKeys.filter(key => key !== k),        });      },      // 添加参数      addParam() {        const { form } = this;        const baseKeys = form.getFieldValue('baseKeys');        const nextKeys = baseKeys.concat(++this.baseId);        form.setFieldsValue({          baseKeys: nextKeys,        });      },      // 添加Http      addHttp() {        const { form } = this;        const httpKeys = form.getFieldValue('httpKeys');        const nextKeys = httpKeys.concat(++this.httpId);        form.setFieldsValue({          httpKeys: nextKeys,        });      }    },  };  </script>  复制代码
<style lang="less">  .mock-info {    .dynamic-delete-button {      cursor: pointer;      position: relative;      top: 4px;      font-size: 24px;      color: #999;      transition: all 0.3s;    }    .dynamic-delete-button:hover {      color: #777;    }    .dynamic-delete-button[disabled] {      cursor: not-allowed;      opacity: 0.5;    }  }  </style>  复制代码

嗯~这种实现的方式还是和舒服的,不用自己布局,不用自己再次思考逻辑;如果你想自己捣鼓一个,那你是真的闲,还不如花点时间捣鼓其他非编程的东西。

注意:能用react版本的ant design尽量用react版本的~

场景二

根据后台接口返回的字段来渲染。类型值对应不同的组件,如下:

  • 类型值1:单行文本组件
  • 类型值2:多行文本组件
  • 类型值3:单选组件
  • 类型值4:多选组件
  • 类型值5:文件上传组件

每种类型出现的次数是大于等于0,而且后端可配置必填或者非必填。嗯,下面实现它~

因为是移动端的业务,肯定是选UI框架帮我干活啊,这里我选了有赞的vant。用的还是vue去搭建工程,别问为啥不用react,公司给我时间,我就用react~这是业务线啊,想得倒是美,而且还是疫情期间,不压你时间就很好了。所以做完后,乖乖申请回去中台~

下面实现的思路,效果和关键代码~

  1. 动态组件,那么每个字段都要有一个字段标识该组件,这里后端没有配,那么我自己创建一个uuid(能叫得动后端,就叫后端配吧…)
 <!-- 进行字段的遍历 -->    <div v-for="(type, type_index) in alterFields">        <!-- 单行文本和多行文本区域 -->      <div class="advertise part" v-if="type.fieldType===1 || type.fieldType ===2">        <!-- 单行文本内容 -->        <!-- 多行文本内容 -->      </div>        <!-- 单选和多选区域 -->      <div class="advertise part" v-if="type.fieldType===3 || type.fieldType ===4">        <!-- 单选内容 -->        <!-- 多选内容 -->      </div>        <!-- 资源上传的区域 -->      <div class="advertise part" v-if="type.fieldType===5">          <!--文件上传内容-->      </div>      </div>  复制代码
  1. 后端返回类型(优先)/前端写死类型(备选),对后端返回的动态数据进行遍历,以展示不同类型的组件

我这里前端写死了,蓝瘦香菇,三个字"人真懒"。

types: [{          code: 1,          text: '单行文本'        }, {          code: 2,          text: '多行文本'        }, {          code: 3,          text: '单选'        }, {          code: 4,          text: '多选'        }, {          code: 5,          text: '文件上传'        }],  
  1. 编辑的时候,信息回填前要考虑动态数据时候已经发生改动(时刻以后端返回的动态数据为准来回填)
// 将返回的字段和编辑的字段进行配对,回填  let _alterFields = []  for (let i = 0; i < vm.alterFields.length; i++) {    let _item = vm.alterFields[i]    _alterFields.push(_item)    for (let j = 0; j < vm.editFileds.length; j++) {      if (_item.id === vm.editFileds[i].id) {        // 替换值        _alterFields.splice(i, 1, Object.assign(_item, {          fieldValue: vm.editFileds[i].fieldValue,          [_item.uuid]: vm.editFileds[i].fieldValue,        }))      }    }  }  vm.alterFields = _alterFields  
  1. 前端限制文件上传的大小,有必要自己做什么压缩文件之类的骚操作,这里是内嵌到app里面的,app里面已经对图片处理。上传文件不要直接调公司的服务,直接调上传到云的操作就行,不然公司服务会崩溃的~
// 文件资源的限制  prompt_for_oversize () {    this.$dialog({      title: '提  示',      text: '单个文件大小不应该大于10M',      confirmText: '了解',      showCancelBtn: false,      confirm () { }    })  }  
  1. 文件上传使用async await来操作,更加直观明了
realUploadFile (item) {    let vm = this    let temp_valueFiled = []    let be_upload = false    return new Promise(resolve => {      // 上传到云      let formData = new FormData();      for (let i = 0; i < item[item.uuid].length; i++) {        let row = item[item.uuid][i]        if (row.fileName) { // 编辑的时候存在文件就不用再上传到服务器了          temp_valueFiled.push(row)          if (temp_valueFiled.length === item[item.uuid].length) { resolve(temp_valueFiled) }        } else {          be_upload = true          console.log('row.file', row.file.size)          formData.append("files", row.file);        }      }        if (!be_upload) { // 不需要上传的时候直接返回        return      }        vm.api.apply.uploadMultiFiles(formData).then(res => {        console.log(res)        if (res.code === '00000') {          temp_valueFiled.push(...res.data)          resolve(temp_valueFiled)        } else {          vm.$toast({ msg: res.message || '上传失败,请重试!' })          vm.forbidden = false        }      })    })  }  复制代码
  1. 对单选组件进行处理,非必填的状态下,要允许取消勾选
// 处理单选框(如果是非必填字段,允许用户取消)  handleRadio (type, type_index, item_title) { // type是整个项目,type_index是类型遍历的索引, item_title是选中项目的名称    if (type.isRequired) { return } // 必选的单选框,啥都不做    let vm = this    let union = `${type['uuid']}_${item_title}` // 唯一的标识    if (vm.radioSet.has(union)) { // 存在集合中      vm.radioSet.delete(union)      vm.alterFields.splice(type_index, 1, Object.assign(type, {        [type.uuid]: ''      }))    } else {      if (vm.radioSet.size > 0) {        vm.radioSet.forEach(function (val) {          if (val.indexOf(`${type['uuid']}_`) >= 0) {            vm.radioSet.delete(val) // 移除当前组件的唯一标识的所有值          }        })      }      vm.radioSet.add(union) // 每个单选的组件只维护一个数据    }  },  复制代码

效果如下:

公司业务,我怂,不敢放全部代码