项目概况

FormSearch.vue

2026-01-293 min read未分类
v-ui-pro
vue
1<template> 2 <div class="query-form-container"> 3 <a-card :bordered="false" class="form-card"> 4 <a-form 5 ref="formRef" 6 :model="model" 7 layout="vertical" 8 class="query-form" 9 @submit="handleSearch" 10 > 11 <!-- 第一行 - 始终显示 --> 12 <a-grid 13 :cols="{ xs: 1, sm: 2, md: 3, lg: 4, xl: 5, xxl: 6 }" 14 :col-gap="4" 15 :row-gap="4" 16 class="grid-demo-grid" 17 :collapsed="isAdvancedSearch" 18 > 19 <a-grid-item 20 v-for="item in formItemAttributes" 21 :key="item.label" 22 class="demo-item animate__animated animate__fadeIn" 23 > 24 <a-form-item 25 :label="item.label" 26 :field="item.field" 27 class="form-item" 28 > 29 <a-input v-model="model[item.field]" v-bind="item.attrs" style="width: 100%" v-if="item.type ==='input'"/> 30 <a-input-number v-model="model[item.field]" v-bind="item.attrs" style="width: 100%" 31 v-else-if="item.type ==='input-number'"/> 32 <a-date-picker v-model="model[item.field]" v-bind="item.attrs" style="width: 100%" 33 v-else-if="item.type ==='date-picker'"/> 34 <a-month-picker v-model="model[item.field]" v-bind="item.attrs" style="width: 100%" 35 v-else-if="item.type ==='month-picker'"/> 36 <a-year-picker v-model="model[item.field]" v-bind="item.attrs" style="width: 100%" 37 v-else-if="item.type ==='year-picker'"/> 38 <a-quarter-picker v-model="model[item.field]" v-bind="item.attrs" style="width: 100%" 39 v-else-if="item.type ==='quarter-picker'"/> 40 <a-week-picker v-model="model[item.field]" v-bind="item.attrs" style="width: 100%" 41 v-else-if="item.type ==='week-picker'"/> 42 <a-select v-model="model[item.field]" v-bind="item.attrs" style="width: 100%" 43 v-else-if="item.type ==='select'"/> 44 <slot style="width: 100%" v-else/> 45 </a-form-item> 46 </a-grid-item> 47 <a-grid-item suffix> 48 <a-form-item label="搜索"> 49 <a-space> 50 <a-button @click="handleReset"> 51 <template #icon> 52 <icon-refresh/> 53 </template> 54 </a-button> 55 <a-button @click="handleAdvancedSearch"> 56 <a-space> 57 <span>高级搜索</span> 58 <span> 59 <icon-right v-if="isAdvancedSearch"/> 60 <icon-down v-else/> 61 </span> 62 </a-space> 63 <template #icon> 64 <icon-filter/> 65 </template> 66 </a-button> 67 <a-button 68 type="primary" 69 html-type="submit" 70 :loading="loading" 71 > 72 <template #icon> 73 <icon-search/> 74 </template> 75 搜索 76 </a-button> 77 </a-space> 78 </a-form-item> 79 </a-grid-item> 80 </a-grid> 81 </a-form> 82 </a-card> 83 </div> 84</template> 85 86<script lang="ts" setup> 87import {ref, useTemplateRef} from 'vue'; 88import type {FormItemAttribute} from '../v-ui-pro' 89import { 90 IconSearch, 91 IconRefresh, 92 IconDown, 93} from '@arco-design/web-vue/es/icon'; 94import {useFormModel} from "../../hooks/useFormModel.ts"; 95 96const props = defineProps({ 97 modelValue: { 98 type: Object, 99 default: () => ({ 100 orderTime: '', 101 }), 102 }, 103 loading: { 104 type: Boolean, 105 default: false, 106 }, 107 formItemAttributes: { 108 type: Array as () => FormItemAttribute[], 109 default: () => [ 110 { 111 label: '文本类型', 112 field: 'orderTime', 113 defaultValue: 'nb', 114 type: 'input', 115 attrs: { 116 placeholder: '绑定属性', 117 } 118 }, 119 { 120 label: '数字类型', 121 field: 'age', 122 defaultValue: 2, 123 attrs: { 124 placeholder: '数字类型', 125 mode: 'button', 126 max: 10, 127 min: 0 128 }, 129 type: 'input-number', 130 }, 131 { 132 label: '日期选择器', 133 field: 'rate', 134 type: 'date-picker', 135 }, 136 { 137 label: '月份选择器', 138 field: 'orderTime2', 139 type: 'month-picker', 140 }, 141 { 142 label: '年份选择器', 143 field: 'orderTime3', 144 type: 'year-picker', 145 }, 146 { 147 label: '季度选择器', 148 field: 'orderTime4', 149 type: 'quarter-picker', 150 }, 151 { 152 label: '周选择器', 153 field: 'orderTime5', 154 type: 'week-picker', 155 }, 156 { 157 label: '下拉框', 158 field: 'orderTime6', 159 type: 'select', 160 attrs: { 161 options: [ 162 { 163 label: '选项一', 164 value: '1', 165 }, 166 { 167 label: '选项二', 168 value: '2', 169 }, 170 ], 171 } 172 }, 173 ], 174 }, 175}); 176 177const emit = defineEmits(['update:modelValue', 'search']) 178 179const {model, loading, formItemAttributes, formRef} = useFormModel(props, emit) 180 181const isAdvancedSearch = ref(false); 182 183// 搜索处理 184const handleSearch = async () => { 185 emit('search', model.value); 186}; 187 188// 重置处理 189const handleReset = () => { 190 formRef.value?.resetFields(); 191}; 192 193const handleAdvancedSearch = () => { 194 isAdvancedSearch.value = !isAdvancedSearch.value; 195}; 196 197defineExpose({handleAdvancedSearch, handleSearch, handleReset}) 198</script> 199 200<style scoped> 201.query-form-container { 202 background-color: #f5f5f5; 203} 204 205.form-card { 206 margin-bottom: 16px; 207 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); 208} 209 210.query-form { 211 padding: 8px 0; 212} 213 214.form-item { 215 margin-bottom: 16px; 216 margin-right: 24px; 217} 218 219.form-item :deep(.arco-form-item-label) { 220 font-weight: 500; 221 color: var(--color-text-2); 222 min-width: 80px; 223} 224 225.form-actions { 226 margin-left: auto; 227} 228 229.result-card { 230 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); 231} 232 233.result-card :deep(.arco-card-header) { 234 border-bottom: 1px solid #e5e6eb; 235 padding: 16px 20px; 236} 237 238.result-card :deep(.arco-card-body) { 239 padding: 0; 240} 241 242.form-row { 243 display: flex; 244 flex-wrap: wrap; 245 align-items: center; 246 margin-bottom: 16px; 247} 248 249.advanced-search-area { 250 border-top: 1px solid #e5e6eb; 251 padding-top: 16px; 252 margin-top: 8px; 253 animation: slideDown 0.3s ease-in-out; 254} 255 256@keyframes slideDown { 257 from { 258 opacity: 0; 259 transform: translateY(-10px); 260 } 261 to { 262 opacity: 1; 263 transform: translateY(0); 264 } 265} 266 267.form-actions { 268 margin-left: auto; 269} 270 271/* 响应式设计 */ 272@media (max-width: 1200px) { 273 .query-form { 274 flex-wrap: wrap; 275 } 276 277 .form-item { 278 margin-right: 16px; 279 margin-bottom: 12px; 280 } 281} 282 283@media (max-width: 768px) { 284 .query-form-container { 285 padding: 12px; 286 } 287 288 .query-form { 289 flex-direction: column; 290 } 291 292 .form-item { 293 margin-right: 0; 294 width: 100%; 295 } 296 297 .form-item :deep(.arco-range-picker), 298 .form-item :deep(.arco-select), 299 .form-item :deep(.arco-input) { 300 width: 100% !important; 301 } 302} 303</style> 304