經(jīng)常做后臺的小伙伴兒可能經(jīng)常要遭遇各種表單結(jié)構(gòu)非迹,input,select纯趋,時間憎兽、日期插件等等,各種屬性配置到吐吵冒,今天就帶大家來封裝一個可復(fù)用表單纯命。表單參考了網(wǎng)上的一些例子,再結(jié)合自己的需求痹栖,封裝了一個自己的業(yè)務(wù)表單組件亿汞。
先上圖:
首先給大家看一下結(jié)構(gòu)布局:
我們現(xiàn)實放了一個form表單。然后利用el-form-item來循環(huán)遍歷揪阿,給每一個el-form-item設(shè)置動態(tài)label疗我,動態(tài)rules(校驗規(guī)則)以及相對應(yīng)的prop。
然后我們給每一種類型的表單元素添加動態(tài)disabled的屬性南捂,控制可編輯狀態(tài)吴裤。
給屬性綁定動態(tài)表單值,并且添加placeholder提示語溺健;
對于select嚼摩,checkbox等多值域綁定的表單元素,配置項中提供options屬性矿瘦。
然后我們的圖片上傳用的是我們之前封裝的圖片上傳組件枕面,傳遞一個綁定值和一個可編輯狀態(tài)即可。
最后我們給每個表單元素添加了一個tooltip缚去,用于提示用戶每一個表單元素要填寫的內(nèi)容或者提示語潮秘。對于每一個label標簽,我們也做了最基本的處理易结,大于四個字我們就給變成4字加省略號枕荞,通過tooltip來查看標簽名稱。
<template>
<div>
<el-form :label-position="labelPosition" :inline="inline" :label-width="labelWidth" :model="myForm" ref="customForm">
<el-form-item v-for="(item,index) in formData" :label="returnLable(item.label)+':'" :rules="item.rules" :key="index" :prop="item.prop">
<el-tooltip v-if="item.type == 'input'" :effect="effect" :content="'請?zhí)顚?+item.label" placement="top">
<!-- prefix-icon 首部圖標--><!-- suffix-icon 尾部圖標-->
<el-input
v-model="myForm[item.prop]" clearable :suffix-icon="item.sufIcon" :show-password ="item.password"
:prefix-icon="item.preIcon" :placeholder="'選擇'+item.label" :disabled="item.disabled">
</el-input>
</el-tooltip>
<el-tooltip v-if="item.type == 'select'" :effect="effect" :content="'請選擇'+item.label" placement="top">
<el-select v-model="myForm[item.prop]" filterable clearable :multiple="item.multiple" filterable :placeholder="'選擇'+item.label" :disabled="item.disabled">
<el-option v-for="o in item.options" :key="o.value" :label="o.label" :value="o.value">
</el-option>
</el-select>
</el-tooltip>
<el-tooltip v-if="item.type == 'radio'" :effect="effect" :content="'請選擇'+item.label" placement="top">
<el-radio-group v-model="myForm[item.prop]" :disabled="item.disabled">
<el-radio v-for="o in item.options" :label="o.value" :placeholder="'選擇'+item.label">{{o.label}}</el-radio>
</el-radio-group>
</el-tooltip>
<el-tooltip v-if="item.type == 'checkbox'" :effect="effect" :content="'請選擇'+item.label" placement="top">
<el-checkbox-group v-model="myForm[item.prop]" :disabled="item.disabled">
<el-checkbox v-for="o in item.options" :label="o.value" :placeholder="'選擇'+item.label">{{o.label}}</el-checkbox>
</el-checkbox-group>
</el-tooltip>
<el-tooltip v-if="item.type == 'date'" :effect="effect" :content="'請選擇'+item.label" placement="top">
<el-date-picker
v-model="myForm[item.prop]" type="date"
value-format="yyyy-MM-dd"
:picker-options="pickerOptions"
:placeholder="'選擇'+item.label"
:disabled="item.disabled">
</el-date-picker>
</el-tooltip>
<el-tooltip v-if="item.type == 'datetime'" :effect="effect" :content="'請選擇'+item.label" placement="top">
<el-date-picker
v-model="myForm[item.prop]"
type="datetime"
value-format="yyyy-MM-dd HH:mm:ss"
:picker-options="pickerOptions"
:placeholder="'選擇'+item.label"
default-time="12:00:00"
:disabled="item.disabled">
</el-date-picker>
</el-tooltip>
<el-tooltip v-if="item.type == 'image'" :effect="effect" :content="'請選擇'+item.label" placement="top">
<upload-image v-model="myForm[item.prop]" :disabled="item.disabled"></upload-image>
</el-tooltip>
</el-form-item>
</el-form>
<el-form v-if="isHandle" style="padding-left: 20px;">
<el-form-item>
<slot name="handle"></slot>
</el-form-item>
</el-form>
</div>
</template>
下面是組件內(nèi)的方法及屬性定義
<script>
import uploadImage from '@/mycomponents/UploadImage/index.vue'
export default {
components:{uploadImage},
props:{
//ref命名
formName:{
type:String,
default:'right'
},
//操作欄
isHandle:{
type:Boolean,
default:true
},
//表單標簽位置
labelPosition:{
type:String,
default:'right'
},
//tooltip風(fēng)格是dark還是light
effect:{
type:String,
default:'light'
},
//表單元素是并列同行還是獨占一行
inline:{
type:Boolean,
default:true
},
//表單標簽寬度
labelWidth:{
type:String,
default:'100px'
},
//綁定的表單
myForm:{
type:Object,
default:()=>{}
},
//表單配置數(shù)據(jù)
formData:{
type:Array,
default:()=>[]
}
},
data(){
return {
pickerOptions : {
shortcuts: [{
text: '今天',
onClick(picker) {
picker.$emit('pick', new Date())
}
}, {
text: '昨天',
onClick(picker) {
const date = new Date()
date.setTime(date.getTime() - 3600 * 1000 * 24)
picker.$emit('pick', date)
}
}, {
text: '一周前',
onClick(picker) {
const date = new Date()
date.setTime(date.getTime() - 3600 * 1000 * 24 * 7)
picker.$emit('pick', date)
}
}]
}
}
},
methods:{
//過濾超長標簽
returnLable(label){
if(label.length>4){
return label.substring(0,4)+'...'
}
return label
},
// 表單驗證
async submitForm(prop) {
try {
return await this.$refs["customForm"].validate();
} catch (error) {
return error;
}
},
}
}
</script>
我們在頁面里使用時搞动,只需要調(diào)用一下校驗方法躏精,看是否通過校驗即可,這里的表單校驗方法返回的是一個promise鹦肿,所以 這里用async 和 await搭配使用來renturn校驗flag矗烛。
再來看我們的組件應(yīng)用頁面;(這里的具體屬性可根據(jù)element文檔自行查看箩溃。)
<template>
<div class="pages-container">
<dynamic-form :myForm="myForm" :formData="formData" ref="dynamic">
<div slot="handle">
<el-button type="primary" @click="onSubmit()">{{submitText}}</el-button>
<el-button type="warning">取消</el-button>
</div>
</dynamic-form>
<div>提交狀態(tài):{{text}}</div>
<!-- 表單使用說明 -->
<p>基本使用定義一個myForm(表單綁定字段的對象集合)瞭吃,再定義一個配置項formData(表單需要配置的驗證方法碌嘀、元素類型、元素標簽名稱等)</p>
<p>label:'用戶名',//表單元素的標簽名稱</p>
<p>prop:'name',//表單元素需要綁定的字段</p>
<p>type:'textInput',//表單元素類型歪架,類型在下方會有說明具體是什么元素</p>
<p>preIcon:'',//input輸入框等元素首部圖標股冗,寫空就是不顯示</p>
<p>sufIcon:'',//input輸入框等元素尾部圖標,寫空就是不顯示</p>
<p>password:'',//input輸入框是否為密碼輸入</p>
<p>disabled:'',//表單元素可編輯狀態(tài)</p>
<p>rules:Check(true, null, 'blur', '用戶名不能為空'),//校驗規(guī)則和蚪,借鑒的是網(wǎng)上一個大神的封裝方法止状。Check中填寫的四個參數(shù)分別是:是否必填、正則方法名稱攒霹、觸發(fā)動作怯疤、校驗提示語
</p>
<p>
<span>配置項屬性type值釋義:</span>
<span>input : 普通輸入框</span>
<span>select : 下拉框</span>
<span>radio : 單選</span>
<span>checkbox : 復(fù)選</span>
<span>date : 日期</span>
<span>dateTime : 日期時間</span>
<span>image : 圖片</span>
</p>
</div>
</template>
<script>
import DynamicForm from '@/mycomponents/Form/dynamic.vue'
import { Check } from '@/utils/rule.js'
export default {
components:{DynamicForm},
data(){
const options = [{
value: '1',
label: '黃金糕'
}, {
value: '2',
label: '雙皮奶'
}, {
value: '3',
label: '蚵仔煎'
}, {
value: '4',
label: '龍須面'
}, {
value: '5',
label: '北京烤鴨'
}]
return {
text:'',
submitText:'編輯',
//表單值綁定
myForm:{
name:'',
vscode:'',
prodType:'',
prodp:'2',
commissions:['1','5'],
commdate:'',
commdatetime:'',
images:['https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif?imageView2/1/w/80/h/80'],
},
//基本的表單配置項示例、有需要可自行添加其他屬性
formData:[
{
label:'用戶名',//標簽名稱
prop:'name',//字段值
type:'input',//表單元素類型
preIcon:'el-icon-search',//首部標簽剔蹋,input元素獨有
rules:Check(true, null, 'blur', '用戶名不能為空'),//校驗規(guī)則
disabled:true,
},
{
label:'管理員密碼',
prop:'vscode',
type:'input',
sufIcon:'',//尾部標簽,input元素獨有
password:true,//input是否是密碼類型
rules:Check(true, null, 'blur', '密碼不能為空'),
disabled:true,
},
{
label:'產(chǎn)品類型',
prop:'prodType',
type:'select',
rules:Check(true, null, 'change', '請選擇產(chǎn)品類型'),
options:options,
multiple:false,//select是否可以多選辅髓,多選綁定值要對應(yīng)變?yōu)閿?shù)組
disabled:true,
},
{
label:'產(chǎn)品分類',
prop:'prodp',
type:'radio',
rules:Check(true, null, 'change', '請選擇產(chǎn)品分類'),
options:options,//屬性選項值
disabled:true,
},
{
label:'訂單選擇',
prop:'commissions',
type:'checkbox',
rules:Check(true, null, 'change', '請訂單選擇'),
options:options,//屬性選項值
disabled:true,
},
{
label:'訂單日期',
prop:'commdate',
type:'date',
rules:Check(true, null, 'change', '請選擇訂單日期'),
disabled:true,
},
{
label:'訂單日期時間',
prop:'commdatetime',
type:'datetime',
rules:Check(true, null, 'change', '請選擇訂單日期時間'),
disabled:true,
},
{
label:'訂單圖標',
prop:'images',
type:'image',
rules:Check(true, null, 'change', '請選擇訂單圖標'),
disabled:true,
},
]
}
},
mounted() {
},
methods:{
//點擊提交泣崩,校驗表單,進行業(yè)務(wù)操作
async onSubmit(){
if(this.submitText == '編輯'){
this.formData.forEach(item=>{
item.disabled = false;
})
this.submitText = '提交'
}else{
this.formData.forEach(item=>{
item.disabled = true;
this.submitText = '編輯'
})
let flag = await this.$refs.dynamic.submitForm();
if(flag){
this.text = "success submit"
}else{
this.text = "fail submit"
}
}
},
}
}
</script>
我的rules校驗方法是在網(wǎng)上看到的一個大神的校驗方法洛口,了解詳細矫付,拿來即用,給大家看一下第焰。
//Check方法
//校驗規(guī)則列表(可擴展)
const rules = {
URL(url) {
const regex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?"\\+&%$#=~_-]+))*$/
return valid(url, regex, "URL格式不正確")
},
LowerCase(str) {
const regex = /^[a-z]+$/
return valid(str, regex, "只能輸入小寫字母")
},
UpperCase(str) {
const regex = /^[A-Z]+$/
return valid(str, regex, "只能輸入大寫字母")
},
Alphabets(str) {
const regex = /^[A-Za-z]+$/
return valid(str, regex, "只能輸入字母")
},
Email(email) {
const regex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
return valid(email, regex, "郵箱地址格式不正確")
},
Mobile(mobile) {
const regex = /^1\d{10}$/
return valid(mobile, regex, "手機號格式不正確")
},
Phone(phone) {
const regex = /^(0\d{2,3})?-?\d{7,8}$/
return valid(phone, regex, "電話號碼格式不正確")
},
Postcode(postcode) {
const regex = /^[0-9][0-9]{5}$/
return valid(postcode, regex, "郵編格式不正確")
},
Number(num) {
const regex = /^\d+$/
return valid(num, regex, "只能輸入純數(shù)字")
},
Fax(fax) {
const regex = /^(\d{3,4}-)?\d{7,8}$/
return valid(fax, regex, "傳真格式不正確")
},
Int(num) {
const regex = /^((0)|([1-9]\d*))$/
return valid(num, regex, "只能輸入非負整數(shù)")
},
IntPlus(num){
const regex = /^[1-9]\d*$/
return valid(num, regex, "只能輸入正整數(shù)")
},
Float1(num){
const regex = /^-?\d+(\.\d)?$/
return valid(num, regex, "只能輸入數(shù)字买优,最多一位小數(shù)")
},
Float2(num){
const regex = /^-?\d+(\.\d{1,2})?$/
return valid(num, regex, "只能輸入數(shù)字,最多兩位小數(shù)")
},
Float3(num){
const regex = /^-?\d+(\.\d{1,3})?$/
return valid(num, regex, "只能輸入數(shù)字挺举,最多三位小數(shù)")
},
FloatPlus3(num){
const regex = /^\d+(\.\d{1,3})?$/
return valid(num, regex, "只能輸入數(shù)字杀赢,最多三位小數(shù)")
},
Encode(code){
const regex = /^(_|-|[a-zA-Z0-9])+$/
return valid(code, regex, "編碼只能使用字母、數(shù)字湘纵、下劃線脂崔、中劃線")
},
Encode2(code){
const regex = /^[a-zA-Z0-9]+$/
return valid(code, regex, "編碼只能使用字母、數(shù)字")
},
Encode3(code){
const regex = /^(_|[a-zA-Z0-9])+$/
return valid(code, regex, "編碼只能使用字母梧喷、數(shù)字砌左、下劃線")
},
IdCard(code){
const regex = /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/
return valid(code, regex, "請輸入正確的身份證號碼")
},
USCC(code){
const regex = /^[0-9A-Z]{18}/
return valid(code, regex, "請輸入正確的社會信用號")
},
CarNum(code){
const regex = /^(([京津滬渝冀豫云遼黑湘皖魯新蘇浙贛鄂桂甘晉蒙陜吉閩貴粵青藏川寧瓊使領(lǐng)][A-Z](([0-9]{5}[DF])|([DF]([A-HJ-NP-Z0-9])[0-9]{4})))|([京津滬渝冀豫云遼黑湘皖魯新蘇浙贛鄂桂甘晉蒙陜吉閩貴粵青藏川寧瓊使領(lǐng)][A-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9掛學(xué)警港澳使領(lǐng)]))$/i
return valid(code, regex, "請輸入正確的車牌號")
},
CNandEN(code){
const regex = /^[a-zA-Z\u4e00-\u9fa5]+$/
return valid(code, regex, "只能使用中文、英文")
},
MobileOrPhone(val){
const result = /^1\d{10}$/.test(val) || /^(0\d{2,3})?-?\d{7,8}$/.test(val)
return valid(result, null, "手機或電話號格式不正確")
}
}
//val:String 要校驗的值
//regex:RegExp 校驗正則,不是正則時val作為result的值
//msg:String 校驗不通過的錯誤信息
function valid(val, regex, msg){
return {result: regex instanceof RegExp? regex.test(val) : !!val, errMsg: msg}
}
//required:Boolean 是否必填項铺敌,選填汇歹,默認"true"
//type:String/Function 校驗類型,選填偿凭,
// String時必須是上面rules中存在的函數(shù)名产弹,
// Function時只接收一個參數(shù)(輸入值),返回格式: {result:Boolean, errMsg:String}
//trigger:String 觸發(fā)動作弯囊,選填取视,默認"blur"
//nullMsg:String 未輸入的提示語笤妙,選填诵盼,required=true時有效
export function Check(required=true, type, trigger="blur", nullMsg="該字段為必填項"){
const rule = { required: !!required, trigger}
let check = null
if(typeof type === "function"){
check = type
}else{
check = type ? rules[type+""] : null
}
if(check){//存在規(guī)則時添加規(guī)則
rule.validator = (r, v, c) => {
const {result, errMsg} = check(v)
if(required){
//必填項: null,undefined,""," " 都算無輸入內(nèi)容
return (v==null || (v+"").trim()==="") ? c(new Error(nullMsg)) : result ? c() : c(new Error(errMsg))
}
//選填項: null,undefined,"" 都算無輸入內(nèi)容," "會被校驗
return (v==null || (v+"")==="" || result) ? c() : c(new Error(errMsg))
}
}else{
rule.message = nullMsg
}
return [rule]
}
校驗方法很完美。
寫到這里弟塞,我們的一個簡單的form表單封裝就完成了。有需要的小伙伴兒可以添加更多的元素進去叠萍,例如開關(guān)谓着,例如radio-button等等,這里的封裝元素也只添加了最基本的元素锐秦,想要的更多可以自行添加咪奖,示例都在上面。大家覺得對你有幫助的酱床,可以點個贊Q蛘浴!扇谣!謝謝C两荨!