首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >文档驱动 —— 表单组件(一):表单元素组件 优点缺点选择文本类的Inputcheck 多选value的类型问题

文档驱动 —— 表单组件(一):表单元素组件 优点缺点选择文本类的Inputcheck 多选value的类型问题

作者头像
用户1174620
发布2020-09-15 09:59:15
7990
发布2020-09-15 09:59:15
举报

文档驱动

想要做到文档驱动表单,首先要做几个表单元素组件。基于原生的HTML5的表单元素,做了一下分类,比如文本类、数字、日期、选择等,具体如下图。 【图片】

然后就是

文档 >> json >> vue >> UI >>表单

这个流程了。 其中Vue提供了很方便的数据双向绑定的功能, UI提供了非常好看的视觉效果。 那么既然他们都做好了,我就不重复制造轮子,直接拿过来使用。

可能你会奇怪,UI库提供了一些列的组件,为啥还要自己封装组件呢? 主要原因就是使用方式和我的想法有点不太一样,比如element的select要这么用:(代码来自官方)

<template>
  <el-select v-model="value" placeholder="请选择">
    <el-option
      v-for="item in options"
      :key="item.value"
      :label="item.label"
      :value="item.value"
      :disabled="item.disabled">
    </el-option>
  </el-select>
</template>
<script>
  export default {
    data() {
      return {
        options: [{
          value: '选项1',
          label: '黄金糕'
        }, {
          value: '选项2',
          label: '双皮奶',
          disabled: true
        }, {
          value: '选项3',
          label: '蚵仔煎'
        }, {
          value: '选项4',
          label: '龙须面'
        }, {
          value: '选项5',
          label: '北京烤鸭'
        }],
        value: ''
      }
    }
  }
</script>

这是非常标准的用法,也非常灵活,只是有一点点麻烦。好吧,我承认是我太懒了,我不想每次用的时候都写这么多的代码(html+js)。我想这样写:

<template>
  <nfSelect v-model="modelValue" :meta="meta"/>
</template>
<script>
export default {
  name: 'Form',
  setup () {
    const modelValue = ref('') // model
    const meta = require('./FormDemo.json') // 加载meta信息,json格式
    
    return {
      modelValue,
      meta
    }
  }
}

</script>

三行代码搞定,一行html,两行js。 组件只需要设置两个属性,一个是model,一个是元数据(meta),也就是json格式的属性信息。

实现方法

其实方法也很简单,只需要自己做一个组件,把上面那段el的select(原生的HTML5测试通过,el的还没测试,应该可以吧)放进去,把需要的各种属性值(包含options的数据项)做成json通过meta属性传递进去就可以了。

优点

非常简单,可以大大减少代码量,而且还可以用v-for来遍历,这样就算再大的表单,一个for就搞定了。

缺点

灵活度不够,肯定没有直接使用select来的灵活。

选择

不过最终“懒惰战胜了灵活的需求”,我还是想按照我的想法做出来一套东东玩玩。

代码

文本类的Input

下面是文本类的input的封装方式,基于原生html5。为啥不用element呢?因为我跳过了vue2.*,直接使用vue3.0来写的,但是在安装element的时候,报了一大堆错误。 我基本功太差没搞不定,所以就先不用element了。 用原生的做验证我的想法是否可以实现,以后搞定了在加上其他UI。 本来我的想法就是基于每个UI都做一套,可以跨UI,甚至跨架构。

/** 文本类的,text、密码、url、邮件等 */
<template>
  <span>
    <input :id="'c' + meta.controlId"
    :type="type[meta.controlType]"
    :name="'c' + meta.controlId"
    :value="modelValue"
    :disabled="meta.disabled"
    :readonly="meta.readonly"
    :class="meta.class"
    :placeholder="meta.placeholder"
    :title="meta.title"
    :size="meta.size"
    :maxlength="meta.maxlength"
    :autocomplete="meta.autocomplete"
    :list="meta.optionKey"
    @input="myInput"
    :key="'ckey_'+meta.controlId">
    <!--文本框的备选项-->
    <datalist v-if="typeof(meta.optionKey)!=='undefined'" :id="meta.optionKey">
      <option :key="item.value" v-for="item in meta.optionList" :label="item.value" :value="item.title" />
    </datalist>
  </span>
