优雅封装:Vue3 + Element Plus 智能紧凑型搜索组件开发实践

Vue3 + Element Plus 智能紧凑型搜索组件深度解析

引言:解决后台管理系统搜索痛点

在后台管理系统开发中,我们经常面临一个共同挑战:如何优雅地处理大量搜索条件。传统的解决方案要么将所有条件平铺展示(导致页面冗长),影响页面美观如下图

要么全部折叠在高级搜索中(增加用户操作成本)。


本文将深入解析一款智能紧凑型搜索组件,它通过创新设计解决了这一痛点。

组件设计思路

核心设计理念

  • 主次分离:高频搜索项外露,次要条件收纳
  • 配置驱动:通过JSON配置动态生成表单
  • 智能联动:支持字段间联动关系
  • 空间优化:紧凑布局节省页面空间
  • 用户体验:一键展开/收起,操作流畅

架构示意图

css 复制代码
┌──────────────────────────────────────────────┐
│  筛选 [展开图标]  [主搜索输入框]      [搜索] [重置]  │
├──────────────────────────────────────────────┤
│  ▼ 展开面板                                  │
│  ┌────────────┬────────────┬────────────┐    │
│  │ 条件1标签   │ 条件2标签   │ 条件3标签   │    │
│  │ 条件1控件   │ 条件2控件   │ 条件3控件   │    │
│  └────────────┴────────────┴────────────┘    │
│  ┌────────────┬────────────┬────────────┐    │
│  │ 条件4标签   │ 条件5标签   │ 条件6标签   │    │
│  │ 条件4控件   │ 条件5控件   │ 条件6控件   │    │
│  └────────────┴────────────┴────────────┘    │
└──────────────────────────────────────────────┘

组件实现深度解析

1. 智能搜索条件分发

通过 ifShow 属性区分主次搜索条件,为true表示在主面板中直接展示搜索条件:

javascript 复制代码
const computedOnlyShowInputObject = computed(() => {
  return props.searchConfig.find(i => i.ifShow === true);
});

在模板中动态渲染主搜索项:

vue 复制代码
<el-input
  v-model="queryParams[computedOnlyShowInputObject.prop]"
  :placeholder="computedOnlyShowInputObject.placeholder"
/>

2. 动态表单渲染引擎

组件支持多种表单类型,通过配置动态渲染:

vue 复制代码
<template v-if="item.type === 'daterange'">
  <!-- 日期范围选择器 -->
</template>

<template v-else-if="item.type === 'input'">
  <!-- 文本输入框 -->
</template>

<template v-else-if="item.type === 'radio'">
  <!-- 单选按钮组 -->
</template>

<!-- 更多类型... -->

3. 条件分组算法

将搜索条件按每行3个分组展示:

javascript 复制代码
const searchRows = computed(() => {
  const rows = [];
  const visibleItems = props.searchConfig.filter(i => !i.ifShow);
  
  for (let i = 0; i < visibleItems.length; i += 3) {
    rows.push(visibleItems.slice(i, i + 3));
  }
  
  return rows;
});

4. 智能联动系统

通过配置实现字段间联动:

javascript 复制代码
{
  label: '来源渠道',
  prop: 'bidChannelId',
  type: 'radio',
  linkage: {
    resetFields: ['bidAccountId'],
    onChange: async (val) => {
      await getbidAccountList(val);
      queryParams.bidAccountId = undefined;
    }
  }
}

联动监听实现:

javascript 复制代码
watch(
  () => props.queryParams[item.prop],
  async (newVal) => {
    if (newVal) {
      await item.linkage?.onChange?.(newVal);
    } else {
      item.linkage?.resetFields?.forEach(field => {
        props.queryParams[field] = undefined;
      });
    }
  },
  { deep: true }
);

5. 智能高度计算

根据屏幕高度动态调整展开面板高度:

javascript 复制代码
const computedHeight = computed(() => {
  const bodyHeight = document.body.offsetHeight;
  return Math.max(bodyHeight - 240, 300) + 'px';
});

