技术干货:Layui 表格中按行禁用复选框的正确实现方法
在日常开发中,你是否需要禁止用户勾选表格中的特定数据行?例如,已关闭的订单、过期的活动或仅有只读权限的记录。虽然看起来简单,但如果你使用 Layui 的 table 模块,内置的 {type: 'checkbox'} 列并不会支持按行禁用。你可能会尝试在数据中添加 disabled: true 字段,但 Layui 对此完全不识别。
那么,正确的解决方案是什么?答案是自定义模板(templet)。移除自动生成的 checkbox 列,改用普通列并通过模板手动创建 ,然后根据行数据(如 d.status === 'closed')动态插入 disabled 属性。这样你就能实现精确的、按行生效的禁用控制。

以下几点细节务必注意:
name="layTableCheckbox"必须保留——它是 Layui 内部识别多选行的硬性约定。缺少该属性,table.checkStatus()将无法获取当前行的数据。- 仅设置
disabled不够,还需同步处理checked状态。虽然字段d.layChecked存在,但它不会自动映射到 DOM 上,因此必须根据数据显式渲染checked属性。 - 禁用逻辑只能依赖当前行数据
d。不要依赖外部变量或异步结果,模板渲染时只认行数据本身。
示例模板一目了然:
一个常被忽略的坑:table.checkStatus() 不会过滤禁用行
你以为加上 disabled 属性后,Layui 会自动跳过这些行?并不会。table.checkStatus('demo').data 依然会包含那些被禁用但曾被勾选过的行。得到的“已勾选数据”其实是掺杂的。
正确的做法是:先获取表格缓存,再手动过滤。
const cache = table.cache['demo'];
const validChecked = cache.filter(d => d.status !== 'closed' && d.LAY_CHECKED);
注意字段名是 LAY_CHECKED(全大写),不是 layChecked。这是 Layui 内部状态字段,其值可能与模板中的同名属性不一致。
另一个容易踩坑的点是“全选判断”:
- 不要用
res.count比对选中数量 - 正确的逻辑是:
validChecked.length === cache.filter(d => d.status !== 'closed').length
如果长度匹配,才能认为“所有可选中行都被勾选了”。否则,禁用行会干扰全选统计。
拦截 checkbox 事件:没有“一劳永逸”,只有“主动防御”
添加 disabled 属性只是 UI 层面的视觉遮蔽,用户在全选、批量操作时依然可能触发事件。例如点击全选时,禁用行的 checkbox DOM 不会变化,但 table.on('checkbox(test)') 仍然会触发。这会导致后续的批量操作或导出逻辑,把不该选中的行也纳入计算。
解决办法是在事件回调开头进行拦截。标准写法:
table.on('checkbox(test)', function(obj){
if (obj.disabled) return;
// 后续的业务逻辑
});
如此处理,即使全选、反选触发事件,禁用行也不会参与任何计算。否则,数据污染只是时间问题。
最容易被忽视的本质问题:disabled 只是 UI 层的“遮羞布”
Layui 的底层状态管理(包括 LAY_CHECKED 字段、全选统计、父子联动机制)对 disabled 属性完全不感知。换句话说,你虽然在界面上让用户无法点击该复选框,但 Layui 内部该记录的数据、该触发的统计,一个都不会少。所有涉及“已选中”的逻辑——无论是提取 ID 列表、判断全选状态、还是提交批量数据——都需要你亲自补一层数据过滤,没有任何例外。
另外,提醒一下:不要尝试用 CSS 的 pointer-events: none 替代 disabled。那只是禁止鼠标点击,键盘的 Space 键依然可以切换状态,而且无障碍支持会完全失效——对用户来说就是“看不见的坑”。
checkRender 方法(仅在 Layui ≥2.8.0 中可用)也别指望能帮你省事,它的返回值不会参与 table.checkStatus() 的识别,过滤逻辑还是得自己写。
最终结论很简单:想要在 Layui 表格里实现“按行禁用勾选”,必须过自己的手、补自己的过滤、写自己的判断。没有捷径,也没有黑魔法。
