FormSearch.vue
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