@ -0,0 +1,24 @@
|
|||||||
|
class BiddingUsersController < ApplicationController
|
||||||
|
before_action :require_login, :check_auth
|
||||||
|
|
||||||
|
def create
|
||||||
|
ProjectPackages::BiddingService.call(current_package, current_user)
|
||||||
|
render_ok
|
||||||
|
rescue ProjectPackages::BiddingService::Error => ex
|
||||||
|
render_error(ex.message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def win
|
||||||
|
package = current_user.project_packages.find(params[:project_package_id])
|
||||||
|
ProjectPackages::WinBiddingService.call(package, params)
|
||||||
|
render_ok
|
||||||
|
rescue ProjectPackages::WinBiddingService::Error => ex
|
||||||
|
render_error(ex.message)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def current_package
|
||||||
|
@_current_package ||= ProjectPackage.find(params[:project_package_id])
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,6 @@
|
|||||||
|
class ProjectPackageCategoriesController < ApplicationController
|
||||||
|
def index
|
||||||
|
categories = ProjectPackageCategory.cached_data
|
||||||
|
render_ok(count: categories.size, categories: categories)
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,78 @@
|
|||||||
|
class ProjectPackagesController < ApplicationController
|
||||||
|
include PaginateHelper
|
||||||
|
|
||||||
|
before_action :require_login, :check_auth, only: %i[create update destroy]
|
||||||
|
|
||||||
|
helper_method :current_package, :package_manageable?
|
||||||
|
|
||||||
|
def index
|
||||||
|
packages = ProjectPackage.where(status: %w(published bidding_ended bidding_finished))
|
||||||
|
|
||||||
|
packages = packages.where(project_package_category_id: params[:category_id]) if params[:category_id].present?
|
||||||
|
|
||||||
|
keyword = params[:keyword].to_s.strip
|
||||||
|
packages = packages.where('title LIKE ?', "%#{keyword}%") if keyword.present?
|
||||||
|
|
||||||
|
@count = packages.count
|
||||||
|
|
||||||
|
direction = params[:sort_direction] == 'asc' ? 'asc' : 'desc'
|
||||||
|
sort = params[:sort_by] == 'price' ? 'min_price' : 'published_at'
|
||||||
|
packages = packages.order("#{sort} #{direction}")
|
||||||
|
|
||||||
|
@packages = paginate packages.includes(:creator, :attachments, :project_package_category, bidding_users: :user)
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
return render_forbidden unless current_package.visitable? || package_manageable?
|
||||||
|
|
||||||
|
current_package.increment!(:visit_count)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
package = current_user.project_packages.new
|
||||||
|
ProjectPackages::SaveService.call(package, save_params)
|
||||||
|
|
||||||
|
package.increment!(:visit_count)
|
||||||
|
render_ok(id: package.id)
|
||||||
|
rescue ProjectPackages::SaveService::Error => ex
|
||||||
|
render_error(ex.message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
package = current_user.project_packages.find(params[:id])
|
||||||
|
return render_error('该状态下不能编辑') unless package.editable?
|
||||||
|
|
||||||
|
ProjectPackages::SaveService.call(package, save_params)
|
||||||
|
package.increment!(:visit_count)
|
||||||
|
render_ok(id: package.id)
|
||||||
|
rescue ProjectPackages::SaveService::Error => ex
|
||||||
|
render_error(ex.message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
package = ProjectPackage.find(params[:id])
|
||||||
|
return render_forbidden unless package.deletable? && package_manageable?
|
||||||
|
|
||||||
|
package.destroy!
|
||||||
|
|
||||||
|
Tiding.create!(user_id: package.creator_id, trigger_user_id: 1, container_id: package.id,
|
||||||
|
container_type: 'ProjectPackage', tiding_type: 'Destroyed', extra: package.title)
|
||||||
|
|
||||||
|
render_ok
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def current_package
|
||||||
|
@_current_package ||= ProjectPackage.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def package_manageable?
|
||||||
|
current_user&.id == current_package.creator_id || admin_or_business?
|
||||||
|
end
|
||||||
|
|
||||||
|
def save_params
|
||||||
|
params.permit(*%i[category_id title content attachment_ids deadline_at min_price max_price
|
||||||
|
contact_name contact_phone code publish])
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,5 @@
|
|||||||
|
module ProjectPackageDecorator
|
||||||
|
extend ApplicationDecorator
|
||||||
|
|
||||||
|
display_time_method :updated_at, :deadline_at, :published_at
|
||||||
|
end
|
@ -0,0 +1,15 @@
|
|||||||
|
class ProjectPackages::SaveForm
|
||||||
|
include ActiveModel::Model
|
||||||
|
|
||||||
|
attr_accessor :category_id, :title, :content, :attachment_ids, :deadline_at,
|
||||||
|
:min_price, :max_price, :contact_name, :contact_phone, :code, :publish
|
||||||
|
|
||||||
|
validates :category_id, presence: true
|
||||||
|
validates :title, presence: true, length: { maximum: 60 }
|
||||||
|
validates :content, presence: true
|
||||||
|
validates :deadline_at, presence: true
|
||||||
|
validates :min_price, numericality: { greater_than: 0 }, allow_blank: true
|
||||||
|
validates :max_price, numericality: { greater_than: ->(obj){ obj.min_price.to_i } }, allow_blank: true
|
||||||
|
validates :contact_name, presence: true, length: { maximum: 20 }
|
||||||
|
validates :contact_phone, presence: true, format: { with: /1\d{10}/ }
|
||||||
|
end
|
@ -0,0 +1,24 @@
|
|||||||
|
class BiddingUser < ApplicationRecord
|
||||||
|
include AASM
|
||||||
|
|
||||||
|
belongs_to :user
|
||||||
|
belongs_to :project_package, counter_cache: true
|
||||||
|
|
||||||
|
aasm(:status) do
|
||||||
|
state :pending, initiali: true
|
||||||
|
state :bidding_won
|
||||||
|
state :bidding_lost
|
||||||
|
|
||||||
|
event :win do
|
||||||
|
transitions from: [:pending], to: :bid_won
|
||||||
|
end
|
||||||
|
|
||||||
|
event :lose do
|
||||||
|
transitions from: [:pending], to: :bid_lost
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def status_text
|
||||||
|
I18n.t("bidding_user.status.#{status}")
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,78 @@
|
|||||||
|
class ProjectPackage < ApplicationRecord
|
||||||
|
include AASM
|
||||||
|
|
||||||
|
belongs_to :creator, class_name: 'User'
|
||||||
|
belongs_to :project_package_category
|
||||||
|
|
||||||
|
has_many :project_package_applies, dependent: :destroy
|
||||||
|
has_one :process_project_package_apply, -> { where(status: :pending) }, class_name: 'ProjectPackageApply'
|
||||||
|
|
||||||
|
has_many :bidding_users, dependent: :delete_all
|
||||||
|
has_many :win_bidding_users, -> { where(status: :bidding_won) }, class_name: 'BiddingUser'
|
||||||
|
has_many :lose_bidding_users, -> { where(status: :bidding_lost) }, class_name: 'BiddingUser'
|
||||||
|
|
||||||
|
has_many :attachments, as: :container, dependent: :destroy
|
||||||
|
|
||||||
|
aasm(:status) do
|
||||||
|
state :pending, initiali: true
|
||||||
|
state :applying
|
||||||
|
state :refused
|
||||||
|
state :published
|
||||||
|
state :bidding_ended
|
||||||
|
state :bidding_finished
|
||||||
|
|
||||||
|
event :apply do
|
||||||
|
transitions from: [:pending, :refused], to: :applying
|
||||||
|
end
|
||||||
|
|
||||||
|
event :refuse do
|
||||||
|
transitions from: :applying, to: :refused
|
||||||
|
end
|
||||||
|
|
||||||
|
event :publish do
|
||||||
|
transitions from: :applying, to: :published
|
||||||
|
end
|
||||||
|
|
||||||
|
event :end_bidding do
|
||||||
|
transitions from: :published, to: :bidding_ended
|
||||||
|
end
|
||||||
|
|
||||||
|
event :finish_bidding do
|
||||||
|
transitions from: [:bidding_ended], to: :bidding_finished
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def category_name
|
||||||
|
project_package_category.name
|
||||||
|
end
|
||||||
|
|
||||||
|
def visitable?
|
||||||
|
!editable?
|
||||||
|
end
|
||||||
|
|
||||||
|
def editable?
|
||||||
|
pending? || applying? || refused?
|
||||||
|
end
|
||||||
|
|
||||||
|
def deletable?
|
||||||
|
pending? || refused?
|
||||||
|
end
|
||||||
|
|
||||||
|
def deadline?
|
||||||
|
deadline_at < Time.now
|
||||||
|
end
|
||||||
|
|
||||||
|
def bidding_end?
|
||||||
|
flag = deadline?
|
||||||
|
ProjectPackages::EndBiddingService.call(self) if flag && may_end_bidding?
|
||||||
|
flag
|
||||||
|
end
|
||||||
|
|
||||||
|
def can_bidding?(user)
|
||||||
|
published? && !bidding_end? && user.id != creator_id && !bidding_users.exists?(user_id: user.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def status_text
|
||||||
|
I18n.t("project_package.status.#{status}")
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,19 @@
|
|||||||
|
class ProjectPackageApply < ApplicationRecord
|
||||||
|
include AASM
|
||||||
|
|
||||||
|
belongs_to :project_package
|
||||||
|
|
||||||
|
aasm(:status) do
|
||||||
|
state :pending, initiali: true
|
||||||
|
state :refused
|
||||||
|
state :agreed
|
||||||
|
|
||||||
|
event :refuse do
|
||||||
|
transitions from: :pending, to: :refused
|
||||||
|
end
|
||||||
|
|
||||||
|
event :agree do
|
||||||
|
transitions from: :pending, to: :agreed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,23 @@
|
|||||||
|
class ProjectPackageCategory < ApplicationRecord
|
||||||
|
default_scope { order(position: :asc) }
|
||||||
|
|
||||||
|
has_many :project_packages, dependent: :destroy
|
||||||
|
|
||||||
|
after_commit :reset_cache_data
|
||||||
|
|
||||||
|
def self.cached_data
|
||||||
|
Rails.cache.fetch(data_cache_key, expires_in: 1.days) do
|
||||||
|
ProjectPackageCategory.select(:id, :name).as_json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.data_cache_key
|
||||||
|
'project_package_category/cached_data'
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def reset_cache_data
|
||||||
|
Rails.cache.delete(self.class.data_cache_key)
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,36 @@
|
|||||||
|
class ProjectPackages::AgreeApplyService < ApplicationService
|
||||||
|
Error = Class.new(StandardError)
|
||||||
|
|
||||||
|
attr_reader :apply, :package
|
||||||
|
|
||||||
|
def initialize(apply)
|
||||||
|
@apply = apply
|
||||||
|
@package = apply.project_package
|
||||||
|
end
|
||||||
|
|
||||||
|
def call
|
||||||
|
raise Error, '该状态下不能进行此操作' unless apply.may_agree? && package.may_publish?
|
||||||
|
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
apply.agree!
|
||||||
|
|
||||||
|
# 发布
|
||||||
|
package.publish
|
||||||
|
package.published_at = Time.now
|
||||||
|
package.save!
|
||||||
|
|
||||||
|
# 消息
|
||||||
|
send_agree_notify!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def send_agree_notify!
|
||||||
|
Tiding.where(container_id: package.id, container_type: 'ProjectPackage',
|
||||||
|
tiding_type: 'Apply', status: 0).update_all(status: 1)
|
||||||
|
|
||||||
|
Tiding.create!(user_id: package.creator_id, trigger_user_id: 1,
|
||||||
|
container_id: package.id, container_type: 'ProjectPackage',
|
||||||
|
tiding_type: 'System', status: 1)
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,31 @@
|
|||||||
|
class ProjectPackages::ApplyPublishService < ApplicationService
|
||||||
|
Error = Class.new(StandardError)
|
||||||
|
|
||||||
|
attr_reader :package
|
||||||
|
|
||||||
|
def initialize(package)
|
||||||
|
@package = package
|
||||||
|
end
|
||||||
|
|
||||||
|
def call
|
||||||
|
return if package.applying?
|
||||||
|
|
||||||
|
raise Error, '该状态下不能申请发布' unless package.may_apply?
|
||||||
|
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
package.apply!
|
||||||
|
|
||||||
|
package.project_package_applies.create!
|
||||||
|
|
||||||
|
send_project_package_apply_notify!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def send_project_package_apply_notify!
|
||||||
|
Tiding.create!(user_id: 1, trigger_user_id: package.creator_id,
|
||||||
|
container_id: package.id, container_type: 'ProjectPackage',
|
||||||
|
tiding_type: 'Apply', status: 0)
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,29 @@
|
|||||||
|
class ProjectPackages::BiddingService < ApplicationService
|
||||||
|
Error = Class.new(StandardError)
|
||||||
|
|
||||||
|
attr_reader :package, :user
|
||||||
|
|
||||||
|
def initialize(package, user)
|
||||||
|
@package = package
|
||||||
|
@user = user
|
||||||
|
end
|
||||||
|
|
||||||
|
def call
|
||||||
|
raise Error, '竞标已截止' if package.bidding_end?
|
||||||
|
raise Error, '不能参与自己发布的竞标' if package.creator_id == user.id
|
||||||
|
raise Error, '您已参与竞标' if package.bidding_users.exists?(user_id: user.id)
|
||||||
|
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
package.bidding_users.create!(user_id: user.id)
|
||||||
|
|
||||||
|
send_bidding_notify!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def send_bidding_notify!
|
||||||
|
Tiding.create!(user_id: package.creator_id, trigger_user_id: user.id,
|
||||||
|
container_id: package.id, container_type: 'ProjectPackage', tiding_type: 'Bidding')
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,26 @@
|
|||||||
|
class ProjectPackages::EndBiddingService < ApplicationService
|
||||||
|
attr_reader :package
|
||||||
|
|
||||||
|
def initialize(package)
|
||||||
|
@package = package
|
||||||
|
end
|
||||||
|
|
||||||
|
def call
|
||||||
|
return unless package_deadline?
|
||||||
|
|
||||||
|
package.end_bidding!
|
||||||
|
|
||||||
|
send_bidding_end_notify!
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def send_bidding_end_notify!
|
||||||
|
Tiding.create!(user_id: package.creator_id, trigger_user_id: 1,
|
||||||
|
container_id: package.id, container_type: 'ProjectPackage', tiding_type: 'BiddingEnd')
|
||||||
|
end
|
||||||
|
|
||||||
|
def package_deadline?
|
||||||
|
package.may_end_bidding? && package.deadline_at < Time.now
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,38 @@
|
|||||||
|
class ProjectPackages::RefuseApplyService < ApplicationService
|
||||||
|
Error = Class.new(StandardError)
|
||||||
|
|
||||||
|
attr_reader :apply, :package, :params
|
||||||
|
|
||||||
|
def initialize(apply, params)
|
||||||
|
@apply = apply
|
||||||
|
@package = apply.project_package
|
||||||
|
@params = params
|
||||||
|
end
|
||||||
|
|
||||||
|
def call
|
||||||
|
raise Error, '该状态下不能进行此操作' unless apply.may_refuse? && package.may_refuse?
|
||||||
|
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
apply.refuse
|
||||||
|
apply.reason = params[:reason].to_s.strip
|
||||||
|
apply.save!
|
||||||
|
|
||||||
|
# 发布
|
||||||
|
package.refuse!
|
||||||
|
|
||||||
|
# 消息
|
||||||
|
send_refuse_notify!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def send_refuse_notify!
|
||||||
|
Tiding.where(container_id: package.id, container_type: 'ProjectPackage',
|
||||||
|
tiding_type: 'Apply', status: 0).update_all(status: 1)
|
||||||
|
|
||||||
|
Tiding.create!(user_id: package.creator_id, trigger_user_id: 1,
|
||||||
|
container_id: package.id, container_type: 'ProjectPackage',
|
||||||
|
tiding_type: 'System', status: 2, extra: apply.reason)
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,79 @@
|
|||||||
|
class ProjectPackages::SaveService < ApplicationService
|
||||||
|
Error = Class.new(StandardError)
|
||||||
|
|
||||||
|
attr_reader :package, :params
|
||||||
|
|
||||||
|
def initialize(package, params)
|
||||||
|
@package = package
|
||||||
|
@params = params
|
||||||
|
end
|
||||||
|
|
||||||
|
def call
|
||||||
|
ProjectPackages::SaveForm.new(params).validate!
|
||||||
|
|
||||||
|
check_code_valid! if need_check_code?
|
||||||
|
|
||||||
|
is_create = package.new_record?
|
||||||
|
raise Error, '类型不存在' unless ProjectPackageCategory.where(id: params[:category_id]).exists?
|
||||||
|
params[:project_package_category_id] = params[:category_id].to_i
|
||||||
|
|
||||||
|
raise Error, '竞标截止时间不能小于当前时间' if params[:deadline_at].present? && params[:deadline_at].to_time < Time.now
|
||||||
|
|
||||||
|
if params[:min_price].blank? && params[:max_price].present?
|
||||||
|
params[:min_price] = params[:max_price]
|
||||||
|
params[:max_price] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
package.assign_attributes(params)
|
||||||
|
package.save!
|
||||||
|
|
||||||
|
# 处理附件
|
||||||
|
deal_attachments
|
||||||
|
|
||||||
|
send_create_notify! if is_create
|
||||||
|
|
||||||
|
ProjectPackages::ApplyPublishService.call(package) if with_publish?
|
||||||
|
end
|
||||||
|
|
||||||
|
package
|
||||||
|
rescue ProjectPackages::ApplyPublishService::Error => ex
|
||||||
|
raise Error, ex.message
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def need_check_code?
|
||||||
|
(package.new_record? && params[:contact_phone] != package.creator.phone) ||
|
||||||
|
(!package.new_record? && package.contact_phone != params[:contact_phone])
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_code_valid!
|
||||||
|
raise Error, '验证码不能为空' if params[:code].blank?
|
||||||
|
|
||||||
|
code = VerificationCode.where(phone: params[:contact_phone], code_type: 9, code: params[:code]).last
|
||||||
|
raise Error, '无效的验证码' if code.blank? || !code.valid_code?
|
||||||
|
end
|
||||||
|
|
||||||
|
def deal_attachments
|
||||||
|
attachment_ids = Array.wrap(params[:attachment_ids]).compact.map(&:to_i) || []
|
||||||
|
old_attachment_ids = package.attachments.pluck(:id)
|
||||||
|
|
||||||
|
destroy_ids = old_attachment_ids - attachment_ids
|
||||||
|
package.attachments.where(id: destroy_ids).delete_all
|
||||||
|
|
||||||
|
new_ids = attachment_ids - old_attachment_ids
|
||||||
|
if new_ids.present?
|
||||||
|
Attachment.where(id: new_ids, container_id: nil).update_all(container_id: package.id, container_type: 'ProjectPackage')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_create_notify!
|
||||||
|
Tiding.create!(user_id: package.creator_id, trigger_user_id: 1,
|
||||||
|
container_id: package.id, container_type: 'ProjectPackage', tiding_type: 'Created')
|
||||||
|
end
|
||||||
|
|
||||||
|
def with_publish?
|
||||||
|
params[:publish].to_s == 'true'
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,50 @@
|
|||||||
|
class ProjectPackages::WinBiddingService < ApplicationService
|
||||||
|
Error = Class.new(StandardError)
|
||||||
|
|
||||||
|
attr_reader :package, :params
|
||||||
|
|
||||||
|
def initialize(package, params)
|
||||||
|
@package = package
|
||||||
|
@params = params
|
||||||
|
end
|
||||||
|
|
||||||
|
def call
|
||||||
|
raise Error, '竞标报名还未结束' unless package.bidding_end?
|
||||||
|
raise Error, '该状态下不能选择中标者' unless package.may_finish_bidding?
|
||||||
|
|
||||||
|
win_user_ids = Array.wrap(params[:user_ids]).compact.map(&:to_i)
|
||||||
|
bidding_user_ids = package.bidding_users.pluck(:user_id)
|
||||||
|
|
||||||
|
win_user_ids = bidding_user_ids & win_user_ids
|
||||||
|
raise Error, '请选择中标者' if win_user_ids.blank?
|
||||||
|
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
package.finish_bidding!
|
||||||
|
|
||||||
|
# win bidding users
|
||||||
|
package.bidding_users.where(user_id: win_user_ids).update_all(status: :bidding_won)
|
||||||
|
# lose bidding users
|
||||||
|
lost_user_ids = bidding_user_ids - win_user_ids
|
||||||
|
package.bidding_users.where(user_id: lost_user_ids).update_all(status: :bidding_lost)
|
||||||
|
|
||||||
|
send_bidding_result_notify!('BiddingWon', win_user_ids)
|
||||||
|
send_bidding_result_notify!('BiddingLost', lost_user_ids)
|
||||||
|
end
|
||||||
|
|
||||||
|
package
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def send_bidding_result_notify!(type, user_ids)
|
||||||
|
columns = %i[user_id trigger_user_id container_id container_type tiding_type created_at updated_at]
|
||||||
|
|
||||||
|
Tiding.bulk_insert(*columns) do |worker|
|
||||||
|
base_attr = { trigger_user_id: package.creator_id, container_id: package.id,
|
||||||
|
container_type: 'ProjectPackage', tiding_type: type }
|
||||||
|
user_ids.each do |user_id|
|
||||||
|
worker.add(base_attr.merge(user_id: user_id))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,12 @@
|
|||||||
|
class CheckProjectPackageDeadlineTask
|
||||||
|
def call
|
||||||
|
ProjectPackage.where(status: :published).where('deadline_at < ?', Time.now).find_each do |package|
|
||||||
|
begin
|
||||||
|
ProjectPackages::EndBiddingService.new(package).call
|
||||||
|
rescue => ex
|
||||||
|
Rails.logger.error ex.message
|
||||||
|
Rails.logger.error ex.backtrace.join('\n')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,13 @@
|
|||||||
|
json.count @count
|
||||||
|
json.project_packages do
|
||||||
|
json.array! @packages.each do |package|
|
||||||
|
json.extract! package, :id, :title, :content, :category_name, :status,
|
||||||
|
:visit_count, :bidding_users_count, :min_price, :max_price
|
||||||
|
|
||||||
|
json.category_id package.project_package_category_id
|
||||||
|
|
||||||
|
json.updated_at package.display_updated_at
|
||||||
|
json.deadline_at package.display_deadline_at
|
||||||
|
json.published_at package.display_published_at
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,43 @@
|
|||||||
|
package = current_package
|
||||||
|
|
||||||
|
json.extract! package, :id, :title, :content, :category_name, :status,
|
||||||
|
:visit_count, :bidding_users_count, :min_price, :max_price
|
||||||
|
|
||||||
|
json.category_id package.project_package_category_id
|
||||||
|
|
||||||
|
# 只有自己和管理员才返回私人信息
|
||||||
|
if package_manageable?
|
||||||
|
json.contact_name package.contact_name
|
||||||
|
json.contact_phone package.contact_phone
|
||||||
|
end
|
||||||
|
|
||||||
|
json.updated_at package.display_updated_at
|
||||||
|
json.deadline_at package.display_deadline_at
|
||||||
|
json.published_at package.display_published_at
|
||||||
|
|
||||||
|
json.creator do
|
||||||
|
json.partial! 'users/user_simple', user: package.creator
|
||||||
|
end
|
||||||
|
|
||||||
|
json.attachments do
|
||||||
|
json.array! package.attachments, partial: 'attachments/attachment_simple', as: :attachment
|
||||||
|
end
|
||||||
|
|
||||||
|
json.bidding_users do
|
||||||
|
json.array! package.bidding_users.includes(:user).each do |bidding_user|
|
||||||
|
json.partial! 'users/user_simple', user: bidding_user.user
|
||||||
|
|
||||||
|
json.status bidding_user.status
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
json.operation do
|
||||||
|
if current_user
|
||||||
|
manageable = package_manageable?
|
||||||
|
|
||||||
|
json.can_bidding package.can_bidding?(current_user)
|
||||||
|
json.can_select_bidding_user package.bidding_end? && package.bidding_ended? && manageable
|
||||||
|
json.can_edit package.editable? && manageable
|
||||||
|
json.can_delete package.deletable? && manageable
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,6 @@
|
|||||||
|
'zh-CN':
|
||||||
|
bidding_user:
|
||||||
|
status:
|
||||||
|
pending: 竞标中
|
||||||
|
bidding_won: 已中标
|
||||||
|
bidding_lost: 未中标
|
@ -0,0 +1,13 @@
|
|||||||
|
'zh-CN':
|
||||||
|
activemodel:
|
||||||
|
attributes:
|
||||||
|
project_packages/save_form:
|
||||||
|
category_id: 类型
|
||||||
|
title: 标题
|
||||||
|
content: 描述
|
||||||
|
deadline_at: 截止日期
|
||||||
|
min_price: 最小价格
|
||||||
|
max_price: 最大价格
|
||||||
|
contact_name: 联系人姓名
|
||||||
|
contact_phone: 联系人电话
|
||||||
|
code: 验证码
|
@ -0,0 +1,9 @@
|
|||||||
|
zh-CN:
|
||||||
|
project_package:
|
||||||
|
status:
|
||||||
|
pending: 已创建
|
||||||
|
applying: 审核中
|
||||||
|
refused: 已拒绝
|
||||||
|
published: 竞标中
|
||||||
|
bidding_ended: 待选标
|
||||||
|
bidding_finished: 已完成
|
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 9.6 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 154 KiB |
@ -0,0 +1,9 @@
|
|||||||
|
/*!
|
||||||
|
* Cropper.js v1.5.2
|
||||||
|
* https://fengyuanchen.github.io/cropperjs
|
||||||
|
*
|
||||||
|
* Copyright 2015-present Chen Fengyuan
|
||||||
|
* Released under the MIT license
|
||||||
|
*
|
||||||
|
* Date: 2019-06-30T06:01:02.389Z
|
||||||
|
*/.cropper-container{direction:ltr;font-size:0;line-height:0;position:relative;-ms-touch-action:none;touch-action:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.cropper-container img{display:block;height:100%;image-orientation:0deg;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;width:100%}.cropper-canvas,.cropper-crop-box,.cropper-drag-box,.cropper-modal,.cropper-wrap-box{bottom:0;left:0;position:absolute;right:0;top:0}.cropper-canvas,.cropper-wrap-box{overflow:hidden}.cropper-drag-box{background-color:#fff;opacity:0}.cropper-modal{background-color:#000;opacity:.5}.cropper-view-box{display:block;height:100%;outline:1px solid #39f;outline-color:rgba(51,153,255,.75);overflow:hidden;width:100%}.cropper-dashed{border:0 dashed #eee;display:block;opacity:.5;position:absolute}.cropper-dashed.dashed-h{border-bottom-width:1px;border-top-width:1px;height:33.33333%;left:0;top:33.33333%;width:100%}.cropper-dashed.dashed-v{border-left-width:1px;border-right-width:1px;height:100%;left:33.33333%;top:0;width:33.33333%}.cropper-center{display:block;height:0;left:50%;opacity:.75;position:absolute;top:50%;width:0}.cropper-center:after,.cropper-center:before{background-color:#eee;content:" ";display:block;position:absolute}.cropper-center:before{height:1px;left:-3px;top:0;width:7px}.cropper-center:after{height:7px;left:0;top:-3px;width:1px}.cropper-face,.cropper-line,.cropper-point{display:block;height:100%;opacity:.1;position:absolute;width:100%}.cropper-face{background-color:#fff;left:0;top:0}.cropper-line{background-color:#39f}.cropper-line.line-e{cursor:ew-resize;right:-3px;top:0;width:5px}.cropper-line.line-n{cursor:ns-resize;height:5px;left:0;top:-3px}.cropper-line.line-w{cursor:ew-resize;left:-3px;top:0;width:5px}.cropper-line.line-s{bottom:-3px;cursor:ns-resize;height:5px;left:0}.cropper-point{background-color:#39f;height:5px;opacity:.75;width:5px}.cropper-point.point-e{cursor:ew-resize;margin-top:-3px;right:-3px;top:50%}.cropper-point.point-n{cursor:ns-resize;left:50%;margin-left:-3px;top:-3px}.cropper-point.point-w{cursor:ew-resize;left:-3px;margin-top:-3px;top:50%}.cropper-point.point-s{bottom:-3px;cursor:s-resize;left:50%;margin-left:-3px}.cropper-point.point-ne{cursor:nesw-resize;right:-3px;top:-3px}.cropper-point.point-nw{cursor:nwse-resize;left:-3px;top:-3px}.cropper-point.point-sw{bottom:-3px;cursor:nesw-resize;left:-3px}.cropper-point.point-se{bottom:-3px;cursor:nwse-resize;height:20px;opacity:1;right:-3px;width:20px}@media (min-width:768px){.cropper-point.point-se{height:15px;width:15px}}@media (min-width:992px){.cropper-point.point-se{height:10px;width:10px}}@media (min-width:1200px){.cropper-point.point-se{height:5px;opacity:.75;width:5px}}.cropper-point.point-se:before{background-color:#39f;bottom:-50%;content:" ";display:block;height:200%;opacity:0;position:absolute;right:-50%;width:200%}.cropper-invisible{opacity:0}.cropper-bg{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC")}.cropper-hide{display:block;height:0;position:absolute;width:0}.cropper-hidden{display:none!important}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-drag-box,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed}
|
@ -1,103 +1,103 @@
|
|||||||
function Base64() {
|
function Base64() {
|
||||||
|
|
||||||
// private property
|
// private property
|
||||||
_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||||
|
|
||||||
// public method for encoding
|
// public method for encoding
|
||||||
this.encode = function (input) {
|
this.encode = function (input) {
|
||||||
var output = "";
|
var output = "";
|
||||||
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
|
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
|
||||||
var i = 0;
|
var i = 0;
|
||||||
input = _utf8_encode(input);
|
input = _utf8_encode(input);
|
||||||
while (i < input.length) {
|
while (i < input.length) {
|
||||||
chr1 = input.charCodeAt(i++);
|
chr1 = input.charCodeAt(i++);
|
||||||
chr2 = input.charCodeAt(i++);
|
chr2 = input.charCodeAt(i++);
|
||||||
chr3 = input.charCodeAt(i++);
|
chr3 = input.charCodeAt(i++);
|
||||||
enc1 = chr1 >> 2;
|
enc1 = chr1 >> 2;
|
||||||
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
|
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
|
||||||
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
|
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
|
||||||
enc4 = chr3 & 63;
|
enc4 = chr3 & 63;
|
||||||
if (isNaN(chr2)) {
|
if (isNaN(chr2)) {
|
||||||
enc3 = enc4 = 64;
|
enc3 = enc4 = 64;
|
||||||
} else if (isNaN(chr3)) {
|
} else if (isNaN(chr3)) {
|
||||||
enc4 = 64;
|
enc4 = 64;
|
||||||
}
|
}
|
||||||
output = output +
|
output = output +
|
||||||
_keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
|
_keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
|
||||||
_keyStr.charAt(enc3) + _keyStr.charAt(enc4);
|
_keyStr.charAt(enc3) + _keyStr.charAt(enc4);
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
// public method for decoding
|
// public method for decoding
|
||||||
this.decode = function (input) {
|
this.decode = function (input) {
|
||||||
var output = "";
|
var output = "";
|
||||||
var chr1, chr2, chr3;
|
var chr1, chr2, chr3;
|
||||||
var enc1, enc2, enc3, enc4;
|
var enc1, enc2, enc3, enc4;
|
||||||
var i = 0;
|
var i = 0;
|
||||||
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
|
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
|
||||||
while (i < input.length) {
|
while (i < input.length) {
|
||||||
enc1 = _keyStr.indexOf(input.charAt(i++));
|
enc1 = _keyStr.indexOf(input.charAt(i++));
|
||||||
enc2 = _keyStr.indexOf(input.charAt(i++));
|
enc2 = _keyStr.indexOf(input.charAt(i++));
|
||||||
enc3 = _keyStr.indexOf(input.charAt(i++));
|
enc3 = _keyStr.indexOf(input.charAt(i++));
|
||||||
enc4 = _keyStr.indexOf(input.charAt(i++));
|
enc4 = _keyStr.indexOf(input.charAt(i++));
|
||||||
chr1 = (enc1 << 2) | (enc2 >> 4);
|
chr1 = (enc1 << 2) | (enc2 >> 4);
|
||||||
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
||||||
chr3 = ((enc3 & 3) << 6) | enc4;
|
chr3 = ((enc3 & 3) << 6) | enc4;
|
||||||
output = output + String.fromCharCode(chr1);
|
output = output + String.fromCharCode(chr1);
|
||||||
if (enc3 != 64) {
|
if (enc3 != 64) {
|
||||||
output = output + String.fromCharCode(chr2);
|
output = output + String.fromCharCode(chr2);
|
||||||
}
|
}
|
||||||
if (enc4 != 64) {
|
if (enc4 != 64) {
|
||||||
output = output + String.fromCharCode(chr3);
|
output = output + String.fromCharCode(chr3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
output = _utf8_decode(output);
|
output = _utf8_decode(output);
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
// private method for UTF-8 encoding
|
// private method for UTF-8 encoding
|
||||||
_utf8_encode = function (string) {
|
_utf8_encode = function (string) {
|
||||||
string = string.replace(/\r\n/g,"\n");
|
string = string.replace(/\r\n/g,"\n");
|
||||||
var utftext = "";
|
var utftext = "";
|
||||||
for (var n = 0; n < string.length; n++) {
|
for (var n = 0; n < string.length; n++) {
|
||||||
var c = string.charCodeAt(n);
|
var c = string.charCodeAt(n);
|
||||||
if (c < 128) {
|
if (c < 128) {
|
||||||
utftext += String.fromCharCode(c);
|
utftext += String.fromCharCode(c);
|
||||||
} else if((c > 127) && (c < 2048)) {
|
} else if((c > 127) && (c < 2048)) {
|
||||||
utftext += String.fromCharCode((c >> 6) | 192);
|
utftext += String.fromCharCode((c >> 6) | 192);
|
||||||
utftext += String.fromCharCode((c & 63) | 128);
|
utftext += String.fromCharCode((c & 63) | 128);
|
||||||
} else {
|
} else {
|
||||||
utftext += String.fromCharCode((c >> 12) | 224);
|
utftext += String.fromCharCode((c >> 12) | 224);
|
||||||
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
|
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
|
||||||
utftext += String.fromCharCode((c & 63) | 128);
|
utftext += String.fromCharCode((c & 63) | 128);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return utftext;
|
return utftext;
|
||||||
}
|
}
|
||||||
|
|
||||||
// private method for UTF-8 decoding
|
// private method for UTF-8 decoding
|
||||||
_utf8_decode = function (utftext) {
|
_utf8_decode = function (utftext) {
|
||||||
var string = "";
|
var string = "";
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var c = c1 = c2 = 0;
|
var c = c1 = c2 = 0;
|
||||||
while ( i < utftext.length ) {
|
while ( i < utftext.length ) {
|
||||||
c = utftext.charCodeAt(i);
|
c = utftext.charCodeAt(i);
|
||||||
if (c < 128) {
|
if (c < 128) {
|
||||||
string += String.fromCharCode(c);
|
string += String.fromCharCode(c);
|
||||||
i++;
|
i++;
|
||||||
} else if((c > 191) && (c < 224)) {
|
} else if((c > 191) && (c < 224)) {
|
||||||
c2 = utftext.charCodeAt(i+1);
|
c2 = utftext.charCodeAt(i+1);
|
||||||
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
|
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
|
||||||
i += 2;
|
i += 2;
|
||||||
} else {
|
} else {
|
||||||
c2 = utftext.charCodeAt(i+1);
|
c2 = utftext.charCodeAt(i+1);
|
||||||
c3 = utftext.charCodeAt(i+2);
|
c3 = utftext.charCodeAt(i+2);
|
||||||
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
|
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
|
||||||
i += 3;
|
i += 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,19 @@
|
|||||||
|
/**
|
||||||
|
EDU_ADMIN = 1 # 超级管理员
|
||||||
|
EDU_BUSINESS = 2 # 运营人员
|
||||||
|
EDU_SHIXUN_MANAGER = 3 # 实训管理员
|
||||||
|
EDU_SHIXUN_MEMBER = 4 # 实训成员
|
||||||
|
EDU_CERTIFICATION_TEACHER = 5 # 平台认证的老师
|
||||||
|
EDU_GAME_MANAGER = 6 # TPI的创建者
|
||||||
|
EDU_TEACHER = 7 # 平台老师,但是未认证
|
||||||
|
EDU_NORMAL = 8 # 普通用户
|
||||||
|
*/
|
||||||
|
|
||||||
export const EDU_ADMIN = 1 // 超级管理员
|
export const EDU_ADMIN = 1 // 超级管理员
|
||||||
export const EDU_SHIXUN_MANAGER = 2 // 实训管理员
|
export const EDU_BUSINESS = 2 // # 运营人员
|
||||||
export const EDU_SHIXUN_MEMBER = 3 // 实训成员
|
export const EDU_SHIXUN_MANAGER = 3 // 实训管理员
|
||||||
export const EDU_CERTIFICATION_TEACHER = 4 // 平台认证的老师
|
export const EDU_SHIXUN_MEMBER = 4 // 实训成员
|
||||||
export const EDU_GAME_MANAGER = 5 // TPI的创建者
|
export const EDU_CERTIFICATION_TEACHER = 5 // 平台认证的老师
|
||||||
export const EDU_TEACHER = 6 // 平台老师,但是未认证
|
export const EDU_GAME_MANAGER = 6 // TPI的创建者
|
||||||
export const EDU_NORMAL = 7 // 普通用户
|
export const EDU_TEACHER = 7 // 平台老师,但是未认证
|
||||||
|
export const EDU_NORMAL = 8 // 普通用户
|
@ -1,6 +1,6 @@
|
|||||||
export function bytesToSize(bytes) {
|
export function bytesToSize(bytes) {
|
||||||
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||||
if (bytes == 0) return '0 Byte';
|
if (bytes == 0) return '0 Byte';
|
||||||
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
|
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
|
||||||
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
|
return parseFloat(bytes / Math.pow(1024, i), 2).toFixed(1) + ' ' + sizes[i];
|
||||||
}
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export const themes = {
|
||||||
|
light: {
|
||||||
|
foreground: '#000000',
|
||||||
|
background: '#eeeeee',
|
||||||
|
foreground_select: '#4CACFF'
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
|
foreground: '#ffffff',
|
||||||
|
background: '#222222',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ThemeContext = React.createContext(
|
||||||
|
themes.light // default value
|
||||||
|
);
|
@ -0,0 +1,48 @@
|
|||||||
|
import React, { useState, useEffect, memo } from 'react';
|
||||||
|
import ImageLayer from '../../modules/page/layers/ImageLayer';
|
||||||
|
import { isImageExtension } from 'educoder';
|
||||||
|
const $ = window.$;
|
||||||
|
function ImageLayerHook(props) {
|
||||||
|
const [showImage, setShowImage] = useState(false)
|
||||||
|
const [imageSrc, setImageSrc] = useState('')
|
||||||
|
|
||||||
|
const { parentSel, childSel, watchPropsArray } = props
|
||||||
|
|
||||||
|
const onImageLayerClose = () => {
|
||||||
|
setShowImage(false)
|
||||||
|
setImageSrc('')
|
||||||
|
}
|
||||||
|
const onDelegateClick = (event) => {
|
||||||
|
const imageSrc = event.target.src || event.target.getAttribute('src') || event.target.getAttribute('href')
|
||||||
|
// 判断imageSrc是否是图片
|
||||||
|
const fileName = event.target.innerHTML.trim()
|
||||||
|
if (isImageExtension(imageSrc.trim()) || isImageExtension(fileName) || event.target.tagName == 'IMG' || imageSrc.indexOf('base64,') != -1) {
|
||||||
|
// 非回复里的头像图片; 非emoticons
|
||||||
|
if (imageSrc.indexOf('/images/avatars/User') === -1 &&
|
||||||
|
imageSrc.indexOf('kindeditor/plugins/emoticons') === -1 ) {
|
||||||
|
setShowImage(true)
|
||||||
|
setImageSrc(imageSrc)
|
||||||
|
}
|
||||||
|
event.stopPropagation()
|
||||||
|
event.preventDefault && event.preventDefault()
|
||||||
|
event.originalEvent.preventDefault()
|
||||||
|
// event.originalEvent.stopPropagation()
|
||||||
|
// event.originalEvent.cancelBubble = true
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
$(parentSel)
|
||||||
|
.delegate(childSel, "click", onDelegateClick);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
$(parentSel).undelegate(childSel, "click", onDelegateClick )
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ImageLayer showImage={showImage} imageSrc={imageSrc} onImageLayerClose={onImageLayerClose}></ImageLayer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(ImageLayerHook)
|
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 29 KiB |
@ -1,272 +0,0 @@
|
|||||||
import React,{ Component } from "react";
|
|
||||||
import { Modal,Checkbox,notification} from "antd";
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
class ShixunWorkModal extends Component{
|
|
||||||
constructor(props){
|
|
||||||
super(props);
|
|
||||||
this.state={
|
|
||||||
course_groups:undefined,
|
|
||||||
limit:10,
|
|
||||||
page:1,
|
|
||||||
group_ids:undefined,
|
|
||||||
group_list:undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
componentDidMount() {
|
|
||||||
|
|
||||||
let url="/homework_commons/"+this.props.match.params.homeworkid+"/group_list.json";
|
|
||||||
|
|
||||||
axios.get(url,{params:{
|
|
||||||
limit:10,
|
|
||||||
page:1,
|
|
||||||
}
|
|
||||||
}).then((response) => {
|
|
||||||
if(response.data.group_list===undefined){
|
|
||||||
this.setState({
|
|
||||||
course_groups:response.data,
|
|
||||||
group_list:undefined
|
|
||||||
})
|
|
||||||
}else{
|
|
||||||
this.setState({
|
|
||||||
course_groups:response.data,
|
|
||||||
group_list:response.data.group_list
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}).catch((error) => {
|
|
||||||
console.log(error)
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//勾选实训
|
|
||||||
shixunhomeworkedit=(checkedValues)=>{
|
|
||||||
let{group_list}=this.state;
|
|
||||||
if(checkedValues.length===group_list.length){
|
|
||||||
this.setState({
|
|
||||||
onChangetype:true,
|
|
||||||
group_ids:checkedValues
|
|
||||||
})
|
|
||||||
}else{
|
|
||||||
this.setState({
|
|
||||||
group_ids:checkedValues,
|
|
||||||
onChangetype:false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contentViewScroll=(e)=>{
|
|
||||||
//滑动到底判断
|
|
||||||
if(e.currentTarget.scrollHeight-e.currentTarget.scrollTop===e.currentTarget.clientHeight){
|
|
||||||
let {page,limit,group_list}=this.state;
|
|
||||||
let newpage=page+1;
|
|
||||||
let newgroup_list=group_list;
|
|
||||||
|
|
||||||
let url="/homework_commons/"+this.props.match.params.homeworkid+"/group_list.json";
|
|
||||||
|
|
||||||
axios.get(url,{params:{
|
|
||||||
limit:limit,
|
|
||||||
page:newpage,
|
|
||||||
}
|
|
||||||
}).then((response) => {
|
|
||||||
response.data. course_groups.group_list&&response.data.group_list.map((item,key)=>{
|
|
||||||
newgroup_list.push(item)
|
|
||||||
})
|
|
||||||
this.setState({
|
|
||||||
course_groups:response.data,
|
|
||||||
group_list:newgroup_list,
|
|
||||||
page:newpage
|
|
||||||
})
|
|
||||||
}).catch((error) => {
|
|
||||||
console.log(error)
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onChange=(e)=>{
|
|
||||||
let{group_list}=this.state;
|
|
||||||
|
|
||||||
let {data}=this.props;
|
|
||||||
if(e.target.checked===true){
|
|
||||||
if(data&&data.length===0){
|
|
||||||
let id=[]
|
|
||||||
group_list.forEach((item,key)=>{
|
|
||||||
id.push(item.id)
|
|
||||||
})
|
|
||||||
this.setState({
|
|
||||||
group_ids:id,
|
|
||||||
onChangetype:e.target.checked
|
|
||||||
})
|
|
||||||
}else{
|
|
||||||
let id=[]
|
|
||||||
group_list.forEach((item,key)=>{
|
|
||||||
id.push(item.id)
|
|
||||||
})
|
|
||||||
this.setState({
|
|
||||||
group_ids:id,
|
|
||||||
onChangetype:e.target.checked
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
this.setState({
|
|
||||||
group_ids:[],
|
|
||||||
onChangetype:e.target.checked
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
isSave=()=>{
|
|
||||||
let{group_ids}=this.state;
|
|
||||||
let url="/homework_commons/"+this.props.match.params.homeworkid+"/homework_code_repeat.json";
|
|
||||||
axios.post(url, {
|
|
||||||
group_ids: group_ids,
|
|
||||||
})
|
|
||||||
.then((response) => {
|
|
||||||
if (response.data.status === 0) {
|
|
||||||
this.props.updatas()
|
|
||||||
this.props.issCancel()
|
|
||||||
notification.open({
|
|
||||||
message:"提示",
|
|
||||||
description: response.data.message
|
|
||||||
});
|
|
||||||
console.log(this.props)
|
|
||||||
}else if(response.data.status === -1){
|
|
||||||
notification.open({
|
|
||||||
message:"提示",
|
|
||||||
description: response.data.message
|
|
||||||
});
|
|
||||||
}else if(response.data.status === -2){
|
|
||||||
notification.open({
|
|
||||||
message:"提示",
|
|
||||||
description: response.data.message
|
|
||||||
});
|
|
||||||
}else if(response.data.status === -3){
|
|
||||||
notification.open({
|
|
||||||
message:"提示",
|
|
||||||
description: response.data.message
|
|
||||||
});
|
|
||||||
}else if(response.data.status === -4){
|
|
||||||
notification.open({
|
|
||||||
message:"提示",
|
|
||||||
description: response.data.message
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(function (error) {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
issCancel=()=>{
|
|
||||||
this.props.issCancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
render(){
|
|
||||||
let {course_groups,group_ids,onChangetype,group_list}=this.state;
|
|
||||||
// let {data}=this.props;
|
|
||||||
// console.log(group_list)
|
|
||||||
// console.log(course_groups)
|
|
||||||
return(
|
|
||||||
<div>
|
|
||||||
<Modal
|
|
||||||
keyboard={false}
|
|
||||||
className={"HomeworkModal"}
|
|
||||||
title={this.props.modalname}
|
|
||||||
visible={this.props.visible}
|
|
||||||
closable={false}
|
|
||||||
footer={null}
|
|
||||||
destroyOnClose={true}
|
|
||||||
>
|
|
||||||
<div className="task-popup-content">
|
|
||||||
|
|
||||||
<style>{`
|
|
||||||
.greybackHead{
|
|
||||||
padding:0px 30px;
|
|
||||||
}
|
|
||||||
.fontlefts{text-align: left;}
|
|
||||||
`}</style>
|
|
||||||
<ul className="clearfix edu-txt-center">
|
|
||||||
<li className="fl paddingleft22 fontlefts" style={{width:'150px'}}>分班名称</li>
|
|
||||||
<li className="fl edu-txt-left" style={{width:'150px'}}>有效作品数</li>
|
|
||||||
<li className="fl" style={{width:'100px'}}>上次查重时间</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
{course_groups===undefined?"":
|
|
||||||
<ul className="upload_select_box fl clearfix mt10 mb10" tyle={{"overflow-y":"auto"}}id="search_not_members_list"
|
|
||||||
|
|
||||||
onScroll={this.contentViewScroll}
|
|
||||||
>
|
|
||||||
|
|
||||||
|
|
||||||
<Checkbox.Group style={{ width: '100%' }} onChange={this.shixunhomeworkedit} value={group_ids}>
|
|
||||||
{
|
|
||||||
group_list===undefined?
|
|
||||||
|
|
||||||
<div className="clearfix edu-txt-center lineh-40 bor-bottom-greyE">
|
|
||||||
<li className="fl" style={{width: '100px'}}>
|
|
||||||
<Checkbox
|
|
||||||
className="fl task-hide edu-txt-left"
|
|
||||||
name="shixun_homework[]"
|
|
||||||
value={course_groups.ungroup_list.id}
|
|
||||||
>
|
|
||||||
<label style={{"textAlign": "left", "color": "#05101A"}}
|
|
||||||
className="task-hide color-grey-name" title="frerere">{course_groups.ungroup_list.name}</label>
|
|
||||||
</Checkbox>
|
|
||||||
</li>
|
|
||||||
<li className="fl" style={{width: '150px'}}>
|
|
||||||
{course_groups.ungroup_list.works_count}
|
|
||||||
</li>
|
|
||||||
<li className="fl" style={{width: '160px'}}>
|
|
||||||
{course_groups.ungroup_list.last_review_time}
|
|
||||||
</li>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
:
|
|
||||||
group_list&&group_list.map((item,key)=>{
|
|
||||||
return(
|
|
||||||
<div className="clearfix edu-txt-center lineh-40 bor-bottom-greyE" key={key}>
|
|
||||||
<li className="fl" style={{width: '100px'}}>
|
|
||||||
<Checkbox
|
|
||||||
className="fl task-hide edu-txt-left"
|
|
||||||
name="shixun_homework[]"
|
|
||||||
value={item.id}
|
|
||||||
>
|
|
||||||
<label style={{"textAlign": "left", "color": "#05101A"}}
|
|
||||||
className="task-hide color-grey-name" title="frerere">{item.name}</label>
|
|
||||||
</Checkbox>
|
|
||||||
</li>
|
|
||||||
<li className="fl" style={{width: '150px'}}>
|
|
||||||
{item.works_count}
|
|
||||||
</li>
|
|
||||||
<li className="fl" style={{width: '160px'}}>
|
|
||||||
{item.last_review_time}
|
|
||||||
</li>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</Checkbox.Group>
|
|
||||||
</ul>
|
|
||||||
}
|
|
||||||
<div className={"clearfix"}>
|
|
||||||
<Checkbox checked={onChangetype} onChange={this.onChange} className={"ml10"}>{onChangetype===true?"清除":"全选"}</Checkbox>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="clearfix mt30 edu-txt-center mb10">
|
|
||||||
<a className="task-btn color-white mr30" onClick={this.issCancel}>取消</a>
|
|
||||||
<a className="task-btn task-btn-orange" onClick={this.isSave}>确认</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export default ShixunWorkModal;
|
|