partner: customer list && manager group manage

issues25489
p31729568 5 years ago
parent 5b9688669e
commit ab7ea6a077

@ -3,13 +3,24 @@
//= require jquery3
//= require popper
//= require bootstrap-sprockets
//= require jquery.validate.min
//= require additional-methods.min
//= require bootstrap-notify
//= require select2
//= require common
//= require echarts
//= require ./i18n/jquery-validate-message-zh
//= require ./i18n/select2-i18n.zh-CN
//= require_tree ./colleges
Turbolinks.setProgressBarDelay(200);
// ******** select2 global config ********
$.fn.select2.defaults.set('theme', 'bootstrap4');
$.fn.select2.defaults.set('language', 'zh-CN');
$(document).on('turbolinks:load', function() {
$('[data-toggle="tooltip"]').tooltip();
$('[data-toggle="popover"]').popover();

@ -0,0 +1,28 @@
$(document).on('turbolinks:load', function() {
if ($('body.partners-customers-page').length > 0) {
var $customerContainer = $('.customer-list-container');
var partnerId = $customerContainer.find('.customer-list-body').data('id');
$customerContainer.on('change', '.manager-group-select', function(){
console.log('manager-group-select change', $(this).val());
var $select = $(this);
var customerId = $select.data('id');
var managerGroupId = $select.val();
$.ajax({
url: '/partners/' + partnerId + '/customer_manager_group.json',
method: 'POST',
dataType: 'json',
data: { customer_id: customerId, manager_group_id: managerGroupId },
success: function(){
showSuccessFlash();
$select.data('last', managerGroupId);
},
error: function(res){
showErrorNotify(res.responseJSON.message);
$select.val($select.data('last'));
}
})
})
}
});

@ -0,0 +1,123 @@
$(document).on('turbolinks:load', function() {
if ($('body.partners-partner-manager-groups-page').length > 0) {
var $container = $('.manager-group-list-container');
var partnerId = $container.find('.manager-group-list-body').data('id');
// ------- 新建编辑权限组弹窗 --------
var $managerGroupModal = $('.modal.partner-save-manager-group-modal');
var $managerGroupForm = $managerGroupModal.find('form.partner-save-manager-group-form');
var $managerGroupIdInput = $managerGroupForm.find('input[name="manager_group_id"]');
var $managerGroupNameInput = $managerGroupForm.find('input[name="manager_group_name"]');
$managerGroupForm.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
manager_group_name: {
required: true,
maxlength: 20
},
}
});
$managerGroupModal.on('show.bs.modal', function(event){
var $link = $(event.relatedTarget);
var managerGroupId = $link.data('id');
var managerGroupName = $link.data('name');
if(managerGroupId && managerGroupId !== ''){
$managerGroupIdInput.val(managerGroupId);
$managerGroupNameInput.val(managerGroupName)
} else {
$managerGroupIdInput.val('');
$managerGroupNameInput.val('');
}
});
$managerGroupModal.on('hide.bs.modal', function(){
$managerGroupIdInput.val('');
$managerGroupNameInput.val('');
});
$managerGroupModal.on('click', '.submit-btn', function(){
$managerGroupForm.find('.error').html('');
var url = $managerGroupForm.data('url');
if ($managerGroupForm.valid()) {
$.ajax({
method: 'POST',
dataType: 'script',
url: url,
data: $managerGroupForm.serialize()
});
}
});
// ---------- 添加管理员弹窗 ------------
var $partnerManagerModal = $('.modal.partner-add-partner-manager-modal');
var $partnerManagerForm = $partnerManagerModal.find('form.partner-add-partner-manager-form');
var $managerGroupIdInput = $partnerManagerForm.find('input[name="manager_group_id"]');
var $userSelect = $partnerManagerForm.find('.partner-manager-select');
$userSelect.select2({
theme: 'bootstrap4',
placeholder: '请输入要添加的管理员姓名',
multiple: true,
closeOnSelect: false,
minimumInputLength: 1,
ajax: {
delay: 500,
url: '/api/users_for_partners',
dataType: 'json',
data: function(params){
return { name: params.term, partner_id: partnerId, page: params.page || 1, per_page: 20 };
},
processResults: function(data, params){
params.page = params.page || 1;
return {
results: data.users,
pagination: {
more: (params.page * 20) < data.count
}
};
}
},
templateResult: function (item) {
if(!item.id || item.id === '') return item.text;
return $("<span>" + item.real_name + " <span class='font-12'>" + item.school_name + ' ' + item.identity + "</span></span>");
},
templateSelection: function(item){
if (item.id) {
}
return item.real_name || item.text;
}
});
$partnerManagerModal.on('show.bs.modal', function(event){
var $link = $(event.relatedTarget);
var managerGroupId = $link.data('id');
$managerGroupIdInput.val(managerGroupId);
$userSelect.select2('val', ' ');
$partnerManagerModal.find('.error').html('');
});
$partnerManagerModal.on('click', '.submit-btn', function(){
$partnerManagerModal.find('.error').html('');
var managerGroupId = $managerGroupIdInput.val();
var userIds = $userSelect.val();
if (userIds && userIds.length > 0) {
$.ajax({
method: 'POST',
dataType: 'script',
url: '/partners/' + partnerId + '/partner_managers',
data: { user_ids: userIds, manager_group_id: managerGroupId }
});
} else {
$partnerManagerModal.modal('hide');
}
});
}
});

