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
spanproperty
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
