diff --git a/llms-full.txt b/llms-full.txt index 6e21fcd..7cb6ce5 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -2780,6 +2780,40 @@ form({ - **半截状态别提交**:`form` 的 `required` 依赖各自 `value` 信号,日期没选时提交会被拦下并定位到该字段;不要自己写 `if (!day()) return` 之类的旁路判断。 - 需要区间就用 `datePicker({ range: true })` + 两个 `timePicker`(起 / 止),拼装时各自独立,别指望一个信号同时表达两端。 +## 配方 14:标签筛选条 → 表格联动 + +```ts +import { computed, signal } from '@ui-arch/frame'; +import { button, dataTable, inputTag, select, space } from '@ui-arch/ui'; + +const tags = signal(['本地推理']); // 条件条:可增删的多个标签(「且」关系) +const city = signal('all'); // 地区:单值下拉,all 表示不限 +const rows = signal(listFromApi()); // 全量数据集(轮询 / 搜索时整体替换) + +// 派生行集合:信号一变就重算,直接喂给表格的信号数据源 +const matched = computed(() => + rows().filter((row) => + (city() === 'all' || row.city === city()) && + tags().every((need) => row.tags.includes(need)), // 多标签是「且」:逐条都要命中 + ), +); + +space({ gap: 'sm' }, [ + inputTag({ value: tags, max: 5, placeholder: '按标签筛选,回车添加' }), + select({ value: city, options: [{ value: 'all', label: '全部地区' }, { value: 'sh', label: '上海' }] }), + button({ label: '重置', variant: 'ghost', onClick: () => { tags([]); city('all'); } }), +]); + +const table = dataTable({ data: matched, columns, pageSize: 10 }); // 信号数据源:换源不重置排序与页码 +``` + +要点: +- **筛选在信号层做,不在表格里做**:`dataTable` 的工具栏筛选是「关键字单列」的轻量搜索,多条件组合交给 `computed` 派生行集合,表格只负责渲染当前结果。 +- **给表格传信号而不是数组**:`data: matched`(`Computed` 同样是 `Signal`)换源时内核保留排序、每页行数、当前页,并按 `rowKey` 剔除已消失行的选中态——手写 `replaceChildren(dataTable(...))` 会把这些状态全部丢掉。 +- **条件条本身就是一个受控信号集合**:`inputTag` 的 `value` 就是唯一数据源,删除 chip 与增删条件走同一出口,别在业务层再存一份「当前条件」。 +- **「且」还是「或」写在派生里**:`every` 是且(收窄)、`some` 是或(放大),两种语义只差一个方法调用,但决定了空条件时的行为——空数组 `every` 恒为真(等于不限),`some` 恒为假(等于无结果),重置逻辑据此写。 +- 结果为空时不要另写空状态:`dataTable` 已内置空行提示,筛选无命中会自己显示「没有匹配的行」。 + ## 通用约定 - 状态一律放信号(`signal` / `computed`),组件只读写信号,不持有业务数据副本。 diff --git a/llms.txt b/llms.txt index ca94114..832d089 100644 --- a/llms.txt +++ b/llms.txt @@ -168,3 +168,4 @@ - 配方 11:数据看板筛选区(区间滑块 + 日期预设 + 分组下拉)(3 条要点) - 配方 12:命令面板(⌘K 全局操作入口)(6 条要点) - 配方 13:预约时点(日期 + 时间两段拼装)(5 条要点) +- 配方 14:标签筛选条 → 表格联动(5 条要点) diff --git a/packages/tools/cli/recipes.md b/packages/tools/cli/recipes.md index e68a629..df2bed6 100644 --- a/packages/tools/cli/recipes.md +++ b/packages/tools/cli/recipes.md @@ -293,6 +293,40 @@ form({ - **半截状态别提交**:`form` 的 `required` 依赖各自 `value` 信号,日期没选时提交会被拦下并定位到该字段;不要自己写 `if (!day()) return` 之类的旁路判断。 - 需要区间就用 `datePicker({ range: true })` + 两个 `timePicker`(起 / 止),拼装时各自独立,别指望一个信号同时表达两端。 +## 配方 14:标签筛选条 → 表格联动 + +```ts +import { computed, signal } from '@ui-arch/frame'; +import { button, dataTable, inputTag, select, space } from '@ui-arch/ui'; + +const tags = signal(['本地推理']); // 条件条:可增删的多个标签(「且」关系) +const city = signal('all'); // 地区:单值下拉,all 表示不限 +const rows = signal(listFromApi()); // 全量数据集(轮询 / 搜索时整体替换) + +// 派生行集合:信号一变就重算,直接喂给表格的信号数据源 +const matched = computed(() => + rows().filter((row) => + (city() === 'all' || row.city === city()) && + tags().every((need) => row.tags.includes(need)), // 多标签是「且」:逐条都要命中 + ), +); + +space({ gap: 'sm' }, [ + inputTag({ value: tags, max: 5, placeholder: '按标签筛选,回车添加' }), + select({ value: city, options: [{ value: 'all', label: '全部地区' }, { value: 'sh', label: '上海' }] }), + button({ label: '重置', variant: 'ghost', onClick: () => { tags([]); city('all'); } }), +]); + +const table = dataTable({ data: matched, columns, pageSize: 10 }); // 信号数据源:换源不重置排序与页码 +``` + +要点: +- **筛选在信号层做,不在表格里做**:`dataTable` 的工具栏筛选是「关键字单列」的轻量搜索,多条件组合交给 `computed` 派生行集合,表格只负责渲染当前结果。 +- **给表格传信号而不是数组**:`data: matched`(`Computed` 同样是 `Signal`)换源时内核保留排序、每页行数、当前页,并按 `rowKey` 剔除已消失行的选中态——手写 `replaceChildren(dataTable(...))` 会把这些状态全部丢掉。 +- **条件条本身就是一个受控信号集合**:`inputTag` 的 `value` 就是唯一数据源,删除 chip 与增删条件走同一出口,别在业务层再存一份「当前条件」。 +- **「且」还是「或」写在派生里**:`every` 是且(收窄)、`some` 是或(放大),两种语义只差一个方法调用,但决定了空条件时的行为——空数组 `every` 恒为真(等于不限),`some` 恒为假(等于无结果),重置逻辑据此写。 +- 结果为空时不要另写空状态:`dataTable` 已内置空行提示,筛选无命中会自己显示「没有匹配的行」。 + ## 通用约定 - 状态一律放信号(`signal` / `computed`),组件只读写信号,不持有业务数据副本。