返回博客列表

FormSearch.vue

2026-01-29
3 min read
v-ui-pro

vue
<template>
  <div class="query-form-container">
    <a-card :bordered="false" class="form-card">
      <a-form
          ref="formRef"
          :model="model"
          layout="vertical"
          class="query-form"
          @submit="handleSearch"
      >
        <!-- 第一行 - 始终显示 -->
        <a-grid
            :cols="{ xs: 1, sm: 2, md: 3, lg: 4, xl: 5, xxl: 6 }"
            :col-gap="4"
            :row-gap="4"
            class="grid-demo-grid"
            :collapsed="isAdvancedSearch"
        >
          <a-grid-item
              v-for="item in formItemAttributes"
              :key="item.label"
              class="demo-item animate__animated animate__fadeIn"
          >
            <a-form-item
                :label="item.label"
                :field="item.field"
                class="form-item"
            >
              <a-input v-model="model[item.field]" v-bind="item.attrs" style="width: 100%" v-if="item.type ==='input'"/>
              <a-input-number v-model="model[item.field]" v-bind="item.attrs" style="width: 100%"
                              v-else-if="item.type ==='input-number'"/>
              <a-date-picker v-model="model[item.field]" v-bind="item.attrs" style="width: 100%"
                             v-else-if="item.type ==='date-picker'"/>
              <a-month-picker v-model="model[item.field]" v-bind="item.attrs" style="width: 100%"
                              v-else-if="item.type ==='month-picker'"/>
              <a-year-picker v-model="model[item.field]" v-bind="item.attrs" style="width: 100%"
                             v-else-if="item.type ==='year-picker'"/>
              <a-quarter-picker v-model="model[item.field]" v-bind="item.attrs" style="width: 100%"
                                v-else-if="item.type ==='quarter-picker'"/>
              <a-week-picker v-model="model[item.field]" v-bind="item.attrs" style="width: 100%"
                             v-else-if="item.type ==='week-picker'"/>
              <a-select v-model="model[item.field]" v-bind="item.attrs" style="width: 100%"
                        v-else-if="item.type ==='select'"/>
              <slot style="width: 100%" v-else/>
            </a-form-item>
          </a-grid-item>
          <a-grid-item suffix>
            <a-form-item label="搜索">
              <a-space>
                <a-button @click="handleReset">
                  <template #icon>
                    <icon-refresh/>
                  </template>
                </a-button>
                <a-button @click="handleAdvancedSearch">
                  <a-space>
                    <span>高级搜索</span>
                    <span>
                      <icon-right v-if="isAdvancedSearch"/>
                      <icon-down v-else/>
                    </span>
                  </a-space>
                  <template #icon>
                    <icon-filter/>
                  </template>
                </a-button>
                <a-button
                    type="primary"
                    html-type="submit"
                    :loading="loading"
                >
                  <template #icon>
                    <icon-search/>
                  </template>
                  搜索
                </a-button>
              </a-space>
            </a-form-item>
          </a-grid-item>
        </a-grid>
      </a-form>
    </a-card>
  </div>
</template>

<script lang="ts" setup>
import {ref, useTemplateRef} from 'vue';
import type {FormItemAttribute} from '../v-ui-pro'
import {
  IconSearch,
  IconRefresh,
  IconDown,
} from '@arco-design/web-vue/es/icon';
import {useFormModel} from "../../hooks/useFormModel.ts";

const props = defineProps({
  modelValue: {
    type: Object,
    default: () => ({
      orderTime: '',
    }),
  },
  loading: {
    type: Boolean,
    default: false,
  },
  formItemAttributes: {
    type: Array as () => FormItemAttribute[],
    default: () => [
      {
        label: '文本类型',
        field: 'orderTime',
        defaultValue: 'nb',
        type: 'input',
        attrs: {
          placeholder: '绑定属性',
        }
      },
      {
        label: '数字类型',
        field: 'age',
        defaultValue: 2,
        attrs: {
          placeholder: '数字类型',
          mode: 'button',
          max: 10,
          min: 0
        },
        type: 'input-number',
      },
      {
        label: '日期选择器',
        field: 'rate',
        type: 'date-picker',
      },
      {
        label: '月份选择器',
        field: 'orderTime2',
        type: 'month-picker',
      },
      {
        label: '年份选择器',
        field: 'orderTime3',
        type: 'year-picker',
      },
      {
        label: '季度选择器',
        field: 'orderTime4',
        type: 'quarter-picker',
      },
      {
        label: '周选择器',
        field: 'orderTime5',
        type: 'week-picker',
      },
      {
        label: '下拉框',
        field: 'orderTime6',
        type: 'select',
        attrs: {
          options: [
            {
              label: '选项一',
              value: '1',
            },
            {
              label: '选项二',
              value: '2',
            },
          ],
        }
      },
    ],
  },
});

const emit = defineEmits(['update:modelValue', 'search'])

const {model, loading, formItemAttributes, formRef} = useFormModel(props, emit)

const isAdvancedSearch = ref(false);

// 搜索处理
const handleSearch = async () => {
  emit('search', model.value);
};

// 重置处理
const handleReset = () => {
  formRef.value?.resetFields();
};

const handleAdvancedSearch = () => {
  isAdvancedSearch.value = !isAdvancedSearch.value;
};

defineExpose({handleAdvancedSearch, handleSearch, handleReset})
</script>

<style scoped>
.query-form-container {
  background-color: #f5f5f5;
}

.form-card {
  margin-bottom: 16px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.query-form {
  padding: 8px 0;
}

.form-item {
  margin-bottom: 16px;
  margin-right: 24px;
}

.form-item :deep(.arco-form-item-label) {
  font-weight: 500;
  color: var(--color-text-2);
  min-width: 80px;
}

.form-actions {
  margin-left: auto;
}

.result-card {
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.result-card :deep(.arco-card-header) {
  border-bottom: 1px solid #e5e6eb;
  padding: 16px 20px;
}

.result-card :deep(.arco-card-body) {
  padding: 0;
}

.form-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  margin-bottom: 16px;
}

.advanced-search-area {
  border-top: 1px solid #e5e6eb;
  padding-top: 16px;
  margin-top: 8px;
  animation: slideDown 0.3s ease-in-out;
}

@keyframes slideDown {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.form-actions {
  margin-left: auto;
}

/* 响应式设计 */
@media (max-width: 1200px) {
  .query-form {
    flex-wrap: wrap;
  }

  .form-item {
    margin-right: 16px;
    margin-bottom: 12px;
  }
}

@media (max-width: 768px) {
  .query-form-container {
    padding: 12px;
  }

  .query-form {
    flex-direction: column;
  }

  .form-item {
    margin-right: 0;
    width: 100%;
  }

  .form-item :deep(.arco-range-picker),
  .form-item :deep(.arco-select),
  .form-item :deep(.arco-input) {
    width: 100% !important;
  }
}
</style>

返回博客列表
最后更新于 2026-01-29
想法或问题?在 GitHub Issue 下方参与讨论
去评论