From c39c2761d485aa15bd5bc47114b2b2570f1ab995 Mon Sep 17 00:00:00 2001 From: Misaka_Company Date: Thu, 18 Jun 2026 09:21:03 +0800 Subject: [PATCH] Add design spec for row highlight and column-header filters - Row highlight: hover + click-to-pin via onRow/rowClassName, persists across horizontal scroll and filtering (keyed by rowKey=ID) - Column filters: antd controlled filterDropdown per column, type auto-inferred (date/category/text), multi-column AND; which columns are filterable is chosen by the user in the existing column-settings modal and persisted to localStorage - New module src/app/table-filters.tsx for FilterDropdown + type inference - 'Clear filters' button in the search bar - Long-content display explicitly deferred to a later iteration Co-Authored-By: Claude --- ...row-highlight-and-column-filters-design.md | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 docs/superpowers/specs/2026-06-18-row-highlight-and-column-filters-design.md diff --git a/docs/superpowers/specs/2026-06-18-row-highlight-and-column-filters-design.md b/docs/superpowers/specs/2026-06-18-row-highlight-and-column-filters-design.md new file mode 100644 index 0000000..43e1f45 --- /dev/null +++ b/docs/superpowers/specs/2026-06-18-row-highlight-and-column-filters-design.md @@ -0,0 +1,139 @@ +# 行高亮 与 表头列筛选 — 设计文档 + +- 日期:2026-06-18 +- 状态:已确认,待编写实现计划 +- 适用项目:`web-table`(生产数据查询工具,Next.js 16 + React 19 + Ant Design v6) + +## 背景与上下文 + +生产部使用「压力表合同生产数据」查询页(`src/app/page.tsx`)。数据按车间号模糊查询后一次性加载到客户端,表格约 57 列、很宽、横向滚动。现有功能:列显示/隐藏 + 拖拽排序(持久化)、模糊搜索、搜索历史、Excel 导出全部、展开行查看 5 个长文本字段。 + +本次按生产部需求新增两项功能;**「长内容完整显示」本次不做,留待后续单独开发**。 + +### 现状关键事实(实现依据) +- 表格为 antd ``,`rowKey="ID"`,每列 `ellipsis: true`,`sticky` 表头,`scroll={{ x: 总宽, y: calc(100vh-140px) }}`。 +- 数据接口 `GET /api/production-data?workshopNo=...` 返回 `{ columns, data, total }`;**日期字段已被序列化为 `YYYY-MM-DD` 字符串**。 +- `ColumnConfig`(可见性 + 顺序)持久化在 `localStorage` 的 `web-table-column-config`。 +- 「导出全部」走独立服务端接口 `GET /api/export-excel`,导出全量、与前端无关。 + +## 目标 + +1. **行高亮**:横向滚动时不会看错行。悬停轻高亮跟随鼠标;点击锁定更醒目高亮、滚动时保持,再点切换/取消。 +2. **表头列筛选**:在列标题挂筛选下拉,支持分类勾选、文本搜索、日期范围,多列 AND 组合。哪些列可筛选由用户在「列设置」里自行勾选。 + +## 非目标(本次不做) +- 长内容完整显示(换行/弹窗/tooltip/列宽自适应)。 +- 服务端筛选/分页(本次纯客户端,作用于已加载数据)。 +- 导出当前筛选结果(导出仍为全量)。 +- 筛选条件持久化(刷新即清;仅持久化"哪些列可筛选"这一配置)。 + +## 设计总览 + +两项功能均为 antd 原生能力 + 少量托管状态,纯前端,不新增接口、不改数据层。 + +- 行高亮:`onRow`(点击切换锁定)+ `rowClassName`(锁定行加 class)+ 一段 CSS。 +- 列筛选:antd 受控筛选(`filterDropdown` / `onFilter` / `filteredValue` / Table `onChange`),列的"可筛选"开关并入现有「列设置」弹窗。 + +--- + +## 功能一:行高亮(悬停 + 点击锁定) + +### 交互 +- 悬停:复用 antd 默认 hover 行高亮(用 CSS 略加深,与锁定色区分)。 +- 点击行:锁定该行为高亮;点击当前已锁定行 → 取消;点击其他行 → 切换到该行。任意时刻至多一行被锁定。 +- 横向滚动:锁定行整行(固定列 + 滚动列)保持高亮。 +- 与筛选/排序/列隐藏均独立:锁定基于 `rowKey`,class 施加于整行所有片段。 + +### 实现 +- 状态:`const [lockedRowKey, setLockedRowKey] = useState(null);` +- `
({ onClick: () => setLockedRowKey(prev => prev === record.ID ? null : String(record.ID)) })} />` +- `rowClassName={(record) => String(record.ID) === lockedRowKey ? "row-locked" : ""}` +- CSS(全局,建议放 `src/app/globals.css` 或等价处): + ```css + .row-locked > td { background: #fff7e6 !important; } /* 锁定:橙底 */ + /* antd 默认 hover 已存在;如需加深可加:.ant-table-tbody > tr:hover > td { background:#fafafa; } */ + ``` + > 实现时核对 antd v6 实际 DOM 结构(固定列与滚动列的 ``/`
` 层级),确保 `.row-locked` 选择器对左右片段都生效;必要时调整为作用于 row 容器的 class。 + +--- + +## 功能二:表头列筛选(用户勾选可筛选列) + +### 配置扩展 +`ColumnConfig` 增加字段: +```ts +interface ColumnConfig { + key: string; + visible: boolean; + filterable: boolean; // 新增,默认 false +} +``` +- 随现有 `web-table-column-config` 一起持久化。 +- 向前兼容:读取旧配置时缺 `filterable` 字段按 `false` 处理(`loadConfig` 兼容)。 + +### 「列设置」弹窗改动 +现有每行结构:`拖拽手柄 | 可见 Checkbox | 字段名`。新增**漏斗开关**表示「可筛选」: +- UI:在可见 Checkbox 旁加一个漏斗图标按钮(`FilterOutlined`),点击切换 `filterable`。 +- 与可见性、拖拽排序并列,共用同一份 `local` 配置,「确定」时一次性 apply。 +- 弹窗底部统计文案同步:「确定 (可见 X/总数)」可扩展为同时提示可筛选数量(可选)。 + +### 列构建(`buildTableColumns`) +对 `filterable && visible` 的列,挂上筛选相关属性;类型自动推断: + +```ts +type FilterType = "date" | "category" | "text"; +function inferFilterType(key: string, data: DataRow[]): FilterType { ... } +``` + +推断规则: +- **date**:非空值全部匹配 `/^\d{4}-\d{2}-\d{2}$/`。 +- **category**:去重后非空值数量 ≤ 50(阈值常量 `CATEGORY_THRESHOLD = 50`,可调)。 +- **text**:其余。 + +每列附带: +- `filterDropdown`: 根据 `FilterType` 渲染 + - date → `DatePicker.RangePicker`(含「确定/重置」)。 + - category → 带搜索框的勾选列表(distinct 值,`useMemo` 计算)。 + - text → 单行搜索输入框(子串匹配,不区分大小写)。 +- `onFilter(value, record)`:按类型判断命中。 +- `filteredValue`: 受控值(见下)。 + +> `FilterDropdown` 组件、`inferFilterType`、distinct 计算等统一放入 **`src/app/table-filters.tsx`**(已确认拆分)。 + +### 筛选状态管理(受控) +- `const [filters, setFilters] = useState>({});` +- 每个可筛选列:`filteredValue={filters[key] ?? null}`。 +- ` setFilters(tableFilters as Record)} />` +- 多列 = AND(antd 默认组合)。 +- distinct 列表与类型推断用 `useMemo` 依赖 `data`。 + +### 清除筛选 +- 搜索栏(`SearchBar`)旁新增 **「清除筛选」按钮**(`FilterOutlined` 或 `ClearOutlined`),`disabled` 于无任何活跃筛选时。 +- 点击:`setFilters({})`,并对每列传 `filteredValue={null}`(空对象即清空,antd 会复位各下拉)。 +- 同时在搜索栏显示「已筛选 X/共 Y 条」之类的提示(可选增强)。 + +--- + +## 边界与交互 + +- 可筛选列被隐藏:antd 仅对可见列应用筛选,自动忽略;重新显示后筛选值仍在。 +- 锁定行被筛选掉:该行不显示高亮,但 `lockedRowKey` 保留;清除筛选后该行重现并高亮。 +- 筛选只作用于已加载的客户端数据,**不发新请求**。 +- 「导出全部」始终导出全量,与前端筛选/高亮无关(保持现状)。 +- 重新查询(换车间号)或「清除」:重置 `filters`、`lockedRowKey`(新数据集,旧锁定无意义)。 +- 性能:客户端筛选,适合预期数据量(数百~数千行)。 + +## 文件结构 +- `src/app/table-filters.tsx`(新增):`FilterDropdown` 组件、`inferFilterType`、distinct 工具、阈值常量。 +- `src/app/page.tsx`(改动):扩展 `ColumnConfig`;`Home` 增 `lockedRowKey`、`filters` 状态与 `onChange`;`buildTableColumns` 接筛选属性;`SearchBar` 增「清除筛选」按钮;列设置弹窗加漏斗开关。 +- 全局 CSS:`.row-locked` 规则。 + +## 验证(手动) +1. 查询后点行 → 整行橙底高亮且横向滚动保持;再点该行 → 取消;点其他行 → 切换。 +2. 列设置勾选若干可筛选列 → 对应表头出现漏斗 → 分类勾选 / 文本搜索 / 日期范围分别生效,且多列 AND 组合;点「清除筛选」复位。 +3. 锁定某行 → 用筛选将其筛掉 → 高亮消失 → 清筛选 → 该行重新高亮。 +4. 重新查询或点「清除」→ 筛选与锁定均重置。 +5. 刷新页面 → 可筛选列配置仍在;活跃筛选值已清空。 + +## 实现注意事项 +- 项目为 **antd v6 + Next.js 16**,与训练数据可能不同。写代码前核对 `node_modules/antd`(`filterDropdown` 受控用法、`onRow`/`rowClassName`、`Table` `onChange` 签名、固定列 DOM 结构)与 `node_modules/next/dist/docs/`(按 `AGENTS.md` 要求),heed 弃用提示。