6. 交互优化细节

点击外部关闭面板

javascript 复制代码
const handleClickOutside = (event: MouseEvent) => {
  const target = event.target as HTMLElement;
  const isDatePicker = target.closest('.el-picker__panel') || 
                      target.closest('.el-date-range-picker') || 
                      target.closest('.el-popper');

  if (showSearch.value && searchContainer.value && 
      !searchContainer.value.contains(target) && !isDatePicker) {
    showSearch.value = false;
  }
};

重置功能

javascript 复制代码
const resetQuery = () => {
  // 重置基础字段
  Object.keys(props.queryParams).forEach(key => {
    props.queryParams[key] = Array.isArray(props.queryParams[key]) ? [] : undefined;
  });
  
  // 处理联动重置
  props.searchConfig.forEach(item => {
    item.onReset?.();
    item.linkage?.resetFields?.forEach(field => {
      props.queryParams[field] = undefined;
    });
  });
  
  emit('reset');
};

父组件集成示例

1. 基本使用

vue 复制代码
<div class="search-container mb-20px">
  <CompactSearch
    :queryParams="queryParams"
    :searchConfig="searchConfig"
    :shortCuts="shortCuts"
    @query="handleQuery"
    @reset="resetQuery"
  />
  
  <el-button type="primary" @click="openForm('create')">
    <Icon icon="ep:plus" class="mr-5px" />
    新增
  </el-button>
</div>

2. 搜索配置详解

javascript 复制代码
const searchConfig = computed(() => {
  return [
    // 主搜索项(始终显示)通过ifShow: true 控制
    {
      label: '线索名称',
      prop: 'keyword',
      type: 'input',
      placeholder: '线索名称/手机号/微信/qq',
      ifShow: true 
    },
    
    // 次要搜索项(在展开面板中显示)
    {
      label: '发布时间',
      prop: 'publishTime',
      type: 'daterange',
      ifShow: false,
    },
    
    // 复选框示例
    {
      label: '线索状态',
      prop: 'clueStatus',
      type: 'checkbox',
      options: getIntDictOptions(DICT_TYPE.CRM_CLUE_STATUS),
      ifShow: false,
    },
    
    // 联动字段示例
    {
      label: '来源渠道',
      prop: 'bidChannelId',
      type: 'radio',
      options: bidOriginList.value.map(item => ({
        value: item.id,
        label: item.name
      })),
      linkage: {
        resetFields: ['bidAccountId'],
        onChange: async (val) => {
          await getbidAccountList(val);
          queryParams.bidAccountId = undefined;
        }
      },
      ifShow: false,
    },
    //非关联关系直接通过接口动态获取options
    {
      label: '来源账户',
      prop: 'bidAccountId',
      type: 'radio',
      options: bidAccountList.value.map((item) => ({
        value: item.id,
        label: item.name
      })),
      ifShow: false
    },
    
    // 更多配置项...
  ];
});

设计优势分析

  1. 用户体验提升

    • 高频操作零点击访问
    • 复杂操作两步完成(展开->操作)
    • 视觉层次分明,重点突出
  2. 开发效率提升

    • 配置化开发,减少重复代码
    • 统一交互模式,降低学习成本
    • 组件解耦,便于维护
  3. 性能优化

    • 条件渲染避免不必要的DOM节点
    • 按需加载联动数据
    • 智能高度计算避免页面跳动
  4. 扩展性强

    • 支持多种表单类型
    • 易于添加新表单控件
    • 支持复杂联动场景

实际应用场景

典型使用场景

  1. CRM系统的客户筛选
  2. ERP系统的订单查询
  3. 数据分析平台的多维筛选
  4. 后台管理系统的日志查询

场景示例:线索管理系统

