前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >800行代码写了个表单

800行代码写了个表单

作者头像
terrence386
发布2022-07-14 21:55:06
4290
发布2022-07-14 21:55:06
举报
文章被收录于专栏:JavaScript高级程序设计

航天器达到环绕地球、脱离地球和飞出太阳系所需要的最小发射速度,分别称为第一宇宙速度(牛顿称之为环绕速度)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基础知识总结

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-06-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JavaScript高级程序设计 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 800多行代码的表单
  • 表单校验规则
  • 具体解决流程
  • 关于表单和表单的交互
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档