必须操作table.cache而非直接移动tr元素——因layui渲染完全依赖cache数据,dom改动会被reload覆盖;需用三步法交换数组元素、校验边界、parseint(data-index),并同步更新服务端排序字段。
必须操作 table.cache,不能直接移动 tr 元素
直接用 $(tr).insertbefore() 或 $(tr).after() 移动 dom,表面看行位置变了,但下一次 table.reload() 会立刻覆盖——因为 layui 渲染完全依赖 table.cache 里的数据,dom 只是快照。你挪的 tr 不在数据源里,自然被丢弃。
更严重的是:data-index 不变,多选状态清空,滚动位置重置,排序字段失效,分页后数据错位。所有这些都不是 bug,而是设计逻辑必然结果。
-
table.cache['your-id']是当前页已过滤、已分页的数据副本,才是唯一可信的数据源 - 别用初始化时传入的原始数组(比如
data: myData),Layui 内部已做深拷贝和处理 - 如果开了服务端分页,
table.cache仅含当前页数据;跨页移动需先调table.getData()拿全量
怎么安全交换数组元素:上移 / 下移的边界与写法
拿到点击行的 data-index(字符串),转成数字后判断边界,再交换 table.cache 数组中对应下标的两个对象。关键不是“移动”,而是“交换”。
- 上移:交换索引
i和i-1,前提是i > 0 - 下移:交换索引
i和i+1,前提是i - 绝对不要用
splice链式调用,例如arr.splice(i,1,arr.splice(i-1,1,arr[i-1]))—— 返回值类型混乱,极易出错 - 坚持三步法:
let tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp;
示例(上移):
const cache = table.cache['testReload'];
const idx = parseInt(obj.tr.attr('data-index'));
if (idx > 0) {
const tmp = cache[idx];
cache[idx] = cache[idx - 1];
cache[idx - 1] = tmp;
}
监听按钮和获取当前行索引的正确姿势
按钮放在列配置里,用 toolbar 模板,别靠 $(this).closest('tr').index() —— 表格有固定表头、合计行、分页栏时,index() 返回的是整个 tbody 的物理位置,和视觉顺序完全不一致。
- 列配置写法:
{"title":"操作","toolbar":"#moveBar"} - 事件监听:
table.on('tool(your-id)', function(obj){}) - 取索引:
obj.tr.attr('data-index')(字符串,必须parseInt()) - 这个索引只对当前页有效;全局唯一标识建议在原始数据里加
id字段,避免搜索/分页后错位
reload() 后状态丢失怎么办
table.reload() 是全量重绘,UI 状态全部清空。这不是缺陷,是 Layui 的设计选择。你需要手动补救,且必须在 done 回调里做,确保 DOM 已更新。
- 多选状态:
reload前存table.checkStatus('id').data,done里遍历调table.checkRow()或给对应数据项加checked: true - 滚动位置:存
$('.layui-table-box').scrollTop(),done里用setTimeout(() => { ... }, 0)再设回去 - 单行高亮:不能只靠 class,得在
done里查tr[data-index="X"]并重新加 class - 表格超 500 行时,频繁
reload卡顿,应改用table.cache手动 patch 对应tr和td,避免整表重绘
最常被忽略的一点:前端重排只是临时态。如果你没同步更新服务端的排序字段(比如数据库里的 sort_order),下次刷新或换页,顺序就回去了。











