npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

proformsmz

v0.3.6

Published

表单组件,函数式弹窗,可组合使用,也可单独使用表单组件

Readme

Prop-Form 表单组件

概述

ProForm 是一个基于 Element Plus 的高级表单组件,支持动态配置、多种数据源、丰富的事件处理和灵活的布局。

安装和引入

安装

pnpm add prop-form@latest --registry http://localhost:4873/

引入

import { createApp } from 'vue'
import App from './App.vue'
import PropForm from 'prop-form'
import 'prop-form/dist/prop-form.css'
const app = createApp(App)
app.use(PropForm)
app.mount('#app')

// 按需引入
import { ProForm, useModalForm } from '@your-org/proform'

基础用法

<template>
  <ProForm
    :options="formOptions"
    v-model="formData"
    @submit="handleSubmit"
  />
</template>

<script setup>
import { ref } from 'vue';

const formData = ref({
  username: '',
  status: 1,
  department: ''
});

const formOptions = {
  form: {
    labelWidth: '100px',
    rules: {
      username: [{ required: true, message: '请输入用户名', trigger: 'blur' }]
    }
  },
  columns: [
    {
      el: 'input',
      label: '用户名',
      prop: 'username',
      props: {
        placeholder: '请输入用户名'
      }
    },
    {
      el: 'select',
      label: '状态',
      prop: 'status',
      enum: [
        { value: 1, label: '启用' },
        { value: 0, label: '禁用' }
      ]
    }
  ]
};

const handleSubmit = (data) => {
  console.log('表单数据:', data);
};
</script>

配置参数

ProForm Props

| 参数 | 类型 | 默认值 | 说明 | | --- |-----------------|--------| --- | | modelValue | Object | {} | 表单数据对象 (v-model) | | data | Object | {} | 表单数据对象 (非响应式备用) | | cols | number string | 4 | 网格布局列数 | | options | Object | 必需 | 表单配置选项 |

ProFormOptions

| 属性 | 类型 | 说明
| --- |-----------|-----------------------| |form | Object | Element Plus Form 配置 | |columns | IColumn[] | 表单项配置数组 |
|emptyText| string | 空值显示文本 |
|dictConfig| Object | 全局字典配置 |
|on | Object| 全局事件处理器|

IColumn 表单项配置

| 属性 | 类型 | 说明 | |-----------------------------------------------------------------------------|--------------------------------------------------------------------------------------|--------------------------------------------------------------------| | el| string | 元素类型: input, select, radio, checkbox, text, title, custom, button | | label| string | 标签文本 | | prop| string | 字段名称 | | span| number ㇑ string | 跨越列数 | | | | enum| string ㇑ array | 字典 | | | | object | function 枚举数据配置 |props| Object | Element UI 组件属性 | |disabled| boolean | 是否禁用 | |on| Object | 表单项事件处理器 | |prefixSupplement| string | 值前缀补充 | |suffixSupplement| string | 值后缀补充 | |positiveNumber| boolean | 是否只允许正数 |

字典数据配置

  • 本地字典(字符串)
{
  el: 'select',
  label: '用户状态',
  prop: 'status',
  enum: 'user_status' // 使用本地字典类型
}
  • 静态数据(数组)
{
  el: 'select',
  label: '性别',
  prop: 'gender',
  enum: [
    { value: 1, label: '男' },
    { value: 2, label: '女' }
  ]
}
  • API 远程获取(对象配置)
{
    el: 'select',
        label: '部门',
        prop: 'department',
    enum: {
        api: '/api/departments/list',
            params: { active: 1 },
        transform: (data) => data.map(item => ({
            value: item.id,
            label: item.name
        }))
    }
}
  • 自定义函数获取
{
    el: 'select',
        label: '角色',
        prop: 'role',
    enum: async () => {
        const response = await fetch('/api/roles');
        const data = await response.json();
        return data.map(role => ({
            value: role.id,
            label: role.name
        }));
    }
}

事件处理

  • 组件级别事件
<ProForm
  :options="formOptions"
  @change="handleChange"
  @focus="handleFocus"
  @blur="handleBlur"
  @click="handleClick"
  @validate="handleValidate"
  @dict-loaded="handleDictLoaded"
/>
  • 配置级别事件
const formOptions = {
  // ... 其他配置
  on: {
    change: (event) => {
      console.log('表单变更:', event);
    },
    validate: (result) => {
      console.log('验证结果:', result.valid);
    }
  }
};
  • 表单项级别事件
{
  el: 'input',
  label: '用户名',
  prop: 'username',
  on: {
    change: (value, oldValue) => {
      console.log('用户名变更:', value, oldValue);
    },
    focus: (event) => {
      console.log('获得焦点');
    }
  }
}

方法 API

通过 ref 调用组件方法:

<template>
  <ProForm ref="proFormRef" :options="formOptions" />
  <button @click="handleSubmit">提交</button>
</template>

<script setup>
import { ref } from 'vue';

const proFormRef = ref();

const handleSubmit = async () => {
  try {
    const formData = await proFormRef.value.submitForm();
    console.log('提交成功:', formData);
  } catch (error) {
    console.error('提交失败:', error);
  }
};

// 其他可用方法
const refreshDict = () => {
  proFormRef.value.refreshDict('department');
};

const validateField = async () => {
  const isValid = await proFormRef.value.validateField('username');
  console.log('字段验证:', isValid);
};
</script>

可用方法

| 方法名 | 说明 | 参数 | 返回值 | |--------------------|-----------------------|------------------------------------------|-------------------| | submitForm()| 提交表单验证| - | Promise<Object> |
| resetForm()| 重置表单| - | void |
| clearValidate()| 清除验证| props?: string ㇑ string[] | void | | refreshDict()| 刷新字典| prop?: string | Promise<void> | | validate()| 验证表单| props?: string ㇑ string[] | Promise<boolean> | | validateField()| 验证字段| props: string ㇑ string[] | Promise<boolean> |
|getFormData()| 获取表单数据| - | Object | |setFormData()| 设置表单数据| data: Object | void | |resetToInitial()| 重置到初始值| - | void |

布局配置

网格布局

// 4列网格布局
<ProForm :cols="4" :options="formOptions" />

// 自定义列跨度
{
  el: 'input',
  label: '详细地址',
  prop: 'address',
  span: 2 // 跨越2列
}

// 标题行
{
  el: 'title',
  label: '基本信息',
  span: 4 // 整行标题
}

完整示例

<template>
  <div>
    <ProForm
      ref="proFormRef"
      :options="formOptions"
      v-model="formData"
      @change="handleFormChange"
      @validate="handleFormValidate"
    />
    
    <div class="actions">
      <el-button @click="submitForm">提交</el-button>
      <el-button @click="resetForm">重置</el-button>
      <el-button @click="refreshDepartment">刷新部门</el-button>
    </div>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue';

const proFormRef = ref();
const formData = reactive({
  name: '',
  age: '',
  gender: 1,
  department: '',
  status: 1,
  description: ''
});

const formOptions = {
  form: {
    labelWidth: '120px',
    rules: {
      name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
      age: [
        { required: true, message: '请输入年龄', trigger: 'blur' },
        { pattern: /^\d+$/, message: '年龄必须为数字', trigger: 'blur' }
      ]
    }
  },
  columns: [
    {
      el: 'title',
      label: '基本信息',
      span: 4
    },
    {
      el: 'input',
      label: '姓名',
      prop: 'name',
      props: {
        placeholder: '请输入姓名',
        maxlength: 20
      },
      on: {
        change: (value) => {
          console.log('姓名变更:', value);
        }
      }
    },
    {
      el: 'input',
      label: '年龄',
      prop: 'age',
      positiveNumber: true,
      props: {
        placeholder: '请输入年龄'
      }
    },
    {
      el: 'radio',
      label: '性别',
      prop: 'gender',
      enum: [
        { value: 1, label: '男' },
        { value: 2, label: '女' }
      ]
    },
    {
      el: 'title',
      label: '工作信息',
      span: 4
    },
    {
      el: 'select',
      label: '部门',
      prop: 'department',
      enum: {
        api: '/api/departments',
        params: { status: 1 },
        transform: (data) => data.map(dept => ({
          value: dept.id,
          label: dept.name
        }))
      }
    },
    {
      el: 'select',
      label: '状态',
      prop: 'status',
      enum: 'user_status'
    },
    {
      el: 'textarea',
      label: '描述',
      prop: 'description',
      span: 2,
      props: {
        rows: 3,
        placeholder: '请输入描述信息'
      }
    }
  ],
  on: {
    change: (event) => {
      console.log('表单变更事件:', event);
    }
  }
};

