@flowertip/search-form

基于 Vue 3 + Element Plus 的可配置搜索表单组件,支持十三种字段类型、响应式布局、远程数据、字段联动、展开折叠、自定义插槽
2,187
Install
npm install @flowertip/search-form
Latest Version:0.2.2
Vue:3
License:MIT
Last Updated:Jul 16, 2026
Links: npm
Maintainer: flowertip

@flowertip/search-form

基于 Vue 3 + Element Plus 的可配置搜索表单组件。通过 fields 配置数组动态生成查询表单,支持十三种字段类型、响应式栅格布局、远程数据源、字段联动、展开/折叠等功能。CSS 样式自动注入,无需额外引入样式文件。

安装

npm install @flowertip/search-form

本组件依赖以下 peerDependencies,请确保项目中已安装:

npm install vue@^3.4 element-plus@^2.14 @element-plus/icons-vue@^2.3

快速开始

组件 CSS 已内联注入,import { SearchForm } 即可获得完整样式,无需手动引入 CSS 文件。

import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import { SearchForm } from '@flowertip/search-form'

const app = createApp(App)
app.use(ElementPlus)
app.component('SearchForm', SearchForm)
app.mount('#app')
<template>
  <div class="search-form-demo">
    <SearchForm
      ref="searchFormRef"
      :fields="formFields"
      :label-width="'110px'"
      label-suffix=":"
      :cols="3"
      :gutter="20"
      submit-text="查询"
      reset-text="清空"
      @submit="onSubmit"
      @reset="onReset"
      @toggle-collapse="onToggleCollapse"
      @field-change="onFieldChange"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { SearchForm } from '@flowertip/search-form'
import { InfoFilled } from '@element-plus/icons-vue'

const searchFormRef = ref()
const result = ref(null)

/** 表单字段配置 */
const formFields = ref([
  // ========== 1. 基础输入框 ==========
  {
    type: 'input',
    label: '用户名',
    field: 'username',
    placeholder: '请输入用户名',
    span: { xs: 24, sm: 12, md: 8, lg: 6 },
    labelTip: '请输入系统注册用户名,支持中英文及数字组合',
  },

  // ========== 2. 本地数据源下拉框 ==========
  {
    type: 'select',
    label: '本地状态',
    field: 'localStatus',
    placeholder: '请选择状态(本地数据)',
    span: { xs: 24, sm: 12, md: 8, lg: 6 },
    options: [
      { label: '草稿', value: 'draft' },
      { label: '已发布', value: 'published' },
      { label: '已归档', value: 'archived' }
    ]
  },

  // ========== 3. 远程数据源下拉框(GitHub API 演示)==========
  {
    type: 'select',
    label: '远程状态',
    field: 'remoteStatus',
    placeholder: '请选择(远程数据)',
    span: { xs: 24, sm: 12, md: 8, lg: 6 },
    remoteOptions: {
      url: 'https://api.github.com/repos/vuejs/core/issues',
      filterable: true,
      queryKey: 'page',
      transform: (response) => {
        return response.slice(0, 5).map((item, index) => ({
          label: `#${index + 1} ${(item.title || '').substring(0, 20)}`,
          value: item.number || index
        }))
      }
    }
  },

  // ========== 4. 日期选择器 ==========
  {
    type: 'date',
    label: '开始日期',
    field: 'startDate',
    placeholder: '请选择日期',
    span: { xs: 24, sm: 12, md: 8, lg: 6 },
    dateFormat: 'YYYY-MM-DD',
    dateValueFormat: 'YYYY-MM-DD'
  },

  // ========== 5. 日期时间选择器 ==========
  {
    type: 'datetime',
    label: '创建时间',
    field: 'createTime',
    placeholder: '请选择日期时间',
    span: { xs: 24, sm: 12, md: 8, lg: 6 },
    dateFormat: 'YYYY-MM-DD HH:mm:ss',
    dateValueFormat: 'YYYY-MM-DD HH:mm:ss'
  },

  // ========== 6. 级联选择器 ==========
  {
    type: 'cascader',
    label: '所属地区',
    field: 'region',
    placeholder: '请选择地区',
    span: { xs: 24, sm: 12, md: 8, lg: 6 },
    options: [
      {
        label: '广东',
        value: 'guangdong',
        children: [
          { label: '深圳', value: 'shenzhen' },
          { label: '广州', value: 'guangzhou' }
        ]
      },
      {
        label: '浙江',
        value: 'zhejiang',
        children: [
          { label: '杭州', value: 'hangzhou' },
          { label: '宁波', value: 'ningbo' }
        ]
      }
    ],
    cascaderProps: { expandTrigger: 'hover' }
  },

  // ========== 7. 数值输入框 ==========
  {
    type: 'input-number',
    label: '年龄',
    field: 'age',
    placeholder: '请输入年龄',
    span: { xs: 24, sm: 12, md: 8, lg: 6 },
    labelTip: '请输入有效年龄,范围 0-120 岁',
    labelIcon: InfoFilled,
    attrs: { min: 0, max: 120, step: 1 }
  },

  // ========== 8. 复合型输入框:select 嵌套在 el-input 的 #prepend 中 ==========
  {
    type: 'composite',
    label: '金额',
    field: 'amount',
    span: { xs: 24, sm: 12, md: 8, lg: 6 },
    labelTip: '选择币种并输入金额数值',
    composite: {
      prefix: {
        field: 'amountUnit',
        defaultValue: 'CNY',
        placeholder: '币种',
        options: [
          { label: 'CNY', value: 'CNY' },
          { label: 'USD', value: 'USD' },
          { label: 'EUR', value: 'EUR' }
        ]
      },
      suffix: {
        field: 'amountValue',
        placeholder: '请输入金额'
      }
    }
  },

  // ========== 9. 额外字段:演示折叠 — 默认隐藏,展开后可见 ==========
  {
    type: 'input',
    label: '邮箱',
    field: 'email',
    placeholder: '请输入邮箱',
    span: { xs: 24, sm: 12, md: 8, lg: 6 },
    rules: [{ type: 'email', message: '请输入正确的邮箱', trigger: 'blur' }]
  },
  {
    type: 'select',
    label: '主类别',
    field: 'mainCategory',
    placeholder: '请选择主类别',
    span: { xs: 24, sm: 12, md: 8, lg: 6 },
    labelTip: '选择主类别后,子类别选项自动联动更新',
    linkage: {
      targets: ['subCategory'],
      handler: (newVal, targets, formModel, helpers) => {
        // 根据主类别动态设置子类别选项
        const subOptionsMap = {
          electronics: [
            { label: '手机', value: 'phone' },
            { label: '电脑', value: 'computer' },
            { label: '耳机', value: 'headphone' }
          ],
          clothing: [
            { label: '上衣', value: 'top' },
            { label: '裤子', value: 'pants' },
            { label: '鞋子', value: 'shoes' }
          ],
          food: [
            { label: '零食', value: 'snack' },
            { label: '饮料', value: 'drink' },
            { label: '生鲜', value: 'fresh' }
          ]
        }
        // 更新子类别字段的 options(通过查找字段并直接修改)
        const subField = formFields.value.find(f => f.field === 'subCategory')
        if (subField) {
          subField.options = subOptionsMap[newVal] || []
        }
      }
    },
    options: [
      { label: '电子产品', value: 'electronics' },
      { label: '服装', value: 'clothing' },
      { label: '食品', value: 'food' }
    ]
  },
  {
    type: 'select',
    label: '子类别',
    field: 'subCategory',
    placeholder: '先选择主类别',
    span: { xs: 24, sm: 12, md: 8, lg: 6 },
    labelTip: '根据主类别动态变化',
    dependsOn: 'mainCategory',
    disabled: false,
    options: []  // 由 linkage handler 动态填充
  },
  {
    type: 'input-number',
    label: '排序权重',
    field: 'weight',
    placeholder: '请输入权重',
    span: { xs: 24, sm: 12, md: 8, lg: 6 },
    attrs: { min: 0, max: 100 }
  }
])

