routes.eb
get 'areas/tree'
Area表的数据
{ id: 1, parent_id: nil, position: 1, name: "广东省", area_type: "District"},
{ id: 2, parent_id: 1, position: 1 , name: "广州市", area_type: "City" },
{ id: 3, parent_id: 1, position: 2 , name: "韶关市", area_type: "City" },
{ id: 4, parent_id: 1, position: 3 , name: "深圳市", area_type: "City" },
...
{ id: 23, parent_id: 2 , position: 1, name: "荔湾区", area_type: "District"},
{ id: 24, parent_id: 2 , position: 2, name: "越秀区", area_type: "District"},
{ id: 25, parent_id: 2 , position: 3, name: "海珠区", area_type: "District"},
....
app/controllers/areas_controller.rb
class AreasController < ApplicationController
def tree
all_areas = Area.all.map do |area|
area.attributes.symbolize_keys
end
parent_id = nil
areas = get_children(all_areas, parent_id)
render json: {
code: 0,
data: areas,
}
end
private
def get_children(areas, parent_id)
children = areas.
select { |area| area[:parent_id] == parent_id }.
sort_by! { |area| area[:position] }
return nil if children.empty?
children = children.map do |child|
grandchildren = get_children(areas, child[:id])
child = {
id: child[:id],
name: child[:name],
}
child[:children] = grandchildren unless grandchildren.nil?
child
end
children
end
end
app/models/area.rb
class Area < ActiveRecord::Base
belongs_to :parent, class_name: 'Area'
def ancestor_areas
return @ancestor_areas if @ancestor_areas
@ancestor_areas = []
area = parent
while area
@ancestor_areas << area
area = area.parent
end
@ancestor_areas
end
end
app/models/customer.rb
# 直接产品分类及其祖先分类
# 按分类的 level 由低到高排列
def areas
# if area
# ar = area.id
# @areas = []
# while ar
# @areas << Area.find(ar)
# ar = Area.find(ar).parent_id
# end
# end
# @areas.reverse unless @areas.blank?
if area
area.ancestor_areas.reverse << area
else
[]
end
end
app/views/customers/_form.html.erb
<div class="form-group customer_select" data-api="<%= areas_tree_path%>" >
<%= f.hidden_field :area_id, autocomplete: 'off' %>
<%
areas = @customer.areas.to_a
areas.fill(nil, areas.size...5)
%>
<% areas.each do |area|%>
<div class="form-group <%= 'hide' unless area %>" style="width:200px;top:3px;position:relative;">
<select class="form-control" <%== %Q{data-selected="#{area.id}"} if area %>>
<% if area %>
<option value="<%= area.id %>"><%= area.name %></option>
<% else %>
<option value="">请选择</option>
<% end %>
</select>
</div>
<% end %>
</div>
area.js
<script>
(function(){
var container = $('.customer_select');
// 禁止修改分类
if (container.attr('data-disabled')) {
container.find('select').prop('disabled', true);
return;
}
var input = container.find('input')[0];
container.find('select').prop('disabled', false);
// 转为 id => [{id: 1, name: ''}],便于使用
var idToChildren = {};
var setChildren = function(areas){
var i$, len$, customer;
for (i$ = 0, len$ = areas.length; i$ < len$; ++i$) {
customer = areas[i$];
if (customer.children && customer.children.length) {
idToChildren[customer.id] = customer.children;
setChildren(customer.children);
}
}
};
// 初始化
var initializeSelector = function(areas){
setChildren(areas);
// 重置,避免页面刷新后与选择器状态不一致
input.value = container.find('select[data-selected]:last').val();
updateOptions(container.find('select:first'), areas);
};
// 下一级分类的 select
var nextSelect = function(select){
return $(select).closest('.form-group').next('.form-group').find('select');
};
// 隐藏后面的选择框
var hideFollowingSelect = function(select){
$(select).closest('.form-group').nextAll('.form-group').addClass('hide').find('select').prop('disabled', true);
};
// 更新选项
var updateOptions = function(select, areas){
select.closest('.form-group').removeClass('hide');
select.prop('disabled', false);
var html = '<option value="">请选择</option>';
var i, len, customer;
for (i = 0, len = areas.length; i < len; ++i){
customer = areas[i];
html += '<option value="' + customer.id + '">' + customer.name + '</option>';
}
select.html(html);
var id = select.attr('data-selected');
if (id) {
select.val(id);
select.trigger('change');
select.removeAttr('data-selected');
}
else {
select.val('');
hideFollowingSelect(select);
}
};
// 选项改变时,保存选中 id,并更新后代选项
container.on('change', 'select', function(){
var id = this.value;
// 保存选中值
if (id) {
input.value = id;
}
// 选中且有子分类时,更新后代节点
if (id && idToChildren[id]) {
updateOptions(nextSelect(this), idToChildren[id]);
}
// 否则隐藏后代节点
else {
hideFollowingSelect(this);
}
});
// 获取分类数据,初始化
$.get(container.attr('data-api'), null, function(data){
if (data && data.code === 0) {
initializeSelector(data.data);
}
else {
flashPrompt('获取产品分类信息失败');
}
});
})();
</script>
app/views/customers/show.html.erb
<% areas = @customer.areas[0..-1].to_a %>
<% areas.each do |area|%>
<div class="form-group" style="width:200px;top:3px;position:relative;">
<input type="text" class="form-control" disabled="" value="<%= area.name %>">
</div>
<% end %>