---
description: "工具栏塞不下的筛选就归它：一栏控件——文本搜索、下拉、复选组、数值范围——组合成一个 { search, filters } 对象驱动结果列表。筛选维度一多、条件要组合、链接要可分享，就是它"
---

# 筛选面板（高级筛选 / 组合搜索）

工具栏塞不下的筛选就归它：一栏控件——文本搜索、下拉、复选组、数值范围——组合成一个 `{ search, filters }` 对象驱动结果列表。筛选维度一多、条件要组合、链接要可分享，就是它。

<iframe src="/skills/dashboard/assets/demo-filter-panel.html"
        style="width:100%;height:660px;border:1px solid #8884;border-radius:10px"
        loading="lazy" title="形状 demo：筛选面板"></iframe>

试这几下：

- 随便改控件 —— 结果**不动**，「应用」按钮亮起并预告条数；点它（或搜索框回车）才提交
- 应用后活跃筛选变成结果区上方的可删 chip，点 × 单删即时生效；「清除全部」一键归零
- 勾一个分类、再把价格下限抬到 500 —— 结果归零，出现空态
- 盯着面板底部的 ListParams JSON —— 每个控件怎么落进 `{ search, filters }` 一目了然

## 规矩

- **多控件一个形状**：所有控件归并进 `ListParams` 的 `{ search, filters }`——下拉是单值、复选组是数组、范围拆成 `priceMin`/`priceMax` 两键。demo 在内存里过滤，真实版把同一份参数交给 `Repository.list()`（服务端过滤）
- **筛选状态进 URL**：真实版用 `validateSearch` 把 filters 放进 URL，链接可分享、后退可回溯；适配器按白名单映射筛选键（`filterColumns`），未知键直接丢弃。demo 是内存态，省掉了这一层
- **「清除全部」和结果计数是标配**：计数实时跟随，空结果给空态；活跃筛选必须可见（chip）且可单个撤销
- 待应用态是取舍：筛选便宜（内存、快查询）就即改即生效；昂贵（慢查询、按次计费）就像 demo 这样攒草稿、点「应用」一次提交

> 蓝本：`add-filter-panel.md`[^add-filter-panel]（open-dashboard @ `aa9815f`，MIT，Invariants 已消化进上面「规矩」）

??? abstract "demo 源码：`assets/demo-filter-panel.html`（自包含、未压缩）"

    ```html
    --8<-- "skills/dashboard/assets/demo-filter-panel.html"
    ```

[^add-filter-panel]: [`add-filter-panel.md`](https://github.com/ahpxex/open-dashboard/blob/aa9815f426e9520841c4aa15a341bfd9f866091f/.claude/skills/add-component/references/add-filter-panel.md)，2026-07-28 读。