</template>
<script>

export default {
  name: 'nf-form-input',
  model: {
    prop: 'modelValue',
    event: 'input'
  },
  props: {
    modelValue: String,
    meta: {
      type: Object,
      default: () => {
        return {
          // 通用
          controlId: Number, // 编号,区别同一个表单里的其他控件
          colName: String, // 字段名称
          controlType: Number, // 用类型编号表示type
          isClear: {
            // isClear  连续添加时是否恢复默认值
            type: Boolean,
            default: false
          },
          defaultValue: String, // 默认值
          autofocus: { // 是否自动获得焦点
            type: Boolean,
            default: false
          },
          disabled: {
            // 是否禁用
            type: Boolean,
            default: false
          },
          required: { // 必填
            type: Boolean,
            default: true
          },
          readonly: { // 只读
            type: Boolean,
            default: false
          },
          pattern: String, // 用正则做验证。
          class: String, // 'cssTxt input_t1'
          placeholder: String,
          title: String, // 提示信息
          size: Number, // 字符宽度
          maxlength: Number, // 最大字符数
          autocomplete: { // off
            type: String,
            default: 'on'
          },
          optionKey: String, // 备选文字标识
          optionItem: Object // 备选的选项
        }
      }
    }
  },
  data: () => {
    return {
      type: {
        101: 'text', // 单行文本框
        102: 'password', // 密码
        103: 'tel', // 电话
        104: 'email', // 电子邮件
        105: 'url', // url
        106: 'search' // 搜索
      }
    }
  },
  methods: {
    myInput: function (e) {
      var returnValue = event.target.value
      var colName = this.meta.colName // 字段属性名
      this.$emit('update:modelValue', returnValue) // 返回给调用者,vue3.0的新写法
      this.$emit('getvalue', returnValue, colName) // 返回给中间组件
    }
  }
}
</script>

check 多选

再贴一个多选的组件,使用type=”check”实现,这个因为要实现多选,所以代码有些不同。其他的代码都是雷同的,就不一一贴了。想看代码可以到这里看 。https://github.com/naturefwvue/nfComponents

/**多选组,返回逗号连接的value值,比如:“1,2,3” */
<template>
  <span>
    <!--多选组item.checked-->
    <label role="checkbox" v-for="item in meta.optionList"
      :class="meta.class"
      :key="'lblchks'+item.value">
        <input :id="'c'+meta.controlId"
        type="checkbox"
        :name="'c'+meta.controlId"
        :class="meta.class"
        :value="item.value"
        :checked="(','+modelValue+',').indexOf(','+item.value+',') != -1"
        :readonly="meta.readonly"
        :key="'chks'+item.value"
        @input="myCheck"
        >
        <span>{{item.title}}</span>
    </label>
  </span>
</template>
<script>

export default {
  name: 'nf-form-checks',
  model: {
    prop: 'modelValue',
    event: 'input'
  },
  props: {
    modelValue: String,
    meta: {
      type: Object,
      default: () => {
        return {
          controlId: Number, // 编号,区别同一个表单里的其他控件
          controlType: Number, // 用类型编号表示type
          colName: String, // 中文名称
          isClear: {
            // isClear  连续添加时是否恢复默认值
            type: Boolean,
            default: false
          },
          // 通用
          disabled: {
            // 是否禁用
            type: Boolean,
            default: false
          },
          required: { // 必填
            type: Boolean,
            default: true
          },
          class: String, // 
          title: String // 提示信息
        }
      }
    }
  },
  data: function () {
    return {
      optionsChecks: {}
    }
  },
  methods: {
    myCheck: function (e) {
      var returnValue = e.target.value
      var colName = this.meta.colName  
      if (e.target.checked) {
        this.optionsChecks[returnValue] = 1
      } else {
        delete this.optionsChecks[returnValue]
      }
   
      var arr = []
      for (var key in this.optionsChecks) {
        arr.push(key)
      }
      returnValue = arr.join(',')
      this.$emit('update:modelValue', returnValue)
      this.$emit('getvalue', returnValue, colName)
    }
  }
}
</script>

