实现拖拽排序

如何在rails 中添加拖拽排序


准备

需要使用的gem

1
2
gem 'acts_as_list'
gem 'jquery-ui-rails'

需要的js文件

1
//= require jquery-ui/jquery-ui.js

页面添加想要排序的列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<table class="table table-bordered table-hover">
<thead>
<th>视频编号</th>
...
<th>操作</th>
</thead>
<tbody data-url="<%= sort_admin_round_manage_good_videos_path %>" data-behavior='sortable'>
<% @good_videos.each_with_index do |g, index| %>
<tr id="<%= dom_id(g) %>">
<td><%= g.id %></td>
...
<td><%= operate_buttons(good_video_operation(g)) %></td>
</tr>
<% end %>
</tbody>
</table>
<script>
$( "[data-behavior='sortable']" ).sortable({
update: function(e, ui) {
$.ajax({
url: $(this).data("url"),
type: "PATCH",
data: $(this).sortable('serialize')
})
}
});
</script>

第一种方式我们将排序后的序列整体发送到后台,后台需要添加响应的排序字段

1
2
rails g migration AddPositionToGoodVideo position:integer
rails db:migrate

控制器添加列表和排序action

1
2
3
4
5
6
7
8
9
10
11
12
def index
@q = GoodVideo.ransack(params[:q])
@good_videos = @q.result.order(position: :desc)
end
# 这里使用第一种排序方式(优点:如果position值为空或者0,在经过之后将会按排序赋值,缺点:每次排序根据当前页面数据量生成大量sql,大量操作不建议使用。)
def sort
params[:good_video].reverse.each_with_index do |id, index|
GoodVideo.find_by(id: id).update(position: index + 1)
end
head :ok
end

路由配置

1
2
3
4
5
resources :good_videos do
collection do
patch :sort
end
end

总结:第一种方法 这样既可实现拖拽排序(这种方法弊端:如果有分页的话第二页的排序会有问题,所以这种排序是不可以有分页的,所有数据只能在一个页面,也就限制了不适用太大的数据量和频繁的操作。)

第二种方法

页面基本保持一致 js 有一些变化,这里我们获取了当前拖拽元素id释放后的位置的前后元素的id

1
2
3
4
5
6
7
8
9
10
$( "[data-behavior='sortable']" ).sortable({
update: function(e, ui) {
params = "prev_id=" + $(ui.item[0]).prev().attr("id").split("_")[2] + "&id=" + $(ui.item[0]).attr("id").split("_")[2] + "&next_id=" + $(ui.item[0]).next().attr("id").split("_")[2];
$.ajax({
url: $(this).data("url"),
type: "PATCH",
data: params
})
}
});

排序方法也有相应修改并且position字段应采用decimal类型长度应尽量长一些,防止因长度不够,而造成多个position 都为 0.00的情况;对移动到第一位,或者最后一位做if处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
def sort
good_video = GoodVideo.find(params[:id])
next_task = params[:next_id] && GoodVideo.find(params[:next_id])
prev_task = params[:prev_id] && GoodVideo.find(params[:prev_id])
position = if params[:prev_id].blank?
next_task.position / 2
elsif params[:next_id].blank?
prev_task.position + 100000
else
(prev_task.position + next_task.position) / 2
end
good_video.update(position: position)
end

总结:第二种方法排序生成的sql更少,仅仅通过上一位置和下一位置来生当前选择元素的排序值。分页的话也不会有问题,不过对于很多初始值就是0.0的排序来将不会给赋值,要手动拖拽后才会生成排序值。