怎么個百變法呢,就是做一個組件,可以實現 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的目的了。