Yii2本身不内置多列表拖拽功能,但可集成SortableJS实现跨列表拖拽排序:1.视图中引入SortableJS并配置相同group;2.数据库增加list_id和sort_order字段;3.控制器提供安全的AJAX更新接口;4.可选增强拖拽样式与失败回滚。

Yii2 本身不内置多列表拖拽功能,但可以轻松集成第三方拖拽库(如 SortableJS)实现跨列表拖拽排序,配合 Yii2 的 ActiveRecord 和 AJAX 路由完成数据持久化。
1. 引入 SortableJS 并初始化多列表拖拽
在视图文件(如 index.php)中引入 SortableJS,并为多个列表容器启用 group 配置,确保它们属于同一拖拽组:
<!-- 多个列表容器,class 名统一为 'sortable-list' -->
<div id="list-a" class="sortable-list" data-list-id="a">
<div class="item" data-id="1">任务 A1</div>
<div class="item" data-id="3">任务 A2</div>
</div>
<p></p><div id="list-b" class="sortable-list" data-list-id="b">
<div class="item" data-id="2">任务 B1</div>
<div class="item" data-id="4">任务 B2</div>
</div><p><script src="<a%20href=" https:>https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>>
<script>
new Sortable(document.querySelectorAll('.sortable-list'), {
group: 'shared-lists', // 关键:同 group 才能跨容器拖拽
sort: true,
animation: 150,
onEnd: function (evt) {
const oldList = evt.from.getAttribute('data-list-id');
const newList = evt.to.getAttribute('data-list-id');
const itemId = evt.item.getAttribute('data-id');
const newIndex = Array.from(evt.to.children).indexOf(evt.item);</script></p><pre class="brush:php;toolbar:false;"> // 发送 AJAX 更新顺序和所属列表
fetch('/task/update-order', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
id: itemId,
list_id: newList,
sort_order: newIndex + 1,
_csrf: yii.getCsrfToken() // Yii2 自动注入的 CSRF token
})
});
}
});
2. 定义支持多列表的数据库结构
确保模型表包含区分列表归属和排序位置的字段,例如:
- list_id:标识所属列表(如 'todo', 'doing', 'done' 或自定义字符串)
- sort_order:整型,记录该列表内的显示顺序(建议用整数而非浮点数,便于维护)
- created_at / updated_at:可选,用于审计
对应 ActiveRecord 模型(如 Task)需在 rules() 中允许批量赋值:['list_id', 'sort_order'],并开启场景或使用 load() + save() 安全更新。
3. 实现 AJAX 更新接口(Controller Action)
在控制器中添加安全的更新动作,校验权限、参数与数据存在性:
public function actionUpdateOrder()
{
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
<pre class="brush:php;toolbar:false;">$request = \Yii::$app->request;
if (!$request->isPost || !$request->post()) {
return ['success' => false, 'msg' => '请求方法错误'];
}
$id = (int)$request->post('id');
$listId = trim($request->post('list_id'));
$sortOrder = (int)$request->post('sort_order');
$model = Task::findOne($id);
if (!$model) {
return ['success' => false, 'msg' => '任务不存在'];
}
// 可选:检查用户是否有权操作该任务
if ($model->created_by !== \Yii::$app->user->id) {
return ['success' => false, 'msg' => '无权修改'];
}
$model->list_id = $listId;
$model->sort_order = $sortOrder;
if ($model->save(false)) { // false 表示跳过验证(若仅更新这两个字段且已校验)
return ['success' => true];
}
return ['success' => false, 'msg' => '保存失败:' . implode(', ', $model->getErrorSummary(true))];
}
4. 前端增强体验(可选)
- 拖拽时添加
ghost-class或chosen-class样式,提升视觉反馈 - 禁用拖拽过程中原列表项的 pointer-events,避免误触
- AJAX 失败时 revert 拖拽操作(Sortable 提供
onMove+evt.cancel()支持) - 后端返回新顺序后,前端可局部刷新所有列表的
sort_order文本,避免页面重载











