800行代码写了个表单

2022-07-14 21:55:06 浏览数 (1)

航天器达到环绕地球、脱离地球和飞出太阳系所需要的最小发射速度,分别称为第一宇宙速度(牛顿称之为环绕速度)7.9km/s、第二宇宙速度(脱离速度)11.2km/s和第三宇宙速度(太阳的逃逸速度)16.7km/s。

最近一直在忙别的事情,没有更新。接下来如果不忙的时候应该会重新巩固一下基础知识,然后再看看框架源码,研究一下DSL方案的低代码实现方式,研究一下如果实现一个web版的光影魔术手都需要那些技术点。

800多行代码的表单

说一点最近做的东西,一个相对复杂一点的表单。其原型大致如下:

如图: 该表单分三个步骤,第一步,可以为【系统】(这里为了避嫌用系统两个字代替)配置基本信息。第二步可以为【系统】添加用户信息,且可以添加多个用户,用户客户设置为默认用户,当其中一个用户为默认状态时,其他则取消默认状态,是一个互斥的关系。第三部最为复杂,存在7个配置项,7个配置项中有四个可以添加多条记录,另外三个是系统物料配置(banner图上传及详情页图片上传)其中banner图也可以上传多张。如图:

右上角的开关关闭后,卡片内容收起;右上角开关打开后,卡片内容展开;同时点击添加按钮,添加新的上传banner区域,点击删除,移除对应的banner。右上角开关逻辑适用于7个配置项,同时7个配置项中有两个配置项是数组形式,且每项配置都带同样功能的开关,合计开关共计15个。

表单校验规则

当开关关闭时,卡片内容不校验。当开关开启时,对卡片内容全部校验。

具体解决流程

对于复杂的业务,通常情况还是将其拆分成几个简单的业务组件。比如,因为该项目采用vue框架进行开发,在该表单中拆出来三个组件baseInfoForm,userInfoForm,ruleInfoForm。分别写三个步骤的逻辑。将对应的表单信息baseInfo, userInfo,ruleInfo存入store, 通过mapstate或computed,映射到组件内,然后绑定表单,实时更新。

表单验证部分,在对应的组件设置ref,某些表单内容通过遍历数组对象进行渲染,设置prop=.${keyName},在组件上配置相应的校验规则。

表单提交时,通过this.refs[formName].validate(v=>{...})进行校验,校验通过后,调用服务端接口进行数据提交。

一个比较直观的例子是早几年前写的:

代码语言:javascript复制
<template>
  <div>
    <div class="funcs-box func-set"
         v-for="(item, key) in actions"
         :key="key">
      <div class="header">
        <h4 class="title">功能{{key 1}}</h4>
        <div class="handle">
          <Button type="error"
                  size="small"
                  @click="deleteFunc(key)"
                  style="float:right;">删除功能
          </Button>
        </div>
      </div>
      <Form :model="item"
            :rules="ruleValidate"
            :label-width="50"
            style="width:300px"
            ref="funcForm"
            >
        <FormItem prop="name"
                  label="名称">
          <Input type="text"
                 v-model="item.name"
                 placeholder="名称" />
        </FormItem>
        <FormItem label="标题"
                  prop="title">
          <Input type="text"
                 v-model="item.title"
                 placeholder="标题" />
        </FormItem>
        <FormItem label="类型"
                  prop="type">
          <Select v-model="item.type"
                  >
            <Option v-for="inner in item.typeArray"
                    :value="inner.value"
                    :key="inner.value">{{ inner.label }}</Option>
          </Select>
        </FormItem>
      </Form>
      <div v-for="(inner, index) in item.content"
           class="params-item"
           :key="index">
        <row>
           <Button type="error"
                  size="small"
                  @click="deleFuncAttr(key,index)"
                  style="float:right;">删除该参数
          </Button>
        </row>
        <p class="title">参数{{index   1}}:</p>
        <Form :model="inner"
              :label-width="80"
              :rules="innerRuleValidate"
              ref="funcFormParams"
              style="width:300px"
              >
          <FormItem label="名称"
                    prop="name">
            <Input type="text"
                   v-model="inner.name"
                   placeholder="请输入名称" />
          </FormItem>
          <FormItem label="关联属性"
                    :label-width="80">
            <Select v-model="inner.relateName"
                    >
              <Option v-for="innerItem in associatedProperty"
                      :value="innerItem.value"
                      :key="innerItem.value">{{ innerItem.label }}
              </Option>
            </Select>
          </FormItem>
        </Form>
      </div>
        <row class="add-func">
          <i-col :span='4'>
            <Button icon="plus-round"
                    @click="addNewFuncParam(key)">引用属性</Button>
          </i-col>
        </row>
    </div>
    <template>
      <row class="add-func">
        <i-col :span='4'>
          <Button icon="plus-round"
                  @click="addNewFunc">增加新功能</Button>
        </i-col>
      </row>
    </template>
  </div>
