vue3+elementPlus二次封裝表單的實現(xiàn)代碼
更新時間:2024年03月25日 08:39:29 作者:Vhen
最近使用Vue3+ElementPlus開發(fā)項目,從整體上構(gòu)思組件的封裝。能寫成組件的內(nèi)容都進行封裝,方便多個地方使用,這篇文章給大家介紹了vue3+elementPlus二次封裝表單的實現(xiàn),并通過代碼介紹的非常詳細,需要的朋友可以參考下
功能
Input輸入框
autocomplete自動補齊輸入框
radio 單選框
checkbox 復選框
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)聽父組件的值 watch( () => props.modelValue, () => { modelValue.value = props.modelValue; }, ); // 通過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)聽父組件的值 watch( () => props.modelValue, () => { modelValue.value = props.modelValue; }, ); // 通過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)聽父組件的值 watch( () => props.modelValue, () => { modelValue.value = props.modelValue; }, ); // 通過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)聽父組件的值 watch( () => props.modelValue, () => { Val.value = props.modelValue; }, ); // 通過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)聽父組件的值 watch( () => props.modelValue, () => { Val.value = props.modelValue; }, ); // 通過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類型:button/border type: String, default: "", }, change: { type: Function, default: () => () => {}, }, }); const modelValue = ref(props.modelValue); //監(jiān)聽父組件的值 watch( () => props.modelValue, () => { modelValue.value = props.modelValue; }, ); // 通過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)聽父組件的值 watch( () => props.modelValue, () => { modelValue.value = props.modelValue; }, ); // 通過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)在的努力是為了小時候吹過的牛逼! * @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 //輸入框類型 label: string //輸入框標題 disabled?: boolean//表單是否可修改 默認false placeholder?: any //輸入框默認顯示內(nèi)容 prop: string //表單校驗 options?: ItemOption[] | (() => ItemOption[]) //選擇器的可選子選項 select span?: number // 表單柵格數(shù) offset?: number // 表單柵格偏移 clearable?: boolean // 是否可清空 size?: string // 輸入框大小 multiple?: boolean // 是否多選 collapseTags?: boolean // 是否折疊 collapseTagsThreshold?: number // 多選時標簽的閾值,大于該閾值時折疊 filterable?: boolean // 是否可搜索 allowCreate?: boolean // 是否支持創(chuàng)建 radioType?: string // 單選框類型 } export interface PropsVO { formData: object // 表單數(shù)據(jù) formItem: FormItemVO[] // 表單配置項 span?: number // 表單柵格數(shù) isSeniorSearch?: boolean // 是否高級搜索 gutter?: number // 表單柵格間隔 showButton?: boolean // 是否顯示查詢按鈕 }
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 ? "收起" : "展開" }}</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" >查詢</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: "請輸入姓名", // 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: "工作地點", 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: "請輸入姓名", trigger: "blur" }], email: [{ required: true, message: "請輸入郵箱", 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é)束語
簡單二次封裝form 表單組件,如大家有更好的方案,歡迎大家評論區(qū)討論,一起學習一起成長....
以上就是vue3+elementPlus二次封裝表單的實現(xiàn)代碼的詳細內(nèi)容,更多關(guān)于vue3 elementPlus二次封裝表單的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue-router 源碼之實現(xiàn)一個簡單的 vue-router
這篇文章主要介紹了vue-router 源碼之實現(xiàn)一個簡單的 vue-router,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-07-07