Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

疫情期间,写的两个场景 #62

Open
reng99 opened this issue Mar 29, 2020 · 0 comments
Open

疫情期间,写的两个场景 #62

reng99 opened this issue Mar 29, 2020 · 0 comments
Labels
blog a single blog javascript javascript tag

Comments

@reng99
Copy link
Owner

reng99 commented Mar 29, 2020

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

场景一

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

basic info01

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

basic info02

相关代码:

<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) // 每个单选的组件只维护一个数据
  }
},

...

效果如下:

basic info03

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

后话

谢谢各位看官的捧场~

@reng99 reng99 added blog a single blog javascript javascript tag labels Mar 29, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blog a single blog javascript javascript tag
Projects
None yet
Development

No branches or pull requests

1 participant