前言
上篇table 表格封裝 講到項(xiàng)目中經(jīng)常會(huì)用到 table 表格,所以做了封裝。當(dāng)然,form 表單使用的頻率依然很高,所以和封裝 table 表格的思路相似,對(duì) form 表單也做了一個(gè)二次封裝的組件。
效果圖
預(yù)覽地址
-
查看國內(nèi)預(yù)覽站
-
查看國外預(yù)覽站
1. EasyForm 表單組件封裝
src/components/EasyForm/index.vue
Form 表單組件封裝
<template>
<el-form @submit.prevent :model="formModel" v-bind="_options" ref="formRef">
<template v-for="(item, index) in fieldList" :key="index">
<!-- 單選框 -->
<el-form-item :label="item.label" v-if="item.type === 'radio'" :rules="item.rules" :prop="[item.field]">
<el-radio-group v-model="formModel[item.field]" :disabled="item.disabled">
<el-radio
:label="val[item.options?.valueKey || 'value']"
size="large"
v-for="val in item.options?.data"
:key="val[item.options?.valueKey || 'value']">
{{ val[item.options?.labelkey || 'label'] }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 復(fù)選框 -->
<el-form-item
:label="item.label"
v-else-if="item.type === 'checkbox'"
:rules="item.rules"
:prop="[item.field]">
<el-checkbox-group v-model="formModel[item.field]" :disabled="item.disabled">
<el-checkbox
v-for="c in item.options?.data"
:key="c[item.options?.valueKey || 'value']"
:label="c[item.options?.valueKey || 'value']"
>{{ c[item.options?.labelkey || 'label'] }}</el-checkbox
>
</el-checkbox-group>
</el-form-item>
<!-- 下拉框 -->
<el-form-item
:label="item.label"
v-else-if="item.type === 'select'"
:rules="item.rules"
:prop="[item.field]">
<!-- <EasySelect
v-model="formModel[item.field]"
clearable
:disabled="item.disabled"
:label-key="item.options?.labelkey"
:value-key="item.options?.valueKey"
:select-data="item.options?.data" /> -->
<el-select v-model="formModel[item.field]" :placeholder="item.options?.placeholder || '請(qǐng)選擇'" :clearable="item.clearable">
<el-option
v-for="s in item.options?.data"
:key="s[item.options?.valueKey || 'value']"
:label="s[item.options?.labelkey || 'label']"
:value="s[item.options?.valueKey || 'value']" />
</el-select>
</el-form-item>
<!-- 默認(rèn)輸入框 -->
<el-form-item :label="item.label" :rules="item.rules" :prop="[item.field]" v-else>
<el-input
v-model="formModel[item.field]"
:readonly="item.readonly"
:type="item.type ?? 'text'"
:placeholder="item.placeholder || item.label"
:disabled="item.disabled"
:showPassword="item.showPassword"
:clearable="item.clearable"
@keyup.enter="handleKeyUp(item.enterable)"/>
</el-form-item>
</template>
<el-form-item>
<slot name="buttons" :model="formModel" :formRef="formRef">
<el-button type="primary" @click="onSubmit(formRef)">{{ _options.submitButtonText }}</el-button>
<el-button v-if="_options.showResetButton" type="info" @click="resetForm(formRef)">
{{ _options.resetButtonText }}
</el-button>
<el-button v-if="_options.showCancelButton" @click="emit('cancel')">
{{ _options.cancelButtonText }}
</el-button>
</slot>
</el-form-item>
</el-form>
</template>
<script lang="ts" setup>
import type { FormInstance } from 'element-plus'
import { ComputedRef, ref, computed } from 'vue'
// 父組件傳遞的值
interface Props {
fieldList: Form.FieldItem[]
model?: Record<Form.FieldItem['field'], Form.FieldItem['value']>
options?: Form.Options
}
// 表單的數(shù)據(jù)
const formModel = ref<Record<string, any>>({})
const formRef = ref<FormInstance>()
const props = defineProps<Props>()
// 設(shè)置option默認(rèn)值,如果傳入自定義的配置則合并option配置項(xiàng)
const _options: ComputedRef<Form.Options> = computed(() => {
const option = {
labelPosition: 'right',
disabled: false,
submitButtonText: '提交',
resetButtonText: '重置',
cancelButtonText: '取消'
}
return Object.assign(option, props?.options)
})
interface EmitEvent {
(e: 'submit', params: any): void
(e: 'reset'): void
(e: 'cancel'): void
}
const emit = defineEmits<EmitEvent>()
defineExpose({
formRef
})
// 根據(jù)fieldList初始化model, 如果formModel有傳值就用傳遞的model數(shù)據(jù)模型,否則就給上面聲明的formModel設(shè)置相應(yīng)的(key,value) [item.field], item.value是表單的默認(rèn)值(選填)
watch(
() => props.model,
() => {
props.fieldList.map((item: Form.FieldItem) => {
// 如果類型為checkbox,默認(rèn)值需要設(shè)置一個(gè)空數(shù)組
const value = item.type === 'checkbox' ? [] : ''
props.model ? (formModel.value = props.model) : (formModel.value[item.field] = item.value || value)
})
},
{ immediate: true }
)
// 提交按鈕
const onSubmit = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.validate((valid) => {
if (valid) {
emit('submit', formModel.value)
} else {
return false
}
})
}
// 輸入框回車事件
const handleKeyUp = (enterable: boolean | undefined) => {
if (!enterable) return
onSubmit(formRef.value)
}
// 重置
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
}
</script>
<style lang="scss" scoped></style>
復(fù)制完Form組件的代碼后,會(huì)報(bào)紅線,F(xiàn)orm.XXXXX 找不到,這個(gè)是全局類型聲明。 聲明文件已在下方貼出來,直接復(fù)制進(jìn)項(xiàng)目中, 紅色警告自然消失。
2. 基本表單使用
src/views/form/index.vue
<template>
<el-card class="mb-5">
<template #header> 基本表單 </template>
<easy-form :fieldList="fieldList" :model="model" @submit="handleBaseSubmit">
<!-- 如果不使用默認(rèn)的按鈕可以使用插槽自定義內(nèi)容, 插槽返回的model就是當(dāng)前表單的數(shù)據(jù) -->
<!-- <template #buttons="{ model }">
<el-button">提交</el-button>
</template> -->
</easy-form>
</el-card>
</template>
<script lang="ts" setup>
import { exampleForm } from '@/config/form'
// import { ref } from 'vue'
// import EasyForm from '@/components/EasyForm/index.vue'
// 本項(xiàng)目EasyForm組件自動(dòng)引入,如復(fù)制此代碼,需根據(jù)路徑引入Form組件后使用
const fieldList: Form.FieldItem[] = exampleForm.base
const model = ref<Record<string, any>>({
name: '張三',
gender: 1,
hobbies: [1],
job: 3,
readonly: '只讀輸入框',
summary: '尤雨溪懂個(gè)錘子vue是什么梗'
})
/**
* 注意: model數(shù)據(jù)模型非必填項(xiàng),如果僅僅是用于數(shù)據(jù)收集,model參數(shù)可以不用填,表單的submit事件會(huì)返回所有搜集的數(shù)據(jù)對(duì)象
* 如果是編輯的情況下,頁面需要回顯數(shù)據(jù),則model數(shù)據(jù)模型必須要填寫
*/
const handleBaseSubmit = (model: Record<string, any>) => {
console.log(model)
}
</script>
<style lang="scss" scoped></style>
3. 自定義 key
src/views/form/index.vue
<template>
<el-card class="mb-5">
<template #header> 自定義key </template>
<easy-form :fieldList="customKeyFieldList" :model="model2" />
</el-card>
</template>
<script lang="ts" setup>
import { exampleForm } from '@/config/form'
// import { ref } from 'vue'
// import EasyForm from '@/components/EasyForm/index.vue'
// 本項(xiàng)目EasyForm組件自動(dòng)引入,如復(fù)制此代碼,需根據(jù)路徑引入Form組件后使用
const customKeyFieldList: Form.FieldItem[] = exampleForm.customkeyForm
const model2 = ref<Record<string, any>>({
name: '自定義key',
gender: 1
})
/**
* 注意: 如果使用到checkbox,radio,或者select等組件,需要傳入組件額外需要的數(shù)據(jù),本組件默認(rèn)設(shè)定的讀取數(shù)據(jù)的字段是 label, value
* 可參考下方聲明文件 FiledItem options的參數(shù)類型描述
* 比如,當(dāng)前傳入的data數(shù)據(jù)字段名和label、value不匹配,可使用預(yù)留的參數(shù) labelkey, valueKey指定字段名
* customkeyForm: [
{ label: '標(biāo)題', field: 'name' },
{ label: '性別', field: 'gender', type: 'radio', options: { labelkey: 'title', valueKey: 'val', data: [{ title: '男', val: 1 }, { title: '女', val: 0 }] } },
],
*/
const handleBaseSubmit = (model: Record<string, any>) => {
console.log(model)
}
</script>
<style lang="scss" scoped></style>
4. 自定義表單驗(yàn)證
src/views/form/index.vue
<template>
<el-card class="mb-5">
<template #header> 自定義驗(yàn)證的表單 (使用slot自定義按鈕) </template>
<easy-form :fieldList="ruleFieldList">
<!-- 如果不使用默認(rèn)的按鈕可以使用插槽自定義內(nèi)容, 插槽返回的model就是當(dāng)前表單的數(shù)據(jù), formRef是當(dāng)前表單的FormInstance -->
<template #buttons="{ model, formRef }">
<el-button @click="handleSubmit(model, formRef)">保存</el-button>
</template>
</easy-form>
</el-card>
</template>
<script lang="ts" setup>
import type { FormInstance } from 'element-plus'
import { exampleForm } from '@/config/form'
// import EasyForm from '@/components/EasyForm/index.vue'
// 本項(xiàng)目EasyForm組件自動(dòng)引入,如復(fù)制此代碼,需根據(jù)路徑引入Form組件后使用
const ruleFieldList: Form.FieldItem[] = exampleForm.ruleForm
/**
* 如果用到了表單驗(yàn)證,又使用slot自定義按鈕的話,需要自行實(shí)現(xiàn)驗(yàn)證邏輯
* 組件內(nèi)部已經(jīng)集成驗(yàn)證,及重置邏輯。表單驗(yàn)證建議使用內(nèi)置的提交按鈕。當(dāng)通過驗(yàn)證規(guī)則,內(nèi)置提交按鈕才會(huì)出發(fā)submit事件
*/
// 下方是使用slot自定義按鈕,需要自己實(shí)現(xiàn)驗(yàn)證邏輯
const handleSubmit = (model: any, formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.validate((valid) => {
if (valid) {
console.log('submit!', model)
} else {
console.log('error submit!')
return false
}
})
}
</script>
<style lang="scss" scoped></style>
src/config/form.ts
表單配置項(xiàng), 參數(shù)請(qǐng)參考下面參數(shù)介紹
// 自定義驗(yàn)證郵箱方法
const checkEmail = (rule: any, value: any, callback: any) => {
if (!value) callback(new Error('Please input the email'))
const regExp = /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.(com|cn|net)$/
regExp.test(value) ? callback() : callback(new Error('Please input the correct email address'))
}
// // 自定義驗(yàn)證表單配置數(shù)據(jù)
// export const validationFormFieldList = [
// { label: '姓名', field: 'name', rules: [{ required: true, message: 'name is required' }] },
// { label: '郵箱', field: 'email', rules: [{ required: true, validator: checkEmail }] },
// ] as Form.FieldItem[]
// 表單配置示例
export const exampleForm = {
base: [
{ label: '姓名', field: 'name', disabled: true },
{ label: '性別', field: 'gender', type: 'radio', options: { data: [{ label: '男', value: 1 }, { label: '女', value: 0 }] } },
{ label: '愛好', field: 'hobbies', type: 'checkbox', options: { data: [{ label: '吃飯', value: 1 }, { label: '睡覺', value: 2 }, { label: '寫代碼', value: 3 }] } },
{ label: '工作', field: 'job', type: 'select', options: { data: [{ label: '吃飯', value: 1 }, { label: '睡覺', value: 2 }, { label: '寫代碼', value: 3 }] } },
{ label: '密碼', field: 'password', type: 'password', placeholder: '這是一個(gè)密碼輸入框' },
{ label: '只讀', field: 'readonly', readonly: true, placeholder: '這是一個(gè)只讀輸入框' },
{ label: '留言板', field: 'summary', type: 'textarea', placeholder: '留言板' },
],
customkeyForm: [
{ label: '標(biāo)題', field: 'name' },
{ label: '性別', field: 'gender', type: 'radio', options: { labelkey: 'title', valueKey: 'val', data: [{ title: '男', val: 1 }, { title: '女', val: 0 }] } },
],
ruleForm: [
{ label: '姓名', field: 'name', rules: [{ required: true, message: 'name is required' }] },
{ label: '郵箱', field: 'email', rules: [{ required: true, validator: checkEmail }] },
]
} as Record<string, Form.FieldItem[]>
src/types/form/index.d.ts
參數(shù)類型聲明(聲明為全局的類型,方便使用)文章來源:http://www.zghlxwxcb.cn/news/detail-470575.html
import { type FormItemRule } from 'element-plus'
export = Form;
export as namespace Form;
declare namespace Form {
type ItemType = 'password' | 'text'|'textarea' | 'radio' | 'checkbox' | 'select'
// 當(dāng)FiledItem的type === 'radio' | 'checkbox'時(shí),options的參數(shù)類型
interface IFieldOptions {
labelkey?: string,
valueKey?: string,
placeholder?: string,
data: Record<string, any>[]
}
interface Options {
labelWidth?: string | number,
labelPosition?: 'left' | 'right' | 'top',
disabled?: boolean,
size?: 'large' | 'small' | 'default',
showResetButton?: boolean, // 是否展示重置按鈕
showCancelButton?: boolean, // 是否展示取消按鈕
submitButtonText?: string,
resetButtonText?: string,
cancelButtonText?: string
}
interface FieldItem {
label?: string,
labelWidth?: string | number, // 標(biāo)簽寬度,例如 '50px'。 可以使用 auto。
field: string,
type?: ItemType,
value?: any,
placeholder?: string,
disabled?: boolean,
readonly?: boolean,
options?: IFieldOptions,
rules?: FormItemRule[]
clearable?: boolean // 是否可清空
showPassword?: boolean, // 是否顯示切換密碼圖標(biāo)
enterable?: boolean, // 當(dāng)為輸入框時(shí),是否啟用回車觸發(fā)提交功能
}
}
參數(shù)介紹
Form 屬性
參數(shù) | 說明 | 類型 | 是否必填 | 默認(rèn)值 |
---|---|---|---|---|
model | 表單數(shù)據(jù)對(duì)象 | Record<string, any> | 否 | — |
options | 自定義配置 | object | 否 | — |
fieldList | formItem 配置數(shù)組 | Array<object> | 是 | — |
Options 配置項(xiàng)
參數(shù) | 說明 | 類型 | 是否必填 | 默認(rèn)值 |
---|---|---|---|---|
labelWidth | 標(biāo)簽的長度,例如 ‘50px’。 作為 Form 直接子元素的 form-item 會(huì)繼承該值。 可以使用 auto。 | string / number | 否 | — |
labelPosition | 表單域標(biāo)簽的位置, 當(dāng)設(shè)置為 left 或 right 時(shí),則也需要設(shè)置 label-width 屬性 | ‘left’ / ‘right’ / ‘top’ | 否 | ‘right’ |
size | 用于控制該表單內(nèi)組件的尺寸 | large / default /small | 否 | — |
disabled | 是否禁用該表單內(nèi)的所有組件。 如果設(shè)置為 true, 它將覆蓋內(nèi)部組件的 disabled 屬性。 | boolean | 否 | false |
submitButtonText | 提交按鈕默認(rèn)顯示的文本內(nèi)容 | string | 否 | ‘提交’ |
resetButtonText | 重置按鈕默認(rèn)顯示的文本內(nèi)容 | string | 否 | ‘重置’ |
cancelButtonText | 取消按鈕默認(rèn)顯示的文本內(nèi)容 | string | 否 | ‘取消’ |
showResetButton | 是否顯示重置按鈕 | boolean | 否 | — |
showCancelButton | 是否顯示取消按鈕 | boolean | 否 | — |
fieldItem 配置項(xiàng)
參數(shù) | 說明 | 類型 | 是否必填 | 默認(rèn)值 |
---|---|---|---|---|
field | model 的鍵名 | string | 是 | — |
label | 標(biāo)簽文本 | string | 是 | — |
type | 當(dāng)前 fieldItem 的類型 | ‘password’ / ‘text’ / ‘textarea’ / ‘radio’ / ‘checkbox’ / ‘select’ | 否 | ‘text’ |
value | 默認(rèn)顯示的值 | any | 否 | — |
placeholder | 輸入框占位文本 | string | 否 | — |
disabled | 是否禁用 | boolean | 否 | false |
options | 如果 type=‘checkbox’ / ‘radio’ / 'select’時(shí),需傳入此配置項(xiàng)。格式參考 fieldItem options 配置項(xiàng) | object | 否 | - |
rules | 表單驗(yàn)證規(guī)則。格式參考element-plus form 表單 或者參數(shù)類型聲明 | Array<RuleItem> | 否 | - |
clearable | 是否可清空 | boolean | 否 | false |
showPassword | 是否顯示切換密碼圖標(biāo) | boolean | 否 | false |
enterable | 當(dāng)為輸入框時(shí),是否啟用回車觸發(fā)提交功能 | boolean | 否 | false |
fieldItem options 配置項(xiàng)
參數(shù) | 說明 | 類型 | 是否必填 | 默認(rèn)值 |
---|---|---|---|---|
labelkey | label 自定義字段名 | string | 否 | ‘label’ |
valueKey | value 自定義字段名 | string | 否 | ‘value’ |
placeholder | 當(dāng) fieldItem type= 'select’時(shí),選擇框的提示語 | string | 否 | - |
data | type=‘checkbox’ / ‘radio’ / 'select’時(shí), 需要的數(shù)據(jù) | Array<object> | 否 | - |
Form 插槽
插槽名 | 說明 | 插槽作用域 |
---|---|---|
buttons | 自定義按鈕區(qū)域的內(nèi)容 | { model, formRef } |
Form 事件
事件名 | 說明 | 回調(diào)參數(shù) |
---|---|---|
submit | 點(diǎn)擊默認(rèn)的提交按鈕觸發(fā) | model |
cancel | 點(diǎn)擊取消按鈕觸發(fā) | - |
reset | 重置該表單項(xiàng),將其值重置為初始值,并移除校驗(yàn)結(jié)果 | - |
其他
此文檔只提供基本的封裝思路,如需使用到更多的業(yè)務(wù)場景,可自行擴(kuò)展。
FiledItem type 類型可增加 富文本編輯器、 markdown 編輯器, 上傳圖片等類型,然后根據(jù)類型判斷把封裝好的富文本編輯器組件、markdown 編輯器組件放入表單內(nèi)。文章來源地址http://www.zghlxwxcb.cn/news/detail-470575.html
到了這里,關(guān)于vue3 ts element plus form表單二次封裝詳細(xì)步驟 (附參數(shù)、類型詳細(xì)介紹及簡單使用示例)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!