vue3+elementPlus二次封裝表單的實(shí)現(xiàn)代碼
功能
Input輸入框
autocomplete自動(dòng)補(bǔ)齊輸入框
radio 單選框
checkbox 復(fù)選框
date 日期選擇框
select 下拉框
如需添加更多功能參考elementPlus或者根據(jù)業(yè)務(wù)需求自行
自定義組件
效果圖
目錄結(jié)構(gòu)
input
<template> <el-input v-bind="$attrs" v-model="modelValue" w-full @blur="props.blur ? props.blur($event) : false" @focus="props.focus ? props.focus($event) : false" @change="props.change ? props.change($event) : false" @input="props.input ? props.input($event) : false" @clear="props.clear ? props.clear() : false" /> </template> <script lang="ts" setup> import { ref, watch } from "vue"; const emit = defineEmits(["update:modelValue"]); const props = defineProps({ modelValue: { type: String, default: () => "", }, blur: { type: Function, default: () => () => {}, }, focus: { type: Function, default: () => () => {}, }, change: { type: Function, default: () => () => {}, }, input: { type: Function, default: () => () => {}, }, clear: { type: Function, default: () => () => {}, }, }); const modelValue = ref(props.modelValue); //監(jiān)聽(tīng)父組件的值 watch( () => props.modelValue, () => { modelValue.value = props.modelValue; }, ); // 通過(guò)emit將值傳遞給父組件 emit("update:modelValue", modelValue); </script> <style lang="scss" scoped></style>
select
<template> <el-select v-model="modelValue" v-bind="$attrs" w-full @change="props.change ? props.change($event) : false" @visible-change="props.visibleChange ? props.visibleChange($event) : false" @remove-tag="props.removeTag ? props.removeTag($event) : false" @clear="props.clear ? props.clear() : false" @blur="props.blur ? props.blur($event) : false" @focus="props.focus ? props.focus($event) : false" > <el-option v-for="item in options" :key="item[valueFiled]" :label="item[labelFiled]" :value="item[valueFiled]" ></el-option> </el-select> </template> <script lang="ts" setup> import { ref, watch } from "vue"; const emit = defineEmits(["update:modelValue"]); const props = defineProps({ modelValue: { type: [String, Array], default: () => "", }, options: { type: Array as any, default: () => [], }, valueFiled: { type: String, default: "value", }, labelFiled: { type: String, default: "label", }, change: { type: Function, default: () => () => {}, }, visibleChange: { type: Function, default: () => () => {}, }, removeTag: { type: Function, default: () => () => {}, }, clear: { type: Function, default: () => () => {}, }, blur: { type: Function, default: () => () => {}, }, focus: { type: Function, default: () => () => {}, }, }); const modelValue = ref(props.modelValue); //監(jiān)聽(tīng)父組件的值 watch( () => props.modelValue, () => { modelValue.value = props.modelValue; }, ); // 通過(guò)emit將值傳遞給父組件 emit("update:modelValue", modelValue); </script> <style lang="scss" scoped></style>
autocomplete
<template> <el-autocomplete v-bind="$attrs" v-model="modelValue" style="width: 100%" @select="props.select ? props.select($event) : false" @change="props.change ? props.change($event) : false" /> </template> <script lang="ts" setup> import { ref, watch } from "vue"; const emit = defineEmits(["update:modelValue"]); const props = defineProps({ modelValue: { type: String, default: () => "", }, select: { type: Function, default: () => () => {}, }, change: { type: Function, default: () => () => {}, }, }); const modelValue = ref(props.modelValue); //監(jiān)聽(tīng)父組件的值 watch( () => props.modelValue, () => { modelValue.value = props.modelValue; }, ); // 通過(guò)emit將值傳遞給父組件 emit("update:modelValue", modelValue); </script>
date
<template> <el-date-picker v-model="Val" v-bind="$attrs" style="width: 100%" @change="props.change ? props.change($event) : false" @blur="props.blur ? props.blur($event) : false" @focus="props.focus ? props.focus($event) : false" @calendar-change="props.calendarChange ? props.calendarChange($event) : false" @panel-change="(a, b, c) => (props.panelChange ? props.panelChange(a, b, c) : false)" @visible-change="props.visibleChange ? props.visibleChange($event) : false" ></el-date-picker> </template> <script lang="ts" setup> import { ref, watch } from "vue"; const emit = defineEmits(["update:modelValue"]); const props = defineProps({ modelValue: { type: [String, Array, Date], default: () => "", }, change: { type: Function, default: () => () => {}, }, blur: { type: Function, default: () => () => {}, }, focus: { type: Function, default: () => () => {}, }, calendarChange: { type: Function, default: () => () => {}, }, panelChange: { type: Function, default: () => () => {}, }, visibleChange: { type: Function, default: () => () => {}, }, }); const Val = ref(props.modelValue); //監(jiān)聽(tīng)父組件的值 watch( () => props.modelValue, () => { Val.value = props.modelValue; }, ); // 通過(guò)emit將值傳遞給父組件 emit("update:modelValue", Val); </script>
checkbox
<template> <el-checkbox-group v-model="Val" v-bind="$attrs" @change="props.change ? props.change($event) : false" > <template v-if="props.cType === 'button'"> <el-checkbox-button v-for="item in options" :key="item[valueFiled]" :value="item[valueFiled]" >{{ item[labelFiled] }}</el-checkbox-button > </template> <template v-else> <el-checkbox v-for="item in options" :key="item[valueFiled]" :value="item[valueFiled]" :border="props.cType === 'border'" >{{ item[labelFiled] }}</el-checkbox > </template> </el-checkbox-group> </template> <script lang="ts" setup> import { ref, watch } from "vue"; const emit = defineEmits(["update:modelValue"]); const props = defineProps({ modelValue: { type: Array, default: () => "", }, options: { type: Array as any, default: () => [], }, valueFiled: { type: String, default: "value", }, labelFiled: { type: String, default: "label", }, cType: { type: String, default: "", }, change: { type: Function, default: () => () => {}, }, }); const Val = ref(props.modelValue); //監(jiān)聽(tīng)父組件的值 watch( () => props.modelValue, () => { Val.value = props.modelValue; }, ); // 通過(guò)emit將值傳遞給父組件 emit("update:modelValue", Val); </script>
radio
<template> <el-radio-group v-model="modelValue" v-bind="$attrs" @change="props.change ? props.change($event) : false" > <template v-if="props.cType === 'button'"> <el-radio-button v-for="item in options" :key="item[valueFiled]" :value="item[valueFiled]">{{ item[labelFiled] }}</el-radio-button> </template> <template v-else> <el-radio v-for="item in options" :border="props.cType === 'border'" :key="item[valueFiled]" :value="item[valueFiled]" >{{ item[labelFiled] }}</el-radio > </template> </el-radio-group> </template> <script lang="ts" setup> import { ref, watch } from "vue"; const emit = defineEmits(["update:modelValue"]); const props = defineProps({ modelValue: { type: [Number, String, Boolean], default: () => "", }, options: { type: Array as any, default: () => [], }, valueFiled: { type: String, default: "value", }, labelFiled: { type: String, default: "label", }, cType: { //radio類(lèi)型:button/border type: String, default: "", }, change: { type: Function, default: () => () => {}, }, }); const modelValue = ref(props.modelValue); //監(jiān)聽(tīng)父組件的值 watch( () => props.modelValue, () => { modelValue.value = props.modelValue; }, ); // 通過(guò)emit將值傳遞給父組件 emit("update:modelValue", modelValue); </script>
cascader
<template> <el-cascader v-bind="$attrs" v-model="modelValue" style="width: 100%" @change="props.change ? props.change($event) : false" @expand-change="props.expandChange ? props.expandChange($event) : false" /> </template> <script lang="ts" setup> import { ref, watch } from "vue"; const emit = defineEmits(["update:modelValue"]); const props = defineProps({ modelValue: { type: Array, default: () => [], }, change: { type: Function, default: () => () => {}, }, expandChange: { type: Function, default: () => () => {}, }, }); const modelValue = ref(props.modelValue); //監(jiān)聽(tīng)父組件的值 watch( () => props.modelValue, () => { modelValue.value = props.modelValue; }, ); // 通過(guò)emit將值傳遞給父組件 emit("update:modelValue", modelValue); </script>
types
/* * @Author: vhen * @Date: 2024-03-24 00:36:03 * @LastEditTime: 2024-03-24 15:21:30 * @Description: 現(xiàn)在的努力是為了小時(shí)候吹過(guò)的牛逼! * @FilePath: \vhen-vue3-admin-pro\src\components\SearchForm\types.ts * */ export type FormType = "input" | "select" | "radio" | "cascader" | "autocomplete" | "date" | "daterange" | "checkbox"; export interface ItemOption { label: string value: string | number disabled?: boolean } export interface FormItemVO { type: FormType //輸入框類(lèi)型 label: string //輸入框標(biāo)題 disabled?: boolean//表單是否可修改 默認(rèn)false placeholder?: any //輸入框默認(rèn)顯示內(nèi)容 prop: string //表單校驗(yàn) options?: ItemOption[] | (() => ItemOption[]) //選擇器的可選子選項(xiàng) select span?: number // 表單柵格數(shù) offset?: number // 表單柵格偏移 clearable?: boolean // 是否可清空 size?: string // 輸入框大小 multiple?: boolean // 是否多選 collapseTags?: boolean // 是否折疊 collapseTagsThreshold?: number // 多選時(shí)標(biāo)簽的閾值,大于該閾值時(shí)折疊 filterable?: boolean // 是否可搜索 allowCreate?: boolean // 是否支持創(chuàng)建 radioType?: string // 單選框類(lèi)型 } export interface PropsVO { formData: object // 表單數(shù)據(jù) formItem: FormItemVO[] // 表單配置項(xiàng) span?: number // 表單柵格數(shù) isSeniorSearch?: boolean // 是否高級(jí)搜索 gutter?: number // 表單柵格間隔 showButton?: boolean // 是否顯示查詢(xún)按鈕 }
index.vue
<template> <section class="search-form"> <el-form :model="props.formData" v-bind="$attrs"> <el-row :gutter="props.gutter"> <el-col v-for="column in useFormItem" :key="column.prop" :span="column.span" :offset="column.offset" > <el-form-item :label="`${column.label}`" :prop="column.prop"> <component :is="componentType[column.type]" v-bind="column" v-model="props.formData[column.prop]" /> </el-form-item> </el-col> <template v-if="$slots.default"> <slot /> </template> <el-col :span="props.span" style="flex: 1; max-width: 100%" v-if="showButton"> <div flex justify="end" items-center w-full h-full> <div v-if="isSeniorSearch" flex items-center mr-2 class="senior-search" @click="isShow = !isShow" cursor="pointer" > <div class="text">{{ isShow ? "收起" : "展開(kāi)" }}</div> <div class="flex m-left-4"> <el-icon> <ArrowUp v-if="isShow" /> <ArrowDown v-else /> </el-icon> </div> </div> <el-button @click="$emit('reset')" :icon="RefreshLeft">重置</el-button> <el-button type="primary" class="m-bottom-12" @click="$emit('search')" :icon="Search" >查詢(xún)</el-button > </div> </el-col> </el-row> </el-form> </section> </template> <script lang="ts" setup> import { RefreshLeft, Search } from "@element-plus/icons-vue"; import { FormInstance } from "element-plus"; import { computed, markRaw, ref } from "vue"; import VhenAutocomplete from "./src/VhenAutocomplete.vue"; import VhenCascader from "./src/VhenCascader.vue"; import VhenCheckbox from "./src/VhenCheckbox.vue"; import VhenDatePicker from "./src/VhenDatePicker.vue"; import VhenInput from "./src/VhenInput.vue"; import VhenRadio from "./src/VhenRadio.vue"; import VhenSelect from "./src/VhenSelect.vue"; import { PropsVO } from "./types"; const emit = defineEmits<{ (e: "reset"): void; (e: "search"): void; }>(); const props = withDefaults(defineProps<PropsVO>(), { isSeniorSearch: false, gutter: 12, span: 8, showButton: true, }); const isShow = ref(false); const useFormItem = computed(() => { const isShowRow = props.isSeniorSearch && !isShow.value && props.showButton; if (isShowRow) { const num = Math.floor(24 / props.span) - 1; return props.formItem.slice(0, num); } else { return props.formItem; } }); const componentType = markRaw({ input: VhenInput, select: VhenSelect, radio: VhenRadio, cascader: VhenCascader, autocomplete: VhenAutocomplete, date: VhenDatePicker, daterange: VhenDatePicker, checkbox: VhenCheckbox, }); const formRef = ref<FormInstance>(); defineExpose({ formRef }); </script> <style lang="scss" scoped> .senior-search { color: var(--el-text-color-regular); .text { font-size: 14px; } } </style>
組件案例
<template> <div> <SearchForm :formData="formData" :form-item="formItemList" :rules="formRules" :span="6" label-position="top" label-width="100px" isSeniorSearch @reset="resetData" @search="handleSearch" > </SearchForm> <pre> {{ formData }} </pre> </div> </template> <script lang="ts" setup> import SearchForm from "@/components/SearchForm/index.vue"; import { FormRules } from "element-plus"; import { reactive } from "vue"; const formData = reactive({ userName: "", email: "843348394@qq.com", sex: "1", area: [], city: "", }); const formItemList = reactive([ { type: "input", label: "姓名", prop: "userName", clearable: true, span: 6, placeholder: "請(qǐng)輸入姓名", // disabled: true, input: handleInput, }, { type: "autocomplete", label: "郵箱", prop: "email", span: 6, "fetch-suggestions": querySearch, }, { type: "daterange", label: "出生日期", prop: "birthday", span: 6, }, { type: "radio", label: "-", prop: "sex", cType: "button", span: 6, options: [ { value: "0", label: "男", }, { value: "1", label: "女", }, ], }, { type: "checkbox", label: "工作地點(diǎn)", prop: "area", span: 6, options: [ { label: "北京", value: "beijing", }, { label: "上海", value: "shanghai", }, { label: "深圳", value: "shenzhen", }, ], }, { type: "select", prop: "city", label: "城市", span: 6, options: [ { label: "北京", value: "beijing", }, { label: "上海", value: "shanghai", }, { label: "深圳", value: "shenzhen", }, ], }, ]); const resetData = () => { console.log(formData); }; const handleSearch = () => { console.log(formData); }; const formRules = reactive<FormRules>({ userName: [{ required: true, message: "請(qǐng)輸入姓名", trigger: "blur" }], email: [{ required: true, message: "請(qǐng)輸入郵箱", trigger: "blur" }], }); function handleInput(val: string | number) { console.log(val); } function querySearch(query: string, cb: any) { console.log(query); cb([query]); } </script> <style lang="scss" scoped></style>
結(jié)束語(yǔ)
簡(jiǎn)單二次封裝form 表單組件,如大家有更好的方案,歡迎大家評(píng)論區(qū)討論,一起學(xué)習(xí)一起成長(zhǎng)....
以上就是vue3+elementPlus二次封裝表單的實(shí)現(xiàn)代碼的詳細(xì)內(nèi)容,更多關(guān)于vue3 elementPlus二次封裝表單的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue一步到位的實(shí)現(xiàn)動(dòng)態(tài)路由
這篇文章主要介紹了vue一步到位的實(shí)現(xiàn)動(dòng)態(tài)路由,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06解決IOS端微信H5頁(yè)面軟鍵盤(pán)彈起后頁(yè)面下方留白的問(wèn)題
微信H5項(xiàng)目,ios端出現(xiàn)了軟鍵盤(pán)輸完隱藏后頁(yè)面不會(huì)回彈,下方會(huì)有一大塊留白。這篇文章主要介紹了決微信H5頁(yè)面軟鍵盤(pán)彈起后頁(yè)面下方留白的問(wèn)題(iOS端) ,需要的朋友可以參考下2019-06-06Vue3中的動(dòng)畫(huà)過(guò)渡實(shí)現(xiàn)技巧分享
在現(xiàn)代的前端開(kāi)發(fā)中,用戶(hù)體驗(yàn)的重要性不言而喻,為了讓?xiě)?yīng)用程序更加生動(dòng)和引人注目,動(dòng)畫(huà)和過(guò)渡效果是必不可少的元素,本文將以 Vue3 為基礎(chǔ),深入探討如何在應(yīng)用程序中實(shí)現(xiàn)動(dòng)畫(huà)過(guò)渡,以及一些技巧和最佳實(shí)踐,需要的朋友可以參考下2025-01-01詳解使用webpack打包編寫(xiě)一個(gè)vue-toast插件
本篇文章主要介紹了詳解使用webpack打包編寫(xiě)一個(gè)vue插件,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11vue商城中商品“篩選器”功能的實(shí)現(xiàn)代碼
這篇文章主要介紹了vue商城中商品“篩選器”功能的實(shí)現(xiàn),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07vue-router 源碼之實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 vue-router
這篇文章主要介紹了vue-router 源碼之實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 vue-router,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07