element-ui 表单组件的简单封装-2
form.vue
<template>
<div class="form">
<hr />
<h1>form{{ formData }}</h1>
<el-form
ref="formRef"
:model="formData"
:label-width="labelWidth"
:inline="true"
:label-position="labelPosition"
:label-suffix="labelSubffix"
:size="size"
>
<el-form-item
v-for="(item, index) in formItems"
:key="index"
:prop="item.prop"
:rules="item.rules"
:required="item.required"
>
<!-- 标签设置 -->
<template #label>
<template v-if="item.tip">
<el-tooltip
class="item"
effect="dark"
:content="item.tip"
placement="top"
>
<span
>{{ item.label }}
<i class="el-icon-warning el-icon--right"></i>
</span>
</el-tooltip>
</template>
<template v-else>
{{ item.label }}
</template>
</template>
<template v-if="item.type == 'input'">
<el-input
v-model="formData[item.prop]"
:placeholder="item.placeholder"
:disabled="item.disabled"
></el-input>
</template>
<template v-else-if="item.type == 'select'">
<!-- 有的时候我们需要获取到下拉的字段以及所选到的值 -->
<el-select
v-model="formData[item.prop]"
:placeholder="item.placeholder"
clearable
filterable
@change="(value) => selectChange(item.prop, value)"
:disabled="item.disabled"
:remote="item.remote"
:remote-method="(value) => selectRemoteMethod(item.prop, value)"
>
<el-option
v-for="option in item.options"
:key="option.value"
:label="option.label"
:value="option.value"
>
</el-option>
</el-select>
</template>
<template v-else-if="item.type == 'date'">
<el-date-picker
v-model="formData[item.prop]"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
:disabled="item.disabled"
>
</el-date-picker>
</template>
<template v-else-if="item.type == 'upload'">
<!-- 向回调函数中传递更多的参数 -->
<el-upload
drag
action="https://jsonplaceholder.typicode.com/posts/"
:multiple="item.multiple"
:auto-upload="false"
:file-list="formData[item.prop]"
:list-type="item.listType"
:on-change="
(file, fileList) => {
fileOnChange(item.prop, file, fileList)
}
"
:disabled="item.disabled"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">
将文件拖到此处,或<em>点击上传</em>
</div>
<div class="el-upload__tip" slot="tip">
只能上传jpg/png文件,且不超过500kb
</div>
</el-upload>
</template>
</el-form-item>
</el-form>
<div class="footer" v-if="showFooter">
<el-button type="primary" icon="el-icon-search" @click="resetForm"
>重置</el-button
>
<el-button
type="primary"
icon="el-icon-upload
"
@click="submitForm"
>提交</el-button
>
</div>
</div>
</template>
<script>
export default {
name: 'MyForm',
// 自定义组件上使用v-model
// 1. 定义model。 2. 需要将model中prop值同时在props中定义
// 父组件通过v-model指令绑定值时会将数据传递给子组件中的modelValue,子组件通过触发"update:modelValue"事件可以通知父组件
model: {
prop: 'modelValue',
event: 'update:modelValue',
},
props: {
modelValue: Object,
formItems: {
type: Array,
required: true,
},
labelWidth: {
type: String,
},
labelPosition: {
// 自定义校验规则
validator: (value) => {
return ['left', 'right', 'top'].includes(value)
},
},
labelSubffix: {
type: String,
},
size: {
validator: (value) => {
return ['medium', 'small', 'mini'].includes(value)
},
},
showFooter: {
type: Boolean,
default: true,
},
},
data() {
return {
formData: {},
}
},
emits: ['update:modelValue', 'select-change', 'select-remote-search'],
created() {
// 表单初始化
for (let item of this.formItems) {
if (item.type == 'upload') {
this.$set(this.formData, item.prop, [])
} else {
// 注意:这里要进行响应式的设置,否则表单控件会无法输入的
// 如果有默认值则展示默认值
this.$set(this.formData, item.prop, item.value ? item.value : '')
}
}
this.$emit('update:modelValue', this.formData)
},
methods: {
fileOnChange(field, file, fileList) {
this.$set(this.formData, field, fileList)
},
resetForm() {
this.$refs.formRef.resetFields()
},
submitForm() {
this.$refs.formRef.validate().then((res) => {
console.log(res)
})
},
selectChange(field, value) {
this.$emit('select-change', {
field,
value,
})
console.log(field, value)
},
// 远程搜索
selectRemoteMethod(field, value) {
this.$emit('select-remote-search', { field, value })
},
},
}
</script>
<style scoped>
.footer {
display: flex;
justify-content: center;
}
</style>
test.vue
<template>
<div>
test:{{ formData }}
<hr />
<my-form
v-bind="formConfig"
v-model="formData"
@select-change="handleSelectChange"
@select-remote-search="handleSelectRemoteSearch"
></my-form>
</div>
</template>
<script>
import MyForm from '@/components/form'
import formConfig from './formConfig'
export default {
data() {
return {
formConfig,
formData: {},
}
},
components: {
MyForm,
},
methods: {
handleSelectRemoteSearch({ field, value }) {
console.log(`发起请求,根据${value}获取${field}字段的远程搜索`)
},
handleSelectChange({ field, value }) {
console.log(
`下拉项中${field}字段发现变化,根据${value}去变化其他的表单项,比如:省市联动`
)
},
},
}
</script>
form.config.js
const formConfig = {
formItems: [
{
type: 'input',
prop: 'name',
label: '姓名:',
placeholder: '请输入姓名',
rules: [{ required: true, message: '请输入活动名称', trigger: 'blur' }],
disabled: true,
},
{
type: 'input',
prop: 'age',
label: '年龄:',
placeholder: '请输入年龄',
required: true,
rules: [
{
validator: (rule, value, cb) => {
if (value === '') {
cb(new Error('请输入年龄'))
} else {
if (isNaN(value)) {
cb(new Error('请输入数字'))
} else {
if (value < 18) {
cb(new Error('年龄要大于18岁'))
} else {
cb()
}
}
}
},
trigger: 'blur',
},
],
},
{
type: 'select',
prop: 'address',
label: '地址:',
placeholder: '请输入地址',
options: [
{
label: '广州市',
value: 'gz',
},
{
label: '上海市',
value: 'sh',
},
{
label: '深圳市',
value: 'sz',
},
],
},
{
type: 'date',
prop: 'birthday',
label: '生日:',
placeholder: '请输入生日',
tip: '祝你生日快乐',
value: '2010-09-01',
},
{
type: 'upload',
prop: 'uploadData1',
label: '照片:',
placeholder: '请上传照片',
rules: [{ required: true, message: '请上传照片', trigger: 'blur' }],
listType: '.jpg',
},
{
type: 'select',
prop: 'movie',
label: '电影:',
placeholder: '请输入电影',
rules: [{ required: true, message: '请输入电影', trigger: 'blur' }],
tip: '输入内容进行远程搜索',
remote: true,
},
],
labelWidth: '120px',
labelPosition: 'right',
// labelSubffix: '@',
size: 'mini',
}
export default formConfig