@ -88,6 +88,14 @@ function show_success_flash(message){
});
}
function showSuccessFlash(message){
$.notify({
message: message || '操作成功'
},{
type: 'success'
});
}
function showErrorNotify(message){
$.notify({
message: message || '操作失败'

@ -1,6 +1,8 @@
@import "bootstrap";
@import "font-awesome-sprockets";
@import "font-awesome";
@import "select2.min";
@import "select2-bootstrap4.min";
@import "common";
@ -11,3 +13,33 @@
color: rgba(255, 255, 255, 1);
font-size: 16px;
}
.box {
padding: 20px;
border-radius: 5px;
background: #fff;
}
.custom-nav {
padding: 0 1rem;
display: flex;
border-bottom: 1px solid #EBEBEB;
&-item {
padding: 0 0.5rem;
}
&-link {
display: block;
margin-bottom: 2px;
padding: 0.8rem 0.5rem;
color: #495057;
font-size: 16px;
&.active {
margin-bottom: 0px;
color: #007bff;
border-bottom: 2px solid #007bff;
}
}
}

@ -0,0 +1,132 @@
.college-body-container {
padding: 20px;
flex: 1;
min-height: 100vh;
display: flex;
flex-direction: column;
overflow-y: scroll;
& > .content {
flex: 1;
font-size: 14px;
.box {
padding: 20px;
border-radius: 5px;
background: #fff;
}
}
/* 面包屑 */
.breadcrumb {
padding-left: 5px;
font-size: 20px;
background: unset;
}
/* 内容表格 */
table {
table-layout: fixed;
td {
vertical-align: middle;
}
tr {
&.no-data {
&:hover {
color: darkgrey;
background: unset;
}
& > td {
text-align: center;
height: 300px;
}
}
}
}
.image-preview-container {
display: flex;
flex-direction: column;
align-items: center;
}
.action-container {
& > .action {
padding: 0 3px;
}
.more-action-dropdown {
.dropdown-item {
font-size: 14px;
}
}
}
/* 分页 */
.paginate-container {
margin-top: 20px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.paginate-total {
margin-bottom: 10px;
color: darkgrey;
}
.pagination {
margin-bottom: 0px;
}
}
/* 搜索表单 */
.search-form-container {
display: flex;
margin-bottom: 20px;
.search-form {
flex: 1;
* { font-size: 14px; }
select, input {
margin-right: 10px;
font-size: 14px;
}
}
}
.global-error {
color: grey;
min-height: 300px;
&-code {
font-size: 80px;
}
&-text {
font-size: 24px;
}
}
.nav-tabs {
.nav-link {
padding: 0.5rem 2rem;
}
}
.CodeMirror {
border: 1px solid #ced4da;
}
.batch-action-container {
margin-bottom: -15px;
padding: 10px 20px 0;
background: #fff;
}
}

@ -0,0 +1,5 @@
.partners-customers-page {
.customer-list-body {
min-height: 300px;
}
}

@ -0,0 +1,104 @@
.partners-partner-manager-groups-page {
.customer-list-form {
padding: 10px 20px;
align-items: center;
}
.manager-group-item {
margin-bottom: 20px;
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
&-left {
flex: 1;
}
&-right {
.action {
}
}
}
}
.partner-manager {
&-body {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
}
&-item {
padding: 5px 15px;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
.remove-partner-manager-action {
display: none;
position: absolute;
z-index: 100;
right: 10px;
top: 0;
font-size: 24px;
& > i {
color: #dc3545;
}
}
&:hover {
.remove-partner-manager-action {
display: block;
}
}
&-avatar {
cursor: pointer;
width: 80px;
height: 80px;
overflow: hidden;
border-radius: 50%;
position: relative;
& > img {
width: 80px;
height: 80px;
}
}
&.add-partner-manager-item {
.partner-manager-item-avatar {
background: #E4E4E4;
&:hover {
background: #D0D0D0;
}
&::before {
content: '';
position: absolute;
top: 39px;
left: 20px;
width: 40px;
height: 2px;
background: #fff;
}
&::after {
content: '';
position: absolute;
top: 20px;
left: 39px;
width: 2px;
height: 40px;
background: #fff;
}
}
}
}
}
}

@ -1,5 +1,7 @@
.colleges-statistics-page {
.college-body-container {
padding: 0;
.statistic-header {
width: 100%;
height: 240px;

@ -1,5 +1,5 @@
class CollegesController < ApplicationController
include Admins::PaginateHelper
include PaginateHelper
layout 'college'
@ -154,7 +154,7 @@ class CollegesController < ApplicationController
return true if current_user.admin_or_business? # 超级管理员|运营
return true if current_college.is_a?(Department) && current_college.member?(current_user) # 部门管理员
return true if current_user.is_teacher? && current_user.school_id == current_school.id # 学校老师
return true if current_school.customer_id && current_user.partner&.partner_customers&.exists?(customer_id: current_school.customer_id)
return true if current_school.customers.exists? && current_user.partner&.partner_customers&.exists?(customer_id: current_school.customer_id)
false
end

@ -0,0 +1,133 @@
class PartnersController < ApplicationController
include Base::PaginateHelper
include Admins::RenderHelper
layout 'college'
before_action :require_login, :check_partner_present!, :check_permission!
before_action :check_admin_manager_group_permission!, except: [:customers]
helper_method :current_partner, :manager_permission?
def customers
customers = CustomerQuery.call(current_partner, current_user, params)
@customers = paginate(customers.includes(:school))
load_customer_extra_statistic_data
end
def partner_manager_groups
@manager_groups = current_partner.partner_manager_groups.includes(users: :user_extension).to_a
end
def manager_group
name = params[:manager_group_name].to_s.strip
if params[:manager_group_id].present?
# 重命名
@manager_group = current_partner.partner_manager_groups.find(params[:manager_group_id])
@manager_group.update!(name: name)
else
# 新建
@manager_group = current_partner.partner_manager_groups.create!(name: name)
end
end
def remove_manager_group
manager_group = current_partner.partner_manager_groups.find(params[:manager_group_id])
manager_group.destroy!
render_delete_success
end
def partner_managers
user_ids = Array.wrap(params[:user_ids])
@manager_group = current_partner.partner_manager_groups.find(params[:manager_group_id])
ActiveRecord::Base.transaction do
User.where(id: user_ids).pluck(:id).each do |user_id|
next if current_partner.partner_managers.exists?(partner_manager_group: @manager_group, user_id: user_id)
current_partner.partner_managers.create!(partner_manager_group: @manager_group, user_id: user_id)
end
end
@manager_group.reload
end
def remove_partner_manager
partner_manager = current_partner.partner_managers.find(params[:manager_id])
partner_manager.destroy!
render_delete_success
end
def customer_manager_group
customer = current_partner.customers.find(params[:customer_id])
if params[:manager_group_id].present?
manager_group = current_partner.partner_manager_groups.find(params[:manager_group_id])
customer.update!(partner_manager_group: manager_group)
else
customer.update!(partner_manager_group_id: nil)
end
render_ok
end
private
def current_partner
@_current_partner ||= Partner.find(params[:id].presence || params[:partner_id])
end
def check_partner_present!
return if current_partner.present?
redirect_to '/404'
end
def manager_permission?
admin_or_business? || current_user.partner_managers.exists?(partner_id: current_partner.id)
end
def check_permission!
return if manager_permission?
redirect_to '/403'
end
def check_admin_manager_group_permission!
return if admin_or_business?
return if current_partner.admin_partner_manager_group.partner_managers.exists?(user: current_user)
render_forbidden
end
def load_customer_extra_statistic_data
school_ids = @customers.map(&:school_id)
teacher_map = UserExtension.where(school_id: school_ids, identity: 0).group(:school_id).count
student_map = UserExtension.where(school_id: school_ids, identity: 1).group(:school_id).count
course_map = Course.where(school_id: school_ids, is_delete: 0).where.not(id: 1309).group(:school_id).count
shixun_map = Shixun.visible.joins('left join user_extensions on user_extensions.user_id = shixuns.user_id')
.where(user_extensions: { school_id: school_ids }).group('user_extensions.school_id').count
shixun_report_map = StudentWork.where(work_status: [1, 2]).where('myshixun_id != 0')
.joins('left join user_extensions on user_extensions.user_id = student_works.user_id')
.where(user_extensions: { school_id: school_ids })
.group('user_extensions.school_id').count
course_time_map = Course.where(school_id: school_ids, is_delete: 0)
.where.not(id: 1309).group(:school_id).maximum(:updated_at)
@customers.each do |customer|
customer._extra_data = {
teacher_count: teacher_map[customer.school_id],
student_count: student_map[customer.school_id],
course_count: course_map[customer.school_id],
shixun_count: shixun_map[customer.school_id],
shixun_report_count: shixun_report_map[customer.school_id],
course_time: course_time_map[customer.school_id]
}
end
end
end

@ -0,0 +1,27 @@
class UsersForPartnersController < ApplicationController
include Base::PaginateHelper
before_action :check_partner_manager_permission!
def index
params[:sort_by] = params[:sort_by].presence || 'created_on'
params[:sort_direction] = params[:sort_direction].presence || 'desc'
users = Admins::UserQuery.call(search_params)
@users = paginate users.includes(user_extension: :school)
end
private
def search_params
params.permit(:name, :sort_by, :sort_direction)
end
def check_partner_manager_permission!
partner = Partner.find(params[:partner_id])
return if admin_or_business?
return if partner.admin_partner_manager_group.partner_managers.exists?(user: current_user)
render_forbidden
end
end

@ -48,9 +48,10 @@ module ManageBackHelper
str.presence || default
end
def overflow_hidden_span(text, width: 300)
def overflow_hidden_span(text, width: 300, placement: nil)
opts = { class: 'd-inline-block text-truncate', style: "max-width: #{width}px" }
opts.merge!('data-toggle': 'tooltip', title: text) if text != '--'
opts.merge!('data-placement': placement) if placement
content_tag(:span, text, opts)
end

@ -2,6 +2,7 @@ class Customer < ApplicationRecord
default_scope { order(created_at: :desc) }
belongs_to :school
belongs_to :partner_manager_group, optional: true
has_many :partner_customers, dependent: :destroy
has_many :partners, through: :partner_customers

@ -1,7 +1,10 @@
class Partner < ApplicationRecord
belongs_to :school, optional: true
has_many :users
has_many :partner_customers, dependent: :destroy
has_many :customers, through: :partner_customers
has_many :partner_manager_groups, dependent: :destroy
has_one :admin_partner_manager_group, -> { where(admin: true) }, class_name: 'PartnerManagerGroup'
has_many :partner_managers, dependent: :destroy
end

@ -0,0 +1,5 @@
class PartnerManager < ApplicationRecord
belongs_to :user
belongs_to :partner
belongs_to :partner_manager_group
end

@ -0,0 +1,10 @@
class PartnerManagerGroup < ApplicationRecord
belongs_to :partner
has_many :customers, dependent: :nullify
has_many :partner_managers, dependent: :destroy
has_many :users, through: :partner_managers, source: :user
scope :without_admin, -> { where(admin: false) }
end

@ -14,7 +14,7 @@ class School < ApplicationRecord
has_many :courses
has_many :customers, dependent: :destroy
has_many :partners, dependent: :destroy
has_one :partner, dependent: :destroy
has_many :apply_add_departments, dependent: :destroy
has_many :user_extensions, dependent: :nullify

@ -146,7 +146,7 @@ class User < ApplicationRecord
has_many :videos, dependent: :destroy
# 客户管理
belongs_to :partner, optional: true
has_many :partner_managers, dependent: :destroy
# Groups and active users
scope :active, lambda { where(status: STATUS_ACTIVE) }

@ -0,0 +1,30 @@
class CustomerQuery < ApplicationQuery
attr_reader :partner, :user, :params
def initialize(partner, user, params)
@partner = partner
@user = user
@params = params
end
def call
customers = manager_group_scope
keyword = params[:keyword].to_s.strip.presence
customers = customers.joins(:school).where('schools.name LIKE ?', "%#{keyword}%") if keyword
customers
end
private
def manager_group_scope
# 超级管理员 或者 管理员
if user.admin_or_business? || partner.admin_partner_manager_group.partner_managers.exists?(user: user)
partner.customers
else
manager_group_ids = user.partner_managers.where(partner: partner).joins(:partner_manager_group).pluck('partner_manager_groups.id')
partner.customers.where(partner_manager_group_id: manager_group_ids)
end
end
end

@ -10,7 +10,9 @@
<% if partners.present? %>
<% partners.each do |partner| %>
<tr class="partner-item-<%= partner.id %>">
<td class="text-left"><%= partner.school&.name || partner.name %></td>
<td class="text-left">
<%= link_to partner.school&.name || partner.name, customers_partner_path(partner), target: '_blank' %>
</td>
<td><%= partner.created_at&.strftime('%Y-%m-%d %H:%M') %></td>
<td>
<%= link_to '查看', admins_partner_customers_path(partner), class: 'action' %>

@ -14,7 +14,7 @@
<li class="nav-item"><a class="nav-link" href="/shixuns">实训项目</a></li>
<li class="nav-item"><a class="nav-link" href="/competitions">在线竞赛</a></li>
<li class="nav-item"><a class="nav-link" href="/moop_cases">教学案例</a></li>
<li class="nav-item"><a class="nav-link" href="/crowdsourcing">众包创新</a></li>
<!-- <li class="nav-item"><a class="nav-link" href="/crowdsourcing">众包创新</a></li>-->
<li class="nav-item"><a class="nav-link" href="/forums">交流问答</a></li>
</ul>
</div>

@ -13,7 +13,7 @@
<%= javascript_include_tag 'college', 'data-turbolinks-track': 'reload' %>
</head>
<% body_class = [params[:controller].gsub(/\//, '-').gsub('_', '-'), params[:action], 'page'].join('-') %>
<% body_class = [params[:controller].gsub(/\//, '-').gsub('_', '-'), params[:action].gsub(/\//, '-').gsub('_', '-'), 'page'].join('-') %>
<body class="<%= body_class %>">
<%= render 'colleges/shared/navbar' %>
<!-- Page Content -->

@ -0,0 +1,25 @@
<div class="container mt-3 px-0 bg-white">
<div class="custom-nav">
<div class="custom-nav-item">
<%= link_to '客户列表', customers_partner_path(current_partner), class: 'custom-nav-link active' %>
</div>
<% if manager_permission? %>
<div class="custom-nav-item">
<%= link_to '权限管理', partner_manager_groups_partner_path(current_partner), class: 'custom-nav-link' %>
</div>
<% end %>
</div>
<div class="customer-list-container">
<div class="box customer-list-form">
<%= form_tag(customers_partner_path(current_partner), method: :get, class: 'form-inline search-form', remote: true) do %>
<%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-md-4 ml-3', placeholder: '单位名称检索') %>
<%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
<% end %>
</div>
<div class="customer-list-body" data-id="<%= current_partner.id %>">
<%= render partial: 'partners/shared/customer_list', locals: { customers: @customers } %>
</div>
</div>
</div>

@ -0,0 +1 @@
$('.customer-list-body').html("<%= j(render partial: 'partners/shared/customer_list', locals: { customers: @customers }) %>");

@ -0,0 +1,11 @@
var $container = $('.manager-group-list-body');
var $managerGroupItem = $container.find('.manager-group-item-<%= @manager_group.id %>');
if($managerGroupItem.length > 0){
$managerGroupItem.find('.manager-group-name').html('<%= @manager_group.name %>');
} else {
$container.append('<%= j(render partial: 'partners/shared/manager_group_item', locals: { manager_group: @manager_group }) %>');
}
$('.partner-save-manager-group-modal').modal('hide');
showSuccessFlash();

@ -0,0 +1,25 @@
<div class="container mt-3 px-0">
<div class="custom-nav bg-white">
<div class="custom-nav-item">
<%= link_to '客户列表', customers_partner_path(current_partner), class: 'custom-nav-link' %>
</div>
<div class="custom-nav-item">
<%= link_to '权限管理', partner_manager_groups_partner_path(current_partner), class: 'custom-nav-link active' %>
</div>
</div>
<div class="manager-group-list-container">
<div class="box search-form-container customer-list-form bg-white">
<div class="flex-1">共<span class="text-danger px-1"><%= @manager_groups.size %></span>个权限组</div>
<%= javascript_void_link('新增权限组', class: 'btn btn-primary btn-sm add-manager-group-btn',
data: { toggle: 'modal', target: '.partner-save-manager-group-modal' }) %>
</div>
<div class="manager-group-list-body" data-id="<%= current_partner.id %>">
<%= render partial: 'partners/shared/manager_group_item', collection: @manager_groups, as: :manager_group %>
</div>
</div>
</div>
<%= render partial: 'partners/shared/save_manager_group_modal' %>
<%= render partial: 'partners/shared/add_partner_manager_modal' %>

@ -0,0 +1,6 @@
var $managerGroupContainer = $('.manager-group-list-body .manager-group-item-<%= @manager_group.id %>');
$managerGroupContainer.find('.partner-manager-body')
.html('<%= j(render partial: 'partners/shared/partner_managers', locals: { manager_group: @manager_group }) %>');
showSuccessFlash();
$('.partner-add-partner-manager-modal').modal('hide');

@ -0,0 +1,30 @@
<div class="modal fade partner-add-partner-manager-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">添加管理员</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form class="partner-add-partner-manager-form">
<%= hidden_field_tag(:manager_group_id, nil, id: nil) %>
<div class="form-group d-flex">
<label class="col-form-label">管理员:</label>
<div class="d-flex flex-column-reverse w-75">
<select id="user_ids" name="user_ids" class="form-control partner-manager-select"></select>
</div>
</div>
<div class="error text-danger"></div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary submit-btn">确认</button>
</div>
</div>
</div>
</div>

@ -0,0 +1,53 @@
<% can_manager = manager_permission? %>
<table class="table table-hover text-center customer-list-table">
<thead class="thead-light">
<tr>
<% if can_manager %>
<th width="15%" class="text-left">单位名称</th>
<th width="10%">权限控制</th>
<% else %>
<th width="25%" class="text-left">单位名称</th>
<% end %>
<th width="6%">教师</th>
<th width="6%">学生</th>
<th width="6%">课堂</th>
<th width="8%">发布实训</th>
<th width="10%">实训报告</th>
<th width="14%">最新课堂动态时间</th>
<th width="10%">使用详情</th>
</tr>
</thead>
<tbody>
<% if customers.present? %>
<%- manager_group_options = current_partner.partner_manager_groups.without_admin.map{ |g| [g.name, g.id] }.unshift(['选择权限组', '']) -%>
<% customers.each do |customer| %>
<tr class="customer-item-<%= customer.id %>">
<% if can_manager %>
<td class="text-left"><%= customer.school.name %></td>
<td>
<%= select_tag(:manager_group, options_for_select(manager_group_options, customer.partner_manager_group_id),
data: { id: customer.id, last: customer.partner_manager_group_id },
id: nil, class: 'form-control manager-group-select') %>
</td>
<% else %>
<td class="text-left"><%= customer.school.name %></td>
<% end %>
<td><%= display_text customer.display_extra_data(:teacher_count) %></td>
<td><%= display_text customer.display_extra_data(:student_count) %></td>
<td><%= display_text customer.display_extra_data(:course_count) %></td>
<td><%= display_text customer.display_extra_data(:shixun_count) %></td>
<td><%= display_text customer.display_extra_data(:shixun_report_count) %></td>
<td><%= display_text customer.display_extra_data(:course_time)&.strftime('%Y-%m-%d %H:%M') %></td>
<td>
<%= link_to('查看', statistics_college_path(customer.school), target: '_blank') %>
</td>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: customers } %>

@ -0,0 +1,29 @@
<div class="card manager-group-item manager-group-item-<%= manager_group.id %>">
<div class="card-header">
<div class="card-header-left">
<span class="manager-group-name"><%= manager_group.name %></span>
<% if manager_group.admin? %>
<span class="text-secondary font-12">(访问权限范围:客户列表中的全部客户)</span>
<% else %>
<span class="text-secondary font-12">(访问权限范围:客户列表中“权限控制”勾选了本组的客户)</span>
<% end %>
</div>
<% unless manager_group.admin? %>
<%= link_to 'javascript:void(0)', class: 'action',
data: { toggle: 'modal', target: '.partner-save-manager-group-modal', id: manager_group.id, name: manager_group.name } do %>
<i class="fa fa-lg fa-fw fa-pencil"></i>
<% end %>
<%= delete_link '删除', remove_manager_group_partner_path(current_partner, manager_group_id: manager_group.id, element: ".manager-group-item-#{manager_group.id}"), class: 'action text-danger' do %>
<i class="fa fa-lg fa-fw fa-trash-o"></i>
<% end %>
<% end %>
</div>
<div class="card-body">
<div class="partner-manager-body">
<%= render partial: 'partners/shared/partner_managers', locals: { manager_group: manager_group } %>
</div>
</div>
</div>

@ -0,0 +1,20 @@
<div class="partner-manager-item add-partner-manager-item">
<div class="partner-manager-item-avatar" data-toggle="modal" data-target=".partner-add-partner-manager-modal" data-id="<%= manager_group.id %>"></div>
</div>
<% manager_group.partner_managers.each do |manager| %>
<div class="partner-manager-item partner-manager-item-<%= manager.id %>">
<%= delete_link 'x',
remove_partner_manager_partner_path(current_partner, manager_id: manager.id, element: ".partner-manager-item-#{manager.id}"),
data: { toggle: 'tooltip', title: '删除' },
class: 'remove-partner-manager-action' do %>
<i class="fa fa-times-circle"></i>
<% end %>
<%= link_to "/users/#{manager.user.login}", data: { toggle: 'tooltip', title: '查看个人主页' },
target: '_blank', class: 'partner-manager-item-avatar' do %>
<img src="/images/<%= url_to_avatar(manager.user) %>" />
<% end %>
<div><%= overflow_hidden_span(manager.user.real_name, width: 80, placement: 'bottom') %></div>
</div>
<% end %>

@ -0,0 +1,30 @@
<div class="modal fade partner-save-manager-group-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">新建</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form class="partner-save-manager-group-form" data-url="<%= manager_group_partner_path(current_partner) %>">
<%= hidden_field_tag(:manager_group_id, nil) %>
<div class="form-group d-flex">
<label class="col-form-label">名称:</label>
<div class="d-flex flex-column-reverse w-75">
<input type="text" name="manager_group_name" class="form-control"/>
</div>
</div>
<div class="error text-danger"></div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary submit-btn">确认</button>
</div>
</div>
</div>
</div>

@ -17,7 +17,7 @@ json.top do
json.crowdsourcing_url "/crowdsourcing"
# 客户管理
json.customer_management_url current_user.partner ? "#{@old_domain}/cooperates/#{current_user.partner.try(:id)}/partner_list" : nil
json.customer_management_url current_user.partner_managers.exists? ? "#{@old_domain}/cooperates/#{current_user.partner_managers.first.partner_id}/partner_list" : nil
json.career_url do
json.array! @career.to_a do |c|

@ -0,0 +1,6 @@
json.count @users.total_count
json.users do
json.array! @users.each do |user|
json.extract! user, :id, :real_name, :identity, :school_name
end
end

@ -877,6 +877,8 @@ Rails.application.routes.draw do
resources :searchs, only: [:index]
end
resources :users_for_partners, only: [:index]
resources :trustie_hacks, path: :osshackathon do
collection do
get :edit_hackathon
@ -1155,6 +1157,20 @@ Rails.application.routes.draw do
end
end
resources :partners, only: [] do
member do
get :customers
get :partner_manager_groups
post :customer_manager_group
post :manager_group
delete :remove_manager_group
post :partner_managers
delete :remove_partner_manager
end
end
#git 认证回调
match 'gitauth/*url', to: 'gits#auth', via: :all

@ -0,0 +1,13 @@
class CreatePartnerManagerGroups < ActiveRecord::Migration[5.2]
def change
create_table :partner_manager_groups do |t|
t.references :partner
t.string :name
t.boolean :admin, default: false
t.timestamps
end
add_reference :customers, :partner_manager_group
end
end

@ -0,0 +1,11 @@
class CreatePartnerManagers < ActiveRecord::Migration[5.2]
def change
create_table :partner_managers do |t|
t.references :user
t.references :partner
t.references :partner_manager_group
t.timestamps
end
end
end

@ -0,0 +1,14 @@
class TransferPartnerManagerData < ActiveRecord::Migration[5.2]
def change
ActiveRecord::Base.transaction do
Partner.find_each do |partner|
manager_group = partner.partner_manager_groups.find_or_create_by(name: '管理者', admin: true)
user_ids = User.where(partner_id: partner.id).pluck(:id)
PartnerManager.bulk_insert(*%i[user_id partner_id partner_manager_group_id created_at updated_at]) do |worker|
user_ids.each { |user_id| worker.add(user_id: user_id, partner_id: partner.id, partner_manager_group_id: manager_group.id) }
end
end
end
end
end

@ -0,0 +1,5 @@
class RemovePartnerIdFromUsers < ActiveRecord::Migration[5.2]
def change
remove_reference :users, :partner
end
end

File diff suppressed because one or more lines are too long

@ -29920,6 +29920,14 @@ function show_success_flash(message){
});
}
function showSuccessFlash(message){
$.notify({
message: message || '操作成功'
},{
type: 'success'
});
}
function showErrorNotify(message){
$.notify({
message: message || '操作失败'

@ -29913,6 +29913,14 @@ function show_success_flash(message){
});
}
function showSuccessFlash(message){
$.notify({
message: message || '操作成功'
},{
type: 'success'
});
}
function showErrorNotify(message){
$.notify({
message: message || '操作失败'
Loading…
Cancel
Save