function onSubmit(formData) {
  console.log('查询数据:', formData)
  result.value = formData
}

function onFieldChange({ field, value }) {
  console.log(`字段 [${field}] 变更为:`, value)
}

function onReset(formData) {
  console.log('表单已重置:', formData)
  result.value = null
}

function onToggleCollapse(collapsed) {
  console.log('切换折叠状态:', collapsed ? '已折叠' : '已展开')
}
</script>

<style scoped>
.search-form-demo {
  margin: 20px auto 0;
  width: 90%;
  padding: 10px;
  box-sizing: border-box;
  background-color: #fff;
  border-radius: 4px;
  border: 1px solid var(--el-border-color);
  box-shadow: var(--el-box-shadow-lighter);
}
</style>

Props

属性 类型 默认值 说明
fields Array — (必填) 字段配置数组
initialValue Object {} 初始表单值
labelWidth String '100px' 标签宽度
labelSuffix String '' 标签后缀
cols Number 4 默认每行字段数
gutter Number 20 栅格间距
size String 'default' 表单尺寸:large / default / small
disabled Boolean false 是否全局禁用表单
showActions Boolean true 是否显示操作按钮
submitText String '查询' 提交按钮文本
resetText String '重置' 重置按钮文本
submitIcon Component '' 提交按钮图标
resetIcon Component '' 重置按钮图标
containerWidth Number 0 容器宽度(用于计算折叠行数),不传则自动获取

FieldConfig(字段配置)

每个字段支持以下属性:

通用属性

属性 类型 说明
type String 字段类型,见下方
label String 标签文本
field String 绑定字段名
placeholder String 占位文本
span Number / Object 栅格列宽。数字表示固定值;对象支持 { xs, sm, md, lg, xl } 响应式
visible Boolean 是否可见,默认 true
disabled Boolean 是否禁用
defaultValue Any 字段默认值
rules Array el-form 校验规则
clearable Boolean 是否可清空,默认 true
attrs Object 透传给控件的额外属性
labelIcon Component label 旁图标,默认 QuestionFilled
labelTip String 图标 hover 提示文字
linkage Array / Object 联动配置
dependsOn String / Array 依赖字段名

