怎么个百变法呢,就是做一个组件,可以实现 input 的各种形态。
原生input的类型
先整理一下 input 的 type 都提供了哪些类型。 原:HTML5之前的类型。 新:HTML5提供的新的类型。
这里没有写 radio 和 checkbox,因为这两个被我划分为选择类型,同理还有submit。
我们可以依据这个类型定义一个属性:
const type = { 100: 'textarea', // 多行文本框 101: 'text', // 单行文本框 102: 'password', // 密码 103: 'tel', // 电话 104: 'email', // 电子邮件 105: 'url', // url 106: 'search', // 搜索 107: 'color', // 颜色 108: 'text', // 弹窗选择记录 110: 'date', // 日期 111: 'datetime-local', // 日期时间 112: 'time', // 时间 113: 'week', // 年月 114: 'month', // 年周 120: 'number', // 数字 121: 'range', // 滑块 130: 'file', // 上传文件 131: 'file', // 上传图片 140: 'fulltext', // 富文本编辑器 150: 'checkbox', // 勾选 152: 'checkboxs', // 多选组 153: 'radios', // 单选组 170: 'select', // 下拉列表框 单选 171: 'selects', // 列表框 多选 172: 'selectMore' // 联动下拉列表框 } 复制代码
具体使用方式下面说。
依据 input 的属性定义一个对象
然后我把可以把input的其他属性整理出来做一个对象:
const meta = reactive({ controlId: 1000, colName: 'companyName', controlType: 101, isClear: true, disabled: false, required: true, readonly: false, pattern: '', class: '', placeholder: '请输入公司名称', title: '公司名称', autocomplete: 'on', size: 30, maxlength: 100, optionList: [], tdCount: 1 }) 复制代码
准备工作做好了,我们开始做组件。
做一个多功能的文本类控件
模板部分
<template> <!----> <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.title" :value="item.value" /> </datalist> </template> 复制代码
这个比较简单,就是把 input 具有的属性都给绑定上。 这里要感谢vue的数据绑定功能,没有设置的属性会自动省略,这就不用我们一个一个判断是不是 undefined 了。
代码部分
export default { name: 'nf-h5-form-text', props: { modelValue: String, meta: metaInput }, emits: ['input'], setup (props, context) { console.log(props) // 提交数据 const myInput = (e) => { console.log(e) console.log(context) context.emit('update:modelValue', e.target.value) } return { type, myInput } } } 复制代码
这个比较简单了,设计两个属性,一个是 modelValue 绑定 input 的 value 的。 另一个是 meta,后面跟了一个 metaInput ,这个就是上面整理的 input 的属性对象,在组件的属性里面,改成了带验证的形式。
// 定义属性 const metaInput = { 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: { type: String, default: 'ant-input ant-input-sm' }, placeholder: String, title: String, // 提示信息 size: Number, // 字符宽度 maxlength: Number, // 最大字符数 autocomplete: { // off type: String, default: 'on' }, optionKey: String, // 备选文字标识 optionItem: Object // 备选的选项 } } } 复制代码
这种方式,不仅可以定义组件的属性,而且还能对属性做类型限制、设置默认值等功能。 这样可以让属性设置的更严谨一些。
如果父组件里面设置属性不符合要求,那么浏览器里面会给出来警告提示。
设计目的
为什么要这么设计呢?因为表单里面的元素是各种各样的,比如文本、日期、选择等等,一般需要一个一个写,即是是使用UI库里的form,也还是需要进行具体属性设置。
这个就有点郁闷了,说好的数据绑定呢? 我比较懒,对于这种需要一行一行写的情况比较厌恶,那么能不能简单一点呢,比如使用v-for进行循环?
想要循环,那么就必须规范接口,也就是属性和事件,这两个统一之后我们就可以进行循环了。
如果可以循环的话,那么再大的表单也不用担心了,一个循环搞定。
这里只是处理了一下文本类的控件,后面还要处理选择类的控件,还有其他控件。一路封装下来,最后就可以实现v-for的目的了。