信息发布→ 登录 注册 退出

element基于el-form智能的FormSmart表单组件

发布时间:2026-01-11

点击量:
目录
  • 目的
  • 源码
  • 关键文档,大部分复制el-form
    • Attributes
    • Methods
    • Events
    • Slot
    • Scoped Slot
    • Form-Item Attributes
    • Row Attributes
    • Col Attributes
  • 基本用法
    • 使用场景
      • 1. 通用列表搜索条件
      • 2.弹窗写法优化

    目的

    • el-form 表单配置化,后期可以利用后端进行配置表单
    • 支持栅格布局
    • el-form 属性大部分都支持
    • 公共配置可以统一处理,如trim、input宽度等
    • 最基本的组件,利用这组件后期可以优化简单的新增编辑弹窗写法

    源码

    <script>
    export default {
        props: {
            model: {
                type: Object,
                default: () => {},
            },
            rowItems: {
                type: Array,
                default: () => [],
            },
            formItems: {
                type: Array,
                default: () => [],
            },
        },
        data() {
            return {
                _model: {},
            };
        },
        mounted() {
            // 用于重置,时间范围选择组件时重置不了endTime
            this._model = { ...this.model };
            // 默认选中光标,设置ref=focus
            this.focus();
        },
        methods: {
            // 处理v-modelv value 值
            hanlderValue(prop) {
                // 时间范围选择组件处理
                if (Array.isArray(prop) && prop.length > 0) {
                    const [first] = prop;
                    if (this.model[first]) {
                        const data = prop.map((key) => this.model[key]);
                        return data;
                    }
                    return [];
                }
                return this.model[prop];
            },
            // 处理FormItem绑定值
            hanlderFormItemProp(prop) {
                // 时间范围选择组件处理
                if (Array.isArray(prop) && prop.length > 0) {
                    // 处理时间范围选择的
                    const [first] = prop;
                    return first;
                }
                return prop;
            },
            // 处理改变值
            hanlderModel(prop, value) {
                // 时间范围选择组件处理
                if (prop && Array.isArray(prop)) {
                    if (Array.isArray(value)) {
                        // 数组对应值
                        prop.forEach((key, index) => {
                            this.model[key] = value && value.length > 0 ? value[index] : '';
                        });
                    } else if ([null, '', undefined].includes(value)) {
                        // 数组有清除按钮时,数组一起清理
                        prop.forEach((key) => {
                            this.model[key] = value;
                        });
                    }
                } else {
                    this.model[prop] = value;
                }
            },
            focus() {
                this.$nextTick(() => {
                    if (this.$refs.focus) {
                        setTimeout(() => {
                            this.$refs.focus.focus();
                        }, 100);
                    }
                });
            },
            validate(...props) {
                return this.$refs.form.validate(...props);
            },
            // 对部分字段进行校验
            validateField(...props) {
                return this.$refs.form.validateField(...props);
            },
            // 对整个表单进行重置
            resetFields() {
                Object.assign(this.model, this._model);
                this.$refs.form.resetFields();
            },
            // 移除表单项的校验结果
            clearValidate(...props) {
                this.$refs.form.clearValidate(...props);
            },
        },
        render(createElement) {
            // 父级slots
            // form-item label slot
            const slotLabel = (slotName) => createElement(
                'template',
                {
                    slot: 'label',
                },
                this.$slots[slotName],
            );
            // formItem 组件
            const formItem = (item) => createElement(
                'el-form-item',
                {
                    props: {
                        ...item,
                        prop: this.hanlderFormItemProp(item.prop),
                    },
                    scopedSlots: {
                        error: (prop) => this.$scopedSlots[item.slotErrorName] && this.$scopedSlots[item.slotErrorName](prop),
                    },
                },
                [
                    // 加入label插槽
                    item.slotLabelName ? slotLabel(item.slotLabelName) : '',
                    // 有插槽优先
                    item.slotName ? this.$slots[item.slotName] : createElement(item.type || 'el-input', {
                        //
                        ref: item.ref,
                        attrs: {
                            ...item.props,
                        },
                        props: {
                            clearable: true,
                            ...item.props,
                            value: this.hanlderValue(item.prop),
                        },
                        // 加样式
                        style: {
                            width: '240px',
                            ...(item.props && item.props.style),
                        },
                        on: {
                            ...item.on,
                            change: (value) => {
                                // 重写change方法
                                if (item.on && item.on.change) {
                                    item.on.change(value);
                                }
                                this.hanlderModel(item.prop, value);
                            },
                            // el-input 场景
                            input: (value) => {
                                this.hanlderModel(item.prop, value);
                            },
                        },
                    }),
                ],
            );
            // col 组件
            const col = (item) => createElement('el-col', {
                props: {
                    ...item,
                },
            }, item.formItem ? [formItem(item.formItem)] : '');
            // row组件
            const row = (item) => createElement('el-row', {
                props: {
                    ...item,
                },
            }, item.cols.map((list) => col(list)));
            return createElement(
                'el-form',
                {
                    ref: 'form',
                    props: {
                        ...this.$attrs,
                        model: this.model,
                    },
                    on: {
                        ...this.$listeners,
                    },
                },
                // rowItems 优先
                this.rowItems.length ? this.rowItems.map((item) => row(item)) : this.formItems.map((item) => formItem(item)),
            );
        },
    };
    </script>
    
    <style lang="scss" module="s"></style>

    关键文档,大部分复制el-form

    Attributes

    参数说明类型可选值默认值
    model表单数据对象object
    rules表单验证规则object
    inline行内表单模式booleanfalse
    label-position表单域标签的位置,如果值为 left 或者 right 时,则需要设置 label-widthstringright/left/topright
    label-width表单域标签的宽度,例如 '50px'。作为 Form 直接子元素的 form-item 会继承该值。支持 autostring
    label-suffix表单域标签的后缀string
    hide-required-asterisk是否隐藏必填字段的标签旁边的红色星号booleanfalse
    show-message是否显示校验错误信息booleantrue
    inline-message是否以行内形式展示校验信息booleanfalse
    status-icon是否在输入框中显示校验结果反馈图标booleanfalse
    validate-on-rule-change是否在 rules 属性改变后立即触发一次验证booleantrue
    size用于控制该表单内组件的尺寸stringmedium / small / mini
    disabled是否禁用该表单内的所有组件。若设置为 true,则表单内组件上的 disabled 属性不再生效booleanfalse
    -分割线-------------
    formItems表单内组件arrayForm-Item
    rowItems表单内组件支持layout布局,优先等级高于formItemsarrayRow Attributes

    Methods

    方法名说明参数
    validate对整个表单进行校验的方法,参数为一个回调函数。该回调函数会在校验结束后被调用,并传入两个参数:是否校验成功和未通过校验的字段。若不传入回调函数,则会返回一个 promiseFunction(callback: Function(boolean, object))
    validateField对部分表单字段进行校验的方法Function(props: array | string, callback: Function(errorMessage: string))
    resetFields对整个表单进行重置,将所有字段值重置为初始值并移除校验结果
    clearValidate移除表单项的校验结果。传入待移除的表单项的 prop 属性或者 prop 组成的数组,如不传则移除整个表单的校验结果Function(props: array | string)
    focus使 input 获取焦点-
    blur使 input 失去焦点,并隐藏下拉框-

    Events

    事件名称说明回调参数
    validate任一表单项被校验后触发被校验的表单项 prop 值,校验是否通过,错误消息(如果存在)

    Slot

    name说明
    label标签文本的内容

    Scoped Slot

    name说明
    error自定义表单校验信息的显示方式,参数为 { error }

    Form-Item Attributes

    参数说明类型可选值默认值
    prop表单域 model 字段,在使用 validate、resetFields 方法的情况下,该属性是必填的string传入 Form 组件的 model 中的字段
    label标签文本string
    label-width表单域标签的的宽度,例如 '50px'。支持 autostring
    required是否必填,如不设置,则会根据校验规则自动生成booleanfalse
    rules表单验证规则object
    error表单域验证错误信息, 设置该值会使表单验证状态变为error,并显示该错误信息string
    show-message是否显示校验错误信息booleantrue
    inline-message以行内形式展示校验信息booleanfalse
    size用于控制该表单域下组件的尺寸stringmedium / small / mini-

    Row Attributes

    参数说明类型可选值默认值
    gutter栅格间隔number0
    type布局模式,可选 flex,现代浏览器下有效string
    justifyflex 布局下的水平排列方式stringstart/end/center/space-around/space-betweenstart
    alignflex 布局下的垂直排列方式stringtop/middle/bottom
    tag自定义元素标签string*div

    Col Attributes

    参数说明类型可选值默认值
    span栅格占据的列数number24
    offset栅格左侧的间隔格数number0
    push栅格向右移动格数number0
    pull栅格向左移动格数number0
    xs<768px 响应式栅格数或者栅格属性对象number/object (例如: {span: 4, offset: 4})
    sm≥768px 响应式栅格数或者栅格属性对象number/object (例如: {span: 4, offset: 4})
    md≥992px 响应式栅格数或者栅格属性对象number/object (例如: {span: 4, offset: 4})
    lg≥1200px 响应式栅格数或者栅格属性对象number/object (例如: {span: 4, offset: 4})
    xl≥1920px 响应式栅格数或者栅格属性对象number/object (例如: {span: 4, offset: 4})
    tag自定义元素标签string*div

    基本用法

    demo 直接使用formItems属性来配置表单

    <template>
      <div>
        <d-form-smart
          ref="form"
          :model="form"
          label-width="150px"
          label-position="left"
          :form-items="formItems"
        >
          <template #slotErrorName>
            <span>按钮</span>
          </template>
          <template #region-label>
            <el-badge :value="12" class="item">
              <span>自定义label</span>
            </el-badge>
          </template>
          <template #btn>
            <el-button type="primary" @click="submit">提交</el-button>
          </template>
        </d-form-smart>
      </div>
    </template>
    <script>
      export default {
        data() {
          return {
            form: {
              name: '',
              region: '',
              date: [],
              delivery: false,
              personnel: '',
              radio: '',
              checkbox: [],
              cascader: [],
              timeSelect: '',
              datePicker: '',
              startTime: '',
              endTime: '',
              rate: 0,
              special: '',
              desc: null,
            },
            rules: {
              name: [
                { required: true, message: '请输入活动名称', trigger: 'blur' },
                {
                  min: 3,
                  max: 5,
                  message: '长度在 3 到 5 个字符',
                  trigger: 'blur',
                },
              ],
            },
            formItems: [
              {
                label: '名字',
                prop: 'name',
                type: 'el-input',
                props: {
                  placeholder: '请输入内容',
                },
              },
              {
                label: '开关',
                prop: 'delivery',
                type: 'el-switch',
              },
              {
                prop: 'region',
                type: 'd-select-smart',
                slotLabelName: 'region-label',
                props: {
                  list: [],
                },
              },
              {
                label: '级联',
                prop: 'cascader',
                type: 'el-cascader',
                props: {
                  options: [
                    {
                      value: 'zhinan',
                      label: '指南',
                      children: [
                        {
                          value: 'shejiyuanze',
                          label: '设计原则',
                          children: [
                            {
                              value: 'yizhi',
                              label: '一致',
                            },
                            {
                              value: 'fankui',
                              label: '反馈',
                            },
                            {
                              value: 'xiaolv',
                              label: '效率',
                            },
                            {
                              value: 'kekong',
                              label: '可控',
                            },
                          ],
                        },
                      ],
                    },
                  ],
                },
              },
              {
                label: '时间选择',
                prop: 'timeSelect',
                type: 'el-time-select',
                props: {
                  placeholder: '选择时间',
                },
              },
              {
                label: '日期选择',
                prop: 'datePicker',
                type: 'el-date-picker',
                props: {
                  placeholder: '选择日期',
                  valueFormat: 'yyyy-MM-dd',
                },
              },
              {
                label: '日期范围选择',
                prop: ['startTime', 'endTime'],
                type: 'el-date-picker',
                props: {
                  type: 'daterange',
                  startPlaceholder: '开始日期',
                  endPlaceholder: '结束日期',
                  format: 'yyyy-MM-dd',
                  valueFormat: 'yyyy-MM-dd',
                },
              },
              {
                label: '评分',
                prop: 'rate',
                type: 'el-rate',
              },
              {
                label: '备注',
                prop: 'desc',
                type: 'el-input',
                props: {
                  placeholder: '请输入内容',
                  type: 'textarea',
                  style: {
                    width: '400px',
                  },
                },
              },
              {
                slotName: 'btn',
                props: {},
              },
            ],
          };
        },
        methods: {
          change(val) {
            console.log('change', val);
          },
          submit(){
               console.log(this.form);
          }
        },
      };
    </script>

    使用场景

    1. 通用列表搜索条件

    • 配置
      queryItems: [
            {
                label: '属性编码',
                prop: 'propertyValueCode',
                props: {
                    placeholder: '属性编码',
                },
            },
            {
                label: '属性名称',
                prop: 'name',
                props: {
                    placeholder: '属性名称',
                },
            },
        ],
    • 模板写法
        <d-form-smart
            @submit.native.prevent
            ref="query"
            inline
            :model="query"
            label-width="80px"
            label-position="right"
            :form-items="queryItems"
          >
          </d-form-smart>

    2.弹窗写法优化

    • 简单的新增
    • form v-model绑定的值
    • rules 规则
    • formItems 表单配置
      async add() {
                try {
                    // 重置表单
                    Object.assign(this.form, this.$options.data().form);
                    this.$msgbox({
                        customClass: ['custom-message-box'],
                        title: '新增属性',
                        message: this.$createElement('FormSmart', {
                            ref: 'form',
                            key: Math.random(), // 重新创建,不缓存
                            attrs: {
                                labelWidth: '100px',
                                labelPosition: 'right',
                                model: this.form,
                                rules: this.opRules,
                                formItems: opItems,
                            },
                        }),
                        closeOnClickModal: false,
                        showCancelButton: true,
                        confirmButtonText: '确定',
                        cancelButtonText: '取消',
                        beforeClose: async (action, instance, done) => {
                            if (action === 'confirm') {
                                await this.$refs.form.validate();
                                instance.confirmButtonLoading = true;
                                try {
                                    // 请求接口编写的地方
                                    instance.confirmButtonLoading = false;
                                    done();
                                } catch (error) {
                                    instance.confirmButtonLoading = false;
                                }
                            } else {
                                this.$refs.form.resetFields();
                                done();
                            }
                        },
                    });
                } catch (error) {
                    console.error(error);
                }
            },
    在线客服
    服务热线

    服务热线

    4008888355

    微信咨询
    二维码
    返回顶部
    ×二维码

    截屏,微信识别二维码

    打开微信

    微信号已复制,请打开微信添加咨询详情!