返回博客

下拉选「全部」选了等于没选——Vendor 组件 Falsy 检查吞掉空字符串

某后台管理系统的列表页有一个状态筛选下拉框:正常 / 已关闭 / 待审核。产品要求加一个「全部」选项,点了之后取消状态过滤、展示所有记录。我照着其他字段的做法,给「全部」加了 value: "",上线后用户反馈:选了「全部」,列表还是只显示「正常」的数据,跟没动一样。

现象

  • 选「正常」→ 正确只显示 status=1 的记录
  • 选「已关闭」→ 正确只显示 status=0 的记录
  • 选「全部」→ 依然只显示 status=1,和默认状态一模一样

网络请求里看 payload,选「全部」时根本没有 status 字段——value: "" 选项被静默丢弃了。

根因

问题出在封装好的表格组件内部。该组件有一个 makeData() 方法,负责把当前的筛选变量打包成请求参数:

// 组件内部 src/xtcomponents/XtTablePage/index.vue ~line 713
makeData() {
  var da = { pagesize: this.pagesize, currpage: this.currpage };
  for (var s in this.vars) {
    if (this.vars[s]) {       // ❌ falsy 检查——'' / 0 / false / null 全部被过滤掉
      da[s] = this.vars[s];
    }
  }
  return da;
}

if (this.vars[s]) 是一个 JS falsy 检查。"" 是 falsy,所以用户选了「全部」之后,status: "" 这一项直接被过滤掉,根本不会出现在 makeData() 返回的对象里。

组件随后 $emit('on-change', val) 把这个残缺的对象传给父组件。父组件的处理通常是这样:

getData(val) {
  this.pageOpitons = { ...this.searchForm, ...val };
  // searchForm 里有 status: '1' 作为默认值
  // 因为 val 里没有 status,展开后默认值就复活了
}

两段代码一叠加,结果就是:选「全部」= 用 searchForm 默认值覆盖,最终请求还是带着 status=1

坑:value:'' 在 vendor 组件里是隐形的

把「全部 / 重置」选项的 value 设为 "" 是最直觉的做法,但只要 vendor 组件内部用 if (value) 做参数过滤,"" 就会被静默丢弃——组件不报错、父组件不报错、网络请求也不报错,只有用户会发现「全部」没效果。更隐蔽的是,父组件的 spread 默认值会悄悄补位,让结果看起来像是「全部」等于「默认筛选条件」,完全没有错误提示可循。

正解

不要试图修改 vendor 组件源码(牵一发动全身,其他依赖 falsy 过滤的列可能因此崩掉),而是绕路:用一个 truthy 哨兵值代替空字符串,在父组件层面拦截并删除它

第一步:selectlist 用 truthy 哨兵

selectlist: [
  { name: "全部",   value: "all" },   // ✅ "all" 是 truthy,能通过 makeData 的检查
  { name: "已关闭", value: "0" },
  { name: "正常",   value: "1" },
  { name: "待审核", value: "3" }
]

注意 value: "0" 同样是 falsy——如果你的选项里有 0,也要确认它能顺利通过。这里 "0" 是字符串,是 truthy,没问题;但 0(数字)就不行了。

第二步:父组件拦截并删除哨兵

getData(val) {
  const merged = { ...this.searchForm, ...val };
  if (merged.status === 'all') {
    delete merged.status;   // 后端收到无 status 字段 = 不过滤
  }
  this.pageOpitons = merged;
}

第三步:后端按「字段缺席」判断是否过滤

if (isset($data['status']) && $data['status'] !== '') {
    $where .= " AND s.status = :status";
}
// 注意:不要加 else { status = '1' },那会是一次回归
正解:哨兵值 + 父组件 delete,绕过 vendor falsy 过滤

用一个有意义的 truthy 字符串(如 "all")作为「全部」的 value,让它能顺利通过 vendor 组件的 falsy 过滤;在父组件拿到数据后,检测到哨兵值就 delete 掉对应 key,后端按字段是否存在判断是否施加过滤条件。整个链路不碰 vendor 源码,改动范围最小,副作用可控。

一句话外卖

Vendor 组件只要有 if (value) 的参数过滤,value:"" 的重置选项就是隐形的——不要用空字符串做哨兵,改用 truthy 占位符并在业务层 delete。