javascript 复制代码
const searchConfig = computed(() => {
  return [
    {
      label: '线索名称',
      prop: 'keyword',
      type: 'input',
      placeholder: '线索名称/手机号/微信/qq',
      ifShow: true //唯一键值 判断是在展开的dialog中展示还是在输入框中直接展示
    },
    {
      label: '发布时间',
      prop: 'publishTime',
      type: 'daterange',
      ifShow: false
    },
    {
      label: '线索状态',
      prop: 'clueStatus',
      type: 'checkbox',
      options: getIntDictOptions(DICT_TYPE.CRM_CLUE_STATUS),
      ifShow: false
    },
    {
      label: '来源渠道',
      prop: 'bidChannelId',
      type: 'radio',
      options: bidOriginList.value.map((item) => ({
        value: item.id,
        label: item.name
      })),
      linkage: {
        // 当渠道变化时需要重置的字段
        resetFields: ['bidAccountId'],
        // 当渠道变化时调用的方法
        onChange: async (val) => {
          await getbidAccountList(val)
          queryParams.bidAccountId = undefined
        }
      },
      ifShow: false
    },
    {
      label: '来源账户',
      prop: 'bidAccountId',
      type: 'radio',
      options: bidAccountList.value.map((item) => ({
        value: item.id,
        label: item.name
      })),
      ifShow: false
    },
    {
      label: '超时响应',
      prop: 'overtimeFlag',
      type: 'radio',
      options: [
        { label: '是', value: true },
        { label: '否', value: false }
      ],
      ifShow: false
    },
    {
      label: '地区',
      prop: 'areaId',
      type: 'treeSelect',
      data: areaList.value,
      props: defineProps,
      ifShow: false
    },
    {
      label: '接收公司',
      prop: 'receiveTenantId',
      type: 'select',
      options: companyList.value.map((item) => ({
        value: item.id,
        label: item.name
      })),
      ifShow: false
    }
  ]
})

最佳实践与技巧

配置技巧

  1. 主搜索项选择:选择最常用、最核心的1-2个字段作为主搜索项
  2. 默认值设置:在父组件mounted中设置合理的默认值
  3. 联动优化:对数据量大的联动使用防抖请求

总结与展望

本文介绍了一款基于Vue3和Element Plus的智能紧凑型搜索组件,它通过创新的设计解决了后台管理系统中的搜索条件过多问题。该组件具有以下特点:

  1. 配置驱动:通过JSON配置生成搜索表单
  2. 智能分组:自动按行分组展示搜索条件
  3. 强大联动:支持字段间复杂联动关系
  4. 响应式设计:完美适配不同屏幕尺寸
  5. 用户体验优先:平衡了功能丰富性和界面简洁性

未来可能的改进方向:

  • 添加自定义插槽支持更复杂的表单控件
  • 集成表单验证功能
  • 增加搜索条件保存/加载功能
  • 支持可视化配置面板

完整代码示例

组件内容

父组件调用

成果展示

通过本文的深度解析,希望能帮助开发者更好地理解和应用这种智能搜索模式,提升后台管理系统的用户体验和开发效率。

相关推荐
李剑一5 分钟前
Vue实现一个可以拖拽的工具栏
前端
前端付豪6 分钟前
美团 ETA 模型在线训练与推理加速实践
前端·后端·架构
阿慧勇闯大前端6 分钟前
你真的搞懂JavaScript中的异步了吗?
前端
前端小嘎7 分钟前
告别繁琐,拥抱自由:使用 Sanity.io 轻松搭建你的现代化博客
前端
tkt10 分钟前
Android Compose 中,@Immutable 和 @Stable 的理解
前端
qq_124987075311 分钟前
基于node.js+vue的医院陪诊系统的设计与实现(源码+论文+调试+安装+售后)
前端·vue.js·node.js·毕业设计
markyankee10111 分钟前
JavaScript中的this关键字:全面解析与实战指南
前端·javascript
去伪存真12 分钟前
如何集中管理多个项目的Jenkins Job 流程?
前端·jenkins
邹荣乐13 分钟前
uni-app多端应用开发:常见跨端兼容问题及处理策略
前端·微信小程序·uni-app
前端达人13 分钟前
React Router 是怎么实现灵活导航的?
前端·javascript·react.js·前端框架·ecmascript