本文基于Element-plus实现二次封装表单组件。
我们都知道表单组件应该是后台管理系统中用得最多的组件,我们不可能每个业务都写一次表单,然后每一次修改都去各自业务中大幅修改,这样就可能导致代码重复率太高了,工作效率频频降低,所以我们需要封装起来,这样我们就可以复用,大大减少项目体积,方便项目的后期维护,提高前端娃的工作效率。由于我们使用表单都是直接使用UI组件库的组件,所以我们需要做二次封装,那么问题来了,二次封装表单组件我们需要考虑什么?
话不多说,直接展示代码,以下代码可以直接使用:
<template>
<el-form
:model="model"
:inline="inline"
:label-position="labelPosition"
:label-width="labelWidth"
:label-suffix="labelSuffix"
:hide-required-asterisk="hideRequiredAsterisk"
:require-asterisk-position="requireAsteriskPosition"
:show-message="showMessage"
:inline-message="inlineMessage"
:status-icon="statusIcon"
:validate-on-rule-change="validateOnRuleChange"
:size="size"
:disabled="disabled"
:scroll-to-error="scrollToError"
:scroll-into-view-options="scrollIntoViewOptions"
ref="formPanelRef"
>
<el-row v-if="inline">
<el-col
:span="(item.cols && item.cols != '') ? Number(item.cols) : 24/ Number(cols)"
v-for="(item, index) in formConfig"
:key="index"
>
<el-form-item
v-if="item.type === 'input' && item.type === 'textarea'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-input
type="textarea"
:clearable="clearable"
:style="item.style ? item.style : ''"
v-model="model[item.fieldName]"
:placeholder="item.placeholder ? item.placeholder : `请输入`"
:disabled="item.disabled ? item.disabled : false"
:maxlength="item.maxlength ? item.maxlength : ''"
:minlength="item.minlength ? item.minlength : ''"
:show-word-limit="item.showWordLimit ? item.showWordLimit : false"
:autosize="item.autosize ? item.autosize : false"
:rows="item.rows ? item.rows : 1"
@change="item.change"
>
<template v-if="item.prefix && item.prefix != ''" #prefix>{{item.prefix}}template>
<template v-if="item.suffix && item.suffix != ''" #suffix>{{item.suffix}}template>
el-input>
el-form-item>
<el-form-item
v-else-if="item.type === 'input'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-input
:type="item.inputType ? item.inputType : 'text'"
:clearable="clearable"
:style="item.style ? item.style : ''"
v-model.trim="model[item.fieldName]"
:placeholder="item.placeholder ? item.placeholder : `请输入`"
:disabled="item.disabled ? item.disabled : false"
:maxlength="item.maxlength ? item.maxlength : ''"
:minlength="item.minlength ? item.minlength : ''"
:show-word-limit="item.showWordLimit ? item.showWordLimit : false"
:autosize="item.autosize ? item.autosize : false"
:rows="item.rows ? item.rows : 1"
@change="item.change"
>
<template v-if="item.prefix && item.prefix != ''" #prefix>{{item.prefix}}template>
<template v-if="item.suffix && item.suffix != ''" #suffix>{{item.suffix}}template>
el-input>
el-form-item>
<el-form-item
v-if="item.type === 'autocomplete'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-autocomplete
:clearable="clearable"
:style="item.style ? item.style : ''"
v-model="model[item.fieldName]"
:disabled="item.disabled ? item.disabled : false"
:placeholder="item.placeholder ? item.placeholder : `请输入`"
:fetch-suggestions="item.suggestions"
@select="item.select"
@change="item.change"
@keydown="inputKeydown($event, item.DisableInput)"
>
<template v-if="item.prefix && item.prefix != ''" #prefix>{{item.prefix}}template>
<template v-if="item.suffix && item.suffix != ''" #suffix>{{item.suffix}}template>
el-autocomplete>
el-form-item>
<el-form-item
v-if="item.type === 'inputNumber'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-input
type="Number"
:clearable="clearable"
:style="item.style ? item.style : ''"
v-model="model[item.fieldName]"
:disabled="item.disabled ? item.disabled : false"
:placeholder="item.placeholder ? item.placeholder : `请输入`"
@change="item.change"
@keydown="inputKeydown($event, item.DisableInput)"
>
<template v-if="item.prefix && item.prefix != ''" #prefix>{{item.prefix}}template>
<template v-if="item.suffix && item.suffix != ''" #suffix>{{item.suffix}}template>
el-input>
el-form-item>
<el-form-item
v-if="item.type === 'inputNumberBtn'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-input
type="Number"
:clearable="clearable"
:style="item.style ? item.style : ''"
v-model="model[item.fieldName]"
:disabled="item.disabled ? item.disabled : false"
:placeholder="item.placeholder ? item.placeholder : `请输入`"
@change="inputNumberBtnChange(item.fieldName,item.minNum,item.change);"
ref="inputNumberBtnRef"
@keydown="inputKeydown($event, item.DisableInput)"
>
<template #prepend>
<el-button :disabled="model[item.fieldName] <= 1 || model[item.fieldName] == '' || !model[item.fieldName] || model[item.fieldName] == undefined" style="width: 10px !important; min-width: 10px !important;" @click="inputNumberBtnReduce(item.fieldName,item.change)">-el-button>
template>
<template #append>
<el-button style="width: 10px !important; min-width: 10px !important;" @click="inputNumberBtnAdd(item.fieldName, item.change)">+el-button>
template>
el-input>
el-form-item>
<el-form-item
v-if="item.type === 'inputrange'"
:label="item.label"
:required="item.rules ? true : false"
>
<el-col :span="11">
<el-form-item
:prop="item.fieldName[0]"
:rules="item.rules ? item.rules : []"
>
<el-input
@keydown="inputKeydown($event, item.DisableInput)"
type="Number"
:clearable="clearable"
:style="item.style ? item.style : ''"
v-model="model[item.fieldName[0]]"
:disabled="item.disabled ? item.disabled : false"
@change="inputrangeStarChange($event, item.fieldName[0],item.fieldName[1])"
:placeholder="item.placeholder[0] ? item.placeholder[0] : `请输入`">
el-input>
el-form-item>
el-col>
<el-col style="text-align: center;" :span="2">{{item.rangeSeparator}}el-col>
<el-col :span="11">
<el-form-item
:prop="item.fieldName[1]"
:rules="item.rules ? item.rules : []"
>
<el-input
@keydown="inputKeydown($event, item.DisableInput)"
type="Number"
:clearable="clearable"
:style="item.style ? item.style : ''"
v-model="model[item.fieldName[1]]"
:disabled="item.disabled ? item.disabled : false"
@change="inputrangeEndChange($event, item.fieldName[0],item.fieldName[1])"
:placeholder="item.placeholder[1] ? item.placeholder[1] : `请输入`">
el-input>
el-form-item>
el-col>
el-form-item>
<el-form-item
v-if="item.type === 'select'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-select
:filterable="item.filterable === undefined ? true : item.filterable"
:style="item.style ? item.style : ''"
:clearable="clearable"
v-model="model[item.fieldName]"
:disabled="item.disabled ? item.disabled : false"
:placeholder="item.placeholder ? item.placeholder : `请选择`"
:multiple="item.multiple ? item.multiple : false"
:collapse-tags="item.collapseTags || false"
collapse-tags-tooltip
@change="item.change"
>
<el-option
v-for="(optionItem, optionIndex) in item.optionList"
:key="optionIndex"
:label="optionItem.label"
:disabled="optionItem.disabled"
:value="optionItem.value === undefined ? optionItem.label : optionItem.value">
el-option>
el-select>
el-form-item>
<el-form-item
v-if="item.type === 'date'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-date-picker
v-model="model[item.fieldName]"
:value-format="item.valueFormat ? item.valueFormat :'yyyy-MM-dd'"
:format="item.valueFormat ? item.valueFormat :'yyyy-MM-dd'"
:style="item.style ? item.style : ''"
:clearable="clearable"
:disabled="item.disabled ? item.disabled : false"
:disabled-date="item.disabledDate ? item.disabledDate : null"
:type="item.dataType ? item.dataType : 'date'"
:placeholder="item.placeholder ? item.placeholder : `请选择`"
:start-placeholder="item.startPlaceholder ? item.startPlaceholder : `请选择`"
:end-placeholder="item.endPlaceholder ? item.endPlaceholder : `请选择`"
@change="item.change"
>
el-date-picker>
el-form-item>
<el-form-item
v-if="item.type === 'switch'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-switch
v-model="model[item.fieldName]"
:disabled="item.disabled ? item.disabled : false"
:loading="item.loading ? item.loading : false"
:active-value="item.activeValue ? item.activeValue : true"
:inactive-value="item.inactiveValue ? item.inactiveValue : false"
:style="`--el-switch-on-color: ${item.activeColor}; --el-switch-off-color: ${item.inactiveColor}; --el-switch-border-color: ${item.borderColor}`"
:placeholder="item.placeholder ? item.placeholder : `请输入`"
:before-change="item.beforeChange ? item.beforeChange : undefined"
@change="item.change"
>
el-switch>
el-form-item>
<el-form-item
v-if="item.type === 'radio'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-radio-group
v-model="model[item.fieldName]"
:disabled="item.disabled ? item.disabled : false"
:border="item.border ? item.border : false"
@change="item.change"
>
<el-radio
v-for="(optionItem, optionIndex) in item.optionList"
:label="JSON.stringify(optionItem)"
:key="optionIndex"
>
{{optionItem.label}}
el-radio>
el-radio-group>
el-form-item>
<el-form-item
v-if="item.type === 'checkbox' && model[item.fieldName]"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-checkbox-group
v-model="model[item.fieldName]"
:disabled="item.disabled ? item.disabled : false"
:min="item.min ? item.min : undefined"
:max="item.max ? item.max : undefined"
:text-color="item.textColor ? item.textColor : '#FFFFFF'"
:fill="item.fill ? item.fill : '#3171F1'"
@change="item.change"
>
<el-checkbox
v-for="(optionItem, optionIndex) in item.optionList"
:label="optionItem"
:key="optionIndex">
{{optionItem.label}}
el-checkbox>
el-checkbox-group>
el-form-item>
<el-form-item
v-if="item.type === 'mapInput' && model[item.fieldName]"
:label="item.label"
:prop="item.fieldName.address"
:rules="item.rules ? item.rules : []"
>
<BaiduMap
:placeholder="item.placeholder"
:disabled="item.disabled ? item.disabled : false"
:clearable="clearable"
v-model:mapData="model[item.fieldName]"
/>
el-form-item>
<el-form-item
v-if="item.type === 'regionInput'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-cascader
v-model.trim="model[item.fieldName]"
:clearable="clearable"
:disabled="item.disabled ? item.disabled : false"
:placeholder="item.placeholder ? item.placeholder : `请选择`"
:options="regionList"
separator="-"
:props="{
value: 'name',
label: 'name',
children: 'child'
}"
@change="item.change"
/>
el-form-item>
<el-form-item
v-if="item.type === 'upload'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<UploadPanel
:uploadType="item.uploadType"
:maxLength="item.maxLength"
:maxSize="item.maxSize"
:disabled="item.disabled"
:FileList="item.initValue"
:dir="item.dir"
:localFiles="item.localFiles"
:project_id="item.project_id"
/>
el-form-item>
<el-form-item
v-if="item.type === 'selectProjectUser'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<SelectProjectUser
v-model:userList="model[item.fieldName]"
:disabled="item.disabled"
:placeholder="item.placeholder"
:dialogTitle="item.dialogTitle"
:separator="item.separator"
:bottomCount="item.bottomCount"
:change="item.change"
/>
el-form-item>
<el-form-item
:label-width="item.labelWidth"
v-if="item.type === 'show'"
:label="item.label"
style="margin-bottom:10px"
class="l_formPanel_show"
>
<div :style="`line-height: 20px; ${item.style}`" v-if="model[item.fieldName]" v-html="model[item.fieldName].replace(/\n/g,'')">div>
el-form-item>
el-col>
<el-col :span="24/ Number(cols)"><slot name="after">slot>el-col>
el-row>
<template v-else>
<el-row
v-for="(item, index) in formConfig"
:key="index"
>
<el-col
:span="(item.cols && item.cols != '') ? Number(item.cols) : 24"
>
<el-form-item
v-if="item.type === 'input' && item.inputType === 'textarea'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-input
type="textarea"
:clearable="clearable"
:style="item.style ? item.style : ''"
v-model="model[item.fieldName]"
:placeholder="item.placeholder ? item.placeholder : `请输入`"
:disabled="item.disabled ? item.disabled : false"
:maxlength="item.maxlength ? item.maxlength : ''"
:minlength="item.minlength ? item.minlength : ''"
:show-word-limit="item.showWordLimit ? item.showWordLimit : false"
:autosize="item.autosize ? item.autosize : false"
:rows="item.rows ? item.rows : (item.inputType === 'textarea' ? 2 : 1)"
@change="item.change"
>
<template v-if="item.prefix && item.prefix != ''" #prefix>{{item.prefix}}template>
<template v-if="item.suffix && item.suffix != ''" #suffix>{{item.suffix}}template>
el-input>
el-form-item>
<el-form-item
v-else-if="item.type === 'input'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-input
:type="item.inputType ? item.inputType : 'text'"
:clearable="clearable"
:style="item.style ? item.style : ''"
v-model.trim="model[item.fieldName]"
:placeholder="item.placeholder ? item.placeholder : `请输入`"
:disabled="item.disabled ? item.disabled : false"
:maxlength="item.maxlength ? item.maxlength : ''"
:minlength="item.minlength ? item.minlength : ''"
:show-word-limit="item.showWordLimit ? item.showWordLimit : false"
:autosize="item.autosize ? item.autosize : false"
:rows="item.rows ? item.rows : (item.inputType === 'textarea' ? 2 : 1)"
@change="item.change"
>
<template v-if="item.prefix && item.prefix != ''" #prefix>{{item.prefix}}template>
<template v-if="item.suffix && item.suffix != ''" #suffix>{{item.suffix}}template>
el-input>
el-form-item>
<el-form-item
v-if="item.type === 'autocomplete'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-autocomplete
:clearable="clearable"
:style="item.style ? item.style : ''"
v-model.trim="model[item.fieldName]"
:disabled="item.disabled ? item.disabled : false"
:placeholder="item.placeholder ? item.placeholder : `请输入`"
:fetch-suggestions="item.suggestions"
:show-word-limit="item.showWordLimit ? item.showWordLimit : false"
@select="item.select"
@change="item.change"
@keydown="inputKeydown($event, item.DisableInput)"
>
<template v-if="item.prefix && item.prefix != ''" #prefix>{{item.prefix}}template>
<template v-if="item.suffix && item.suffix != ''" #suffix>{{item.suffix}}template>
el-autocomplete>
el-form-item>
<el-form-item
v-if="item.type === 'inputNumber'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-input
type="Number"
:clearable="clearable"
:style="item.style ? item.style : ''"
v-model="model[item.fieldName]"
:disabled="item.disabled ? item.disabled : false"
:placeholder="item.placeholder ? item.placeholder : `请输入`"
@change="item.change"
@keydown="inputKeydown($event, item.DisableInput)"
>
<template v-if="item.prefix && item.prefix != ''" #prefix>{{item.prefix}}template>
<template v-if="item.suffix && item.suffix != ''" #suffix>{{item.suffix}}template>
el-input>
el-form-item>
<el-form-item
v-if="item.type === 'inputNumberBtn'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-input
type="Number"
:clearable="clearable"
:style="item.style ? item.style : ''"
v-model="model[item.fieldName]"
:disabled="item.disabled ? item.disabled : false"
:placeholder="item.placeholder ? item.placeholder : `请输入`"
@change="inputNumberBtnChange(item.fieldName,item.minNum,item.change);"
ref="inputNumberBtnRef"
@keydown="inputKeydown($event, item.DisableInput)"
>
<template #prepend>
<el-button :disabled="model[item.fieldName] <= 1 || model[item.fieldName] == '' || !model[item.fieldName] || model[item.fieldName] == undefined" style="width: 10px !important; min-width: 10px !important;" @click="inputNumberBtnReduce(item.fieldName,item.change)">-el-button>
template>
<template #append>
<el-button style="width: 10px !important; min-width: 10px !important;" @click="inputNumberBtnAdd(item.fieldName, item.change)">+el-button>
template>
el-input>
el-form-item>
<el-form-item
v-if="item.type === 'inputrange'"
:label="item.label"
:required="item.rules ? true : false"
>
<el-col :span="11">
<el-form-item
:prop="item.fieldName[0]"
:rules="item.rules ? item.rules : []"
>
<el-input
@keydown="inputKeydown($event, item.DisableInput)"
type="Number"
:clearable="clearable"
:style="item.style ? item.style : ''"
v-model="model[item.fieldName[0]]"
:disabled="item.disabled ? item.disabled : false"
@change="inputrangeStarChange($event, item.fieldName[0],item.fieldName[1])"
:placeholder="item.placeholder[0] ? item.placeholder[0] : `请输入`">
el-input>
el-form-item>
el-col>
<el-col style="text-align: center;" :span="2">{{item.rangeSeparator}}el-col>
<el-col :span="11">
<el-form-item
:prop="item.fieldName[1]"
:rules="item.rules ? item.rules : []"
>
<el-input
@keydown="inputKeydown($event, item.DisableInput)"
type="Number"
:clearable="clearable"
:style="item.style ? item.style : ''"
v-model="model[item.fieldName[1]]"
:disabled="item.disabled ? item.disabled : false"
@change="inputrangeEndChange($event, item.fieldName[0],item.fieldName[1])"
:placeholder="item.placeholder[1] ? item.placeholder[1] : `请输入`">
el-input>
el-form-item>
el-col>
el-form-item>
<el-form-item
v-if="item.type === 'select'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-select
:default-first-option="item.defaultFirstOption ? item.defaultFirstOption : false"
:filterable="item.filterable === undefined ? true : item.filterable"
:allow-create="item.allowcreate ? item.allowcreate : false"
:style="item.style ? item.style : ''"
:clearable="clearable"
v-model="model[item.fieldName]"
:disabled="item.disabled ? item.disabled : false"
:placeholder="item.placeholder ? item.placeholder : `请选择`"
:multiple="item.multiple ? item.multiple : false"
:collapse-tags="item.collapseTags || false"
collapse-tags-tooltip
@change="item.change"
@remove-tag="item.remove"
>
<el-option
v-for="(optionItem, optionIndex) in item.optionList"
:key="optionIndex"
:label="optionItem.label"
:disabled="optionItem.disabled"
:value="optionItem.value === undefined ? optionItem.label : optionItem.value">
el-option>
el-select>
el-form-item>
<el-form-item
v-if="item.type === 'date'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-date-picker
v-model="model[item.fieldName]"
:value-format="item.valueFormat ? item.valueFormat :'yyyy-MM-dd'"
:format="item.valueFormat ? item.valueFormat :'yyyy-MM-dd'"
:style="item.style ? item.style : ''"
:clearable="clearable"
:disabled="item.disabled ? item.disabled : false"
:disabled-date="item.disabledDate ? item.disabledDate : null"
:type="item.dataType ? item.dataType : 'date'"
:placeholder="item.placeholder ? item.placeholder : `请选择`"
:start-placeholder="item.startPlaceholder ? item.startPlaceholder : `请选择`"
:end-placeholder="item.endPlaceholder ? item.endPlaceholder : `请选择`"
@change="item.change"
>
el-date-picker>
el-form-item>
<el-form-item
v-if="item.type === 'switch'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-switch
v-model="model[item.fieldName]"
:disabled="item.disabled ? item.disabled : false"
:loading="item.loading ? item.loading : false"
:active-value="item.activeValue ? item.activeValue : true"
:inactive-value="item.inactiveValue ? item.inactiveValue : false"
:style="`--el-switch-on-color: ${item.activeColor}; --el-switch-off-color: ${item.inactiveColor}; --el-switch-border-color: ${item.borderColor}`"
:placeholder="item.placeholder ? item.placeholder : `请输入`"
:before-change="item.beforeChange ? item.beforeChange : undefined"
@change="item.change"
>
el-switch>
el-form-item>
<el-form-item
v-if="item.type === 'radio'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-radio-group
v-model="model[item.fieldName]"
:disabled="item.disabled ? item.disabled : false"
:border="item.border ? item.border : false"
@change="item.change"
>
<el-radio
v-for="(optionItem, optionIndex) in item.optionList"
:label="JSON.stringify(optionItem)"
:key="optionIndex"
>
{{optionItem.label}}
el-radio>
el-radio-group>
el-form-item>
<el-form-item
v-if="item.type === 'checkbox' && model[item.fieldName]"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-checkbox-group
v-model="model[item.fieldName]"
:disabled="item.disabled ? item.disabled : false"
:min="item.min ? item.min : undefined"
:max="item.max ? item.max : undefined"
:text-color="item.textColor ? item.textColor : '#FFFFFF'"
:fill="item.fill ? item.fill : '#3171F1'"
@change="item.change"
>
<el-checkbox
v-for="(optionItem, optionIndex) in item.optionList"
:label="optionItem"
:key="optionIndex">
{{optionItem.label}}
el-checkbox>
el-checkbox-group>
el-form-item>
<el-form-item
v-if="item.type === 'mapInput' && model[item.fieldName]"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<BaiduMap
:placeholder="placeholder"
:disabled="item.disabled ? item.disabled : false"
:clearable="clearable"
v-model:mapData="model[item.fieldName]"
/>
el-form-item>
<el-form-item
v-if="item.type === 'regionInput'"
:label="item.label"
:prop="item.fieldName"
:rules="item.rules ? item.rules : []"
>
<el-cascader
v-model.trim="model[item.fieldName]"
:clearable="clearable"
:disabled="item.disabled ? item.disabled : false"
:placeholder="item.placeholder ? item.placeholder : `请选择`"
:options="regionList"
separator="-"
:props="{
value: 'name',
label: 'name',
children: 'child'
}"
@change="item.change"
/>
el-form-item>
<el-form-item
:label-width="item.labelWidth"
v-if="item.type === 'show'"
:label="item.label"
style="margin-bottom:10px"
class="l_formPanel_show"
>
<div :style="`line-height: 20px; ${item.style}`" v-if="model[item.fieldName]" v-html="model[item.fieldName].replace(/\n/g,'')">div>
el-form-item>
el-col>
el-row>
template>
el-form>
template>
<script>
import { reactive,toRefs,onMounted, getCurrentInstance, watch} from 'vue';
import region from '../../assets/json/region';
export default {
name: '',
props: {
formConfig: { // 表单的渲染数据,必须
type: Array,
default: ()=> {
return [];
},
required: true,
},
// model: { // 表单数据对象
// type: Object,
// default: ()=> {
// return {};
// }
// },
// rules: { // 表单验证规则
// type: Object,
// default: ()=> {
// return {};
// }
// },
cols: { // 每一行显示数量,必须可以被24整除
type: [String,Number],
default: 2
},
inline: { // 行内表单模式
type: Boolean,
default: false
},
labelPosition: { // 表单域标签的位置, 当设置为 left 或 right 时,则也需要设置 label-width 属性
type: String,
default: 'right'
},
labelWidth: { // 标签的长度,例如 '50px'。 作为 Form 直接子元素的 form-item 会继承该值。 可以使用 auto。
type: [String,Number],
default: '100px'
},
labelSuffix: { // 表单域标签的后缀
type: String,
default: ''
},
hideRequiredAsterisk: { // 是否隐藏必填字段标签旁边的红色星号。
type: Boolean,
default: false
},
requireAsteriskPosition: { // 星号的位置。'left' | 'right'
type: String,
default: 'left'
},
showMessage: { // 是否显示校验错误信息
type: Boolean,
default: true
},
inlineMessage: { // 是否以行内形式展示校验信息
type: Boolean,
default: false
},
statusIcon: { // 是否在输入框中显示校验结果反馈图标
type: Boolean,
default: false
},
validateOnRuleChange: { // 是否在 rules 属性改变后立即触发一次验证
type: Boolean,
default: true
},
size: { // 用于控制该表单内组件的尺寸 '' | 'large' | 'default' | 'small'
type: String,
default: ''
},
disabled: { // 是否禁用该表单内的所有组件。 如果设置为 true, 它将覆盖内部组件的 disabled 属性
type: Boolean,
default: false
},
scrollToError: { // 当校验失败时,滚动到第一个错误表单项
type: Boolean,
default: true
},
scrollIntoViewOptions: { //当校验有失败结果时,滚动到第一个失败的表单项目 object/ boolean
type: [Object, Boolean],
default: ''
},
clearable: { // 是否可清空
type: Boolean,
default: true
},
},
components: {
UploadPanel,
SelectProjectUser
},
setup(props) {
const { proxy } = getCurrentInstance(); // 相当于之前的this
const data = reactive({
model: {},
regionList: region,
});
const createFormkey = (type)=> {
let formKeyList = {};
props.formConfig.map(item => {
if(typeof item.fieldName == 'string') {
formKeyList[item.fieldName] = item.initValue === undefined ? '' : item.initValue;
} else { // 处理范围输入的key
formKeyList[item.fieldName[0]] = item.initValue[0] ? item.initValue[0] : '';
formKeyList[item.fieldName[1]] = item.initValue[1] ? item.initValue[1] : '';
}
});
if(type && type == 'update'){ // 更新组件内容保留历史数据
for(let key in data.model){
props.formConfig.map(item => {
if(item.fieldName == key){
formKeyList[key] = data.model[key];
}
})
}
}
data.model = formKeyList;
};
const inputKeydown = (e, DisableInput)=> { // 数字类型输入框禁止输入不合法字符
let key = e.key;
let DisableInputStr = '';
if(DisableInput){
DisableInputStr = DisableInput;
} else {
DisableInputStr = 'e,+,-,Shift'
}
// console.log(key, '---key')
if(DisableInputStr.indexOf(key) > -1) {
e.returnValue = false;
return false
};
return true;
};
/**
* @description: 清除表单验证
* proxy.$refs.FormPanelRef.clearValidate();
* @return {*}
* @author:
*/
const clearValidate = () => {
return proxy.$refs.formPanelRef.clearValidate();
};
/**
* @description: 清除指定数据,不穿清除全部
* proxy.$refs.FormPanelRef.clearKey(['keyName']);
* @return {*}
* @author:
*/
const clearKey = (key) => {
if(key&&key.length>0){
props.formConfig.map(item => {
key.map(keyItem => {
if(item.fieldName == keyItem){
data.model[keyItem] = item.initValue;
}
})
})
} else {
createFormkey();
}
};
/**
* @description: 清除数据和表单验证
* proxy.$refs.FormPanelRef.clear();
* @return {*}
* @author:
*/
const clear = () => {
clearValidate();
clearKey();
};
/**
* @description: 设置数据
* proxy.$refs.FormPanelRef.set('key', 'value');
* @return {*}
* @author:
*/
const set = (key, value) => {
data.model[key] = value;
};
/**
* @description: 得到数据
* proxy.$refs.FormPanelRef.get('key');
* @return {*}
* @author:
*/
const get = (key) => {
return data.model[key];
};
/**
* @description: 验证表单并输入结果通过
* proxy.$refs.FormPanelRef.FormPanelValidate().then((res)=> {
console.log(res,)
})
* @return {*}
* @author:
*/
const validate = ()=> { // 验证结果并输出
return new Promise((resolve, reject) => {
proxy.$refs.formPanelRef.validate((valid) => {
if(valid) {
let formData = JSON.parse(JSON.stringify(proxy.model))
resolve(formData);
} else {
return false;
}
})
})
};
const inputrangeStarChange = (e, skey, eKey)=> { // 范围查询输入框初始值
if(proxy.model[eKey] && Number(proxy.model[eKey]) <= Number(e)) {
if(proxy.model[eKey] > 1) {
proxy.model[skey] = Number(proxy.model[eKey]) - 1;
} else {
proxy.model[skey] = 0;
}
}
};
const inputrangeEndChange = (e, skey, eKey)=> { // 范围查询输入框结束
if(proxy.model[skey] && Number(proxy.model[skey]) >= Number(e)) {
proxy.model[eKey] = Number(proxy.model[skey]) + 1;
}
};
const inputNumberBtnChange = (key, minNum, change)=> {
if(minNum && data.model[key] && data.model[key] != '' && data.model[key] != null && data.model[key] != undefined){
if(Number(data.model[key]) < Number(minNum)){
data.model[key] = Number(minNum)
}
};
change(data.model[key]);
}
const inputNumberBtnAdd = (key, change)=> { // 数字加
if(data.model[key] == '' || !data.model[key] || data.model[key] == null || data.model[key] == undefined){
data.model[key] = 1;
} else {
data.model[key] = Number(data.model[key]) + 1
};
change(data.model[key]);
};
const inputNumberBtnReduce = (key,change)=> { // 数字减
data.model[key] = Number(data.model[key]) - 1;
change(data.model[key]);
};
onMounted(() => {
createFormkey();
});
watch(props.formConfig,(newVal,oldVal)=>{
createFormkey('update');
});
return {
...toRefs(data),
inputKeydown,
clearValidate,
validate,
inputrangeStarChange,
inputrangeEndChange,
inputNumberBtnAdd,
inputNumberBtnReduce,
inputNumberBtnChange,
clearKey,
clear,
set,
get,
};
}
}
script>
<style lang='scss' scoped>
:deep(.el-form-item){
width: 100%;
.el-input{
width: 100% !important;
}
.el-select{
width: 100% !important;
}
.el-cascader{
width: 100% !important;
}
}
:deep(.el-input__prefix){
margin-right: 5px;
}
:deep(.el-input__suffix){
margin-left: 5px;
}
style>
<style>
/* // 取消input的上下箭头 */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button{
-webkit-appearance: none !important;
margin: 0;
}
/* 火狐 */
input[type="number"]{
-moz-appearance: textfield;
appearance: textfield;
}
style>
接下来我来告诉大家怎么使用:
#### FormPanel 组件调用
`组件参数API`
- formConfig{Array | 表单面板组件数据数组,支持动态赋值(数据数组必须是新的引用)}
- inline{Boolean | 是否启用行内表单, 默认值 false}
- cols{Number | 每行显示多小列,默认是 2,注意:只能是被 24 整除的值}
- labelWidth{Number | label 标签的宽度,默认是 120}
`formConfig 公用字段配置项`
- type{String | 类型,支持 input-输入框 / inputNumber-数字类型输入框 / inputrange-区间输入 / select-下拉框 / date-日期选择 / switch-开关 / radio-单选 / checkbox-多选框 / mapInput-地图 / regionInput-省市区选择 / upload-上传}
- label{String | 标题}
- fieldName{String | 字段名称}
- initValue{All | 初始值}
- placeholder{String | 提示文字}
- rules{Array | 验证提示文字}
- style{Object | 内容样式}
- disabled{Boolean | 是否禁用}
- change{Function | 触发函数}
`示例代码`
```bash
# template
<template>
<FormPanel
:formConfig="formConfig"
ref="FormRef"
:inline="true"
:cols="4"
/>
</template>
# js
export default {
data() {
return {
formConfig: this.createFormConfig(),
};
},
methods: {
createFormConfig() {
return [
{
type: 'input', // 普通输入框
inputType: 'text', // 类型, text/textarea
label: '字符串:', // 标签文本
cols: '', // 自定义列
fieldName: 'stringName', // 字段名称
placeholder: '请输入商品名称', // 提示文字
initValue: '', // 默认值
rules: [{ required: false, message: '请选择所属分类', trigger: 'blur' }],
style: {}, // 自定义样式
disabled: false, // 是否禁用 boolean
maxlength: '', //最大输入长度 string/number
minlength: '', // 最小输入长度 number
showWordLimit: false, // 是否显示统计字数, 只在 type 为 'text' 或 'textarea' 的时候生效 boolean
rows: 2, // 输入框行数,仅 inputType 为 'textarea' 时有效 number
autosize: false, // textarea 高度是否自适应,仅 type 为 'textarea' 时生效。 可以接受一个对象,比如: { minRows: 2, maxRows: 6 }
change: (val) => {
console.log(val, 'val--------')
}
},
{
type: 'input', // 普通输入框
inputType: 'textarea', // 类型, text/textarea
label: '文本类型', // 标签文本
cols: '', // 自定义列
fieldName: 'textareaName', // 字段名称
placeholder: '请输入商品名称', // 提示文字
initValue: '', // 默认值
rules: [{ required: false, message: '请选择所属分类', trigger: 'blur' }],
style: {}, // 自定义样式
disabled: false, // 是否禁用 boolean
maxlength: '', //最大输入长度 string/number
minlength: '', // 最小输入长度 number
showWordLimit: false, // 是否显示统计字数, 只在 type 为 'text' 或 'textarea' 的时候生效 boolean
rows: 2, // 输入框行数,仅 inputType 为 'textarea' 时有效 number
autosize: { // 默认使用, 优先级高于rows
minRows: 1, maxRows: 2
}, // textarea 高度是否自适应,仅 type 为 'textarea' 时生效。 可以接受一个对象,比如: { minRows: 2, maxRows: 6 }
change: (val) => {
console.log(val, 'val--------')
}
},
{
type: 'inputNumber',
label: '数字类型 :',
fieldName: 'numeberName',
placeholder: '请输入上门人数',
initValue: '',
prefix: '¥', // 插入左侧的内容
suffix: '人', // 插入右侧的内容
DisableInput: 'e,+,-,Shift,.', // 禁止输入的值,默认值e,+,-,Shift
rules: [{ required: false, message: '请输入上门人数', trigger: 'blur' }],
disabled: false, // 是否禁用
maxlength: '', //最大输入长度
minlength: '', // 最小输入长度
showWordLimit: false, // 是否显示统计字数, 只在 type 为 'text' 或 'textarea' 的时候生效
rows: 2, // 输入框行数,仅 inputType 为 'textarea' 时有效
autosize: false, // textarea 高度是否自适应,仅 type 为 'textarea' 时生效。 可以接受一个对象,比如: { minRows: 2, maxRows: 6 }
style: {
},
change: (val) => {
console.log(val, 'val--------')
}
},
{
type: 'inputNumberBtn', // 加减号input
label: '持续时长:',
fieldName: 'planed_duration',
placeholder: '请输入持续时长',
initValue: '',
DisableInput: 'e,+,-,Shift,.',
style: 'width: 220px !important;',
disabled: false, // 是否禁用
rules: [{ required: true, message: '请选择持续时长', trigger: 'blur' }],
},
{
type: 'inputrange',
label: '区间输入',
fieldName: ['min_price', 'max_price'],
placeholder: ['请输入', '请输入'],
initValue: ['', ''],
rangeSeparator: '至',
rules: [{ required: false, message: '请输入', trigger: 'blur' }],
disabled: false, // 是否禁用
maxlength: '', //最大输入长度
minlength: '', // 最小输入长度
showWordLimit: false, // 是否显示统计字数, 只在 type 为 'text' 或 'textarea' 的时候生效
rows: 2, // 输入框行数,仅 inputType 为 'textarea' 时有效
autosize: false, // textarea 高度是否自适应,仅 type 为 'textarea' 时生效。 可以接受一个对象,比如: { minRows: 2, maxRows: 6 }
},
{
type: 'select',
label: '下拉框:',
fieldName: 'selectName',
placeholder: '请选中所属分类',
initValue: '',
optionList: [{ label: '全部', value: '1' }, { label: '待确认', value: '2' }],
multiple: false, // 是否多选, 多选时initValue类型是数组
collapseTags: true, // 多选时是否合并文字,默认合并
allowcreate: true, //是否创建新的条目
defaultFirstOption: true, //是否默认第一选项,此字段为true时,按下回车就可以选中当前选项列表中第一个选项
style: {width: '200px'},
change: (val) => {
console.log(val, 'val--------')
}
},
{
type: 'date',
label: '日期选择:',
fieldName: 'dateName',
placeholder: '请选择日期',
startPlaceholder: '', // 范围选择时开始日期的占位内容
endPlaceholder: '', // 范围选择时结束日期的占位内容
initValue: '',
dataType: 'monthrange', // year(年-YYYY)/month(月-YYYY-MM)/date(日-YYYY-MM-DD)/dates(多日期选择)/datetime(日期时间-YYYY-MM-DD hh:mm:ss)/ week(周-ww 周)/datetimerange(日期时间段-YYYY-MM-DD hh:mm:ss)/daterange(日期段-YYYY-MM-DD)/ monthrange(月份范围-YYYY-MM)
valueFormat: 'YYYY-MM', // 日期格式默认年月日
disabled: false, // 是否禁用
change: (val) => {
console.log(val, 'val--------')
}
},
{
type: 'switch',
label: '开关:',
fieldName: 'switchName',
initValue: '0',
placeholder: '',
disabled: false, // 是否禁用
loading: false, // 是否显示loading
activeValue: '1', // 打开时的值
inactiveValue: '0', // 关闭时的值
activeColor: '#3171F1', // 打开时的颜色
inactiveColor: '#C0CCDA', // 关闭时的颜色
borderColor: '', // 开关边框颜色
validateEvent: true, // 切换时是否触发表单校验
beforeChange: () => { // switch 状态改变前的钩子, 返回 false 或者返回 Promise 且被 reject 则停止切换
return new Promise((resolve, reject) => {
setTimeout(() => {
return resolve(true)
return reject(new Error('Error'))
}, 1000)
})
},
change: (val) => { // switch 状态发生变化时的回调函数
console.log(val, 'val--------')
}
},
{
type: 'radio',
label: '单选框:',
fieldName: 'radioName',
initValue: '',
disabled: false, // 是否禁用
border: false, // 是否显示边框
optionList: [{ label: '全部', value: '1' }, { label: '待确认', value: '2' }], // 选项数组,返回值为整个对象
change: (val) => {
console.log(val, 'val--------')
}
},
{
type: 'checkbox',
label: '多选框:',
fieldName: 'checkboxName',
placeholder: '请选择日期',
initValue: [],
disabled: false, // 是否禁用
min: undefined, // 可被勾选的 checkbox 的最小数量
max: 999, // 可被勾选的 checkbox 的最大数量
textColor: '#FFFFFF', // 当按钮为活跃状态时的字体颜色
fill: '#3171F1', // 当按钮为活跃状态时的边框和背景颜色
optionList: [{ label: '全部', value: '1' }, { label: '待确认', value: '2' }],
change: (val) => {
console.log(val, 'val--------')
}
},
{
type: 'mapInput',
label: '地图',
fieldName: 'mapInputName',
placeholder: '请选择定位',
initValue: {
province: '', // 省
city: '', // 市
area: '', // 区
address: '', // 详细地址
lng: 0, // 定位横坐标
lat: 0, // 定位纵坐标
},
disabled: false, // 是否禁用
rules: [{ required: true, message: '请选择定位', trigger: 'blur' }],
},
{
type: 'regionInput', // 普通输入框
label: '省市区:', // 标签文本
cols: '', // 自定义列
fieldName: 'regionInputName', // 字段名称
placeholder: '请选择', // 提示文字
initValue: '', // 默认值
rules: [{ required: false, message: '请选择所', trigger: 'blur' }],
change: (val) => {
console.log(val, 'val--------')
}
},
{
type: 'upload', // 普通输入框
label: '上传', // 标签文本
cols: '', // 自定义列
fieldName: 'uploadName', // 字段名称
initValue: [], // 默认值
uploadType: 'uploadFile',// 组件的类型, 支持 uploadFile(上传图频文件),uploadImg(上传图片)showFile(仅展示) 默认值: uploadFile}
maxLength: 10, // 上传数量,默认值10
maxSize: 1000, // 默认1000M
dir: '', // 存储文件的目录,后端提供
disabled: false,
rules: [{ type: 'array', required: true, message: '请上传', trigger: 'change' }],
change: (val) => {
console.log(val, 'val--------')
}
},
{ // 选择项目人员
type: 'selectProjectUser',
label: '审批人:', // 标签文本
fieldName: 'user_data', // 字段名称
placeholder: '请选择', // 提示文字
dialogTitle: '选择审批人', // 弹窗名称
separator: '>', // 分隔符
bottomCount: true, // 启用底部计数
initValue: [], // 默认值
},
{
type: 'show',
label: '仅展示:',
fieldName: 'show',
initValue: '展示的内容'
},
]
},
}
};