</template>
<script>
export default {
  name: 'funcs',
  props: {
    actions: Array,
    associatedProperty: Array
  },
  data() {
    return {
      funcs: [
        {
          name: '启动服务',
          attrID: 'func_start',
          type: '指令下发',
          params: [
            {
              name: '启动类别',
              attrID: 'start_type',
              type: '整数',
              length: '2',
              default: '1',
              required: '是'
            }
          ]
        }
      ],
      ruleValidate: {
        name: [
          {
            required: true,
            message: '请填写英文功能名称!',
            trigger: 'blur',
            type: 'string',
            pattern: /[a-zA-Z]/
          }
        ],
        title: [
          {
            required: true,
            message: '请填写功能标题!',
            trigger: 'blur',
            type: 'string'
          }
        ],
        type: [{ required: true, message: '请选择类型!', trigger: 'change' }]
      },
      innerRuleValidate: {
        name: [
          {
            required: true,
            message: '请填写英文参数名称!',
            trigger: 'blur',
            type: 'string',
            pattern: /[a-zA-Z]/
          }
        ]
      }
    }
  },
  mounted() {
  },
  methods: {
    addNewFunc() {
      this.$store.commit('templateManage/addActions')
    },
    typeChange(key, index) {
      this.actions[key].content[index].defaultValue = ''
    },
    addNewFuncParam(index) {
      for (let i = 0; i < this.actions.length; i  ) {
        if (i === index) {
          this.actions[i].content.push({
            name: '',
            relateName: ''
          })
        }
      }
      this.$store.commit('templateManage/setActions', this.actions)
    },
    deleteFunc(index) {
      for (let i = 0; i < this.actions.length; i  ) {
        if (i === index) {
          this.actions.splice(index, 1)
        }
      }
      this.$store.commit('templateManage/setActions', this.actions)
    },
    deleFuncAttr(parentIndex, index) {
      for (let i = 0; i < this.actions.length; i  ) {
        if (parentIndex === i) {
          this.actions[i].content.splice(index, 1)
        }
      }
      this.$store.commit('templateManage/setActions', this.actions)
    },
    checkFuncName() {
      let self = this
      if (this.actions.length > 0) {
        this.$refs.funcForm.forEach(item => {
          item.validate(valid => {
            if (valid) {
              self.checkFuncParams()
            } else {
              this.$emit('checkSuccess', false)
            }
          })
        })
      } else {
        this.$emit('checkSuccess', true)
      }
    },
    checkFuncParams() {
      this.$refs.funcFormParams.forEach(item => {
        item.validate(valid => {
          if (valid) {
            this.$emit('checkSuccess', true)
          } else {
            this.$emit('checkSuccess', false)
          }
        })
      })
    },
    checkForm() {
      return this.checkFuncName()
    }
  },
  watch: {
    actions: {
      handler(curVal, oldVal) {
        this.$store.commit('templateManage/setActions', curVal)
      },
      deep: true
    }
  }
}
</script>

关于表单和表单的交互

其实对于表单来说,很多时候没必要设计这么复杂,复杂的设计通常表现出来的本质是对业务的理解不够深刻。

拿上面写的那个复杂表单来说,其实也是三个功能,系统管理用户管理配置管理,系统创建成功以后,可以给该系统配置相应的用户物料各种规则即可。这样一来界面可能会增加一些,但是复杂度却可以降低很多。

对于表单的交互,个人的理解是复杂的表单开新界面,简单的表单直接弹窗展示。因为对于用户来说,交互越简单越好,操作越少越好。

总结

800行代码写个表单也好,80行代码写个表单也好,只要能实现业务功能,理解业务本质,又有什么区别呢?

技术原本就是为业务服务的

javascript基础知识总结

0 人点赞