const handleFormChange = (event) => {
  console.log('变更:', event.prop, event.value);
};

const handleFormValidate = (result) => {
  console.log('验证结果:', result.valid, result.fields);
};

const submitForm = async () => {
  try {
    const data = await proFormRef.value.submitForm();
    console.log('表单数据:', data);
    // 调用API提交数据
  } catch (error) {
    console.error('表单验证失败:', error);
  }
};

const resetForm = () => {
  proFormRef.value.resetForm();
};

const refreshDepartment = () => {
  proFormRef.value.refreshDict('department');
};
</script>

<style scoped>
.actions {
  margin-top: 20px;
  display: flex;
  gap: 10px;
}
</style>

类型使用

<template>
  <ProForm
    ref="proFormRef"
    :options="formOptions"
    v-model="formData"
    @change="handleFormChange"
    @validate="handleFormValidate"
  />
</template>

<script setup lang="ts">
import { ref, reactive } from 'vue';
import type { ProFormExposed, IFormEvent, IValidateEvent } from 'prop-form/dist/types/prop-form';

interface FormData {
  name: string;
  age: number | null;
  gender: number;
  department: string;
  status: number;
  description: string;
}

const proFormRef = ref<ProFormExposed>();
const formData = reactive<FormData>({
  name: '',
  age: null,
  gender: 1,
  department: '',
  status: 1,
  description: ''
});

const formOptions = {
  form: {
    labelWidth: '120px',
    rules: {
      name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
      age: [
        { required: true, message: '请输入年龄', trigger: 'blur' },
        { pattern: /^\d+$/, message: '年龄必须为数字', trigger: 'blur' }
      ]
    }
  },
  columns: [
    {
      el: 'input',
      label: '姓名',
      prop: 'name',
      props: {
        placeholder: '请输入姓名',
        maxlength: 20
      }
    },
    {
      el: 'input',
      label: '年龄',
      prop: 'age',
      positiveNumber: true
    }
  ] as const
};

const handleFormChange = (event: IFormEvent): void => {
  console.log('字段变更:', event.prop, event.value);
};

const handleFormValidate = (event: IValidateEvent): void => {
  console.log('验证结果:', event.valid);
};

const submitForm = async (): Promise<void> => {
  try {
    const data = await proFormRef.value?.submitForm();
    console.log('提交成功:', data);
  } catch (error) {
    console.error('提交失败:', error);
  }
};
</script>

高级功能

  • 自定义渲染
{
    el: 'custom',
        label: '自定义区域',
        prop: 'customField',
        span: 2,
    // 使用插槽自定义内容
    // <template #item-customField>自定义内容</template>
}
  <PropForm class="flex-col" ref="proFormRef" :options="formConfig" :data="formData" :cols="2">
  <template #item-qualificationsIamge>
    <el-upload
        ref="uploadRef2"
        class="upload-demo flex-1"
        action="#"
        :auto-upload="false"
        :on-change="onChangeFile2"
        :on-remove="onRemove"
        :on-preview="onPoreviewFile"
        v-model:file-list="fileListModel.qualificationsIamge"
    >
      <template #trigger>
        <el-button plain class="filterButton">
          <svg class="icon" width="16" height="16" aria-hidden="true">
            <use xlink:href="#icon-uploadIcon" />
          </svg>
          上传文件
        </el-button>
      </template>
    </el-upload>
  </template>

  <template #item-Iamge>
    <el-upload
        ref="uploadRef"
        action="#"
        list-type="picture-card"
        :auto-upload="false"
        :file-list="fileLists"
        accept=".jpg,.jpeg,.png,.gif"
        :on-change="handleFileChange"
        :on-preview="handlePictureCardPreview"
        :on-remove="handleRemove"
    >
      <el-icon><Plus /></el-icon>
    </el-upload>
  </template>
