vue3+ts+elementPlus 自定义生成封装form表单组件

一、文件结构 (项目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>
-

完成!

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值