分还是合?

其实这些零散的组件可以合在一个组件里面的,代码也不会太多,但是我想来想去,还是分开的话,便于以后的扩展。

只是这么零散,用的时候还要想我到底用哪个组件,这不符合我懒惰的人设,所以我又做了一个“组合”组件, 就是把分散的各个组件,组成一个组件,这样在使用的时候引用这一个就可以了。

/** 表单元素的综合组件,根据类型自动加载相应的组件 */
<template>
  <span class="hello">
    <nfInput v-if="meta.controlType == 100" :modelValue="modelValue" @getvalue="sendValue" :meta="meta"/>
    <nfArea v-else-if="meta.controlType <= 119" :modelValue="modelValue" @getvalue="sendValue" :meta="meta"/>
    <nfNumber v-else-if="meta.controlType <= 139" :modelValue="modelValue" @getvalue="sendValue" :meta="meta"/>
    <nfDatetime v-else-if="meta.controlType <= 149" :modelValue="modelValue" @getvalue="sendValue" :meta="meta"/>
    <nfUpload v-else-if="meta.controlType <= 159" :modelValue="modelValue" @getvalue="sendValue" :meta="meta"/>
    <nfColor v-else-if="meta.controlType == 160" :modelValue="modelValue" @getvalue="sendValue" :meta="meta"/>
    <nfCheck v-else-if="meta.controlType == 180" :modelValue="modelValue" @getvalue="sendValue" :meta="meta"/>
    <nfChecks v-else-if="meta.controlType == 182" :modelValue="modelValue" @getvalue="sendValue" :meta="meta"/>
    <nfRadios v-else-if="meta.controlType == 183" :modelValue="modelValue" @getvalue="sendValue" :meta="meta"/>
    <nfSelect v-else-if="meta.controlType == 190" :modelValue="modelValue" @getvalue="sendValue" :meta="meta"/>
    <nfInputMore v-else-if="meta.controlType == 200" :modelValue="modelValue" @getvalue="sendValue" :meta="meta"/>
  </span>
</template>
<script>
import nfInput from '@/components/nf-form-textarea.vue' // 100-107
import nfArea from '@/components/nf-form-input.vue' // 100-107
import nfNumber from '@/components/nf-form-number.vue' // 131,132
import nfDatetime from '@/components/nf-form-datetime.vue' // 140-144
import nfUpload from '@/components/nf-form-upload.vue' // 150-151
import nfColor from '@/components/nf-form-color.vue' // 160
import nfCheck from '@/components/nf-form-check.vue' // 180
import nfChecks from '@/components/nf-form-checks.vue' // 182
import nfRadios from '@/components/nf-form-radios.vue' // 183
import nfSelect from '@/components/nf-form-select.vue' // 190
import nfInputMore from '@/components/nf-form-inputmore.vue' // 200

export default {
  name: 'nf-form-item',
  components: {
    nfInput,
    nfArea,
    nfNumber,
    nfDatetime,
    nfUpload,
    nfColor,
    nfCheck,
    nfChecks,
    nfRadios,
    nfSelect,
    nfInputMore
  },
  props: {
    modelValue: Object,
    meta: Object
  },
  methods: {
    sendValue: function (value, colName) {
      this.$emit('update:modelValue', value)
      this.$emit('getvalue', value, colName) // 返回给中间组件
    }
  }
}
</script>

value的类型问题

这里有个数据类型的问题,各个子组件可以规定 modelValue的类型,但是这个组合组件的类型怎么定呢?我写成了 object,虽然运行的时候虽然不会报红色的错误,但是总会报数据类型验证错误的提示,按F12,满眼全是,很烦。

one more thing

代码还在不断完善中,希望能够找到自同道合的人。 还有很多后续,比如meta是如何生成的,表单的代码到底是啥样的?还有查询和数据列表怎么办?等等都有解决方案。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文档驱动
  • 实现方法
    • 优点
      • 缺点
        • 选择
        • 代码
          • 文本类的Input
            • check 多选
            • 分还是合?
              • value的类型问题
              • one more thing
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档