</PropForm>
  • 条件显示
// 根据其他字段值动态显示/隐藏
const shouldShowField = computed(() => {
  return formData.value.type === 'special';
});
  • 动态验证规则
const dynamicRules = computed(() => {
  return {
    email: formData.value.receiveNewsletter ? [
      { required: true, message: '请输入邮箱', trigger: 'blur' },
      { type: 'email', message: '邮箱格式不正确', trigger: 'blur' }
    ] : []
  };
});

函数式调用弹窗

与表单组件绑定,抛弃传统的书写标签,使用函数式调用弹窗,包括一些事件处理,符合思维逻辑。

  1. 引入和使用
import { useModalForm, createModalForm } from 'proform'

const openModalWithoutBoth = () => {
 console.log('开始打开弹窗...')

 const modal = useModalForm({
  title: '弹窗',
  width: '500px', // 明确设置宽度
  formOptions: {
   columns: [
    {
     el: 'input',
     label: '名称',
     prop: 'name',
     props: {
      placeholder: '请输入名称'
     }
    }
   ]
  },
  // defaultButtons: false
 })

 console.log('useModalForm 返回:', modal)

 const closeFn = modal.open();
 console.log('open() 返回:', closeFn)
}
  1. 配置参数

| 参数 | 类型 | 默认值 | 说明 | |----------------------------------------|-------------------------------|----------|---------------| | title| string | '表单' | 弹窗标题 | | width| string ㇑ number | '600px' | 弹窗宽度| | formOptions| ProFormOptions | 必需 |表单配置 | | formData| Record<string, any> | {} | 初始表单数据 | | formProps| any| {} | 表单额外属性 |
| buttons | ModalButton[]| [] | 自定义按钮 |
| defaultButtons | boolean | true | 是否显示默认按钮 |
| showCancel| boolean| true | 是否显示取消按钮 |
| showConfirm| boolean| true | 是否显示确定按钮 |
| cancelText| string| '取消' | 取消按钮文字 |
| confirmText | string| '确定' | 确定按钮文字 | | footerPosition| 'left' ㇑ 'center' ㇑ 'right' | 'right' | 按钮位置| | beforeClose| () => Promise<boolean> ㇑ boolean -| 关闭前回调 |

  1. 事件处理
const modal = useModalForm({
  title: '带事件处理的表单',
  formOptions: {
    columns: [
      { el: 'input', label: '测试', prop: 'test' }
    ]
  },
  // 确认事件
  onConfirm: (data, formApi) => {
    console.log('表单数据:', data)
    // 调用 API 保存数据
    return api.saveData(data).then(() => {
      message.success('保存成功')
    })
  },
  // 取消事件
  onCancel: () => {
    console.log('用户取消了操作')
  },
  // 按钮点击事件
  onButtonClick: (button, data, formApi) => {
    console.log('按钮点击:', button.key, data)
  }
})

高级用法

  1. 自定义按钮
const modal = useModalForm({
  title: '自定义按钮表单',
  defaultButtons: false, // 禁用默认按钮
  buttons: [
    {
      key: 'preview',
      text: '预览',
      props: { type: 'info', icon: 'View' },
      onClick: (data, formApi) => {
        console.log('预览数据:', data)
      }
    },
    {
      key: 'save',
      text: '保存草稿',
      props: { type: 'warning' },
      validate: false, // 不需要验证表单
      onClick: (data, formApi) => {
        return api.saveDraft(data)
      }
    },
    {
      key: 'submit',
      text: '提交审核',
      props: { type: 'primary' },
      validate: true, // 需要验证表单
      onClick: (data, formApi) => {
        return api.submit(data)
      }
    }
  ]
})
  1. 隐藏默认按钮
