1. 项目概述
在开发uni-app小程序时,我们经常会遇到列表页需要支持多状态组合筛选的需求。最近我在一个实际项目中就遇到了这样的场景:列表页需要将"状态1"和"状态2"合并展示为"进行中"的Tab,同时还要保留单独的状态筛选能力。这个看似简单的需求,在实际开发中却遇到了不少挑战。
1.1 核心需求解析
我们的核心需求可以分解为以下几点:
- 前端需要将状态1(2)和状态2(3)合并展示在"正在进行"Tab下
- 状态3(4)单独展示在"历史数据"Tab下
- 列表项按钮需要根据状态和操作情况显示不同的交互按钮
- 必须保持与现有API的兼容性,不能影响其他模块的正常使用
1.2 技术难点分析
实现这个需求主要面临以下几个技术难点:
- 原有API只支持单状态查询,无法直接满足多状态组合查询的需求
- 如果采用前端合并多个请求的方案,会导致分页逻辑复杂且容易出错
- 如果一次性查询所有数据在前端过滤,在数据量大时性能会很差
- 必须确保新功能的实现不会破坏现有功能的正常运行
2. 解决方案设计
2.1 整体架构思路
经过仔细分析,我们决定采用以下架构方案:
- 后端API新增statusList参数支持多状态IN查询
- 保留原有的status参数确保向后兼容
- 设置参数优先级:statusList > status
- 前端将Tab与状态列表进行映射配置
这种设计既满足了新需求,又保证了系统的稳定性,是一个典型的向后兼容设计方案。
2.2 方案优势详解
这个方案具有以下几个显著优势:
| 优势点 | 具体说明 |
|---|---|
| 向后兼容 | 旧的调用方式继续生效,无需修改已有代码和相关依赖 |
| 灵活扩展 | 支持任意状态组合,适应未来业务需求变化 |
| 性能优化 | 数据库层面完成筛选,避免不必要的数据传输 |
| 分页友好 | 在数据库层面完成筛选,分页逻辑正确可靠 |
3. 后端实现细节
3.1 参数扩展设计
我们在原有的查询参数类中新增了statusList字段:
java复制public class ListQueryForm extends PageParam {
/** 单状态筛选(保留,向后兼容) */
private Integer status;
/** 多状态筛选(新增,优先级更高) */
private List<Integer> statusList;
// getter/setter...
}
这种设计保持了参数的简洁性,同时提供了足够的灵活性。
3.2 MyBatis动态SQL实现
在MyBatis的Mapper文件中,我们实现了如下的动态SQL:
xml复制<select id="selectList" resultType="com.example.DataVO">
SELECT data.id, data.title, data.status, ...
FROM t_list_data data
WHERE data.deleted_flag = 0
<!-- 多状态筛选(优先级高) -->
<if test="query.statusList != null and query.statusList.size() > 0">
AND data.status IN
<foreach collection="query.statusList" item="s"
open="(" separator="," close=")">
#{s}
</foreach>
</if>
<!-- 单状态筛选(向后兼容) -->
<if test="(query.statusList == null or query.statusList.size() == 0)
and query.status != null">
AND data.status = #{query.status}
</if>
ORDER BY data.create_time DESC
</select>
这里有几个关键点需要注意:
- 使用
<if>标签确保statusList的优先级高于status - 对statusList进行了非空和size>0的双重判断,避免生成无效SQL
- 使用
<foreach>标签优雅地处理列表参数
3.3 SQL生成示例
根据不同的参数组合,会生成不同的SQL语句:
- 当传入statusList=[2,3]时:
sql复制SELECT ... FROM t_list_data data
WHERE data.deleted_flag = 0
AND data.status IN (2, 3)
ORDER BY data.create_time DESC
LIMIT 0, 10;
- 当只传入status=4时(兼容旧调用):
sql复制SELECT ... FROM t_list_data data
WHERE data.deleted_flag = 0
AND data.status = 4
ORDER BY data.create_time DESC
LIMIT 0, 10;
4. 前端实现细节
4.1 Tab与状态映射配置
在Vue组件中,我们采用了配置化的方式管理Tab与状态的映射关系:
javascript复制const TAB_CONFIG = {
ongoing: {
label: '正在进行',
statusList: [2, 3] // 状态1 + 状态2
},
history: {
label: '历史数据',
statusList: [4] // 状态3
}
}
const currentTab = ref('ongoing')
async function fetchListData(page = 1) {
const params = {
page,
pageSize: 10,
statusList: TAB_CONFIG[currentTab.value].statusList
}
const res = await api.getListData(params)
// 处理响应数据
}
这种配置化的方式使得状态管理更加清晰,也便于后续维护。
4.2 列表项按钮逻辑实现
根据不同的状态和操作情况,我们实现了差异化的按钮展示逻辑:
html复制<view class="list-item" v-for="item in list" :key="item.id">
<view class="item-content">
{{ item.title }}
{{ getStatusText(item.status) }}
</view>
<view class="list-actions" v-if="item.status !== 2">
<!-- 状态2:根据是否已操作显示不同按钮 -->
<template v-if="item.status === 3">
<button v-if="!item.hasOperated" type="primary" @click="goOperate(item)">
去操作
</button>
<button v-else type="default" @click="viewDetail(item)">
查看详情
</button>
</template>
<!-- 状态3:只能查看 -->
<button v-if="item.status === 4" type="default" @click="viewDetail(item)">
查看结果
</button>
</view>
</view>
4.3 状态与按钮对照表
为了更清晰地理解按钮逻辑,我们整理了以下对照表:
| 状态 | status值 | hasOperated | 显示按钮 |
|---|---|---|---|
| 状态1 | 2 | - | 无按钮 |
| 状态2 | 3 | false | 去操作 |
| 状态2 | 3 | true | 查看详情 |
| 状态3 | 4 | - | 查看结果 |
5. 经验总结与优化建议
5.1 API扩展的最佳实践
在API扩展过程中,我们总结了以下原则:
✅ 新增参数,保留旧参数:确保不影响现有功能
✅ 新参数优先,旧参数兜底:明确参数优先级
✅ 不改变原有参数的语义:保持接口行为一致性
❌ 直接修改或删除旧参数:避免破坏性变更
❌ 改变旧参数的默认行为:防止意外影响
5.2 MyBatis多状态查询模式
我们提炼出了一个通用的MyBatis多状态查询模式:
xml复制<if test="query.statusList != null and query.statusList.size() > 0">
AND table.status IN
<foreach collection="query.statusList" item="item"
open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="(query.statusList == null or query.statusList.size() == 0)
and query.status != null">
AND table.status = #{query.status}
</if>
这个模式可以复用到其他需要多状态查询的场景中。
5.3 前端状态管理建议
- 配置化Tab映射:将Tab与状态的对应关系抽取为配置,便于维护
- 统一状态常量:前后端共用状态码定义,避免魔法数字
- 按钮逻辑解耦:将按钮显示规则封装为独立方法或组件
6. 扩展思考与优化方向
6.1 复杂状态组管理
当状态组合变得复杂时,可以考虑使用枚举统一管理状态组:
java复制public enum DataStatusGroup {
ONGOING(Arrays.asList(2, 3)), // 正在进行
HISTORY(Arrays.asList(4)), // 历史
ALL(Arrays.asList(2, 3, 4)); // 全部
private final List<Integer> statusList;
// 构造方法、getter等
}
这样前端只需传入状态组名称,后端自动展开为对应的状态列表,降低了联调成本。
6.2 性能优化建议
为了进一步提升查询性能,可以考虑以下优化措施:
-
添加数据库索引:为status字段创建索引加速查询
sql复制CREATE INDEX idx_status ON t_list_data(status); -
合理使用IN查询:对于固定且数量较少的状态值,IN查询通常比OR查询更高效
-
考虑缓存策略:对于不常变动的状态数据,可以适当引入缓存
在实际项目中,我们通过这种向后兼容的设计方案,既满足了业务需求,又保证了系统的稳定性。特别是在uni-app小程序开发中,这种设计思路可以很好地应对业务需求的快速变化。