
Security News
The Code You Didn't Write Is Still Yours to Defend
AI agents are pulling packages into environments no scanner is watching, creating exposure before security teams can see it.
vue3-search-form
Advanced tools
# npm
npm install vue3-search-form
# yarn
yarn add vue3-search-form
# pnpm
pnpm add vue3-search-form
// main.ts
import { createApp } from 'vue'
import SearchForm from 'vue3-smart-form'
import 'vue3-smart-form/style.css'
const app = createApp(App)
app.use(SearchForm)
<template>
<SearchForm
ref="searchFormRef"
:form-items="formItems"
:buttons="buttons"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import SearchForm from '/vue3-smart-form'
import { FormItemType } from '/vue3-smart-form'
const searchFormRef = ref()
const formItems = {
keyword: {
type: FormItemType.INPUT,
label: '关键字',
props: {
// 通过 props 配置原生事件
onKeyup: (e) => {
if (e.key === 'Enter') {
handleSearch()
}
}
}
},
status: {
type: FormItemType.SELECT,
label: '状态',
options: [
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 }
],
props: {
// 通过 props 配置原生事件
onChange: (val) => {
console.log('状态变化:', val)
}
}
}
}
const buttons = [
{
text: '搜索',
type: 'primary',
icon: 'Search',
onClick: () => {
const formData = searchFormRef.value?.getFormData()
handleSearch(formData)
}
},
{
text: '重置',
icon: 'Refresh',
onClick: () => {
searchFormRef.value?.reset()
}
}
]
const handleSearch = (data) => {
console.log('搜索参数:', data)
}
</script>
<template>
<SearchForm
ref="searchFormRef"
:form-items="formItems"
:buttons="buttons"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import SearchForm from '@/components/SearchForm/index.vue'
import { FormItemType } from '@/components/SearchForm/types'
const searchFormRef = ref()
const formItems = {
keyword: {
type: FormItemType.INPUT,
label: '关键字'
},
status: {
type: FormItemType.SELECT,
label: '状态',
options: [
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 }
]
}
}
// 推荐:使用 buttons 配置
const buttons = [
{
text: '搜索',
type: 'primary',
icon: 'Search',
onClick: () => {
const formData = searchFormRef.value?.getFormData()
console.log('搜索参数:', formData)
}
},
{
text: '重置',
icon: 'Refresh',
onClick: () => {
searchFormRef.value?.reset()
}
},
{
text: '导出',
type: 'success',
icon: 'Download',
onClick: () => {
console.log('导出数据')
}
}
]
</script>
<template>
<SearchForm
ref="searchFormRef"
:form-items="formItems"
:buttons="buttons"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import SearchForm from '@/components/SearchForm/index.vue'
import { FormItemType } from '@/components/SearchForm/types'
const searchFormRef = ref()
// 对象格式配置
const formItems = {
keyword: {
type: FormItemType.INPUT,
label: '关键字',
props: {
placeholder: '请输入关键字'
}
},
status: {
type: FormItemType.SELECT,
label: '状态',
options: [
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 }
]
},
dateRange: {
type: FormItemType.DATE_RANGE,
label: '创建时间'
}
}
const buttons = [
{
text: '查询',
type: 'primary',
onClick: () => {
const data = searchFormRef.value?.getFormData()
console.log('搜索参数:', data)
}
}
]
</script>
<template>
<SearchForm
ref="searchFormRef"
:form-items="formItems"
:buttons="buttons"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import SearchForm from '@/components/SearchForm/index.vue'
import { FormItemType } from '@/components/SearchForm/types'
const searchFormRef = ref()
// 数组格式配置(每个项必须包含 key 字段)
const formItems = [
{
key: 'keyword',
type: FormItemType.INPUT,
label: '关键字',
props: {
placeholder: '请输入关键字'
}
},
{
key: 'status',
type: FormItemType.SELECT,
label: '状态',
options: [
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 }
]
},
{
key: 'dateRange',
type: FormItemType.DATE_RANGE,
label: '创建时间'
}
]
const buttons = [
{
text: '查询',
type: 'primary',
onClick: () => {
const data = searchFormRef.value?.getFormData()
console.log('搜索参数:', data)
}
}
]
</script>
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| formItems | FormConfig | - | 表单项配置(支持对象或数组格式,必填) |
| labelWidth | string | '80px' | 表单标签宽度 |
| defaultCol | ColConfig | { xs: 24, sm: 12, md: 8, lg: 6, xl: 6 } | 默认栅格配置,应用于所有未单独配置 col 的表单项 |
| inline | boolean | false | 是否行内表单(true 时不使用栅格布局) |
| formStyle | object | - | 表单样式 |
| formClass | string | - | 表单类名 |
| buttons | ButtonConfig[] | [] | 按钮配置(配置形式) |
| formProps | object | {} | el-form 原生属性 |
💡 设计理念:完全配置驱动,表单项和按钮均通过配置管理,保持 API 简洁统一。
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| span | number | - | 默认占比 (1-24) |
| xs | number | 24 | <768px 时的占比 |
| sm | number | 12 | ≥768px 时的占比 |
| md | number | 8 | ≥992px 时的占比 |
| lg | number | 6 | ≥1200px 时的占比 |
| xl | number | 6 | ≥1920px 时的占比 |
| 事件名 | 参数 | 说明 |
|---|---|---|
| reset | - | 点击重置按钮触发(内部调用)或手动调用 reset 方法时触发 |
| 方法名 | 参数 | 返回值 | 说明 |
|---|---|---|---|
| getFormData | - | FormData | 获取当前表单数据 |
| reset | - | - | 手动触发重置(并触发 reset 事件) |
| setFieldValue | (key: string, value: any) | - | 设置单个字段的值 |
| setFieldsValue | (values: Record<string, any>) | - | 批量设置字段的值 |
| formRef | - | FormInstance | Element Plus 表单实例引用 |
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| key | string | - | 字段名(必填) |
| type | FormItemType | - | 表单项类型(必填) |
| label | string | - | 标签文本 |
| show | boolean | true | 是否显示 |
| value | any | - | 默认值 |
| trim | boolean | true | 是否自动去除输入框首尾空格(仅 input 生效) |
| col | ColConfig | - | 栅格配置,不传则使用全局 defaultCol |
| options | array | function | - | 下拉选项(仅 select) |
| optionsRef | string | - | 选项数组来源的响应式数据引用(仅 select) |
| component | Component | { render: () => VNode } | - | 自定义渲染组件(仅 slot) |
| props | object | - | 表单组件原生属性和事件(placeholder, clearable, filterable, format, valueFormat, style, class, onKeyup, onChange 等) |
| itemProps | object | - | el-form-item 原生属性(包含 style, class, labelWidth 等) |
💡 设计理念:只保留真正的元数据作为顶级属性,所有原生组件属性(placeholder、clearable、filterable、format、valueFormat、style、class 等)都通过
props配置,保持 API 简洁统一。
| 类型 | 值 | 说明 |
|---|---|---|
| INPUT | 'input' | 输入框 |
| INPUT_NUMBER | 'inputNumber' | 数字输入框 |
| SELECT | 'select' | 选择器 |
| DATE | 'date' | 日期选择器 |
| DATE_RANGE | 'dateRange' | 日期范围选择器 |
| DATETIME | 'datetime' | 日期时间选择器 |
| DATETIME_RANGE | 'datetimeRange' | 日期时间范围选择器 |
| SLOT | 'slot' | 自定义插槽 |
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| text | string | - | 按钮文本(必填) |
| type | string | 'default' | 按钮类型:primary/success/warning/danger/info/text/default |
| icon | string | - | 按钮图标 |
| show | boolean | true | 是否显示 |
| props | object | - | Element Plus 按钮原生属性 |
| onClick | function | - | 点击事件处理函数 |
{
keyword: {
type: FormItemType.INPUT,
label: '关键字',
props: {
placeholder: '请输入关键字'
}
}
}
{
age: {
type: FormItemType.INPUT_NUMBER,
label: '年龄',
props: {
min: 0,
max: 120,
step: 1,
precision: 0
}
},
price: {
type: FormItemType.INPUT_NUMBER,
label: '价格',
props: {
min: 0,
precision: 2,
step: 0.1,
controlsPosition: 'right'
}
}
}
{
status: {
type: FormItemType.SELECT,
label: '状态',
options: [
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 }
],
props: {
placeholder: '请选择状态',
clearable: true,
filterable: true
}
}
}
const statusOptions = ref([
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 }
])
{
status: {
type: FormItemType.SELECT,
label: '状态',
options: () => statusOptions.value
}
}
{
createDate: {
type: FormItemType.DATE,
label: '创建日期',
props: {
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD'
}
}
}
{
dateRange: {
type: FormItemType.DATE_RANGE,
label: '创建时间',
props: {
startPlaceholder: '开始日期',
endPlaceholder: '结束日期'
}
}
}
{
createTime: {
type: FormItemType.DATETIME,
label: '创建时间',
props: {
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss'
}
}
}
{
datetimeRange: {
type: FormItemType.DATETIME_RANGE,
label: '时间范围',
props: {
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss'
}
}
}
<template>
<SearchForm :form-items="formItems">
<template #customSlot="{ field, value, config }">
<el-input v-model="formData[field]" placeholder="自定义内容" />
</template>
</SearchForm>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
import SearchForm from '@/components/SearchForm/index.vue'
import { FormItemType } from '@/components/SearchForm/types'
const formData = reactive({})
const formItems = {
customSlot: {
type: FormItemType.SLOT,
label: '自定义字段'
}
}
</script>
组件完全透明传递所有原生事件,通过 props 配置即可监听任何原生事件。
const formItems = {
// Input 回车事件
keyword: {
type: FormItemType.INPUT,
label: '关键字',
props: {
onKeyup: (e) => {
if (e.key === 'Enter') {
console.log('回车键按下')
// 执行搜索逻辑
}
},
onChange: (value) => {
console.log('值变化:', value)
},
onFocus: () => {
console.log('获得焦点')
},
onBlur: () => {
console.log('失去焦点')
}
}
},
// InputNumber 变化事件
price: {
type: FormItemType.INPUT_NUMBER,
label: '价格',
props: {
onChange: (value) => {
console.log('价格变化:', value)
}
}
},
// Select 变化事件
status: {
type: FormItemType.SELECT,
label: '状态',
options: statusOptions,
props: {
onChange: (value) => {
console.log('状态变化:', value)
// 执行联动逻辑或搜索
},
onVisibleChange: (visible) => {
console.log('下拉框显示状态:', visible)
}
}
},
// DatePicker 事件
dateRange: {
type: FormItemType.DATE_RANGE,
label: '日期范围',
props: {
onCalendarChange: (dates) => {
console.log('日历变化:', dates)
},
onVisibleChange: (visible) => {
console.log('选择器显示状态:', visible)
}
}
}
}
重要说明:
props 配置直接传递给原生组件getFormData() 获取表单数据<template>
<div class="page-container">
<SearchForm
ref="searchFormRef"
:form-items="formItems"
label-width="100px"
>
<!-- 自定义插槽 -->
<template #customField="{ field, value }">
<el-cascader
v-model="formData[field]"
:options="cascaderOptions"
clearable
/>
</template>
</SearchForm>
<!-- 表格区域 -->
<el-table :data="tableData">
<!-- 表格列 -->
</el-table>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import SearchForm from '@/components/SearchForm/index.vue'
import { FormItemType } from '@/components/SearchForm/types'
const searchFormRef = ref()
const formData = reactive({})
const tableData = ref([])
const formItems = {
// 用户名输入框
username: {
type: FormItemType.INPUT,
label: '用户名',
props: {
placeholder: '请输入用户名',
maxlength: 20,
onKeyup: (e) => {
if (e.key === 'Enter') {
handleSearch()
}
}
}
},
// 状态下拉框
status: {
type: FormItemType.SELECT,
label: '状态',
options: [
{ label: '全部', value: '' },
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 }
],
props: {
placeholder: '请选择状态',
clearable: true,
filterable: true
}
},
// 创建时间范围
createTime: {
type: FormItemType.DATE_RANGE,
label: '创建时间',
props: {
startPlaceholder: '开始日期',
endPlaceholder: '结束日期',
unlinkPanels: true
}
},
// 自定义插槽字段
customField: {
type: FormItemType.SLOT,
label: '级联选择'
}
}
// 处理搜索
const handleSearch = () => {
const data = searchFormRef.value?.getFormData()
console.log('搜索参数:', data)
// 调用 API 获取数据
fetchData(data)
}
// 获取表单数据
const getFormData = () => {
const data = searchFormRef.value?.getFormData()
console.log('当前表单数据:', data)
return data
}
// 获取表格数据
const fetchData = async (params) => {
// API 调用
}
</script>
组件默认使用 el-row + el-col 栅格系统,自动适配不同屏幕尺寸。
<template>
<!-- 使用默认栅格配置:小屏1个/行,中屏3个,大屏4个 -->
<SearchForm
:form-items="formItems"
:buttons="buttons"
/>
<!-- 自定义全局栅格配置 -->
<SearchForm
:form-items="formItems"
:default-col="{ xs: 24, sm: 12, md: 12, lg: 8, xl: 6 }"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import SearchForm from 'vue3-smart-form'
import { FormItemType } from 'vue3-smart-form'
const searchFormRef = ref()
const formItems = [
{ key: 'keyword', type: FormItemType.INPUT, label: '关键字' },
{ key: 'status', type: FormItemType.SELECT, label: '状态', options: [...] },
// 某个字段需要占更宽,单独配置 col
{
key: 'remark',
type: FormItemType.INPUT,
label: '备注',
col: { xs: 24, sm: 24, md: 12, lg: 12 } // 占两列宽度
},
// 日期范围通常需要更宽
{
key: 'dateRange',
type: FormItemType.DATE_RANGE,
label: '创建时间',
col: { xs: 24, sm: 24, md: 12, lg: 8 }
}
]
const buttons = [
{
text: '搜索',
type: 'primary',
onClick: () => console.log(searchFormRef.value?.getFormData())
},
{
text: '重置',
onClick: () => searchFormRef.value?.reset()
}
]
</script>
栅格断点说明:
xs: <768px(手机)sm: ≥768px(平板)md: ≥992px(小屏桌面)lg: ≥1200px(桌面)xl: ≥1920px(大屏)const formItems = computed(() => ({
username: {
type: FormItemType.INPUT,
label: '用户名'
},
// 根据条件动态显示
status: {
type: FormItemType.SELECT,
label: '状态',
show: someCondition.value, // 动态控制显示
options: statusOptions.value
}
}))
<script setup lang="ts">
const searchFormRef = ref()
const formItems = {
appId: {
type: FormItemType.SELECT,
label: '应用',
options: appOptions,
props: {
onChange: (value) => {
handleAppChange(value)
}
}
},
topicId: {
type: FormItemType.SELECT,
label: '话题',
options: [], // 初始为空
show: false // 初始隐藏
}
}
// 监听字段变化,实现联动
const handleAppChange = (value) => {
// 根据 appId 加载对应的话题列表
loadTopicsByApp(value)
// 显示话题选择器
formItems.topicId.show = !!value
}
</script>
<script setup lang="ts">
const searchFormRef = ref()
// 设置单个字段
const setKeyword = () => {
searchFormRef.value?.setFieldValue('keyword', '默认关键字')
}
// 批量设置字段
const setDefaultValues = () => {
searchFormRef.value?.setFieldsValue({
keyword: '测试',
status: 1,
dateRange: ['2024-01-01', '2024-12-31']
})
}
// 从 URL 参数回填表单
onMounted(() => {
const query = route.query
if (Object.keys(query).length > 0) {
searchFormRef.value?.setFieldsValue(query)
}
})
</script>
{
keyword: {
type: FormItemType.INPUT,
label: '关键字',
props: {
style: {
width: '300px'
},
class: 'custom-keyword'
}
}
}
组件完全支持 Element Plus 原生属性和事件:
<SearchForm
:form-items="formItems"
:form-props="{
labelPosition: 'right',
size: 'large',
disabled: false,
validateOnRuleChange: true
}"
> </SearchForm>
const formItems = {
username: {
type: FormItemType.INPUT,
label: '用户名',
itemProps: {
required: true,
rules: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' }
],
error: '错误信息',
showMessage: true,
inlineMessage: false
}
}
}
{
// Input 所有原生属性和事件
keyword: {
type: FormItemType.INPUT,
label: '关键字',
props: {
maxlength: 20,
showWordLimit: true,
clearable: true,
onKeyup: (e) => {
if (e.key === 'Enter') handleSearch()
}
}
},
// Select 所有原生属性和事件
status: {
type: FormItemType.SELECT,
label: '状态',
options: [...],
props: {
multiple: true,
filterable: true,
onChange: (val) => console.log('变化', val)
}
},
// DatePicker 所有原生属性和事件
dateRange: {
type: FormItemType.DATE_RANGE,
label: '时间范围',
props: {
unlinkPanels: true,
shortcuts: [
{
text: '最近一周',
value: () => [new Date(Date.now() - 7 * 24 * 3600 * 1000), new Date()]
}
]
}
}
}
组件支持两种方式添加自定义按钮:配置形式和插槽形式。
适合简单的按钮场景,配置集中,代码简洁:
<template>
<SearchForm
ref="searchFormRef"
:form-items="formItems"
:buttons="customButtons"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import SearchForm from '@/components/SearchForm/index.vue'
import { FormItemType } from '@/components/SearchForm/types'
const searchFormRef = ref()
const formItems = {
keyword: {
type: FormItemType.INPUT,
label: '关键字'
},
status: {
type: FormItemType.SELECT,
label: '状态',
options: [
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 }
]
}
}
// 自定义按钮配置
const customButtons = [
{
text: '导出',
type: 'success',
icon: 'Download',
show: true,
onClick: () => {
ElMessage.success('导出数据')
exportData()
}
},
{
text: '批量删除',
type: 'danger',
icon: 'Delete',
show: true,
props: {
disabled: false
},
onClick: () => {
ElMessage.warning('批量删除')
batchDelete()
}
},
{
text: '刷新',
type: 'info',
icon: 'Refresh',
onClick: () => {
const data = searchFormRef.value?.getFormData()
console.log('查询:', data)
}
}
]
const exportData = () => {
// 导出逻辑
}
const batchDelete = () => {
// 批量删除逻辑
}
</script>
适合复杂的按钮场景,如按钮组、下拉菜单等:
<template>
<SearchForm
:form-items="formItems"
>
<!-- 按钮插槽 -->
<template #buttons>
<el-button type="success" :icon="Download" @click="exportData">
导出
</el-button>
<el-dropdown @command="handleDropdownCommand">
<el-button type="primary">
更多操作 <el-icon><arrow-down /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="batchEdit">批量编辑</el-dropdown-item>
<el-dropdown-item command="batchDelete">批量删除</el-dropdown-item>
<el-dropdown-item command="refresh">刷新</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-button-group>
<el-button :icon="Plus" @click="handleAdd">新增</el-button>
<el-button :icon="Upload" @click="handleUpload">上传</el-button>
</el-button-group>
</template>
</SearchForm>
</template>
<script setup lang="ts">
import { ElMessage } from 'element-plus'
import { Download, Plus, Upload, ArrowDown } from '@element-plus/icons-vue'
import SearchForm from '@/components/SearchForm/index.vue'
import { FormItemType } from '@/components/SearchForm/types'
const formItems = {
keyword: {
type: FormItemType.INPUT,
label: '关键字'
}
}
const exportData = () => {
ElMessage.success('导出数据')
}
const handleDropdownCommand = (command) => {
switch (command) {
case 'batchEdit':
ElMessage.info('批量编辑')
break
case 'batchDelete':
ElMessage.warning('批量删除')
break
case 'refresh':
ElMessage.success('刷新')
break
}
}
const handleAdd = () => {
ElMessage.success('新增')
}
const handleUpload = () => {
ElMessage.info('上传')
}
</script>
同时使用配置和插槽,配置按钮在前,插槽按钮在后:
<template>
<SearchForm
ref="searchFormRef"
:form-items="formItems"
:buttons="configButtons"
>
<!-- 插槽按钮会显示在配置按钮之后 -->
<template #buttons>
<el-button type="primary" :icon="Plus" @click="handleAdd">
新增
</el-button>
<el-dropdown @command="handleMoreCommand">
<el-button>
更多 <el-icon><arrow-down /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="export">导出</el-dropdown-item>
<el-dropdown-item command="import">导入</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
</SearchForm>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { ArrowDown, Plus } from '@element-plus/icons-vue'
import SearchForm from '@/components/SearchForm/index.vue'
import { FormItemType } from '@/components/SearchForm/types'
const searchFormRef = ref()
// 配置按钮(查询、重置等基础操作)
const configButtons = [
{
text: '查询',
type: 'primary',
icon: 'Search',
onClick: () => {
const data = searchFormRef.value?.getFormData()
console.log('查询:', data)
}
},
{
text: '重置',
icon: 'Refresh',
onClick: () => {
searchFormRef.value?.reset()
}
}
]
const formItems = {
keyword: {
type: FormItemType.INPUT,
label: '关键字'
}
}
const handleAdd = () => {
console.log('新增')
}
const handleMoreCommand = (command) => {
console.log('更多操作:', command)
}
</script>
<template>
<SearchForm
ref="searchFormRef"
:form-items="formItems"
:buttons="buttons"
>
<template #customSlot="{ field, value }">
<el-input v-model="customValues[field]" placeholder="自定义内容" />
</template>
</SearchForm>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import SearchForm from '@/components/SearchForm/index.vue'
import { FormItemType } from '@/components/SearchForm/types'
const searchFormRef = ref()
const customValues = reactive({})
// 数组格式配置
const formItems = [
{
key: 'username',
type: FormItemType.INPUT,
label: '用户名'
},
{
key: 'status',
type: FormItemType.SELECT,
label: '状态',
options: [
{ label: '全部', value: '' },
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 }
]
},
{
key: 'customSlot',
type: FormItemType.SLOT,
label: '自定义'
}
]
// 自定义按钮(替代默认的搜索和重置按钮)
const buttons = [
{
text: '查询',
type: 'primary',
icon: 'Search',
onClick: () => {
const data = searchFormRef.value?.getFormData()
console.log('查询数据:', data)
}
},
{
text: '重置',
icon: 'Refresh',
onClick: () => {
searchFormRef.value?.reset()
}
},
{
text: '新增',
type: 'success',
icon: 'Plus',
onClick: () => {
// 新增逻辑
}
}
]
</script>
典型的后台管理系统用户列表搜索,包含用户名、状态、角色、注册时间等条件。
<template>
<div class="user-management">
<SearchForm
ref="searchFormRef"
:form-items="formItems"
:buttons="buttons"
label-width="90px"
/>
<el-table :data="userList" v-loading="loading">
<el-table-column prop="username" label="用户名" />
<el-table-column prop="email" label="邮箱" />
<el-table-column prop="roleName" label="角色" />
<el-table-column prop="status" label="状态">
<template #default="{ row }">
<el-tag :type="row.status === 1 ? 'success' : 'danger'">
{{ row.status === 1 ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="注册时间" />
</el-table>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { ElMessage } from 'element-plus'
import SearchForm from '@/components/SearchForm/index.vue'
import { FormItemType } from '@/components/SearchForm/types'
const searchFormRef = ref()
const loading = ref(false)
const userList = ref([])
// 角色选项
const roleOptions = ref([
{ label: '管理员', value: 'admin' },
{ label: '编辑', value: 'editor' },
{ label: '普通用户', value: 'user' }
])
// 表单配置
const formItems = {
username: {
type: FormItemType.INPUT,
label: '用户名',
props: {
placeholder: '请输入用户名',
maxlength: 20,
clearable: true
}
},
email: {
type: FormItemType.INPUT,
label: '邮箱',
props: {
placeholder: '请输入邮箱'
}
},
role: {
type: FormItemType.SELECT,
label: '角色',
options: () => roleOptions.value,
props: {
placeholder: '请选择角色',
clearable: true,
filterable: true
}
},
status: {
type: FormItemType.SELECT,
label: '状态',
options: [
{ label: '全部', value: '' },
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 }
],
props: {
placeholder: '请选择状态',
clearable: true
}
},
registerTime: {
type: FormItemType.DATE_RANGE,
label: '注册时间',
props: {
startPlaceholder: '开始日期',
endPlaceholder: '结束日期',
unlinkPanels: true
}
}
}
// 按钮配置
const buttons = [
{
text: '查询',
type: 'primary',
icon: 'Search',
onClick: () => {
const formData = searchFormRef.value?.getFormData()
fetchUserList(formData)
}
},
{
text: '重置',
icon: 'Refresh',
onClick: () => searchFormRef.value?.reset()
},
{
text: '新增用户',
type: 'success',
icon: 'Plus',
onClick: () => ElMessage.info('打开新增对话框')
},
{
text: '导出',
type: 'warning',
icon: 'Download',
onClick: () => ElMessage.success('导出用户列表')
}
]
// 获取用户列表
const fetchUserList = async (params: any) => {
loading.value = true
try {
const res = await getUserList(params)
userList.value = res.data.list
} catch (error) {
ElMessage.error('获取用户列表失败')
} finally {
loading.value = false
}
}
</script>
包含多个筛选条件、级联选择、自定义插槽等复杂场景。
<template>
<div class="order-management">
<SearchForm
ref="searchFormRef"
:form-items="formItems"
:buttons="buttons"
label-width="100px"
>
<!-- 自定义区域级联选择 -->
<template #region="{ field }">
<el-cascader
v-model="customFormData[field]"
:options="regionOptions"
:props="{
value: 'code',
label: 'name',
children: 'children'
}"
clearable
placeholder="请选择地区"
style="width: 200px"
/>
</template>
<!-- 自定义商品选择 -->
<template #product="{ field }">
<el-select
v-model="customFormData[field]"
filterable
remote
reserve-keyword
placeholder="请输入商品名称"
:remote-method="searchProducts"
:loading="productLoading"
clearable
>
<el-option
v-for="item in productOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</template>
</SearchForm>
<el-table :data="orderList">
<!-- 订单列表列 -->
</el-table>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import SearchForm from '@/components/SearchForm/index.vue'
import { FormItemType } from '@/components/SearchForm/types'
import { getRegionList, searchProducts } from '@/api/common'
const searchFormRef = ref()
const customFormData = reactive({})
const orderList = ref([])
const productLoading = ref(false)
const productOptions = ref([])
// 地区选项
const regionOptions = ref([])
loadRegionOptions()
async function loadRegionOptions() {
const res = await getRegionList()
regionOptions.value = res.data
}
// 表单配置(数组格式)
const formItems = [
{
key: 'orderNo',
type: FormItemType.INPUT,
label: '订单号',
props: {
placeholder: '请输入订单号'
}
},
{
key: 'region',
type: FormItemType.SLOT,
label: '配送地区'
},
{
key: 'product',
type: FormItemType.SLOT,
label: '商品名称'
},
{
key: 'orderStatus',
type: FormItemType.SELECT,
label: '订单状态',
options: [
{ label: '全部', value: '' },
{ label: '待付款', value: 0 },
{ label: '待发货', value: 1 },
{ label: '已发货', value: 2 },
{ label: '已完成', value: 3 },
{ label: '已取消', value: 4 }
],
props: {
clearable: true
}
},
{
key: 'payType',
type: FormItemType.SELECT,
label: '支付方式',
options: [
{ label: '全部', value: '' },
{ label: '微信支付', value: 'wechat' },
{ label: '支付宝', value: 'alipay' },
{ label: '银行卡', value: 'bank' }
],
props: {
clearable: true
}
},
{
key: 'orderTime',
type: FormItemType.DATETIME_RANGE,
label: '下单时间',
props: {
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间'
}
}
]
// 按钮配置
const buttons = [
{
text: '查询',
type: 'primary',
icon: 'Search',
onClick: () => {
const formData = searchFormRef.value?.getFormData()
fetchOrderList(formData)
}
},
{
text: '重置',
icon: 'Refresh',
onClick: () => searchFormRef.value?.reset()
},
{
text: '批量导出',
type: 'success',
icon: 'Download',
onClick: () => console.log('批量导出')
},
{
text: '批量发货',
type: 'warning',
icon: 'Van',
onClick: () => console.log('批量发货')
}
]
// 搜索商品
async function searchProducts(query: string) {
if (!query) return
productLoading.value = true
const res = await searchProducts({ keyword: query })
productOptions.value = res.data
productLoading.value = false
}
// 获取订单列表
async function fetchOrderList(params: any) {
// 合并自定义表单数据
const queryParams = { ...params, ...customFormData }
console.log('查询参数:', queryParams)
// 调用 API
}
</script>
配置格式选择:
key 字段表单项 key 唯一性:确保每个表单项的 key 是唯一的
插槽命名:使用插槽时,插槽名称应与表单项 key 保持一致
默认值类型:日期范围类型的默认值为数组 [],其他类型默认值为空字符串 ''
响应式数据:表单数据是响应式的,可以直接通过 ref 获取和修改
类型安全:项目中已提供完整的 TypeScript 类型定义,建议开启类型检查
按钮配置方式选择:
配置驱动理念:组件完全遵循配置驱动设计,表单项和按钮均通过配置/插槽管理,不提供额外的开关属性
详细类型定义请查看 types.ts 文件。
欢迎贡献代码、提出建议或报告问题!
git checkout -b feature/AmazingFeature)git commit -m 'Add some AmazingFeature')git push origin feature/AmazingFeature)如果这个组件对你有帮助,请给它一个 ⭐️
Made with ❤️ by chenx18
FAQs
基于 Vue3 + Element Plus 的配置驱动式搜索表单组件
We found that vue3-search-form demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
AI agents are pulling packages into environments no scanner is watching, creating exposure before security teams can see it.

Security News
GitHub Actions checkout now blocks risky pull_request_target checkouts by default to help prevent pwn request supply chain attacks.

Product
Socket now supports Custom Roles and Repository Access Permissions so organizations can control who can access specific repositories and actions.