功能
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二次封装表单的资料请关注代码网其它相关文章!
发表评论