dev_home
cxt 5 years ago
commit d513f25540

@ -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";
@ -10,4 +12,34 @@
.navbar-dark .navbar-nav .nav-link {
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
@ -1156,6 +1158,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

File diff suppressed because one or more lines are too long

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 || '操作失败'

@ -29,6 +29,11 @@ class RegisListviewdata extends React.Component {
})
}
getotiku = (url) => {
window.open(url, '_blank');
}
render() {
const {item} = this.props;
return (
@ -53,12 +58,31 @@ class RegisListviewdata extends React.Component {
marginTop: "29px",
marginLeft: "37px"
}}>
<p className="maxnamewidth160" style={{
color: "#05101A",
fontSize: "16px",
width: "160px",
textAlign: "center"
}}>{item.name}</p>
{
this.props.admin?
this.props.admin===true?
<a className="maxnamewidth160" title={item.name} style={{
color: "#05101A",
fontSize: "16px",
width: "160px",
textAlign: "center",
cursor:"pointer"
}}
onClick={()=>this.getotiku(`/competitions/${this.props.match.params.identifier}/competition_teams/${item.id}`)}
>{item.name}</a>
: <p className="maxnamewidth160" style={{
color: "#05101A",
fontSize: "16px",
width: "160px",
textAlign: "center"
}}>{item.name}</p>
: <p className="maxnamewidth160" style={{
color: "#05101A",
fontSize: "16px",
width: "160px",
textAlign: "center"
}}>{item.name}</p>
}
</div>
<div style={{
marginLeft: "37px",

@ -1050,7 +1050,7 @@ class Registration extends React.Component {
{
competition_teams && competition_teams.map((item, index) => {
return (
<RegisListviewdata {...this.props} {...this.state} key={index} item={item}></RegisListviewdata>
<RegisListviewdata {...this.props} {...this.state} key={index} item={item} admin={admin}></RegisListviewdata>
)
})
}

@ -203,19 +203,18 @@ class Competitionteams extends Component{
render: (text, record) => <div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>,
},
{
title: '学习人数',
title: '学生数量',
dataIndex: 'students_count',
key: 'students_count',
render: (text, record) => <div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>,
},
{
title: '被fork发布的学习人数',
title: '发布的实训作业数量',
dataIndex: 'shixun_homework_count',
key: 'shixun_homework_count',
render: (text, record) =>
<Tooltip placement="bottom" title={"fork该实训产生的新实训学习总人数"}>
<div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>
</Tooltip>,
,
},
{
title: '有效作品数',
@ -265,4 +264,4 @@ class Competitionteams extends Component{
)
}
}
export default Competitionteams;
export default Competitionteams;

Loading…
Cancel
Save