type 类型

支持以下十三种字段类型:

type 控件 额外属性
input el-input —
select el-select options, remoteOptions
date el-date-picker dateFormat, dateValueFormat
datetime el-date-picker dateFormat, dateValueFormat
week el-date-picker dateFormat, dateValueFormat
month el-date-picker dateFormat, dateValueFormat
year el-date-picker dateFormat, dateValueFormat
daterange el-date-picker startPlaceholder, endPlaceholder, rangeSeparator, dateFormat, dateValueFormat
datetimerange el-date-picker startPlaceholder, endPlaceholder, rangeSeparator, dateFormat, dateValueFormat
cascader el-cascader options, cascaderProps
input-number el-input-number attrs 传入 min/max/step
composite el-input(prepend) composite: { prefix: {...}, suffix: {...} }
custom 作用域插槽 通过 #field-{field} 插槽自由渲染

remoteOptions(远程数据源)

{
  type: 'select',
  field: 'city',
  remoteOptions: {
    url: '/api/cities',                     // 或 (formModel) => `/api/cities/${formModel.province}`
    params: (formModel) => ({ pid: formModel.province }),  // 动态查询参数
    filterable: true,                        // 是否可搜索
    queryKey: 'keyword',                     // 搜索参数键名
    transform: (response) => response.map(...)  // 响应转换函数
  },
  dependsOn: 'province'
}

composite(复合输入框)

composite 类型将 el-select 嵌套在 el-input 的 #prepend 插槽中,适合「前缀选择 + 后缀输入」的场景(如币种 + 金额):

{
  type: 'composite',
  label: '金额',
  field: 'amount',
  composite: {
    prefix: {
      field: 'amountUnit',      // 前缀绑定的字段名
      defaultValue: 'CNY',       // 前缀默认值
      placeholder: '币种',
      options: [
        { label: 'CNY', value: 'CNY' },
        { label: 'USD', value: 'USD' }
      ]
    },
    suffix: {
      field: 'amountValue',      // 后缀绑定的字段名
      placeholder: '请输入金额'
    }
  }
}

custom(自定义控件)

custom 类型通过作用域插槽 #field-{fieldName} 让使用者自由渲染任意 Element Plus 控件:

<template>
  <SearchForm :fields="fields">
    <!-- 插槽名必须为 field-{field} -->
    <template #field-themeColor="{ modelValue, update, disabled }">
      <el-color-picker
        :model-value="modelValue"
        :disabled="disabled"
        @update:model-value="update"
      />
    </template>
  </SearchForm>
</template>

<script setup>
const fields = [
  { type: 'custom', label: '主题颜色', field: 'themeColor', defaultValue: '#409EFF' }
]
</script>

作用域插槽参数:

参数 说明
field 字段配置对象
modelValue 当前字段值
disabled 是否禁用
update 更新值的函数 (val) => void

linkage(字段联动)

联动有两种写法:

简写(数组):

{ field: 'province', linkage: ['city', 'district'] }

完整写法(对象):

{
  field: 'category',
  linkage: {
    targets: ['subCategory'],
    handler: (newVal, targets, formModel, helpers) => {
      helpers.setFieldValue('otherField', 'defaultValue')
      helpers.reloadField('subCategory')
    }
  }
}

Events

事件 参数 说明
submit formData 表单验证通过后触发
reset formData 重置后触发
toggle-collapse collapsed 折叠/展开切换
field-change { field, value, linkedFields, formData } 联动字段变化时触发

Expose(组件实例方法)

const formRef = ref()

formRef.value.validate()        // 返回 Promise,验证表单
formRef.value.resetFields()     // 重置表单
formRef.value.getFormData()     // 获取表单数据
formRef.value.setFormData({})   // 动态设置表单数据
formRef.value.toggleCollapse()  // 切换折叠
formRef.value.isCollapsed       // 当前折叠状态
formRef.value.remoteLoading     // { [field]: true/false } 远程加载状态

License

MIT

Related Packages

yy-dynamic-search-form

基于 Element Plus 的动态搜索表单组件

0
maku-form-design

基于vue3、element-plus开发的表单设计器,主要用于低代码平台。

2,334
mw-form-create

米未定制版 FormCreate 低代码表单渲染器(基于 @form-create/element-ui v3.3.1 构建...

187
d-render

基于 Element Plus 二次封装的数据驱动渲染组件库 - 表单、搜索表单、表格配置化渲染

8,750 5

Version History

Version Released Vue License
0.2.2 ^3.4.0 MIT
0.2.1 ^3.4.0 MIT
0.2.0 ^3.4.0 MIT
0.1.8 ^3.4.0 MIT
0.1.7 ^3.4.0 MIT