转载

疫情期间,写的两个场景

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

疫情期间,写的两个场景

嗯,我是用的 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"/> 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) // 每个单选的组件只维护一个数据
  }
},
复制代码

...

效果如下:

疫情期间,写的两个场景

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

原文  https://juejin.im/post/5e7dbe8751882573c74d3a8d
正文到此结束
Loading...