一、文件结构 (项目components文件夹下)
createForm
fields 文件夹
modules 文件夹
MyForm.vue
二、 MyForm.vue
<template>
<el-form :model="data" ref="ruleFormRef" :rules="formProps.rules" :inline="formProps.inline" :label-position="formProps.labelPosition" :label-width="formProps.labelWidth" :label-suffix="formProps.labelSuffix" :hide-required-asterisk="formProps.hideRequiredAsterisk" :show-message="formProps.showMessage" :inline-message="formProps.inlineMessage" :status-icon="formProps.statusIcon" :validate-on-rule-change="formProps.validateOnRuleChange" :size="formProps.size" :disabled="formProps.disabled">
<el-row :align="rowProps.align" :gutter="rowProps.gutter" :justify="rowProps.justify" :tag="rowProps.tag">
<el-col class="mb20" :span="formProps.titleSpan">
<h2>{{formProps.title}}</h2>
</el-col>
<el-col class="mb20" v-for="item in formItemProps" :key="item.name" :span="item.span || 12" :style="item.style">
<el-form-item :prop="item.prop" :label="item.label" :label-width="item.labelWidth">
<template v-for="field in item.fields" :key="field.name">
<component :is="'m-' + field.mold" v-model="data[field.name]" :field="field"></component>
</template>
</el-form-item>
</el-col>
<el-col class="mb20" :span="formProps.operateSpan">
<el-form-item>
<!-- <el-button type="info" theme="default" class="form-submit-cancel" variant="base" @click="resetForm">重置</el-button> -->
<el-button type="default" theme="default" class="form-submit-cancel" variant="base" >取消</el-button>
<el-button type="primary" theme="primary" class="form-submit-confirm" @click="submit"> 确认 </el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script lang="ts">
import { defineComponent, ref, reactive, watch, unref, getCurrentInstance } from 'vue';
import { FormCreate } from './modules/FormCreate';
import MInput from './attr/MInput.vue';
export default defineComponent({
name: 'MyFrom',
components: { MInput, MSelect, MRadio },
props: {
modelValue: {
type: Object,
default: () => {
return {};
},
},
form: FormCreate,
},
setup(props, context) {
console.log('传入参数-form', props.form);
const data = reactive(props.modelValue);
watch(data, (value) => {
context.emit('update:modelValue', value);
});
};
const = () => {
console.log('表单数据-', data);
};
return {
...props.form,
data,
submit
};
},
});
</script>
三、 modules 文件夹中
FormCreate.ts
import { FormProps, FormPropsClass, FormItemProps } from './Form';
import { RowField, RowProps } from './RowField';
export class FormCreate {
formProps: FormProps = new FormPropsClass()
rowProps: RowProps = new RowField()
formItemProps: Array<FormItemProps> = [];
url = '';
setFormProps(formProps: FormProps) {
this.formProps = { ...new FormPropsClass(), ...formProps };
return this;
}
setRowProps(rowProps: RowProps) {
this.rowProps = { ...new RowField(), ...rowProps };
return this;
}
setFormItemProps(formItemProps: Array<FormItemProps>) {
this.formItemProps = formItemProps;
return this;
}
setUrl(url: string) {
this.url = url;
return this;
}
}
Form.ts
import { InputFieldInterface } from "./InputField"
export interface FormProps {
// 表单名称
title?: string
titleSpan?: number
operateSpan?: number
// 表单验证规则
rules?: object;
// 行内表单模式
inline?: boolean
// 表单域标签的位置, 如果值为 left 或者 right 时,则需要设置 label-width
labelPosition?: string
// 表单域标签的宽度,例如 '50px'。 作为 Form 直接子元素的 form-item 会继承该值。 支持 auto
labelWidth?: string | number;
// 表单域标签的后缀
labelSuffix?: string
// 是否显示必填字段的标签旁边的红色星号
hideRequiredAsterisk?: boolean
// 是否显示校验错误信息
showMessage?: boolean
// 是否以行内形式展示校验信息
inlineMessage?: boolean
// 是否在输入框中显示校验结果反馈图标
statusIcon?: boolean
// 是否在 rules 属性改变后立即触发一次验证
validateOnRuleChange?: boolean
// 用于控制该表单内组件的尺寸
size?: string
// 是否禁用该表单内的所有组件。 若设置为 true,则表单内组件上的 disabled 属性不再生效
disabled?: boolean
}
export class FormPropsClass implements FormProps {
// 表单名称
title: string = ""
titleSpan: number = 18
operateSpan: number = 8
// 表单验证规则
rules: object = {}
// 行内表单模式
inline: boolean = false
// 表单域标签的位置, 如果值为 left 或者 right 时,则需要设置 label-width
// 可选值 left / right / top
labelPosition: string = "right"
// 表单域标签的宽度,例如 '50px'。 作为 Form 直接子元素的 form-item 会继承该值。 支持 auto。
labelWidth: string | number = "50px"
// 表单域标签的后缀
labelSuffix: string = ""
// 是否显示必填字段的标签旁边的红色星号
hideRequiredAsterisk: boolean = true
// 是否显示校验错误信息
showMessage: boolean = true
// 是否以行内形式展示校验信息
inlineMessage: boolean = false
// 是否在输入框中显示校验结果反馈图标
statusIcon: boolean = false
// 是否在 rules 属性改变后立即触发一次验证
validateOnRuleChange: boolean = true
// 用于控制该表单内组件的尺寸
size: string = "large"
// 是否禁用该表单内的所有组件。 若设置为 true,则表单内组件上的 disabled 属性不再生效
disabled: boolean = false
// 设置属性
setProps(props: object) {
Object.keys(props).forEach(key => {
this[key] = props[key]
})
return this
}
};
export interface FormItemProps {
name: string;
// 文件
fields: Array<InputFieldInterface | SelectFieldInterface | RadioFieldInterface>;
// 标签
label?: string
// 样式
style?: string
// 栅格占据的列数
span?: number
// 表单域 model 字段, 在使用 validate、resetFields 方法的情况下,该属性是必填的
prop?: string
// 表单域标签的宽度,例如 '50px'。 支持 auto。
labelWidth?: string | number
// 是否必填,如不设置,则会根据校验规则自动生成
required?: boolean
// 表单验证规则, 具体配置见下表, 更多内容参考 async-validator
rules?: object | Array<object>
// 表单域验证错误信息, 设置该值会使表单验证状态变为 error,并显示该错误信息
error?: string
// 是否显示校验错误信息
showMessage?: boolean
// 以行内形式展示校验信息
inlineMessage?: boolean
// 控制组件在此表单项中的大小
size?: string
}
export class FormItemPropClass implements FormItemProps {
name: string = ""
// 文件
fields: Array<InputFieldInterface> = []
// 样式
style: string = ""
// 栅格占据的列数
span: number = 9
// 表单域 model 字段, 在使用 validate、resetFields 方法的情况下,该属性是必填的
prop: string = ""
// 标签
label: string = ""
// 表单域标签的宽度,例如 '50px'。 支持 auto。
labelWidth: string | number = "auto"
// 是否必填,如不设置,则会根据校验规则自动生成
required: boolean = false
// 表单验证规则, 具体配置见下表, 更多内容参考 async-validator
rules: object | Array<object> = {}
// 表单域验证错误信息, 设置该值会使表单验证状态变为 error,并显示该错误信息
error: string = ""
// 是否显示校验错误信息
showMessage: boolean = true
// 以行内形式展示校验信息
inlineMessage: boolean = false
// 控制组件在此表单项中的大小
size: string = ""
constructor(name: string, label: string, fields: Array<InputFieldInterface>) {
this.name = name
this.label = label
this.fields = fields
}
// 设置属性
setProps(props: object) {
Object.keys(props).forEach(key => {
this[key] = props[key]
})
return this
}
}
RowField.ts
export interface RowProps {
// 栅格间隔
gutter?: number
// flex 布局下的水平排列方式
justify?: string
// flex 布局下的垂直排列方式
align?: string
// 自定义元素标签
tag?: string
}
export class RowField implements RowProps {
// 栅格间隔
gutter: number = 9
// flex 布局下的水平排列方式
justify: string = "center"
// flex 布局下的垂直排列方式
align: string = "middle"
// 自定义元素标签
tag: string = "div"
}
InputField.ts
export interface InputFieldInterface {
// 要显示的组件类型
mold: string
// 原生属性
name: string
// 类型
type?: string
// 样式
style?: string
// 最大输入长度
maxlength?: string | number
// 原生属性,最小输入长度
minlength?: number
// 是否显示输入字数统计,只在 type = "text" 或 type = "textarea" 时有效
showWordLimit?: boolean
// 输入框占位文本
placeholder?: string
// 是否可清空
clearable?: boolean
// 是否显示切换密码图标
showPassword?: boolean
// 是否禁用
disabled?: boolean
// 输入框尺寸,只在 type !="textarea" 时有效
size?: string
// 自定义前缀图标
prefixIcon?: string
// 自定义后缀图标
suffixIcon?: string
// 输入框行数,只对 type="textarea" 有效
rows?: number
// textarea高度是否自适应,只在 type="textarea" 时生效。 Can accept an object e.g. { minRows?: 2 maxRows?: 6 }
autosize?: boolean | object
// 原生属性,自动补全
autocomplete?: string
// 原生属性,是否只读
readonly?: boolean
// 原生属性,设置最大值
max?: number | string
// 原生属性,设置最小值
min?: number | string
// 原生属性,设置输入字段的合法数字间隔
step?: number | string
// 控制是否能被用户缩放
resize?: string
// 原生属性,自动获取焦点
autofocus?: boolean
// 原生属性
form?: string
// 输入框关联的 label 文字
label?: string
// 输入框的 tabindex
tabindex?: string | number
// 输入时是否触发表单的校验
validateEvent?: boolean
// input 元素或 textarea 元素的 style
inputStyle?: object
}
export class InputField implements InputFieldInterface {
mold: string = "input"
// 原生属性
name: string
// 类型
type: string = "text"
// 样式
style: string = ''
// 最大输入长度
maxlength: string | number = 200
// 原生属性,最小输入长度
minlength: number = 0
// 是否显示输入字数统计,只在 type = "text" 或 type = "textarea" 时有效
showWordLimit: boolean = false
// 输入框占位文本
placeholder: string = ""
// 是否可清空
clearable: boolean = false
// 是否显示切换密码图标
showPassword: boolean = false
// 是否禁用
disabled: boolean = false
// 输入框尺寸,只在 type !="textarea" 时有效
size: string = "large"
// 自定义前缀图标
prefixIcon: string = ""
// 自定义后缀图标
suffixIcon: string = ""
// 输入框行数,只对 type="textarea" 有效
rows: number = 2
// textarea高度是否自适应,只在 type="textarea" 时生效。 Can accept an object e.g. { minRows: 2 maxRows: 6 }
autosize: boolean = false
// 原生属性,自动补全
autocomplete: string = "off"
// 原生属性,是否只读
readonly: boolean = false
// 原生属性,设置最大值
max: number | string = ""
// 原生属性,设置最小值
min: number | string = ""
// 原生属性,设置输入字段的合法数字间隔
step: string | number = ""
// 控制是否能被用户缩放
resize: string = "none"
// 原生属性,自动获取焦点
autofocus: boolean = false
// 原生属性
form: string = ""
// 输入框关联的 label 文字
label: string = ""
// 输入框的 tabindex
tabindex: string = ""
// 输入时是否触发表单的校验
validateEvent: boolean = true
// input 元素或 textarea 元素的 style
inputStyle: object = {}
// 设置属性
constructor(name: string) {
this.name = name
}
setProps(props: object) {
Object.keys(props).forEach(key => {
this[key] = props[key]
})
return this
}
}
四、 attr文件夹中 封装input输入框(可自行添加封装组件)
MInput.vue
<template>
<el-input v-model="value" :type="field.type" :style="field.style" :maxlength="field.maxlength" :minlength="field.minlength" :showWordLimit="field.showWordLimit" :placeholder="field.placeholder" :clearable="field.clearable" :showPassword="field.showPassword" :disabled="field.disabled" :prefixIcon="field.prefixIcon" :suffixIcon="field.suffixIcon" :rows="field.rows" :autosize="field.autosize" :autocomplete="field.autocomplete" :name="field.name" :readonly="field.readonly" :max="field.max" :min="field.min" :step="field.step" :resize="field.resize" :autofocus="field.autofocus" :form="field.form" :label="field.label" :tabindex="field.tabindex" :validateEvent="field.validateEvent" :inputStyle="field.inputStyle"></el-input>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, watch } from 'vue';
import { InputFieldInterface } from '../modules/InputField';
export default defineComponent({
name: 'MInput',
props: {
modelValue: {
type: [Number, String],
default: '',
},
field: {
type: Object as PropType<InputFieldInterface>,
required: true,
},
},
setup(props, context) {
const value = ref(props.modelValue);
watch(value, (newValue) => {
if (newValue !== props.modelValue) {
context.emit('update:modelValue', newValue);
return true;
}
return false;
});
return { value };
},
});
</script>
五、引用
index.vue
<template>
<m-form :modelValue="modelValue" :form="formCreate" @update:modelValue="handleUpdateModelValue" @submit:form="handleSubmit"></m-form>
</template>
<script lang="ts">
import { defineComponent, reactive } from 'vue';
import MForm from '/@/components/createForm/MyForm.vue';
import { FormCreate } from '/@/components/createForm/modules/FormCreate';
import { FormItemPropClass } from '/@/components/createForm/modules/Form';
import { InputField } from '/@/components/createForm/modules/InputField';
export default defineComponent({
components: { MForm },
name: 'sheetToo',
setup() {
let modelValue = reactive({
name: '',
});
let formRules = {
name: [
{ required: true, message: '姓名必传', trigger: 'blur' },
{ min: 2, max: 5, message: '姓名长度为2-5', trigger: 'blur' },
],
};
let formCreate = new FormCreate();
formCreate.setFormItemProps([
new FormItemPropClass('name', '姓名', [new InputField('name').setProps({ style: 'width:300px' })]).setProps({
span: 9,
prop: 'name',
})
]);
formCreate.setFormProps({ title: '自定义表单', rules: formRules });
const handleUpdateModelValue = (e: object) => {
console.log('更新值-', e);
};
const handleSubmit = (e: object) => {
console.log('提交', e);
};
return {
modelValue,
formCreate,
handleUpdateModelValue,
handleSubmit,
};
},
});
</script>
-
完成!