基于vue3 element plus form 表单的二次封装
基于vue3 element plus form 表单的二次封装
·
记录一下开发中对form表单的二次封装及使用
前言
在我们的日常工作中经常会使用到form表来满足提交需求,那么将form表单封装成组件将会大大提高我们的工作效率,避免更多重复代码的书写,只需一些简单的配置就轻松实现。
一、form表单的封装
<template>
<div class='form'>
<el-form
:label-position="labelPosition"
:inline="inline"
:rules="rules"
ref="formRef"
:label-width="labelWidth"
:model="fieldForm"
:class="customClass"
>
<el-form-item
v-for="(item, index) in fieldList"
:key="index"
:prop="item.field"
:validate-on-rule-change="false"
:style="{
flexBasis: item.percent ? item.percent : item.percent ?
`${item.percent}%!important` : '',
width: item.width && isNaN(Number(item.width)) ? item.width +'!important'|| ''
: item.width + 'px!important' || '',
}"
:class="[item.className || '', `is-${item.formType}`]"
:rules="getRules(item)"
>
<template
#label
v-if="item.name"
>
<div style="display: inline">
<a :style="{ color: item.labelColor || '' }">{{ item.name || '' }}</a>
</div>
</template>
<template v-if="item.formType && item.mold && !item.hidden">
<component
:is="com['M' + item.mold]"
v-model="fieldForm[item.field]"
:item="item"
:index="index"
:disabled="disabled"
:size="size"
:field-form="fieldForm"
@change="commonChange"
/>
</template>
</el-form-item>
</el-form>
</div>
</template>
<script lang="ts" setup>
import { toRefs } from 'vue';
//引入组件
import MInput from './components/MInput.vue';
import MDate from './components/MDate.vue';
import MSelect from './components/MSelect.vue';
//这是自定义表单验证规则
import GenerateRules from './GenerateRules.ts';
//接收父组件传递的参数
const props = defineProps({
customClass: {
type: String,
default: 'is-three-columns',
},
rules: {
type: Object,
default: () => {
return {};
},
},
fieldForm: {
type: Object,
default: () => {
return {};
},
},
labelWidth: {
type: Number,
default: 90
},
fieldList: {
type: Array,
default: () => [],
},
// 行内表单
inline: {
type: Boolean,
default: false,
},
// 对齐方式 'right' | 'left' | 'top'
labelPosition: {
type: String,
default: 'right',
},
size: {
type: String,
default: 'default',
},
disabled: {
type: Boolean,
default: false,
},
});
const { customClass, rules, fieldForm, labelWidth, fieldList } = toRefs(props);
// 定义组件
const com: Record<string, any> = {
MInput: MInput,
MDate: MDate,
MSelect: MSelect,
};
//事件传递
const $emit = defineEmits(['change']);
/**
* 常规组件change事件
* @param item 配置数据
* @param index 下标
* @param value 选中/输入的值
* @param data 选中的整条数据
*/
const commonChange = (item, index, value, data) => {
$emit('change', item, index, value, data);
};
//自定义验证规则
const { getRules } = GenerateRules();
</script>
二、components
1.MDate.vue
代码如下(示例):
<template>
<el-date-picker
v-if="item.formType == 'date'"
v-model="fieldForm[item.field]"
:disabled="item.disabled || disabled"
clearable
style="width: 100%"
type="date"
:size="size"
:editable="true"
value-format="YYYY-MM-DD"
:placeholder="item.placeholder || '选择日期'"
@click.stop
@change="commonChange(item, index, $event)"
/>
<el-date-picker
v-else-if="item.formType == 'month'"
v-model="fieldForm[item.field]"
:disabled="item.disabled || disabled"
clearable
style="width: 100%"
type="month"
:size="size"
value-format="YYYY-MM"
:placeholder="item.placeholder || '选择月份'"
@change="commonChange(item, index, $event)"
/>
<el-date-picker
v-else-if="item.formType == 'year'"
v-model="fieldForm[item.field]"
:disabled="item.disabled || disabled"
clearable
style="width: 100%"
type="year"
:size="size"
value-format="YYYY"
:placeholder="item.placeholder || '选择年份'"
@change="commonChange(item, index, $event)"
/>
<el-date-picker
v-else-if="item.formType == 'dateRange'"
v-model="fieldForm[item.field]"
:disabled="item.disabled || disabled"
:type="item.dateType || 'daterange'"
:value-format="item.dateValueFormat || 'YYYY-MM-DD'"
:picker-options="item.pickerOptions || ''"
clearable
:size="size"
style="width: 100%"
:start-placeholder="item.startPlaceholder || '开始日期'"
:end-placeholder="item.endPlaceholder || '结束日期'"
@change="commonChange(item, index, $event)"
/>
<el-date-picker
v-else-if="item.formType == 'datetime'"
v-model="fieldForm[item.field]"
:disabled="item.disabled || disabled"
clearable
style="width: 100%"
:size="size"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
:placeholder="item.placeholder || '选择日期'"
@change="commonChange(item, index, $event)"
/>
</template>
<script lang="ts" setup>
import { toRefs, PropType } from 'vue';
import Hooks from '../Hooks.ts';
// 定义defineProps
const props = defineProps({
modelValue: {
type: [Number, String],
default: '',
},
item: {
type: Object,
default: () => {},
},
disabled: {
type: Boolean,
default: false,
},
size: {
type: String as PropType <''| 'large' | 'small'>,
default: 'small',
},
index: {
type: Number,
default: null,
},
fieldForm: {
type: Object,
default: () => {},
},
});
const { fieldForm } = toRefs(props);
// 定义$emit
const $emit = defineEmits(['change']);
const { commonChange } = Hooks($emit);
</script>
2.MInput.vue
代码如下(示例):
<template>
<el-input
v-if="item.formType == 'text' && !item.hidden"
v-model="fieldForm[item.field]"
:size="size || ''"
:disabled="item.disabled || disabled"
:maxlength="item.maxlength"
:placeholder="item.placeholder"
:type="item.formType"
:show-word-limit="item.showLimit"
:title="fieldForm[item.field]"
clearable
:readonly="item.readonly"
:name="item.field"
@input="commonChange(item, index, $event)"
/>
<el-input-number
v-else-if="['floatnumber', 'number'].includes(item.formType)"
v-model="fieldForm[item.field]"
:placeholder="item.placeholder"
:disabled="item.disabled || disabled"
:controls="item.controls || false"
:controls-position="item.position || 'right'"
clearable
:size="size"
@change="commonChange(item, index, $event)"
/>
<el-input
v-else-if="item.formType === 'textarea'"
v-model="fieldForm[item.field]"
:disabled="item.disabled || disabled"
:rows="item.rows || 2"
:autosize="!item.autosize && { minRows: item.rows || 2 }"
:maxlength="item.maxlength || 800"
:placeholder="item.placeholder"
:type="item.formType"
clearable
:size="size"
:resize="item.resize || 'none'"
@input="commonChange(item, index, $event)"
/>
</template>
<script lang="ts" setup>
import { toRefs} from 'vue';
import Hooks from '../Hooks.ts';
// 定义props
const props = defineProps({
modelValue: {
default: '',
},
item: {
type: Object,
default: () => {},
},
disabled: {
type: Boolean,
default: false,
},
size: {
type: String,
default: 'small',
},
index: {
type: Number,
default: null,
},
fieldForm: {
type: Object,
default: () => {},
},
});
const { fieldForm } = toRefs(props);
// 定义$emit
const $emit = defineEmits(['change']);
const { commonChange } = Hooks($emit);
</script>
3.MInput.vue
代码如下(示例):
<template>
<el-select
v-if="item.formType == 'select'"
v-model="fieldForm[item.field]"
:disabled="item.disabled || disabled"
:clearable="item.clearable || true"
:placeholder="item.placeholder"
:multiple="item.multiple"
:collapse-tags="item.collapse"
:collapse-tags-tooltip="item.tooltip"
:size="size"
:value-key="item.props?.valueKey || 'id'"
filterable
style="width: 100%"
@change="commonChange(item, index, $event)"
>
<el-option
v-for="(items, i) in item.basicGroup
? getGroupValue(item)
: item.setting"
:key="i"
:label="!isEmptyValue(items[item.props?.value || 'value']) ?
items[item.props?.label || 'label'] || items.name : items"
:value="!isEmptyValue(items[item.props?.value || 'value']) ?
items[item.props?.value || 'value'] : items"
/>
</el-select>
<el-radio-group
v-else-if="item.formType == 'radio-group'"
:size="size"
:disabled="item.disabled || disabled"
v-model="fieldForm[item.field]"
@change="commonChange(item, index, $event)"
>
<el-radio
:label="items.value"
v-for="(items, i) in item.setting"
:key="i"
>{{ items.label }}</el-radio>
</el-radio-group>
<el-switch
v-else-if="item.formType == 'switch'"
v-model="fieldForm[item.field]"
inline-prompt
:size="size"
:active-color="item.active&&item.active.color || '#67c23a'"
:inactive-color="item.inactive&&item.inactive.color || '#f56c6c'"
:active-text="item.active&&item.active.text || '是'"
:inactive-text="item.inactive&&item.inactive.text || '否'"
/>
<el-checkbox-group
v-else-if="item.formType == 'checkbox-group'"
:size="size"
:disabled="item.disabled || disabled"
v-model="fieldForm[item.field]"
@change="commonChange(item, index, $event)">
<el-checkbox
:label="items.label"
v-for="(items, i) in item.setting"
:key="i"
/>
</el-checkbox-group>
</template>
<script lang="ts" setup>
import Hooks from '../Hooks.ts';
import { toRefs} from 'vue';
// 定义props
const props = defineProps({
modelValue: {
default: '',
},
item: {
type: Object,
default: () => { },
},
disabled: {
type: Boolean,
default: false,
},
size: {
type: String,
default: 'default',
},
index: {
type: Number,
default: null,
},
fieldForm: {
type: Object,
default: () => { },
},
});
const { fieldForm, size } = toRefs(props);
// 定义$emit
const $emit = defineEmits(['change']);
const { commonChange, isEmptyValue } = Hooks($emit);
</script>
4.Hooks.ts
代码如下(示例):
export default function ($emit: any) {
/**
* 常规组件change事件
*/
const commonChange = (item: any, index: any, value: any, data: any) => {
$emit('change', item, index, value, data);
};
/**
* 判断是空值
*/
const isEmptyValue = (value: any) => {
return value === null || value == undefined;
};
return {
commonChange,
isEmptyValue,
};
}
5.Hooks.ts
代码如下(示例):
/**
* Created by yxk at 2020/6/1
* 单个自定义字段生成校验规则
*/
//类型判断 这些就不贴出来啦
import { isEmpty, isObject, isArray } from '@/utils/types.ts';
export default function () {
/**
* 唯一性校验
* @param data
* @returns {Promise<unknown>}
* @constructor
*/
const UniquePromise = (data: any) => {
return new Promise((resolve: any) => {
resolve(data);
});
};
/**
* 生成单个字段的验证规则
* @param item 字段信息
* @returns {[]}
*/
const getRules = (item: any) => {
const tempList = [];
// 验证必填
if (item.isNull === 1) {
if (item.formType === 'detail_table') {
tempList.push({
validator: ({ item }: any, value: any, callback: any) => {
if (getDetailTableIsEmpty(item.fieldExtendList, value)) {
callback(new Error(item.name + '不能为空'));
} else {
callback();
}
},
item: item,
trigger: ['blur', 'change']
});
} else if (item.formType === 'checkbox' || item.formType === 'dep-select') {
tempList.push({
validator: ({ item }: any, value: any, callback: any) => {
if (!isArray(value) || value.length === 0) {
callback(new Error(item.name + '不能为空'));
} else {
const emptyObj = value.find((valueItem: any) => isEmpty(valueItem));
emptyObj === '' ? callback(new Error(item.name + '不能为空')) : callback();
}
},
item: item,
trigger: ['blur', 'change']
});
} else {
tempList.push({
required: true,
message: item.name + '不能为空',
trigger: ['blur', 'change']
});
}
}
// 验证唯一
if (item.isUnique === 1 && UniquePromise) {
const validateUnique = (rule: any, value: any, callback: any) => {
if (isEmpty(value)) {
callback();
} else {
// 验证唯一
UniquePromise({
field: item,
rule: rule,
value: value
}).then(() => {
callback();
}).catch(msg => {
callback(new Error(msg || '验证出错'));
});
}
};
tempList.push({
validator: validateUnique,
item: item,
trigger:
item.formType === 'checkbox' || item.formType === 'select' ?
['change', 'blur'] :
['blur']
});
}
return tempList;
};
/**
* 获取数值规则
*/
const getNumberRule = (rule: any, value: any, callback: any) => {
const field = rule.item;
const arr = String(value).split('.');
const len = String(value)
.replace('.', '')
.replace('-', '')
.length;
const maxlength = field.formType === 'percent' ? 10 : 15;
const min = isEmpty(field.minNumRestrict) ? -Infinity : Number(field.minNumRestrict
|| -Infinity);
const max = isEmpty(field.maxNumRestrict) ? Infinity : Number(field.maxNumRestrict ||
Infinity);
if (len > maxlength) {
callback(new Error(`最多支持${maxlength}位数字(包含小数位)`));
} else if (isEmpty(field.precisions) && String(value).includes('.')) {
// null 不支持小数 0 不限制小数位
callback(new Error(`不支持小数`));
} else if (arr.length > 1 && arr[1].length > Number(field.precisions)) {
callback(new Error(`小数位不能大于${field.precisions}`));
} else if (value < min) {
callback(new Error(`不能小于${min}`));
} else if (value > max) {
callback(new Error(`不能大于${max}`));
} else {
callback();
}
};
/**
* 判断明细表格是否是空
* @param {*} fieldList
* @param {*} valueObj
*/
const getDetailTableIsEmpty = (fieldList: any, valueObjs: any) => {
for (let index = 0; index < valueObjs.length; index++) {
const valueObj = valueObjs[index];
if (judgeFormValueIsEmpty(fieldList, valueObj)) {
return true;
}
}
return false;
};
/**
* 判断对象值是否是空
*/
const judgeFormValueIsEmpty = (fieldList: any, valueObj: any) => {
for (let index = 0; index < fieldList.length; index++) {
const field = fieldList[index];
const value = valueObj[field.fieldName];
if (field.formType === 'location') {
if (isObject(value) && (!isEmpty(value.lat) || !isEmpty(value.lng) ||
!isEmpty(value.address))) {
return false;
}
} else if (!isEmpty(value)) {
return false;
}
}
return true;
};
return {
UniquePromise,
getRules
};
}
6.form表单的使用
<div class="form_container">
<Form
:field-list="fieldList"
:field-form="fieldForm" />
</div>
const formData = reactive({
fieldForm: {},
fieldList: [
{
field: 'name',
name: '活动名称',
mold: 'Input',
formType: 'text',
// 检验唯一性
isNull:1
},
{
field: 'region',
name: '活动区域',
mold: 'Select',
formType: 'select',
setting: [
{
label: '区域一',
value: 0
},
{
label: '区域二',
value: 0
}
]
},
{
field: 'date',
name: '活动区域',
mold: 'Date',
formType: 'datetime',
},
{
field: 'deliver',
name: '即时配送',
mold: 'Select',
formType: 'switch',
},
{
field: 'type',
name: '活动性质',
mold: 'Select',
formType: 'checkbox-group',
setting: [
{
label: '美食/餐厅线上活动',
},
{
label: '地推活动',
},
{
label: '线下主题活动',
},
{
label: '单纯品牌曝光',
},
]
},
{
field: 'resource',
name: '资源',
mold: 'Select',
formType: 'radio-group',
setting: [
{
label: '线上品牌商赞助',
value:0
},
{
label: '线下场地免费',
value:1
},
]
},
{
field: 'desc',
name: '活动形式',
mold: 'Input',
formType: 'textarea',
rows:3
},
]
});
出来的效果就是这样子滴
总结
以上就是form表单的全部代码啦 如果对您有用就浅浅的点个赞吧
更多推荐
已为社区贡献2条内容
所有评论(0)