admins: competition prize && competition prize api

dev_auth
p31729568 5 years ago
parent 223e65e8f5
commit acd8a0584f

@ -600,6 +600,67 @@ $(document).on('turbolinks:load', function(){
$(".stage-update-form .section-start-time").datetimepicker(timeOptions);
$(".stage-update-form .section-end-time").datetimepicker(timeOptions);
});
// 奖项设置
var $prizeContainer = $('#competition-prize-card');
var competitionId = $prizeContainer.data('id');
$(document).on('prize.save.success', function(){
$.ajax({
method: 'GET',
url: '/admins/competitions/' + competitionId + '/competition_prizes',
dataType: 'script'
})
});
$('.modal.admin-upload-file-modal').on('upload:success', function(e, data){
var $imageElement;
if(data.suffix === '_member'){
$imageElement = $('.prize-member-image-' + data.source_id);
} else if(data.suffix === '_team'){
$imageElement = $('.prize-team-image-' + data.source_id);
} else {
$imageElement = $('.prize-teacher-image-' + data.source_id);
}
$imageElement.attr('src', data.url);
$imageElement.show();
$imageElement.next().html('重新上传');
})
// 生成获奖记录
$prizeContainer.on('click', '.generate-prize-user-action', function(){
var $link = $(this);
var generateRequest = function(){
return $.ajax({
method: 'POST',
url: '/admins/competitions/' + competitionId + '/competition_prize_users',
dataType: 'json',
success: function(data){
if(data && data.status === 0){
show_success_flash();
$link.remove();
} else {
showErrorNotify(data.message);
}
},
error: function(res){
var data = res.responseJSON;
showErrorNotify(data.message);
}
})
}
customConfirm({
content: '确认生成吗?',
ok: function () {
customLoading({
ajax: generateRequest
})
}
})
});
} else {
$(document).unbind('prize.save.success');
}
});

@ -0,0 +1,46 @@
$(document).on('turbolinks:load', function() {
$('.admin-modal-container').on('show.bs.modal', '.admin-save-competition-prize-modal', function(event){
var $modal = $('.modal.admin-save-competition-prize-modal');
var $form = $modal.find('form.admin-save-competition-prize-form');
$form.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
'competition_prize[name]': {
required: true,
maxlength: 10
},
'competition_prize[num]': {
required: true,
digits: true,
min: 1
}
}
});
$modal.on('click', '.submit-btn', function(){
$form.find('.error').html('');
var url = $form.attr('action');
var formMethod = $form.data('form-method')
if ($form.valid()) {
$.ajax({
method: formMethod,
dataType: 'json',
url: url,
data: $form.serialize(),
success: function(data){
if(data && data.status === 0) {
show_success_flash();
$(document).trigger('prize.save.success');
$modal.modal('hide');
} else {
$modal.find('.error').html(data.message)
}
}
});
}
});
})
});

@ -66,10 +66,32 @@ function customConfirm(opts){
return $.confirm($.extend({}, defaultOpts, opts))
}
function show_success_flash(message) {
function customLoading(opts) {
var loading;
var defaultOpts = {
content: opts.ajax,
contentLoaded: function(){
setTimeout(function(){
loading.close()
}, 200);
}
}
loading = $.confirm($.extend({}, defaultOpts, opts));
return loading;
}
function show_success_flash(message){
$.notify({
message: message || '操作成功'
},{
type: 'success'
});
}
function showErrorNotify(message){
$.notify({
message: message || '操作失败'
},{
type: 'danger'
});
}

@ -0,0 +1,41 @@
class Admins::CompetitionPrizeUsersController < Admins::BaseController
def index
@competition = current_competition
prize_users = Admins::CompetitionPrizeUserQuery.call(params.merge(competition_id: current_competition.id))
include_class = [:competition_team, :competition_prize, :approver,
user: [:process_real_name_apply, :process_professional_apply, user_extension: :school]]
@prize_users = paginate(prize_users.preload(include_class))
end
def create
Admins::CreateCompetitionPrizeUsersService.call(current_competition)
render_ok
rescue ApplicationService::Error => ex
render_error(ex.message)
end
def approve
Admins::ApproveCompetitionPrizeUserService.call(current_prize_user, current_user)
@prize_user = current_prize_user
rescue ApplicationService::Error => ex
render_js_error(ex.message, type: :notify)
end
def unapprove
Admins::UnapproveCompetitionPrizeUserService.call(current_prize_user, current_user)
@prize_user = current_prize_user
rescue ApplicationService::Error => ex
render_js_error(ex.message, type: :notify)
end
private
def current_prize_user
@_current_prize_user ||= current_competition.competition_prize_users.find(params[:id])
end
def current_competition
@_current_competition ||= Competition.find(params[:competition_id])
end
end

@ -0,0 +1,43 @@
class Admins::CompetitionPrizesController < Admins::BaseController
def index
@competition = current_competition
end
def new
@prize = current_competition.competition_prizes.new
end
def create
@prize = current_competition.competition_prizes.create!(save_params)
render_ok
end
def edit
@prize = current_competition_prize
end
def update
current_competition_prize.update!(save_params)
render_ok
end
def destroy
current_competition_prize.destroy!
render_delete_success
end
private
def current_competition_prize
@_current_competition_prize ||= current_competition.competition_prizes.find(params[:id])
end
def current_competition
@_current_competition ||= Competition.find(params[:competition_id])
end
def save_params
params.require(:competition_prize).permit(:name, :category, :num)
end
end

@ -0,0 +1,23 @@
class Competitions::CertificatesController < Competitions::BaseController
def personal
prize_user = CompetitionPrizeUser.find_by!(user: current_user, id: params[:id])
return render_not_found unless prize_user.certificate_exist?
team = prize_user.competition_team
prize = prize_user.competition_prize
filename = "#{current_competition.name}-#{prize.name}-#{team.name}-#{prize_user.user.real_name}.pdf"
send_file prize_user.certificate_path, filename: filename
end
def team
team = CompetitionTeam.find(id: params[:id])
return render_forbidden unless team.team_members.exists?(user_id: current_user.id)
return render_not_found unless team.certificate_exist?
prize = team.competition_prize_users.first.competition_prize
filename = "#{current_competition.name}-#{prize.name}-#{team.name}.pdf"
send_file team.certificate_path, filename: filename
end
end

@ -5,6 +5,11 @@ class Competitions::CompetitionModulesController < Competitions::BaseController
def index
@modules = current_competition.unhidden_competition_modules.order(position: :asc)
# 未登录、未获奖用户,不展示获奖证书栏目
if !current_user.logged? || !current_competition.competition_prize_users.exists?(user: current_user)
@modules = @modules.select { |mod| mod.name != '获奖证书' }
end
end
def show

@ -0,0 +1,25 @@
class Competitions::PrizeLeaderAccountsController < Competitions::BaseController
before_action :require_prize_team_leader!
def update
Competitions::SavePrizeTeamAccountService.call(current_competition, current_user, update_params)
render_ok
rescue ApplicationService::Error => ex
render_error(ex.message)
end
private
def require_prize_team_leader!
prize_user = current_competition.competition_prize_users.joins(:competition_prize)
.where(competition_prizes: { category: :bonus })
.find_by(leader: true, user_id: current_user.id)
return if prize_user.present?
render_forbidden
end
def update_params
params.permit(:bank, :second_bank, :card_no)
end
end

@ -0,0 +1,26 @@
class Competitions::PrizesController < Competitions::BaseController
before_action :require_prize_user!
def show
self_prizes = current_competition.competition_prize_users.where(user_id: current_user.id).includes(:competition_team)
@leader = self_prizes.any?(&:leader?) # 是否为队长
@bank_account = self_prizes.find(&:leader?).extra if @leader
@self_prizes = self_prizes.select(&:certificate_exist?) # 个人证书quit
@team_prizes = self_prizes.map(&:competition_team).uniq.select(&:certificate_exists?) # 团队证书
prize_users = current_competition.competition_prize_users.where(competition_team_id: self_prizes.map(&:competition_team_id))
.includes(:competition_team, user: [:user_extension, :process_real_name_apply, :process_professional_apply])
@prize_user_map = prize_users.group_by(&:competition_team)
end
private
def require_prize_user!
return if current_competition.competition_prize_users.exists?(user: current_user)
render_forbidden
end
end

@ -0,0 +1,9 @@
class Competitions::SavePrizeTeamAccountForm
include ActiveModel::Model
attr_accessor :bank, :second_bank, :card_no
validates :bank, presence: true
validates :second_bank, presence: true
validates :card_no, presence: true
end

@ -0,0 +1,10 @@
module Admins::CompetitionPrizeUsersHelper
def display_auth_state(flag, other = false, success: nil, normal: nil, error: nil)
success ||= '<i class="fa fa-check text-success font-16"/>'.html_safe
normal ||= '--'
error ||= '<i class="fa fa-close text-secondary font-16"/>'.html_safe
return success if flag
other ? normal : error
end
end

@ -29,6 +29,9 @@ class Competition < ApplicationRecord
has_many :competition_managers, dependent: :destroy
has_many :managers, through: :competition_managers, source: :user
has_many :competition_prizes, dependent: :destroy
has_many :competition_prize_users, dependent: :destroy
after_create :create_competition_modules
def mode_type
@ -131,7 +134,7 @@ class Competition < ApplicationRecord
end
def all_module_types
%w[home enroll inform chart resource]
%w[home enroll inform chart resource certificate]
end
def max_stage_end_time
@ -146,6 +149,10 @@ class Competition < ApplicationRecord
user && competition_managers.exists?(user_id: user.id)
end
def finished?
end_time.blank? || end_time < Time.now
end
private
def get_module_name type
@ -155,6 +162,7 @@ class Competition < ApplicationRecord
when 'inform' then '通知公告'
when 'chart' then '排行榜'
when 'resource' then '资料下载'
when 'certificate' then '获奖证书'
else ''
end
end

@ -0,0 +1,21 @@
class CompetitionPrize < ApplicationRecord
extend Enumerize
belongs_to :competition
has_many :competition_prize_users, dependent: :destroy
enumerize :category, in: %i[bonus unset]
def self.member_suffix
'_member'
end
def self.teacher_suffix
'_teacher'
end
def self.team_suffix
'_team'
end
end

@ -0,0 +1,48 @@
class CompetitionPrizeUser < ApplicationRecord
include AASM
belongs_to :competition
belongs_to :competition_team
belongs_to :competition_prize
belongs_to :user
belongs_to :approver, class_name: 'User', optional: true # 审批人
serialize :extra, JSON
aasm(:status) do
state :pending, initial: true
state :approved
event :approve do
transitions from: [:pending], to: :approved, guard: :user_certified?
end
event :unapprove do
transitions from: [:approved], to: :pending
end
end
delegate :bank, :second_bank, :card_no, to: :extra, allow_nil: true
def user_certified?
user.certification? && user.professional_certification?
end
def certificate_exist?
Util::FileManage.exists?(self)
end
def certificate_url
Util::FileManage.source_disk_file_url(self)
end
def certificate_path
Util::FileManage.source_disk_filename(self)
end
def role_text
return '队长' if leader?
user.is_teacher? ? '教师' : '队员'
end
end

@ -11,6 +11,8 @@ class CompetitionTeam < ApplicationRecord
has_many :members, -> { without_teachers }, class_name: 'TeamMember'
has_many :teachers, -> { only_teachers }, class_name: 'TeamMember'
has_many :competition_prize_users, dependent: :destroy
def group_team_type?
team_type.zero?
end
@ -48,4 +50,24 @@ class CompetitionTeam < ApplicationRecord
def members_name
members.map{|member| member.user.real_name}.join(",")
end
def all_prize_approved?
!competition_prize_users.exists?(status: :pending)
end
def certificate_exists?
Util::FileManage.exists?(self, self.class.certificate_suffix)
end
def certificate_url
Util::FileManage.source_disk_file_url(self, self.class.certificate_suffix)
end
def certificate_path
Util::FileManage.source_disk_filename(self, self.class.certificate_suffix)
end
def self.certificate_suffix
'_CERT'
end
end

@ -503,6 +503,10 @@ class User < ApplicationRecord
phone.present?
end
def email_binded?
mail.present?
end
def self.current=(user)
Thread.current[:current_user] = user
end

@ -0,0 +1,77 @@
class Admins::CompetitionPrizeUserQuery < ApplicationQuery
include CustomSortable
attr_reader :params
sort_columns :rank, default_by: :rank, default_direction: :asc
def initialize(params)
@params = params
end
def call
records = CompetitionPrizeUser.all
# 竞赛过滤
records = records.where(competition_id: params[:competition_id]) if params[:competition_id].present?
# 关键字检索
keyword = params[:keyword].to_s.strip
if keyword.present?
like_sql = 'competition_teams.name LIKE :keyword OR schools.name LIKE :keyword'
records = records.left_joins(:competition_team, user: { user_extension: :school })
.where(like_sql, keyword: "%#{keyword}%")
end
# 奖项过滤
records = records.where(competition_prize_id: params[:prize_id]) if params[:prize_id].present?
# 审批状态过滤
records = records.where(status: params[:status]) if params[:status].present?
# 职业过滤
if params[:identity].present?
records = records.left_joins(user: :user_extension).where(user_extensions: { identity: params[:identity] })
end
# 实名认证过滤
records = real_name_auth_filter(records) if params[:real_name_auth].present?
# 职业认证过滤
records = professional_auth_filter(records) if params[:professional_auth].present?
custom_sort(records, params[:sort_by], params[:sort_direction])
end
private
def real_name_auth_filter(records)
records = records.left_joins(:user)
sql = ApplyUserAuthentication.real_name_auth.processing.where('apply_user_authentications.user_id = users.id').to_sql
case params[:real_name_auth]
when 'authed' then
records.where(users: { authentication: true })
when 'not_authed' then
records.where(users: { authentication: false }).where("NOT EXISTS (#{sql})")
when 'authing' then
records.where(users: { authentication: false }).where("EXISTS (#{sql})")
else records
end
end
def professional_auth_filter(records)
records = records.left_joins(:user)
sql = ApplyUserAuthentication.professional_auth.processing.where('apply_user_authentications.user_id = users.id').to_sql
case params[:professional_auth]
when 'authed' then
records.where(users: { professional_certification: true })
when 'not_authed' then
records.where(users: { professional_certification: false }).where("NOT EXISTS (#{sql})")
when 'authing' then
records.where(users: { professional_certification: false }).where("EXISTS (#{sql})")
else records
end
end
end

@ -0,0 +1,26 @@
class Admins::ApproveCompetitionPrizeUserService < ApplicationService
attr_reader :prize_user, :approver
def initialize(prize_user, approver)
@prize_user = prize_user
@approver = approver
end
def call
raise Error, '请勿重复审批' if prize_user.approved?
raise Error, '该用户未认证完成' unless prize_user.user_certified?
ActiveRecord::Base.transaction do
prize_user.approve
prize_user.approver = approver
prize_user.approved_at = Time.now
prize_user.save!
if prize_user.competition_team.all_prize_approved?
# TODO: 生成团队证书
end
# TODO: 生成个人证书
end
end
end

@ -0,0 +1,70 @@
class Admins::CreateCompetitionPrizeUsersService < ApplicationService
attr_reader :competition
def initialize(competition)
@competition = competition
end
def call
raise Error, '竞赛还未结束' unless competition.finished?
raise Error, '请勿重复生成' if competition.competition_prize_users.exists?
raise Error, '请先设置奖项' if prizes.blank?
raise Error, '无获奖队伍' if prize_teams.blank?
ActiveRecord::Base.transaction do
columns = %i[competition_id competition_team_id competition_prize_id user_id
status rank leader created_at updated_at]
CompetitionPrizeUser.bulk_insert(*columns) do |worker|
prize_teams.each_with_index do |team, index|
rank = index + 1
prize = team_prize(rank) # 根据排名获取当前队伍奖项
team.team_members.each do |member|
attr = {
competition_id: competition.id,
competition_team_id: team.id,
competition_prize_id: prize.id,
user_id: member.user_id,
leader: member.creator?,
status: :pending,
rank: rank
}
worker.add(attr)
end
end
end
end
end
private
def prizes
@_prizes ||= competition.competition_prizes.order(id: :asc).to_a
end
def team_prize(rank)
current = 0
prizes.each do |prize|
return prize if rank > current && rank <= current + prize.num
current += prize.num
end
end
def prize_teams
@_prize_teams ||= begin
prize_num_total = prizes.sum(&:num)
# 只有一个阶段则成绩为该阶段成绩否则为stage ID为0的总成绩
stage_id = competition.competition_stages.count == 1 ? competition.competition_stages.first.id : 0
competition.competition_teams.joins(:competition_scores)
.where(competition_scores: { competition_stage_id: stage_id })
.order("competition_scores.score desc, competition_scores.cost_time desc")
.includes(:team_members)
.limit(prize_num_total)
end
end
end

@ -0,0 +1,27 @@
class Admins::UnapproveCompetitionPrizeUserService < ApplicationService
attr_reader :prize_user, :approver
def initialize(prize_user, approver)
@prize_user = prize_user
@approver = approver
end
def call
raise Error, '状态有误' if prize_user.pending?
ActiveRecord::Base.transaction do
prize_user.unapprove
prize_user.approver = approver
prize_user.approved_at = nil
prize_user.save!
# 删除团队证书
team_certificate_path = Util::FileManage.source_disk_filename(prize_user.competition_team, CompetitionTeam.certificate_suffix)
File.delete(team_certificate_path) if File.exist?(team_certificate_path)
# 删除个人证书
user_certificate_path = Util::FileManage.source_disk_filename(prize_user)
File.delete(user_certificate_path) if File.exist?(user_certificate_path)
end
end
end

@ -0,0 +1,19 @@
class Competitions::SavePrizeTeamAccountService < ApplicationService
attr_reader :competition, :user, :params
def initialize(competition, user, params)
@competition = competition
@user = user
@params = params
end
def call
Competitions::SavePrizeTeamAccountForm.new(params).validate!
prize_leaders = competition.competition_prize_users.where(competition.competition_prize_users)
raise Error, '审批通过后不能修改' if prize_leaders.exists?(status: :approved)
prize_leaders.update_all(extra: params)
end
end

@ -0,0 +1 @@
$('.competition-prize-user-list-container .competition-prize-user-item-<%= @prize_user.id %>').html("<%= j( render partial: 'admins/competition_prize_users/shared/tr', locals: { prize_user: @prize_user } ) %>");

@ -0,0 +1,58 @@
<%
define_admin_breadcrumbs do
add_admin_breadcrumb('竞赛列表', admins_competitions_path)
add_admin_breadcrumb(@competition.name)
end
%>
<div class="box search-form-container flex-column mb-0 pb-0 competition-prize-user-form">
<ul class="nav nav-tabs w-100 search-form-tabs">
<li class="nav-item">
<%= link_to '报名列表', admins_competition_enroll_lists_path(@competition), class: "nav-link search-form-tab" %>
</li>
<li class="nav-item">
<%= link_to '获奖证书审批', admins_competition_competition_prize_users_path(@competition), class: "nav-link search-form-tab active" %>
</li>
</ul>
<div class="d-flex">
<%= form_tag(admins_competition_competition_prize_users_path(unsafe_params), method: :get, class: 'search-form mt-3 flex-1 d-flex align-items-end', remote: true) do %>
<div class="form-group mb-0 mr-3">
<label for="status">奖项:</label>
<% prize_options = [['不限', '']] + @competition.competition_prizes.map{ |p| [p.name, p.id] } %>
<%= select_tag(:prize_id, options_for_select(prize_options), class: 'form-control') %>
</div>
<% auth_options = [['不限', ''], %w(未认证 not_authed), %w(待认证 authing), %w(已认证 authed)] %>
<div class="form-group mb-0 mr-3">
<label for="status">实名认证状态:</label>
<%= select_tag(:real_name_auth, options_for_select(auth_options), class: 'form-control') %>
</div>
<div class="form-group mb-0 mr-3">
<label for="status">职业认证状态:</label>
<%= select_tag(:professional_auth, options_for_select(auth_options), class: 'form-control') %>
</div>
<div class="form-group mb-0 mr-3">
<label for="status">职业:</label>
<%- identity_options = [['不限', ''], %w(教师 0), %w(学生 1)] %>
<%= select_tag(:identity, options_for_select(identity_options), class: 'form-control') %>
</div>
<div class="form-group mb-0 mr-3">
<label for="status">审批状态:</label>
<%- status_options = [['不限', ''], %w(未审批 pending), %w(已审批 approved)] %>
<%= select_tag(:status, options_for_select(status_options), class: 'form-control') %>
</div>
<%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '战队/学校名称检索') %>
<%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
<%= link_to '清除', admins_competition_competition_prize_users_path(@competition), class: "btn btn-default",'data-disable-with': '清除中...' %>
<% end %>
</div>
</div>
<div class="box competition-prize-user-list-container">
<%= render(partial: 'admins/competition_prize_users/shared/list', locals: { prize_users: @prize_users }) %>
</div>

@ -0,0 +1 @@
$('.competition-prize-user-list-container').html("<%= j( render partial: 'admins/competition_prize_users/shared/list', locals: { prize_users: @prize_users } ) %>");

@ -0,0 +1,33 @@
<table class="table table-hover text-center competition-prize-user-list-table">
<thead class="thead-light">
<tr>
<th width="6%">奖项</th>
<th width="5%">排名</th>
<th width="10%" class="text-left">战队名称</th>
<th width="8%">真实姓名</th>
<th width="5%">职业</th>
<th width="8%">学号</th>
<th width="10%">单位</th>
<th width="5%">实名</th>
<th width="5%">职业</th>
<th width="5%">手机</th>
<th width="5%">队长</th>
<th width="10%">审批时间</th>
<th width="8%">审批人</th>
<th width="10%">操作</th>
</tr>
</thead>
<tbody style="overflow-wrap:break-word;">
<% if prize_users.present? %>
<% prize_users.each do |prize_user| %>
<tr class="competition-prize-user-item-<%= prize_user.id %>">
<%= render('admins/competition_prize_users/shared/tr', prize_user: prize_user) %>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: prize_users } %>

@ -0,0 +1,26 @@
<%- user = prize_user.user -%>
<td><%= prize_user.competition_prize.name %></td>
<td><%= prize_user.rank %></td>
<td class="text-left"><%= prize_user.competition_team.name %></td>
<td><%= user.real_name %></td>
<td><%= user.identity %></td>
<td><%= user.is_teacher? ? user.user_extension.technical_title : user.user_extension.student_id %></td>
<td><%= user.school_name %></td>
<td><%= display_auth_state(user.authentication?, user.process_real_name_apply.present?) %></td>
<td><%= display_auth_state(user.professional_certification?, user.process_professional_apply.present?) %></td>
<td><%= display_auth_state user.phone_binded? %></td>
<td><%= display_auth_state prize_user.leader?, error: '' %></td>
<td><%= display_text prize_user.approved_at&.strftime('%Y-%m-%d %H:%M') %></td>
<td><%= display_text prize_user.approver&.real_name %></td>
<td class="action-container">
<% if prize_user.pending? %>
<%= link_to('审批通过', approve_admins_competition_competition_prize_user_path(prize_user.competition, prize_user),
data: { confirm: '确认审批通过吗?' },
remote: true, method: :post, class: 'approve-action') %>
<% else %>
<%= link_to('撤销审批', unapprove_admins_competition_competition_prize_user_path(prize_user.competition, prize_user),
data: { confirm: '确认撤销审批吗?' },
remote: true, method: :post, class: 'approve-action') %>
<% end %>
</td>

@ -0,0 +1 @@
$('.competition-prize-user-list-container .competition-prize-user-item-<%= @prize_user.id %>').html("<%= j( render partial: 'admins/competition_prize_users/shared/tr', locals: { prize_user: @prize_user } ) %>");

@ -0,0 +1,2 @@
$('.admin-modal-container').html("<%= j( render partial: 'admins/competition_prizes/shared/save_competition_prize_modal', locals: { prize: @prize, title: '编辑奖项', form_method: 'PATCH' } ) %>");
$('.modal.admin-save-competition-prize-modal').modal('show');

@ -0,0 +1 @@
$('#competition-prize-card .competition-prize-table tbody').html("<%= j( render partial: 'admins/competition_settings/shared/competition_prizes', locals: { competition: @competition } ) %>");

@ -0,0 +1,2 @@
$('.admin-modal-container').html("<%= j( render partial: 'admins/competition_prizes/shared/save_competition_prize_modal', locals: { prize: @prize, title: '新建奖项', form_method: 'POST' } ) %>");
$('.modal.admin-save-competition-prize-modal').modal('show');

@ -0,0 +1,29 @@
<div class="modal fade admin-save-competition-prize-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"><%= 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">
<%= simple_form_for([:admins, prize.competition, prize], data: { form_method: form_method }, html: { class: 'admin-save-competition-prize-form' }, defaults: { wrapper_html: { class: 'offset-md-1 col-md-10' } }) do |f| %>
<%= f.input :name, label: '奖项名称:', placeholder: '请输入奖项名称' %>
<%= f.input :num, as: :integer, label: '数量:', placeholder: '请输入数量', input_html: { min: 1 } %>
<%= f.input :category, label: '奖励类型:' do %>
<%= f.select :category, [%w(奖金 bonus), %w(无 unset)], {}, class: 'form-control' %>
<% end %>
<div class="error text-danger"></div>
<% end %>
</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>

@ -266,7 +266,7 @@
<% end %>
</div>
<% when 'inform', 'chart', 'resource' %>
<% when 'inform', 'chart', 'resource', 'certificate' %>
<div class="row mt-2 new_module_div linkFormItem">
<div class="col-1 text-right">
<label class="checkbox checkbox-primary mt-1">
@ -315,16 +315,6 @@
<% end %>
<% end %>
<!-- <div class="row mt-2">-->
<!-- <div class="col-1 text-right">-->
<!-- <label class="checkbox checkbox-primary mt-1">-->
<!-- <input id="checkbox2" type="checkbox">-->
<!-- <label for="checkbox2">&nbsp;</label>-->
<!-- </label>-->
<!-- </div>-->
<!-- <div class="col-md-label mt-2">获奖证书</div>-->
<!-- </div>-->
<div class="error my-2 danger text-danger"></div>
<div class="row mt-2 mb-4">
@ -518,5 +508,36 @@
</div>
</div>
</div>
<div class="card mb-5" id="competition-prize-card" data-id="<%= @competition.id %>">
<div class="card-header d-flex justify-content-between align-items-center">
<span class="flex-1">奖项配置</span>
<%= link_to '新增奖项', new_admins_competition_competition_prize_path(@competition), remote: true, class: 'btn btn-primary btn-sm add-competition-prize-btn' %>
<% if @competition.finished? && !@competition.competition_prize_users.exists? %>
<%= javascript_void_link '生成获奖记录', class: 'btn btn-primary btn-sm ml-2 generate-prize-user-action' %>
<% end %>
</div>
<div class="card-body row pt-0">
<table class="table text-center competition-prize-table">
<thead>
<th width="8%">序号</th>
<th width="10%">奖项名称</th>
<th width="10%">数量</th>
<th width="10%">奖励类型</th>
<th width="16%">队员个人证书模板</th>
<th width="16%">团队证书模板</th>
<th width="16%">指导老师证书模板</th>
<th width="14%">操作</th>
</thead>
<tbody>
<%= render 'admins/competition_settings/shared/competition_prizes', competition: @competition %>
</tbody>
</table>
</div>
</div>
<div style="margin-bottom: 8.5rem;"></div>
<%# end %>
<%# end %>
<%= render partial: 'admins/shared/modal/upload_file_modal', locals: { title: '上传证书模板', accept: 'image/*' } %>

@ -0,0 +1,27 @@
<% competition.competition_prizes.each_with_index do |prize, index| %>
<tr class="competition-prize-item-<%= prize.id %>">
<td><%= index + 1 %></td>
<td><%= prize.name %></td>
<td><%= prize.num %></td>
<td><%= prize.category_text %></td>
<td>
<% member_image_exists = Util::FileManage.exists?(prize, CompetitionPrize.member_suffix) %>
<%= image_tag(member_image_exists ? Util::FileManage.source_disk_file_url(prize, CompetitionPrize.member_suffix) : '', height: 60, class: "w-100 preview-image prize-member-image-#{prize.id}", style: member_image_exists ? '' : 'display:none') %>
<%= javascript_void_link member_image_exists ? '重新上传' : '上传模板', class: 'action upload-prize-member-image-action', data: { source_id: prize.id, source_type: 'CompetitionPrize', suffix: CompetitionPrize.member_suffix, toggle: 'modal', target: '.admin-upload-file-modal' } %>
</td>
<td>
<% team_image_exists = Util::FileManage.exists?(prize, CompetitionPrize.team_suffix) %>
<%= image_tag(team_image_exists ? Util::FileManage.source_disk_file_url(prize, CompetitionPrize.team_suffix) : '', height: 60, class: "w-100 preview-image prize-team-image-#{prize.id}", style: team_image_exists ? '' : 'display:none') %>
<%= javascript_void_link team_image_exists ? '重新上传' : '上传模板', class: 'action upload-prize-team-image-action', data: { source_id: prize.id, source_type: 'CompetitionPrize', suffix: CompetitionPrize.team_suffix, toggle: 'modal', target: '.admin-upload-file-modal' } %>
</td>
<td>
<% teacher_image_exists = Util::FileManage.exists?(prize, CompetitionPrize.teacher_suffix) %>
<%= image_tag(teacher_image_exists ? Util::FileManage.source_disk_file_url(prize, CompetitionPrize.teacher_suffix) : '', height: 60, class: "w-100 preview-image prize-teacher-image-#{prize.id}", style: teacher_image_exists ? '' : 'display:none') %>
<%= javascript_void_link teacher_image_exists ? '重新上传' : '上传模板', class: 'action upload-prize-teacher-image-action', data: { source_id: prize.id, source_type: 'CompetitionPrize', suffix: CompetitionPrize.teacher_suffix, toggle: 'modal', target: '.admin-upload-file-modal' } %>
</td>
<td class="action-container">
<%= link_to '编辑', edit_admins_competition_competition_prize_path(competition, prize), remote: true, class: 'action edit-competition-prize-action' %>
<%= delete_link '删除', admins_competition_competition_prize_path(competition, prize, element: ".competition-prize-item-#{prize.id}"), class: 'action delete-competition-prize-action' %>
</td>
</tr>
<% end %>

@ -11,7 +11,7 @@
<%= link_to '报名列表', admins_competition_enroll_lists_path(@competition), class: "nav-link search-form-tab active" %>
</li>
<li class="nav-item">
<%= link_to '获奖证书审批', admins_competition_enroll_lists_path(@competition), class: "nav-link search-form-tab" %>
<%= link_to '获奖证书审批', admins_competition_competition_prize_users_path(@competition), class: "nav-link search-form-tab" %>
</li>
</ul>

@ -36,4 +36,6 @@
<div class="box subject-list-container">
<%= render partial: 'admins/subjects/shared/list', locals: { subjects: @subjects } %>
</div>
</div>
<%= render partial: 'admins/shared/modal/upload_file_modal', locals: { title: '上传图片', accept: 'image/*' } %>

@ -69,5 +69,4 @@
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: subjects } %>
<%= render partial: 'admins/shared/modal/upload_file_modal', locals: { title: '上传图片', accept: 'image/*' } %>
<%= render partial: 'admins/shared/paginate', locals: { objects: subjects } %>

@ -0,0 +1,55 @@
json.leader @leader
json.bank_account @bank_account if @leader
json.personal_certifications do
json.array! @self_prizes do |prize_user|
json.url personal_competitions_certificate_path(current_competition, prize_user)
end
end
json.team_certifications do
json.array! @team_prizes do |team|
json.url team_competitions_certificate_path(current_competition, team)
end
end
json.teams do
json.array! @prize_user_map do |team, prize_users|
json.extract! team, :id, :name
json.bank_account prize_users.find(&:leader?).extra
json.team_members do
json.array! prize_users do |prize_user|
user = prize_user.user
json.role prize_user.role_text
json.name user.real_name
real_name_auth =
if user.authentication?
'authed'
elsif user.process_real_name_apply.present?
'authing'
else
'not_authed'
end
json.real_name_auth real_name_auth
professional_auth =
if user.professional_certification?
'authed'
elsif user.process_professional_apply.present?
'authing'
else
'not_authed'
end
json.professional_auth professional_auth
json.phone_binded user.phone_binded?
json.email_binded user.email_binded?
end
end
end
end

@ -1,4 +1,6 @@
admins-mirror_scripts: 'admins-mirror_repositories'
admins-laboratory_settings: 'admins-laboratories'
admins-carousels: 'admins-laboratories'
admins-competition_settings: 'admins-competitions'
admins-competition_settings: 'admins-competitions'
admins-enroll_lists: 'admins-competitions'
admins-competition_prize_users: 'admins-competitions'

@ -0,0 +1,6 @@
zh-CN:
enumerize:
competition_prize:
category:
bonus: '奖金'
unset: '无'

@ -0,0 +1,8 @@
'zh-CN':
activemodel:
attributes:
competitions/save_prize_team_account_form:
bank: '开户行'
second_bank: '支行'
card_no: '卡号'

@ -809,6 +809,14 @@ Rails.application.routes.draw do
get :chart_rules
post :update_chart_rules
end
resource :prize_leader_account, only: [:update]
resource :prize, only: [:show]
resources :certificates, only: [] do
member do
get :personal
get :team
end
end
end
end
@ -1043,6 +1051,14 @@ Rails.application.routes.draw do
post :calculate_stage_score
end
end
resources :competition_prizes, only: [:index, :new, :create, :edit, :update, :destroy]
resources :competition_prize_users, only: [:index, :create] do
member do
post :approve
post :unapprove
end
end
end
resources :weapp_carousels, only: [:index, :create, :update, :destroy] do

@ -0,0 +1,10 @@
class InitCompetitionCertificateModuleData < ActiveRecord::Migration[5.2]
def change
Competition.find_each do |competition|
competition.competition_modules.where(module_type: 'certificate').delete_all
position = competition.competition_modules.maximum(:position) + 1
competition.competition_modules.create(module_type: 'certificate', position: position, name: '获奖证书', hidden: true)
end
end
end

@ -0,0 +1,13 @@
class CreateCompetitionPrizes < ActiveRecord::Migration[5.2]
def change
create_table :competition_prizes do |t|
t.references :competition
t.string :name
t.string :category
t.integer :num
t.timestamps
end
end
end

@ -0,0 +1,20 @@
class CreateCompetitionPrizeUsers < ActiveRecord::Migration[5.2]
def change
create_table :competition_prize_users do |t|
t.references :competition
t.references :competition_team
t.references :competition_prize
t.references :user
t.references :approver
t.string :status
t.integer :rank
t.boolean :leader, default: false
t.text :extra
t.datetime :approved_at
t.timestamps
end
end
end

File diff suppressed because one or more lines are too long

@ -20922,9 +20922,9 @@ span.CodeMirror-selectedtext {
}
/*! prefixes.scss v0.1.0 | Author: Pandao | https://github.com/pandao/prefixes.scss | MIT license | Copyright (c) 2015 */
/*!
* Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
/*!
* Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
*/
@font-face {
font-family: FontAwesome;
@ -25681,6 +25681,48 @@ input.form-control {
border: none;
}
/* line 91, app/assets/stylesheets/admins/competition_settings.scss */
.admins-competition-settings-index-page .large_panel .task_Input_div:nth-child(3n-2) > span.col-4 {
-webkit-box-flex: 0;
flex: 0 0 81px;
max-width: 81px;
}
/* line 95, app/assets/stylesheets/admins/competition_settings.scss */
.admins-competition-settings-index-page .large_panel .task_Input_div:nth-child(3n-2) {
-webkit-box-flex: 0;
flex: 0 0 50%;
max-width: 50%;
}
/* line 99, app/assets/stylesheets/admins/competition_settings.scss */
.admins-competition-settings-index-page .large_panel .task_Input_div:nth-child(3n-1) {
-webkit-box-flex: 0;
flex: 0 0 25%;
max-width: 25%;
}
/* line 103, app/assets/stylesheets/admins/competition_settings.scss */
.admins-competition-settings-index-page .large_panel .task_Input_div:nth-child(3n) {
-webkit-box-flex: 0;
flex: 0 0 33.3%;
max-width: 33.3%;
}
/* line 107, app/assets/stylesheets/admins/competition_settings.scss */
.admins-competition-settings-index-page .large_panel .task_Input_div:nth-child(3n) > span.col-4 {
-webkit-box-flex: 0;
flex: 0 0 25%;
max-width: 25%;
}
/* line 111, app/assets/stylesheets/admins/competition_settings.scss */
.admins-competition-settings-index-page .large_panel .task_Input_div:nth-child(3n) > div.col-6 {
-webkit-box-flex: 0;
flex: 0 0 58.3%;
max-width: 58.3%;
}
/* line 4, app/assets/stylesheets/admins/cooperatives.scss */
.admins-cooperatives-index-page .coo-img-card .coo-img-item > .drag {
cursor: move;
@ -26142,7 +26184,7 @@ input.form-control {
/* line 27, app/assets/stylesheets/admins/sidebar.scss */
#sidebar.active .sidebar-header-logo > .logo-label {
display: none;
display: none;
}
/* line 33, app/assets/stylesheets/admins/sidebar.scss */
@ -26214,25 +26256,25 @@ input.form-control {
/* line 83, app/assets/stylesheets/admins/sidebar.scss */
#sidebar .sidebar-header-logo {
display: -webkit-box;
display: flex;
-webkit-box-pack: justify;
justify-content: space-between;
-webkit-box-align: center;
align-items: center;
display: -webkit-box;
display: flex;
-webkit-box-pack: justify;
justify-content: space-between;
-webkit-box-align: center;
align-items: center;
}
/* line 88, app/assets/stylesheets/admins/sidebar.scss */
#sidebar .sidebar-header-logo > img {
width: 40px;
height: auto;
width: 40px;
height: auto;
}
/* line 93, app/assets/stylesheets/admins/sidebar.scss */
#sidebar .sidebar-header-logo > .logo-label {
font-size: 18px;
color: darkgrey;
margin-left: 10px;
font-size: 18px;
color: darkgrey;
margin-left: 10px;
}
/* line 101, app/assets/stylesheets/admins/sidebar.scss */
@ -26336,7 +26378,7 @@ input.form-control {
}
@media (max-width: 768px) {
/* line 182, app/assets/stylesheets/admins/sidebar.scss */
/* line 182, app/assets/stylesheets/admins/sidebar.scss */
#sidebar.active {
padding: 10px 5px;
min-width: 40px;
@ -26346,47 +26388,39 @@ input.form-control {
-webkit-transform: none;
transform: none;
}
/* line 190, app/assets/stylesheets/admins/sidebar.scss */
/* line 190, app/assets/stylesheets/admins/sidebar.scss */
#sidebar.active .sidebar-header {
padding: 0px;
}
/* line 193, app/assets/stylesheets/admins/sidebar.scss */
/* line 193, app/assets/stylesheets/admins/sidebar.scss */
#sidebar.active .sidebar-header .sidebar-header-logo {
display: none;
}
/* line 197, app/assets/stylesheets/admins/sidebar.scss */
/* line 197, app/assets/stylesheets/admins/sidebar.scss */
#sidebar.active .sidebar-header #sidebarCollapse {
width: 30px;
height: 20px;
}
/* line 203, app/assets/stylesheets/admins/sidebar.scss */
/* line 203, app/assets/stylesheets/admins/sidebar.scss */
#sidebar.active ul li a {
padding: 10px;
font-size: 0.85em;
}
/* line 207, app/assets/stylesheets/admins/sidebar.scss */
/* line 207, app/assets/stylesheets/admins/sidebar.scss */
#sidebar.active ul li a i {
margin-right: 0;
display: block;
margin-bottom: 5px;
}
/* line 214, app/assets/stylesheets/admins/sidebar.scss */
/* line 214, app/assets/stylesheets/admins/sidebar.scss */
#sidebar.active > ul > li > a > i {
font-size: 1.8em;
}
/* line 218, app/assets/stylesheets/admins/sidebar.scss */
/* line 218, app/assets/stylesheets/admins/sidebar.scss */
#sidebar.active ul ul a {
padding: 10px !important;
}
/* line 227, app/assets/stylesheets/admins/sidebar.scss */
/* line 227, app/assets/stylesheets/admins/sidebar.scss */
.dropdown-toggle::after {
top: auto;
bottom: 10px;

@ -29891,13 +29891,35 @@ function customConfirm(opts){
return $.confirm($.extend({}, defaultOpts, opts))
}
function show_success_flash(){
function customLoading(opts) {
var loading;
var defaultOpts = {
content: opts.ajax,
contentLoaded: function(){
setTimeout(function(){
loading.close()
}, 200);
}
}
loading = $.confirm($.extend({}, defaultOpts, opts));
return loading;
}
function show_success_flash(message){
$.notify({
message: '操作成功'
message: message || '操作成功'
},{
type: 'success'
});
}
function showErrorNotify(message){
$.notify({
message: message || '操作失败'
},{
type: 'danger'
});
}
;
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save