Vue3+ElementPlus 表單組件的封裝實例
更新時間:2022年06月02日 10:05:46 作者:小小前端程序猿
這篇文章主要介紹了Vue3+ElementPlus 表單組件的封裝實例,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
在系統(tǒng)中,表單作為用戶與后端交互的重要傳遞組件使用頻率極高,故對其進行封裝是必然的,也是一個編寫規(guī)范代碼的前端程序員必須做的一件事。
在Vue3中封裝組件時,能感受到與Vue2有著很大的不同,故作此記錄。
form文件夾
FormItem.tsx
文件是Typescript中的新特性之一,詳細可查閱TS中文文檔index.vue
是主體文件type.ts
表單的規(guī)約
FormItem.tsx
import filter from '@/utils/filters' import { ElCheckbox, ElCheckboxGroup, ElDatePicker, ElInput, ElInputNumber, ElOption, ElRadio, ElRadioGroup, ElSelect, ElTimePicker } from 'element-plus' import { defineComponent } from 'vue' // 普通顯示 const Span = (form: Record<string, any>, data: Record<string, any>) => ( <span>{data.valueProp ? form[data.valueProp] : (data.filter ? filter(form[data.prop], data.filter) : form[data.prop] || '無')}</span> ) // 輸入框 const Input = (form: Record<string, any>, data: Record<string, any>) => ( <ElInput v-model={form[data.prop]} type={data.type} size='small' show-password={data.type == 'password'} clearable placeholder={'請輸入' + data.label} autosize = {{ minRows: 3, maxRows: 4, }} {...data.props} > </ElInput> ) // 數(shù)字輸入框 const InputNumber = (form: Record<string, any>, data: Record<string, any>) => ( <ElInputNumber size='small' v-model={form[data.prop]} controls-position="right" {...data.props} /> ) const setLabelValue = (_item: any, { optionsKey }: any = {}) => { return { label: optionsKey ? _item[optionsKey.label] : _item.label, value: optionsKey ? _item[optionsKey.value] : _item.value, } } // 選擇框 const Select = (form: Record<string, any>, data: Record<string, any>) => ( <ElSelect size='small' v-model={form[data.prop]} filterable clearable placeholder={'請選擇' + data.label} {...data.props} > {data.options.map((item: any) => { return <ElOption {...setLabelValue(item, data)} /> })} </ElSelect> ) // 單選/區(qū)間日期 const Date = (form: Record<string, any>, data: Record<string, any>) => ( <ElDatePicker size='small' v-model={form[data.prop]} type={data.type} value-format={data.valueFormat} format = {data.format} range-separator="至" start-placeholder={data.startPlaceholder} end-placeholder={data.endPlaceholder} placeholder={'請選擇' + data.label} {...data.props} /> ) // 單選/區(qū)間時間 const Time = (form: Record<string, any>, data: Record<string, any>) => ( <ElTimePicker size='small' v-model={[form[data.prop]]} value-format={data.valueFormat} format = {data.format} range-separator="至" disabled = {form.editable} start-placeholder={data.start} is-range={data.isRange} end-placeholder={data.end} {...data.props} /> ) // 單選 const Radio = (form: Record<string, any>, data: Record<string, any>) => ( <ElRadioGroup v-model={form[data.prop]}> {data.radios.map( (item: { label: string | number | boolean; value: any }) => { return ( <ElRadio label={setLabelValue(item, data.prop).label}> {setLabelValue(item, data.prop).value} </ElRadio> ) }, )} </ElRadioGroup> ) // 多選 const Checkbox = (form: Record<string, any>, data: Record<string, any>) => ( <ElCheckboxGroup size='small' v-model={form[data.prop]}> {data.checkboxs.map( (item: { label: string | number | boolean; value: any }) => { return ( <ElCheckbox label={setLabelValue(item, data.prop).label}> {setLabelValue(item, data.prop).value} </ElCheckbox> ) }, )} </ElCheckboxGroup> ) const setFormItem = ( form: Record<string, any> | undefined, data: Record<string, any>, editable: Boolean, ) => { if (!form) return null if (!editable) return Span(form, data) switch (data.type) { case 'input': return Input(form, data) case 'textarea': return Input(form, data) case 'password': return Input(form, data) case 'inputNumber': return InputNumber(form, data) case 'select': return Select(form, data) case 'date': case 'daterange': return Date(form, data) case 'time': return Time(form, data) case 'radio': return Radio(form, data) case 'checkbox': return Checkbox(form, data) default: return null } } export default () => defineComponent({ props: { data: Object, formData: Object, editable: Boolean, }, setup(props) { return () => props.data ? setFormItem(props.formData, props.data, props.editable) : null }, })
index.vue
<template> <el-form ref="FormRef" :model="prop.data.data" :rules="editable ? prop.data.rules : {}" :inline="inline" :label-position="labelPosition" label-width="atuo"> <el-row :gutter="prop.data.elRowGutter"> <el-col v-for="item in prop.data.formItems" :span="item.span"> <el-form-item :label="item.label ? item.label + ':' : ''" :prop="item.prop" :label-width="item.width"> <FormItem :formData="prop.data.data" :editable="editable" :data="item"> </FormItem> </el-form-item> </el-col> <el-col v-if="btnList && btnList.length" :span="24"> <el-form-item> <template v-for="item in btnList"> <Btn :props="item" @click="onClick(item)"></Btn> </template> </el-form-item> </el-col> </el-row> </el-form> </template>
<script lang="ts" setup> import { computed } from '@vue/reactivity' import type { FormInstance } from 'element-plus' import { ref } from 'vue' import formItem from './FormItem' import type { commonForm } from './type' interface Props { ? data: commonForm } const prop = defineProps<Props>() const editable = computed(() => !!prop.data?.editable) const inline = computed(() => !!prop.data.formProps?.inline) const labelWidth = computed(() => prop.data.formProps?.labelWidth || '100px') const labelPosition = computed( ? () => prop.data.formProps?.labelPosition || 'top', ) const btnList = computed(() => { ? return prop.data.formProps?.btn }) // tsx組件 const FormItem = formItem() const FormRef = ref<FormInstance>() // 表單按鈕 function onClick(data: { onClick?: () => void }) { ? if (!data.onClick) return ? data.onClick() } // 表單校驗 async function validate() { ? if (!FormRef.value) return ? const result = await FormRef.value.validate() ? return result } // 清除表單驗證 async function resetFields() { ? return await FormRef.value.resetFields() } defineExpose({ ? validate, ? resetFields, }) </script>
<style scoped> .el-form-item { ? margin: 0 10px !important; } .el-form-item__label { ? position: absolute; } .el-form-item__content { ? width: 100%; ? padding-left: 80px; } .el-select, .el-input_inner { ? width: 100%; } </style>
type.ts
type itemType = | 'input' | 'select' | 'switch' | 'radio' | 'date' | 'time' | 'checkbox' | 'daterange' interface FormProps { inline?: Boolean labelWidth?: string | number labelPosition?: 'left' | 'top' | 'right' btn?: object[] } interface FormItems { type: itemType label?: string prop: string valueProp?: string width?: string | number span?: number filter?: string } export class commonForm { public data: any private rules?: object public elRowGutter?: number public editable?: boolean public formProps?: FormProps public formItems: FormItems[] public dataArray?:object[] constructor({ data = {}, rules = {}, editable = true, formProps = {}, formItems = [], elRowGutter = 0, }: any) { this.data = data this.rules = rules this.elRowGutter = elRowGutter this.editable = editable this.formItems = formItems this.formProps = formProps } }
在頁面中引用
changCarrier.vue
是主題頁面,用來顯示表單userForm.ts
是對表單進行渲染的數(shù)據(jù)項
index.vue
<template> <el-dialog v-model="show" v-if="show" :title="`${title}人員`" :before-close="handleClose" width="60%"> <Form ref="FormRef" :data="formData"></Form> <template #footer> <el-button @click="handleClose">關 閉</el-button> <el-button type="primary" v-show="!isDetail" @click="submit">提 交</el-button> </template> </el-dialog> </template>
<script lang="ts" setup> import { reactive, ref, defineEmits } from 'vue' // import api from '@/api' import { ElMessage } from 'element-plus' import useForm from './hooks/useForm' //表單的 import api from '@/api/index' enum types { 'default' = '', 'add' = '新增', 'unData' = '編輯', 'detail' = '詳情', } const show = ref(false) //控制表單開關 const title = ref(types.default) //表單標題 const FormRef = ref() //表單DOM const emit = defineEmits(['refresh']) //父組件傳過來的方法,作用:在表單提交后觸發(fā),刷新數(shù)據(jù) defineExpose({ //向父組件暴露其屬性及方法,實例:父組件點擊添加,觸發(fā)formRef中的addData行為 show, title, setData, addData, delData, }) // 表單生成 let formData = useForm() //新增 function addData() { handleOpen('add') } // 編輯設置數(shù)據(jù) function setData(data: object) { //父組件點擊編輯,將值通過方法傳過來 formData.data = reactive({ ...data }) handleOpen('unData') } //刪除 async function delData(data: number) { const res: any = await api.gasSite.deleteQueueApply({ idList: [data], }) emit('refresh') } // 請求 async function request() { let res: any // formData是否存在id值, 存在id值表示編輯, 不存在則為添加 if (!formData.data?.id) { //編輯提交 res = await api.gasSite.addQueueApply(formData.data) } else if (formData.data?.id) { //新增提交 res = await api.gasSite.updateStartWarehouse(formData.data) } if (res?.status.state === '00') { ElMessage.success('操作成功') title.value = types.default emit('refresh') //刷新數(shù)據(jù) show.value = false } else if (res?.status.state !== '00') { ElMessage.error(res?.status.state) } show.value = false } //清除驗證信息 async function reset() { await FormRef.value.resetFields() } //新增表單打開事件 function handleOpen(type: any) { formData.formItems = useForm().formItems //表單item formData.editable = true //打開表單編輯 title.value = types[type] show.value = true //表單的打開 } //表單關閉事件 function handleClose() { show.value = false reset() //重置該表單項,將其值重置為初始值,并移除校驗結果 } // 提交 async function submit() { const result = await FormRef.value.validate() if (result) request() } </script>
useForm
import { commonForm } from '@/components/common/form/type' import { reactive } from 'vue' export default () => { const rules = { name: [ { required: true, message: '人員名稱', trigger: 'blur' } ] } const form = reactive( new commonForm({ data: [], editable: true, rules: rules, formItems: [ { label: '人員名稱', type: 'select', prop: 'name', }, { label: '日期范圍', type: 'daterange', prop: 'queueDate', format:'YYYY-MM-DD', valueFormat:'YYYY-MM-DD', startPlaceholder:'開始時間', endPlaceholder:'結束時間', span: 6, }, { label: '時間段范圍', type: 'time', prop: 'timeSlot', format:'HH:mm', valueFormat:'HH:mm', start:'開始時間', end:'結束時間', isRange:true, span: 6, }, { label: '允許排隊數(shù)量', type: 'input', prop: 'queueNum', span: 6, }, { label: '生效類型', type: 'select', prop: 'isDelay', options: [ { label: '當日生效', value: 0, }, { label: '次日生效', value: 1, } ], span: 6, }, { label: '生效時間', type: 'date', prop: 'effectiveTime', format:'YYYY-MM-DD', valueFormat:'YYYY-MM-DD', span: 6, }, ], }), ) return form }
總結
一百個人有一百個編寫代碼的習慣,其上實現(xiàn)是基于模塊化的思想,可能看起來有點累,但是我相信能幫助到你。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。