// 只显示确定按钮
const modal1 = useModalForm({
 showCancel: false,
 confirmText: '我知道了'
})

// 只显示取消按钮
const modal2 = useModalForm({
 showConfirm: false,
 cancelText: '关闭'
})

// 完全自定义按钮
const modal3 = useModalForm({
 defaultButtons: false,
 buttons: [
  { text: '自定义操作', props: { type: 'primary' } }
 ]
})
  1. 表单验证和处理
const modal = useModalForm({
  title: '带验证的表单',
  formOptions: {
    columns: [
      {
        el: 'input',
        label: '手机号',
        prop: 'phone',
        rules: [
          { required: true, message: '请输入手机号' },
          { pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确' }
        ]
      },
      {
        el: 'input',
        label: '年龄',
        prop: 'age',
        positiveNumber: true, // 只允许正数
        rules: [{ min: 18, max: 60, message: '年龄必须在18-60之间' }]
      }
    ]
  },
  onConfirm: async (data, formApi) => {
    // 手动验证
    const isValid = await formApi.validate()
    if (!isValid) {
      throw new Error('表单验证失败')
    }
    
    // 获取表单数据
    const formData = formApi.getFormData()
    return api.submit(formData)
  }
})
  1. 动态表单数据
const modal = useModalForm({
  title: '动态表单',
  formOptions: {
    columns: [
      {
        el: 'select',
        label: '分类',
        prop: 'category',
        enum: async () => {
          // 动态加载选项
          const categories = await api.loadCategories()
          return categories.map(item => ({
            value: item.id,
            label: item.name
          }))
        }
      }
    ]
  }
})

// 打开时传入动态数据
modal.open({ category: 'default' })

ModalFormInstance 方法

| 方法 | 参数 | 返回值 | 说明 | |---------------------------|-------------------------|-------------------------|--------------| | open| data?: Record<string, any>| () => void| 打开弹窗,返回关闭函数 | | close| -| void | 关闭弹窗 | | getFormApi| - | ProFormExposed ㇑ undefined| 获取表单API实例 | |getFormData| - |Record<string, any>| 获取表单数据 |

插槽

多种上传类型示例

import { useModalForm } from '@your-org/proform'
import { h } from 'vue'
import { ElMessage } from 'element-plus'

const modal = useModalForm({
  title: '多类型上传示例',
  formOptions: {
    columns: [
      {
        el: 'custom',
        label: '资质文件',
        prop: 'qualificationsIamge'
      },
      {
        el: 'custom', 
        label: '图片上传',
        prop: 'Iamge'
      },
      {
        el: 'custom',
        label: '其他文件',
        prop: 'otherFiles'
      }
    ]
  },
  slots: {
    // 文件上传插槽
    'item-qualificationsIamge': {
      type: 'file',
      buttonText: '上传资质文件',
      buttonIcon: '#icon-certificate',
      accept: '.pdf,.doc,.docx',
      limit: 5,
      onChange: (file, fieldName) => {
        ElMessage.success(`资质文件 ${file.name} 上传成功`)
      }
    },
    
    // 图片上传插槽
    'item-Iamge': {
      type: 'image',
      listType: 'picture-card',
      accept: '.jpg,.jpeg,.png,.gif',
      limit: 10,
      onPreview: (file, fieldName) => {
        window.open(file.url, '_blank')
      }
    },
    
    // 自定义上传插槽
    'item-otherFiles': {
      type: 'file',
      buttonText: '选择其他文件',
      multiple: true,
      onChange: (file, fieldName) => {
        console.log('文件变更:', file, fieldName)
      }
    },
    
    // 完全自定义的插槽(非上传类型)
    'custom-footer': () => h('div', { class: 'custom-footer' }, [
      h('button', { 
        onClick: () => console.log('自定义操作'),
        class: 'custom-btn' 
      }, '自定义按钮')
    ]),
    
    // 另一个自定义插槽
    'custom-header': () => h('div', { class: 'custom-header' }, [
      h('h3', '自定义标题'),
      h('p', '这是自定义头部内容')
    ])
  }
})

// 打开弹窗
modal.open({
  qualificationsIamge: [],
  Iamge: [],
  otherFiles: []
})

更复杂的示例

import { useModalForm } from '@your-org/proform'
import { h } from 'vue'

const modal = useModalForm({
  title: '企业信息登记',
  formOptions: {
    columns: [
      { el: 'input', label: '企业名称', prop: 'companyName' },
      { el: 'custom', label: '营业执照', prop: 'businessLicense' },
      { el: 'custom', label: '产品图片', prop: 'productImages' },
      { el: 'custom', label: '资质证书', prop: 'certificates' }
    ]
  },
  slots: {
    // 营业执照上传
    'item-businessLicense': {
      type: 'image',
      listType: 'picture-card',
      accept: '.jpg,.jpeg,.png',
      limit: 1,
      buttonText: '上传营业执照',
      onChange: (file, fieldName) => {
        console.log('营业执照上传:', file)
      }
    },
    
    // 产品图片上传
    'item-productImages': {
      type: 'image',
      listType: 'picture',
      multiple: true,
      limit: 20,
      buttonText: '上传产品图片',
      buttonIcon: '#icon-image',
      onChange: (file, fieldName) => {
        console.log('产品图片上传:', file)
      }
    },
    
    // 资质证书上传
    'item-certificates': {
      type: 'file',
      accept: '.pdf,.jpg,.png',
      limit: 10,
      buttonText: '上传资质证书',
      buttonProps: {
        type: 'primary',
        plain: false,
        class: 'certificate-btn'
      },
      onChange: (file, fieldName) => {
        console.log('资质证书上传:', file)
      }
    },
    
    // 自定义底部按钮
    'custom-operation': () => h('div', { class: 'custom-operations' }, [
      h('button', { 
        class: 'btn-save-draft',
        onClick: () => console.log('保存草稿')
      }, '保存草稿'),
      h('button', { 
        class: 'btn-submit',
        onClick: () => console.log('提交审核')
      }, '提交审核')
    ])
  }
})

// 使用
modal.open({
  companyName: '测试企业',
  businessLicense: [],
  productImages: [],
  certificates: []
})

动态配置插槽

// 动态生成插槽配置
function createDynamicSlots(fields) {
  const slots = {}
  
  fields.forEach(field => {
    if (field.type === 'file') {
      slots[`item-${field.name}`] = {
        type: 'file',
        buttonText: field.buttonText || `上传${field.label}`,
        accept: field.accept,
        limit: field.limit,
        onChange: field.onChange
      }
    } else if (field.type === 'image') {
      slots[`item-${field.name}`] = {
        type: 'image',
        listType: field.listType || 'picture-card',
        buttonText: field.buttonText || `上传${field.label}`,
        limit: field.limit,
        onPreview: field.onPreview
      }
    }
  })
  
  return slots
}

// 使用动态配置
const dynamicFields = [
  { name: 'qualificationsIamge', type: 'file', label: '资质文件', accept: '.pdf', limit: 5 },
  { name: 'Iamge', type: 'image', label: '图片', listType: 'picture-card', limit: 10 }
]

const modal = useModalForm({
  formOptions: {
    columns: dynamicFields.map(field => ({
      el: 'custom',
      label: field.label,
      prop: field.name
    }))
  },
  slots: createDynamicSlots(dynamicFields)
})

注意事项

  • 请确保在项目中正确引入了 element-plusvue
  • 性能优化:对于大型表单,建议使用 deep: false 监听选项
  • 字典加载:远程字典加载时有 loading 状态显示
  • 事件冒泡:事件会从表单项 → 配置 → 组件级别依次触发

常见问题

Q: 字典数据不显示怎么办? A: 检查字典配置是否正确,网络请求是否成功,控制台是否有错误信息。

Q: 表单验证不生效? A: 确保在 options.form.rules 中正确配置了验证规则。

Q: 如何动态更新表单选项? A: 直接修改 formOptions.columns 数组,组件会响应式更新。

Q: 如何获取表单实例? A: 使用 ref 获取组件实例,然后调用暴露的方法。