Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

[fields] 记录选择器筛选面板:select 筛选项一律按字符串下发,lookup_filters 派生的数字/布尔选项筛不出记录 #3422

Closed

Description

@yinlianghui

症状

lookup「浏览全部记录」记录选择器的筛选面板里,select 筛选项被选中后,写进 $filter 的永远是字符串。当该筛选项的选项值不是字符串(数字、布尔)时,查询条件与库里存的值类型不符,筛选结果为空 —— 界面上表现为「选了一个明明有记录的选项,列表却空了」。

定位(origin/main,commit 789fe3e)

packages/fields/src/widgets/RecordPickerDialog.tsx:

  • :779 SelectItem value={String(opt.value)} —— Radix Select 的 value 必须是 string,所以选项值在渲染时被 stringify(这一步本身没问题);
  • :772 onValueChange={v = 回调拿到的就是那个 string,直接 handleFilterChange(col.field, v) 存进 filterValues,没有再映射回作者写的原值;
  • :206 filterValuesToRecord 的 select 分支 result[col.field] = v; 原样透传,于是 $filter 里是 "3" 而不是 3。

产生非字符串选项值的来源是筛选列的自动派生:

  • :452 effectiveFilterColumns 从 lookupFilters 派生时,in / notIn 的数组值被直接当成选项值:options: (f.value as any[]).map(v = → { label: String(v), value: v },v 的类型完全由作者写的 lookup_filters 决定;LookupFilterDef.value 在 packages/types/src/field-types.ts:443 声明为 unknown。

复现路径:lookup 字段声明 lookup_filters: [{ field: 'level', operator: 'in', value: [1, 2, 3] }] → 打开记录选择器 → 筛选面板出现 level 下拉(选项 1/2/3)→ 选 1 → 下发 $filter: { level: "1" },而记录里 level 是数字 1。

影响范围与优先级说明(请 triage 复核)

参考修法

表单侧同类问题已有先例:#3090 引入了 matchOptionValue / toControlValue(packages/components/src/renderers/form/),职责正是「控件说 string,payload 还作者写的类型」。筛选面板可以复用同一对语义(在 handleFilterChange 处按 col.options 反查原值),而不是在 filterValuesToRecord 里做类型猜测式的 coerce —— 后者会把 "1" 和 1 的区分权交给消费端猜。

顺带可一并核对:col.type === 'number' 分支已经显式 Number(raw),boolean 分支显式 Boolean(val),只有 select 分支没有回映射,三者行为不一致本身也是个信号。

发现来源

实现 #3336(PR #3421)时顺带发现,已按 Prime Directive #10 另开、不带 pm:queue、未指派。

Activity

  1. yinlianghui commented on Aug 6, 2026

    @yinlianghui
    ContributorAuthor

    分诊:bug + pm:queue(objectui 分片 PM,会话 session_01GTRjn8SM…heljwy 同 #3419,完整 ID:session_01GTRjn8xBqp75dk7kFupVRt)。

    判级理由:具名落点(RecordPickerDialog.tsx:772/:779/:206 + :452 派生)、有明确复现路径(lookup_filters in/notIn 的数字/布尔值 → $filter 字符串 → 筛选空列表)、用户可直接踩到。issue 对影响范围的自证已复核:spec SelectOptionSchema.value 为 string,故 #3336/PR #3421 新路径不受影响,此缺陷系 main 既有,非新引入——立单而非搭车修的判断正确。

    修法方向认同:复用 #3090 的 matchOptionValue/toControlValue 语义,在 handleFilterChange 处按 col.options 反查原值——控件说 string,payload 还作者写的类型;不在 filterValuesToRecord 做猜测式 coerce(那是把类型区分权交给消费端,AGENTS.md #0.1 禁止的形状)。

    ⚠️ 派发前置(step 3 已知坑记录):文件面与刚入队的 PR #3421 同文件(RecordPickerDialog.tsx),严格串行——须待 #3421 MERGED 后从新 main 切分支;届时 effectiveFilterColumns 已重构为 baseFilterColumns + schema options 注入两层,issue 引用的行号会漂移,以合并后代码为准。


    Generated by Claude Code

  2. self-assigned this
    on Aug 6, 2026
  3. yinlianghui commented on Aug 6, 2026

    @yinlianghui
    ContributorAuthor

    认领:objectui 分片 PM 并行批第 27 单
    会话:session_01GTRjn8xBqp75dk7kFupVRt
    分支:claude/issue-3422-picker-filter-value-map
    Worktree:objectui-issue-3422
    文件面:packages/fields/src/widgets/RecordPickerDialog.tsx + 该包测试 + changeset(#3090 的 matchOptionValue/toControlValue 若跨包只 import,不改 components;越界即停,报告说明)
    前置已解除:同文件 PR #3421 已 MERGED(main@5881a2cc4),从新 main 切分支,issue 引用行号以合并后代码为准。
    与同批第 26 单(#3419,packages/app-shell)文件面互不相交。


    Generated by Claude Code

  4. yinlianghui commented on Aug 6, 2026

    @yinlianghui
    ContributorAuthor

    验收:ACCEPT → PR #3428(并行批第 27 单,会话 session_01GTRjn8xBqp75dk7kFupVRt)。CI 绿后转 ready + 入队。

    复核要点:

    1. 控件边界回映射,与裁定一致:toControlValue/matchOptionValue 在 handleFilterChange 处按 col.options 反查原值,filterValuesToRecord 零改动——没有猜测式 coerce。复现用例断言带显式 typeof(松散断言在坏版本会误绿),数字/布尔两条修复前红。
    2. 复制而非复用的处置正确:fields 虽依赖 components,但 表单字段簇:spec↔runtime 是双层词汇——枢纽补缺、覆盖闸门、边界响亮化(objectstack#4115) #3090 的两个函数是模块私有(barrel 未导出、无深层子路径),为四行纯函数拓宽另一包公共 API 不划算;本地副本注明先例。若日后第三个消费者出现,再议提升为公共导出。
    3. 边界情况全部钉住:String() 撞车取首个匹配(tie-break 为确定性,非为歧义列表"能用");空串哨兵语义不变;schema 字符串选项原样往返;number/boolean 分支不动——三分支不一致按"把 select 抬到标准"方向消除。
    4. dev 披露的跨 worktree stash 污染已核:提交恰为 3 个围栏文件,plugin-detail 的瞬时触碰已 byte-identical 还原、未入 stage。危害本身另单 [process] git worktree 共享 refs/stash:并行 agent 之间 stash push/pop 会互相顶掉在途改动 #3430 处理,finding: check-changeset-presence counts only a package's own src/ subtree, so apps/console/index.html — shipped and executable — can change with no changeset required #5733 的在飞 dev 已收到 PM 警告。

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions