ArtSearchBar Component
A powerful and highly configurable form search component that supports multiple form control types, dynamic show/hide, form validation, and other features.
Features
- Multiple Form Controls - Supports 20+ form controls including input, select, date picker, cascader, etc.
- Highly Configurable - Supports custom layout, label position, spacing, etc.
- Responsive Design - Adapts to different screen sizes
- Slot Support - Supports custom components and slot rendering
- Form Validation - Complete form validation support
- Dynamic Control - Supports dynamic show/hide of form items
Basic Usage
The simplest search bar usage:
vue
<template>
<ArtSearchBar
v-model="formData"
:items="formItems"
@search="handleSearch"
@reset="handleReset"
/>
</template>
<script setup>
const formData = ref({
name: '',
status: ''
})
const formItems = [
{
label: 'Username',
key: 'name',
type: 'input',
placeholder: 'Please enter username'
},
{
label: 'Status',
key: 'status',
type: 'select',
props: {
options: [
{ label: 'Enabled', value: '1' },
{ label: 'Disabled', value: '0' }
]
}
}
]
const handleSearch = () => {
console.log('Search params:', formData.value)
}
const handleReset = () => {
console.log('Reset form')
}
</script>
Supported Form Control Types
Input Controls
javascript
// Regular input
{
label: 'Username',
key: 'name',
type: 'input',
placeholder: 'Please enter username'
}
// Number input
{
label: 'Age',
key: 'age',
type: 'number',
props: {
min: 0,
max: 120
}
}
// Textarea
{
label: 'Remarks',
key: 'remark',
type: 'input',
props: {
type: 'textarea',
rows: 3
}
}
Selection Controls
javascript
// Dropdown select
{
label: 'Status',
key: 'status',
type: 'select',
props: {
options: [
{ label: 'Enabled', value: '1' },
{ label: 'Disabled', value: '0' }
]
}
}
// Cascader
{
label: 'Region',
key: 'region',
type: 'cascader',
props: {
options: cascaderOptions,
props: { multiple: true }
}
}
// Tree select
{
label: 'Department',
key: 'department',
type: 'treeselect',
props: {
data: treeData,
multiple: true,
showCheckbox: true
}
}
Date Time Controls
javascript
// Date picker
{
label: 'Create Date',
key: 'createDate',
type: 'datetime',
props: {
type: 'date',
valueFormat: 'YYYY-MM-DD'
}
}
// Date range
{
label: 'Date Range',
key: 'dateRange',
type: 'datetime',
props: {
type: 'daterange',
rangeSeparator: 'to',
startPlaceholder: 'Start date',
endPlaceholder: 'End date'
}
}
// Time picker
{
label: 'Time',
key: 'time',
type: 'timepicker',
props: {
valueFormat: 'HH:mm:ss'
}
}
Other Controls
javascript
// Switch
{
label: 'Enabled',
key: 'enabled',
type: 'switch'
}
// Radio group
{
label: 'Gender',
key: 'gender',
type: 'radiogroup',
props: {
options: [
{ label: 'Male', value: '1' },
{ label: 'Female', value: '2' }
]
}
}
// Checkbox group
{
label: 'Hobbies',
key: 'hobbies',
type: 'checkboxgroup',
props: {
options: [
{ label: 'Reading', value: 'reading' },
{ label: 'Sports', value: 'sports' }
]
}
}
// Rate
{
label: 'Rating',
key: 'rating',
type: 'rate'
}
// Slider
{
label: 'Price Range',
key: 'priceRange',
type: 'slider',
props: {
range: true,
max: 1000
}
}
Custom Components
Using Render Function
javascript
import { h } from 'vue'
import CustomComponent from './CustomComponent.vue'
{
label: 'Custom Component',
key: 'custom',
type: () => h(CustomComponent, {
prop1: 'value1',
onCustomEvent: handleCustomEvent
})
}
Using Slots
vue
<template>
<ArtSearchBar v-model="formData" :items="formItems">
<template #customSlot="{ item, modelValue }">
<el-input
v-model="modelValue[item.key]"
placeholder="I am a slot-rendered component"
/>
</template>
</ArtSearchBar>
</template>
<script setup>
const formItems = [
{
label: 'Custom Slot',
key: 'customSlot',
type: 'input' // This type will be overridden by the slot
}
]
</script>
Form Validation
vue
<template>
<ArtSearchBar
ref="searchBarRef"
v-model="formData"
:items="formItems"
:rules="rules"
@search="handleSearch"
/>
</template>
<script setup>
const searchBarRef = ref()
const rules = {
name: [
{ required: true, message: 'Please enter username', trigger: 'blur' }
],
phone: [
{ required: true, message: 'Please enter phone number', trigger: 'blur' },
{ pattern: /^1[3456789]\d{9}$/, message: 'Please enter a valid phone number', trigger: 'blur' }
]
}
const handleSearch = async () => {
try {
await searchBarRef.value.validate()
console.log('Validation passed, executing search')
} catch (error) {
console.log('Validation failed')
}
}
</script>
Dynamic Control
Dynamic Show/Hide
javascript
const formItems = computed(() => [
{
label: 'Username',
key: 'name',
type: 'input'
},
{
label: 'Advanced Options',
key: 'advanced',
type: 'input',
hidden: !showAdvanced.value // Dynamic control of show/hide
}
])
Dynamic Configuration Update
javascript
const userNameItem = ref({
label: 'Username',
key: 'name',
type: 'input',
placeholder: 'Please enter username'
})
// Dynamically modify configuration
const updateUserNameConfig = () => {
userNameItem.value = {
...userNameItem.value,
label: 'Nickname',
placeholder: 'Please enter nickname'
}
}
Layout Configuration
Grid Layout
vue
<ArtSearchBar
v-model="formData"
:items="formItems"
:span="8"
:gutter="16"
/>
Label Configuration
vue
<ArtSearchBar
v-model="formData"
:items="formItems"
label-position="top"
:label-width="120"
/>
Responsive Layout
The component automatically adapts to different screen sizes:
- Mobile: 1 form item per row
- Tablet: 2 form items per row
- Desktop: Number of form items per row controlled by the
span
property
API
Props
Parameter | Description | Type | Default |
---|---|---|---|
modelValue | Form data object | Record<string, any> | {} |
items | Form item configuration array | SearchFormItem[] | [] |
span | Grid span for each form item | number | 6 |
gutter | Grid gutter | number | 12 |
labelPosition | Label position | 'left' | 'right' | 'top' | 'right' |
labelWidth | Label width | string | number | '70px' |
defaultExpanded | Default expanded state | boolean | false |
showExpand | Show expand/collapse button | boolean | true |
showReset | Show reset button | boolean | true |
showSearch | Show search button | boolean | true |
disabledSearch | Disable search button | boolean | false |
SearchFormItem Configuration
Parameter | Description | Type | Default |
---|---|---|---|
key | Unique identifier for form item | string | - |
label | Label text | string | - |
type | Form item type | string | (() => VNode) | 'input' |
hidden | Whether to hide | boolean | false |
span | Grid span | number | - |
labelWidth | Label width | string | number | - |
placeholder | Placeholder | string | - |
props | Properties passed to component | Record<string, any> | - |
slots | Slot configuration | Record<string, () => any> | - |
Events
Event | Description | Parameters |
---|---|---|
search | Triggered when search button is clicked | - |
reset | Triggered when reset button is clicked | - |
Methods
Method | Description | Parameters |
---|---|---|
validate | Validate form | () => Promise<boolean> |
reset | Reset form | () => void |
Slots
Slot | Description | Parameters |
---|---|---|
[key] | Custom form item content | { item: SearchFormItem, modelValue: Record<string, any> } |
Complete Example
vue
<template>
<div class="search-example">
<ArtSearchBar
ref="searchBarRef"
v-model="formData"
:items="formItems"
:rules="rules"
:defaultExpanded="true"
:labelWidth="100"
labelPosition="right"
:span="6"
:gutter="16"
@search="handleSearch"
@reset="handleReset"
>
<template #customSlot>
<el-input
v-model="formData.customSlot"
placeholder="I am a slot-rendered component"
/>
</template>
</ArtSearchBar>
<div class="result">
<h3>Search Results:</h3>
<pre>{{ JSON.stringify(formData, null, 2) }}</pre>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const searchBarRef = ref()
const formData = ref({
name: '',
phone: '',
status: '',
dateRange: [],
customSlot: ''
})
const rules = {
name: [{ required: true, message: 'Please enter username', trigger: 'blur' }],
phone: [
{ required: true, message: 'Please enter phone number', trigger: 'blur' },
{ pattern: /^1[3456789]\d{9}$/, message: 'Please enter a valid phone number', trigger: 'blur' }
]
}
const formItems = [
{
label: 'Username',
key: 'name',
type: 'input',
placeholder: 'Please enter username',
props: { clearable: true }
},
{
label: 'Phone',
key: 'phone',
type: 'input',
placeholder: 'Please enter phone number',
props: { maxlength: 11 }
},
{
label: 'Status',
key: 'status',
type: 'select',
props: {
placeholder: 'Please select status',
options: [
{ label: 'Enabled', value: '1' },
{ label: 'Disabled', value: '0' }
]
}
},
{
label: 'Date Range',
key: 'dateRange',
type: 'datetime',
props: {
type: 'daterange',
rangeSeparator: 'to',
startPlaceholder: 'Start date',
endPlaceholder: 'End date',
valueFormat: 'YYYY-MM-DD'
}
},
{
label: 'Custom Slot',
key: 'customSlot',
type: 'input'
}
]
const handleSearch = async () => {
try {
await searchBarRef.value.validate()
console.log('Search params:', formData.value)
// Execute search logic
} catch (error) {
console.log('Form validation failed')
}
}
const handleReset = () => {
console.log('Reset form')
}
</script>
<style scoped>
.search-example {
padding: 20px;
}
.result {
margin-top: 20px;
padding: 16px;
background-color: #f5f5f5;
border-radius: 4px;
}
.result pre {
margin: 0;
font-size: 12px;
}
</style>
Notes
- Form item key values must be unique for form data binding and validation
- Props are passed directly to the corresponding Element Plus component, please refer to Element Plus official documentation
- Form validation rules format is consistent with Element Plus Form component