vue3+elementPlus二次封装表单的实现代码
作者:Vhen
最近使用Vue3+ElementPlus开发项目,从整体上构思组件的封装。能写成组件的内容都进行封装,方便多个地方使用,这篇文章给大家介绍了vue3+elementPlus二次封装表单的实现,并通过代码介绍的非常详细,需要的朋友可以参考下
功能
Input输入框
autocomplete自动补齐输入框
radio 单选框
checkbox 复选框
date 日期选择框
select 下拉框
如需添加更多功能参考elementPlus或者根据业务需求自行
自定义组件
效果图
目录结构
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); //监听父组件的值 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); //监听父组件的值 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); //监听父组件的值 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); //监听父组件的值 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); //监听父组件的值 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); //监听父组件的值 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); //监听父组件的值 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: 现在的努力是为了小时候吹过的牛逼! * @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 //输入框默认显示内容 prop: string //表单校验 options?: ItemOption[] | (() => ItemOption[]) //选择器的可选子选项 select span?: number // 表单栅格数 offset?: number // 表单栅格偏移 clearable?: boolean // 是否可清空 size?: string // 输入框大小 multiple?: boolean // 是否多选 collapseTags?: boolean // 是否折叠 collapseTagsThreshold?: number // 多选时标签的阈值,大于该阈值时折叠 filterable?: boolean // 是否可搜索 allowCreate?: boolean // 是否支持创建 radioType?: string // 单选框类型 } export interface PropsVO { formData: object // 表单数据 formItem: FormItemVO[] // 表单配置项 span?: number // 表单栅格数 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>
结束语
简单二次封装form 表单组件,如大家有更好的方案,欢迎大家评论区讨论,一起学习一起成长....
以上就是vue3+elementPlus二次封装表单的实现代码的详细内容,更多关于vue3 elementPlus二次封装表单的资料请关注脚本之家其它相关文章!