diff --git a/.gitignore b/.gitignore index 09b9eab36..98965d6da 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ /.idea/* # Ignore react node_modules +/public/react/.cache /public/react/build /public/react/build/ /public/react/node_modules/ @@ -60,5 +61,4 @@ vendor/bundle/ /public/images/avatars /public/files /workspace -/log - +/log \ No newline at end of file diff --git a/Gemfile b/Gemfile index 4cabd09b9..44f4937ad 100644 --- a/Gemfile +++ b/Gemfile @@ -91,3 +91,5 @@ gem 'bulk_insert' # elasticsearch gem 'searchkick' + +gem 'aasm' diff --git a/Gemfile.lock b/Gemfile.lock index 94893687b..60e1ae811 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,6 +8,8 @@ PATH GEM remote: https://gems.ruby-china.com/ specs: + aasm (5.0.5) + concurrent-ruby (~> 1.0) actioncable (5.2.1) actionpack (= 5.2.1) nio4r (~> 2.0) @@ -324,6 +326,7 @@ PLATFORMS ruby DEPENDENCIES + aasm active_decorator acts-as-taggable-on (~> 6.0) awesome_print diff --git a/app/controllers/bidding_users_controller.rb b/app/controllers/bidding_users_controller.rb new file mode 100644 index 000000000..ad0de6587 --- /dev/null +++ b/app/controllers/bidding_users_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/project_package_categories_controller.rb b/app/controllers/project_package_categories_controller.rb new file mode 100644 index 000000000..80d364a05 --- /dev/null +++ b/app/controllers/project_package_categories_controller.rb @@ -0,0 +1,6 @@ +class ProjectPackageCategoriesController < ApplicationController + def index + categories = ProjectPackageCategory.cached_data + render_ok(count: categories.size, categories: categories) + end +end \ No newline at end of file diff --git a/app/controllers/project_packages_controller.rb b/app/controllers/project_packages_controller.rb new file mode 100644 index 000000000..de896c920 --- /dev/null +++ b/app/controllers/project_packages_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/subjects_controller.rb b/app/controllers/subjects_controller.rb index 37206a52f..b372eb2c0 100644 --- a/app/controllers/subjects_controller.rb +++ b/app/controllers/subjects_controller.rb @@ -194,7 +194,7 @@ class SubjectsController < ApplicationController stages.each do |stage| category = CourseSecondCategory.where(name: stage.name, course_id: @course.id, category_type: "shixun_homework").first || CourseSecondCategory.create!(name: stage.name, course_id: @course.id, category_type: "shixun_homework", - course_module_id: course_module, position: course_module.course_second_categories.count + 1) + course_module_id: course_module.id, position: course_module.course_second_categories.count + 1) stage.shixuns.where(id: params[:shixun_ids], status: 2).each do |shixun| homework = HomeworksService.new.create_homework shixun, @course, category, current_user diff --git a/app/decorators/project_package_decorator.rb b/app/decorators/project_package_decorator.rb new file mode 100644 index 000000000..7dec24565 --- /dev/null +++ b/app/decorators/project_package_decorator.rb @@ -0,0 +1,5 @@ +module ProjectPackageDecorator + extend ApplicationDecorator + + display_time_method :updated_at, :deadline_at, :published_at +end \ No newline at end of file diff --git a/app/forms/project_packages/save_form.rb b/app/forms/project_packages/save_form.rb new file mode 100644 index 000000000..5cc801d2d --- /dev/null +++ b/app/forms/project_packages/save_form.rb @@ -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 diff --git a/app/models/bidding_user.rb b/app/models/bidding_user.rb new file mode 100644 index 000000000..b518fb45e --- /dev/null +++ b/app/models/bidding_user.rb @@ -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 \ No newline at end of file diff --git a/app/models/project_package.rb b/app/models/project_package.rb new file mode 100644 index 000000000..fc541097a --- /dev/null +++ b/app/models/project_package.rb @@ -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 diff --git a/app/models/project_package_apply.rb b/app/models/project_package_apply.rb new file mode 100644 index 000000000..5116f075f --- /dev/null +++ b/app/models/project_package_apply.rb @@ -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 \ No newline at end of file diff --git a/app/models/project_package_category.rb b/app/models/project_package_category.rb new file mode 100644 index 000000000..403fdf4cb --- /dev/null +++ b/app/models/project_package_category.rb @@ -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 \ No newline at end of file diff --git a/app/services/project_packages/agree_apply_service.rb b/app/services/project_packages/agree_apply_service.rb new file mode 100644 index 000000000..7cef7f70c --- /dev/null +++ b/app/services/project_packages/agree_apply_service.rb @@ -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 \ No newline at end of file diff --git a/app/services/project_packages/apply_publish_service.rb b/app/services/project_packages/apply_publish_service.rb new file mode 100644 index 000000000..157ed2cb6 --- /dev/null +++ b/app/services/project_packages/apply_publish_service.rb @@ -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 diff --git a/app/services/project_packages/bidding_service.rb b/app/services/project_packages/bidding_service.rb new file mode 100644 index 000000000..125f707e7 --- /dev/null +++ b/app/services/project_packages/bidding_service.rb @@ -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 \ No newline at end of file diff --git a/app/services/project_packages/end_bidding_service.rb b/app/services/project_packages/end_bidding_service.rb new file mode 100644 index 000000000..6d43db949 --- /dev/null +++ b/app/services/project_packages/end_bidding_service.rb @@ -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 \ No newline at end of file diff --git a/app/services/project_packages/refuse_apply_service.rb b/app/services/project_packages/refuse_apply_service.rb new file mode 100644 index 000000000..142efe1e0 --- /dev/null +++ b/app/services/project_packages/refuse_apply_service.rb @@ -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 diff --git a/app/services/project_packages/save_service.rb b/app/services/project_packages/save_service.rb new file mode 100644 index 000000000..bcfc19a10 --- /dev/null +++ b/app/services/project_packages/save_service.rb @@ -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 \ No newline at end of file diff --git a/app/services/project_packages/win_bidding_service.rb b/app/services/project_packages/win_bidding_service.rb new file mode 100644 index 000000000..831c29449 --- /dev/null +++ b/app/services/project_packages/win_bidding_service.rb @@ -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 \ No newline at end of file diff --git a/app/tasks/check_project_package_deadline_task.rb b/app/tasks/check_project_package_deadline_task.rb new file mode 100644 index 000000000..4ccec257e --- /dev/null +++ b/app/tasks/check_project_package_deadline_task.rb @@ -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 diff --git a/app/views/project_packages/index.json.jbuilder b/app/views/project_packages/index.json.jbuilder new file mode 100644 index 000000000..e26efe36e --- /dev/null +++ b/app/views/project_packages/index.json.jbuilder @@ -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 \ No newline at end of file diff --git a/app/views/project_packages/show.json.jbuilder b/app/views/project_packages/show.json.jbuilder new file mode 100644 index 000000000..7fee5af29 --- /dev/null +++ b/app/views/project_packages/show.json.jbuilder @@ -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 \ No newline at end of file diff --git a/config/locales/bidding_users/zh-CN.yml b/config/locales/bidding_users/zh-CN.yml new file mode 100644 index 000000000..6420725fd --- /dev/null +++ b/config/locales/bidding_users/zh-CN.yml @@ -0,0 +1,6 @@ +'zh-CN': + bidding_user: + status: + pending: 竞标中 + bidding_won: 已中标 + bidding_lost: 未中标 \ No newline at end of file diff --git a/config/locales/forms/save_project_package_form.zh-CN.yml b/config/locales/forms/save_project_package_form.zh-CN.yml new file mode 100644 index 000000000..662e59941 --- /dev/null +++ b/config/locales/forms/save_project_package_form.zh-CN.yml @@ -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: 验证码 diff --git a/config/locales/project_packages/zh-CN.yml b/config/locales/project_packages/zh-CN.yml new file mode 100644 index 000000000..82075ffab --- /dev/null +++ b/config/locales/project_packages/zh-CN.yml @@ -0,0 +1,9 @@ +zh-CN: + project_package: + status: + pending: 已创建 + applying: 审核中 + refused: 已拒绝 + published: 竞标中 + bidding_ended: 待选标 + bidding_finished: 已完成 \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index d69d432b3..9da4d4a7e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -683,6 +683,13 @@ Rails.application.routes.draw do resources :students, only: [:index] end end + + resources :project_package_categories, only: [:index] + resources :project_packages, only: [:index, :show, :create, :update, :destroy] do + resources :bidding_users, only: [:create] do + post :win, on: :collection + end + end end #git 认证回调 diff --git a/public/images/course/guide/1-1.png b/public/images/course/guide/1-1.png new file mode 100644 index 000000000..c59467c5d Binary files /dev/null and b/public/images/course/guide/1-1.png differ diff --git a/public/images/course/guide/1-2.png b/public/images/course/guide/1-2.png new file mode 100644 index 000000000..1a4f26f38 Binary files /dev/null and b/public/images/course/guide/1-2.png differ diff --git a/public/images/course/guide/1-3.png b/public/images/course/guide/1-3.png new file mode 100644 index 000000000..f54250747 Binary files /dev/null and b/public/images/course/guide/1-3.png differ diff --git a/public/images/course/guide/1-4.png b/public/images/course/guide/1-4.png new file mode 100644 index 000000000..6ecd3bfb7 Binary files /dev/null and b/public/images/course/guide/1-4.png differ diff --git a/public/images/course/guide/1-5.png b/public/images/course/guide/1-5.png new file mode 100644 index 000000000..c4205496d Binary files /dev/null and b/public/images/course/guide/1-5.png differ diff --git a/public/images/course/guide/1-6.png b/public/images/course/guide/1-6.png new file mode 100644 index 000000000..5d5f76f7f Binary files /dev/null and b/public/images/course/guide/1-6.png differ diff --git a/public/images/course/guide/1-7.png b/public/images/course/guide/1-7.png new file mode 100644 index 000000000..65cadf0c7 Binary files /dev/null and b/public/images/course/guide/1-7.png differ diff --git a/public/images/educoder/EWM.jpg b/public/images/educoder/EWM.jpg new file mode 100644 index 000000000..3b79aca8b Binary files /dev/null and b/public/images/educoder/EWM.jpg differ diff --git a/public/javascripts/educoder/edu_application.js b/public/javascripts/educoder/edu_application.js index 91e04ce80..612a5a44b 100644 --- a/public/javascripts/educoder/edu_application.js +++ b/public/javascripts/educoder/edu_application.js @@ -1,1288 +1,1288 @@ -document.write(""); - -/*! - * JavaScript Cookie v2.2.0 - * https://github.com/js-cookie/js-cookie - * - * Copyright 2006, 2015 Klaus Hartl & Fagner Brack - * Released under the MIT license - */ -!function(e) { - var n; - if ("function" == typeof define && define.amd && (define(e), - n = !0), - "object" == typeof exports && (module.exports = e(), - n = !0), - !n) { - var t = window.Cookies - , o = window.Cookies = e(); - o.noConflict = function() { - return window.Cookies = t, - o - } - } -}(function() { - function e() { - for (var e = 0, n = {}; e < arguments.length; e++) { - var t = arguments[e]; - for (var o in t) - n[o] = t[o] - } - return n - } - function n(e) { - return e.replace(/(%[0-9A-Z]{2})+/g, decodeURIComponent) - } - return function t(o) { - function r() {} - function i(n, t, i) { - if ("undefined" != typeof document) { - "number" == typeof (i = e({ - path: "/" - }, r.defaults, i)).expires && (i.expires = new Date(1 * new Date + 864e5 * i.expires)), - i.expires = i.expires ? i.expires.toUTCString() : ""; - try { - var c = JSON.stringify(t); - /^[\{\[]/.test(c) && (t = c) - } catch (e) {} - t = o.write ? o.write(t, n) : encodeURIComponent(String(t)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent), - n = encodeURIComponent(String(n)).replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent).replace(/[\(\)]/g, escape); - var f = ""; - for (var u in i) - i[u] && (f += "; " + u, - !0 !== i[u] && (f += "=" + i[u].split(";")[0])); - return document.cookie = n + "=" + t + f - } - } - function c(e, t) { - if ("undefined" != typeof document) { - for (var r = {}, i = document.cookie ? document.cookie.split("; ") : [], c = 0; c < i.length; c++) { - var f = i[c].split("=") - , u = f.slice(1).join("="); - t || '"' !== u.charAt(0) || (u = u.slice(1, -1)); - try { - var a = n(f[0]); - if (u = (o.read || o)(u, a) || n(u), - t) - try { - u = JSON.parse(u) - } catch (e) {} - if (r[a] = u, - e === a) - break - } catch (e) {} - } - return e ? r[e] : r - } - } - return r.set = i, - r.get = function(e) { - return c(e, !1) - } - , - r.getJSON = function(e) { - return c(e, !0) - } - , - r.remove = function(n, t) { - i(n, "", e(t, { - expires: -1 - })) - } - , - r.defaults = {}, - r.withConverter = t, - r - }(function() {}) -}); - -$(function() { - var result = location.search.match(/\?search=(\w*)&?/i) - if (result && result[1]) { - var searchText = result[1] - $('#search-input').val(searchText) - } - // 未报名用户登录时弹框 - // console.log(Cookies.get('enroll_status')); - // if(Cookies.get('enroll_status') == 0){ - // Cookies.remove('enroll_status'); - // var html='
'+ - // '
'+ - // ''+ - // ''+ - // '立即报名'+ - // '
'; - // $(".newContainer").append(html); - // } -}); - -function CloseBox() { - $(".CompetitionEnrollBox").remove(); -} - -//根据页面大小决定侧边栏的位置 -$(window).resize(function() { - rightSlider(); -}); -function rightSlider() { - var poi = parseInt((parseInt($(window).width()) - 1200) / 2) - 60; - // console.log(parseInt($(window).width())+" "+poi); - if (poi > 0) { - $(".-task-sidebar").css("right", poi); - } else { - $(".-task-sidebar").css("right", "0px"); - } - $(".-task-sidebar").show(); -} -function open_course(id, allowVisit) { - if (allowVisit) { - window.open("/courses/" + id); - } -} -function open_project(id, allowVisit) { - if (allowVisit) { - window.open("/projects/" + id); - } -} - -function conver_size(limit) { - var size = ""; - if (limit < 1024) { - //如果小于1KB转化成B - size = limit.toFixed(2) + "B"; - } else if (limit < 1024 * 1024) { - //如果小于1MB转化成KB - size = (limit / 1024).toFixed(2) + "KB"; - } else if (limit < 1024 * 1024 * 1024) { - //如果小于1GB转化成MB - size = (limit / (1024 * 1024)).toFixed(2) + "MB"; - } else { - //其他转化成GB - size = (limit / (1024 * 1024 * 1024)).toFixed(2) + "GB"; - } - - var sizestr = size + ""; - var len = sizestr.indexOf("\."); - var dec = sizestr.substr(len + 1, 2); - if (dec == "00") { - //当小数点后为00时 去掉小数部分 - return sizestr.substring(0, len) + sizestr.substr(len + 3, 2); - } - return sizestr; -} - -function _initSider() { - var $descSide = $("
").appendTo("body"); - $(".-task-sidebar>div").hover(function() { - //移入显示二维码 - if ($(this).hasClass("scan")) { - $(".scan_ewm").show().css({ - right: "75px", - opacity: 0 - }).stop().animate({ - right: "45px", - opacity: 1 - }) - return; - } - var $tool = $(this).attr("tooltips"); - $descSide.html($tool + "
"); - $descSide.data('_dom', this) - $descSide.show().css({ - left: $(this).offset().left - $descSide.width() - 30, - opacity: 0, - top: $(this).offset().top - }).stop().animate({ - left: $(this).offset().left - $descSide.width() - 5, - opacity: 1 - }, 400); - }, function() { - if ($(this).hasClass("scan")) { - $(".scan_ewm").stop().animate({ - right: "75px", - opacity: 0 - }, 200).hide(); - } - $descSide.stop().animate({ - left: $(this).offset().left - $descSide.width() - 30, - opacity: 0 - }, 200).hide(); - }); - rightSlider(); - - $(window).scroll(function() { - if ($descSide.height()) { - var hoverIcon = $descSide.data('_dom') - $descSide.css('top', $(hoverIcon).offset().top) - } - }) -} -$(function() { - loadHeader(); - _initSider(); - - $(window).scroll(function() { - if ($(".gotop").length > 0) { - if ($(document).scrollTop() > 0) { - $(".-task-sidebar .gotop").show(); - $(".gotop").click(function() { - $("html,body").scrollTop(0); - }); - } - if ($(document).scrollTop() == 0) { - $(".-task-sidebar .gotop").hide(); - } - } - }); - - // 翻页的GO - $(".page_GO").live("keydown", function(event) { - var code; - if (!event) { - event = window.event; - //针对ie浏览器 - code = event.keyCode; - } else { - code = event.keyCode; - } - if (code == 13) { - var prev = $(this).prev().find("a").html().trim(); - var page = $(this).val().trim(); - if (parseInt(prev) >= parseInt(page)) { - if (typeof ($(this).prev().children("a").attr("href")) == "undefined") { - var href = $(this).parent().find("li:first-child").children("a").attr("href"); - } else { - var href = $(this).prev().children("a").attr("href"); - } - var new_href = href.replace(/page=(\d*)/, 'page=' + page); - console.log(new_href); - $.get(new_href); - return false; - } - } - }); - - // 试用申请弹框 - $("#apply_trail_submit_btn").live('click', function() { - if ($("#apply_reason").val().trim() == "") { - $("#hint_message").show(); - } else { - $("#hint_message").hide(); - $("#apply_trail_form").submit(); - hideModal(); - } - }); - -}); - -// editor 存在了jquery对象上,应用不需要自己写md_rec_data方法了 -function md_rec_data(k, mdu, id) { - if (window.sessionStorage.getItem(k + mdu) !== null) { - editor = $("#e_tips_" + id).data('editor'); - editor.setValue(window.sessionStorage.getItem(k + mdu)); - - md_clear_data(k, mdu, id); - } -} -// markdown的自动保存 -function md_elocalStorage(editor, mdu, id) { - if (window.sessionStorage) { - var oc = window.sessionStorage.getItem('content' + mdu); - if (oc !== null) { - $("#e_tips_" + id).data('editor', editor); - var h = '您上次有已保存的数据,是否恢复 ? / 不恢复'; - $("#e_tips_" + id).html(h); - } - setInterval(function() { - d = new Date(); - var h = d.getHours(); - var m = d.getMinutes(); - var s = d.getSeconds(); - h = h < 10 ? '0' + h : h; - m = m < 10 ? '0' + m : m; - s = s < 10 ? '0' + s : s; - if (editor.getValue().trim() != "") { - md_add_data("content", mdu, editor.getValue()); - var id1 = "#e_tip_" + id; - var id2 = "#e_tips_" + id; - $(id1).html(" 数据已于 " + h + ':' + m + ':' + s + " 保存 "); - $(id2).html(""); - } - }, 10000); - - } else { - $("#e_tip_" + id).after('您的浏览器不支持localStorage.无法开启自动保存草稿服务,请升级浏览器!'); - } -} -// 保存数据 -function md_add_data(k, mdu, d) { - window.sessionStorage.setItem(k + mdu, d); -} -// 恢复数据 -//function md_rec_data(k,mdu,id, editor){ -// if(window.sessionStorage.getItem(k+mdu) !== null){ -// editor.setValue(window.sessionStorage.getItem(k+mdu)); -// md_clear_data(k,mdu,id); -// } -//} -// 清空保存的数据 -function md_clear_data(k, mdu, id) { - window.sessionStorage.removeItem(k + mdu); - var id1 = "#e_tip_" + id; - var id2 = "#e_tips_" + id; - if (k == 'content') { - $(id2).html(""); - } else { - $(id1).html(""); - } -} - -// editorMD to create -/** - * - * @param id 渲染DOM的id - * @param width 宽度 - * @param high 高度 - * @param placeholder - * @param imageUrl 上传图片的url - * @returns {*} 返回一个editorMD实例 - */ -function create_editorMD(id, width, high, placeholder, imageUrl, readonly) { - var readonly = readonly == undefined ? false : readonly; - var editorName = editormd(id, { - width: width, - height: high, - syncScrolling: "single", - //你的lib目录的路径,我这边用JSP做测试的 - path: "/editormd/lib/", - tex: true, - tocm: true, - emoji: true, - taskList: true, - codeFold: true, - searchReplace: true, - htmlDecode: "style,script,iframe", - sequenceDiagram: true, - autoFocus: false, - readonly: readonly, - toolbarIcons: function() { - // Or return editormd.toolbarModes[name]; // full, simple, mini - // Using "||" set icons align right. - return ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "|", "testIcon", "testIcon1", '|', "image", "table", '|', "watch", "clear"] - }, - toolbarCustomIcons: { - testIcon: "
", - testIcon1: "
" - }, - //这个配置在simple.html中并没有,但是为了能够提交表单,使用这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中,方便post提交表单。 - saveHTMLToTextarea: true, - // 用于增加自定义工具栏的功能,可以直接插入HTML标签,不使用默认的元素创建图标 - dialogMaskOpacity: 0.6, - placeholder: placeholder, - imageUpload: true, - imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp", "JPG", "JPEG", "GIF", "PNG", "BMP", "WEBP"], - imageUploadURL: imageUrl, - //url - onload: function(cMirror) { - $("#" + id + " [type=\"latex\"]").bind("click", function() { - editorName.cm.replaceSelection("```latex"); - editorName.cm.replaceSelection("\n"); - editorName.cm.replaceSelection("\n"); - editorName.cm.replaceSelection("```"); - var __Cursor = editorName.cm.getDoc().getCursor(); - editorName.cm.setCursor(__Cursor.line - 1, 0); - }); - - $("#" + id + " [type=\"inline\"]").bind("click", function() { - editorName.cm.replaceSelection("$$$$"); - var __Cursor = editorName.cm.getDoc().getCursor(); - editorName.cm.setCursor(__Cursor.line, __Cursor.ch - 2); - editorName.cm.focus(); - }); - $("[type=\"inline\"]").attr("title", "行内公式"); - $("[type=\"latex\"]").attr("title", "多行公式"); - setTimeout(function() { - editorName.resize(); - editorName.cm.refresh(); - window.new_md = editorName; - }, 300); - } - }); - return editorName; -} - -// editormd to html -/** - * - * @param id 渲染的id - * @param callback onload回調 暫時未用 - */ -function editormd_to_html(id, callback) { - editormd.loadKaTeX(function() { - editormd.markdownToHTML(id, { - htmlDecode: "style,script,iframe", - // you can filter tags decode - onload: function() { - callback && callback() - }, - taskList: true, - tex: true, - // 默认不解析 - flowChart: true, - // 默认不解析 - sequenceDiagram: true// 默认不解析 - }); - }); -} - -function loadHeader() { - //头部导航条的----------显示搜索框 - $("#search-open").on("click", function(e) { - $(this).hide(); - // $("#header-nav").animate({opacity:"0"},1000); - $(".posi-search").show() - // .animate({opacity:"1"},1000); - $("#header-nav").css("z-index", "2"); - $(".posi-search").css("z-index", "3"); - // $(".search-input").val(""); // 不清空 - $(".search-input").focus(); - $(".search-all .search-content").hide(); - e.stopPropagation(); - //阻止冒泡 - }); - $(".search-input").on("click", function(e) { - e.stopPropagation(); - //阻止冒泡 - }); - //搜索框输入内容 - $(".search-input").on("input", function(e) { - if ($(".search-input").val() == "") { - $(".search-all .search-content").hide(); - } else { - $(".search-all .search-content").show(); - } - e.stopPropagation(); - //阻止冒泡 - }); - //搜索 - $("#header_keyword_search").on("click", header_search); - $("input[name='search_keyword']").on("keydown", function(event) { - var code; - if (!event) { - event = window.event; - //针对ie浏览器 - code = event.keyCode; - } else { - code = event.keyCode; - } - if (code == 13) { - header_search(); - return false; - } - }); - $(".search-clear").click(function(e) { - e.stopPropagation(); - }); - //切换搜索条件 - $("#searchkey li").click(function(e) { - var key = $($(this).children("a")[0]).html(); - switch (key) { - case '实训': - $("#search_type").val('1'); - break; - case '课堂': - $("#search_type").val('2'); - break; - case '用户': - $("#search_type").val('3'); - break; - } - $("#searchkey").siblings(".searchkey").html(key); - // $("#searchkey").hide(); - e.stopPropagation(); - //阻止冒泡 - }); - //切换选择导航条 - $("#header-nav li").click(function() { - $("#header-nav li").removeClass("active"); - $(this).addClass("active"); - }); - //点击页面其它(与搜索框无关的地方)都会将搜索框隐藏,所以与搜索框有关的地方需要阻止冒泡 - $("body").on("click", function() { - closeSearch(); - }); - - $(".search_history").on("click", function() { - $("input[name='search_keyword']").val($(this).html()); - header_search(); - }); -} - -function header_search() { - var keyword = $("input[name='search_keyword']").val(); - // 搜索关键字 - var index = $("#search_type").val(); - // 搜索课程/项目 - keyword = encodeURIComponent(keyword); - // $.get('/users/search_shixuns_or_course', - // { search: keyword, - // index: index}); - window.location.href = "/users/search_shixuns_or_courses" + "?search=" + keyword + "&index=" + index; - //e.stopPropagation();//阻止冒泡 -} - -//头部导航条的隐藏 -function closeSearch() { - $('#posi-search').hide(); - $("#search-open").show(); - // $(".posi-search").animate({opacity:"0"},800); - $("#header-nav").animate({ - opacity: "1" - }, 1000); - $(".posi-search").css("z-index", "2"); - $("#header-nav").css("z-index", "3"); -} -(function($) { - $.fn.drag = function(options) { - var x, drag = this, isMove = false, defaults = {}; - var options = $.extend(defaults, options); - //添加背景,文字,滑块 - var html = '
' + '
请拖住滑块,拖动到最右边
' + '
'; - this.append(html); - - var handler = drag.find('.handler'); - var drag_bg = drag.find('.drag_bg'); - var text = drag.find('.drag_text'); - var maxWidth = text.width() - handler.width(); - //能滑动的最大间距 - //鼠标按下时候的x轴的位置 - handler.mousedown(function(e) { - isMove = true; - x = e.pageX - parseInt(handler.css('left'), 10); - }); - - //鼠标指针在上下文移动时,移动距离大于0小于最大间距,滑块x轴位置等于鼠标移动距离 - $(document).mousemove(function(e) { - var _x = e.pageX - x; - var handler_offset = handler.offset(); - var lastX = e.clientX - x; - lastX = Math.max(0, Math.min(maxWidth, lastX)); - if (isMove) { - if (_x > 0 && _x <= maxWidth) { - handler.css({ - 'left': lastX - }); - drag_bg.css({ - 'width': lastX - }); - } else if (lastX > maxWidth - 5 && lastX < maxWidth + 5) { - //鼠标指针移动距离达到最大时清空事件 - dragOk(); - } - } - }); - handler.mouseup(function(e) { - isMove = false; - var _x = e.pageX - x; - if (_x < maxWidth) { - //鼠标松开时,如果没有达到最大距离位置,滑块就返回初始位置 - handler.css({ - 'left': 0 - }); - drag_bg.css({ - 'width': 0 - }); - } - }); - - //清空事件 - function dragOk() { - var kuaiwidth = drag.width() - handler.width(); - handler.removeClass('handler_bg').addClass('handler_ok_bg'); - handler.css({ - 'left': kuaiwidth + 'px' - }) - text.css({ - 'width': kuaiwidth + 'px' - }); - text.text('验证通过'); - drag.css({ - 'color': '#fff' - }); - drag_bg.css({ - 'width': kuaiwidth + 'px' - }) - handler.unbind('mousedown'); - $(document).unbind('mousemove'); - $(document).unbind('mouseup'); - handler.parent().next().find("p").html("").hide(); - } - } - ; -} -)(jQuery); - -//判断是手机端还是电脑端 -function IsPC() { - var userAgentInfo = navigator.userAgent; - var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"]; - var flag = true; - for (var v = 0; v < Agents.length; v++) { - if (userAgentInfo.indexOf(Agents[v]) > 0) { - flag = false; - break; - } - } - return flag; -} - -//Dom:绑定事件的节点对象,ChangeDOM:操作的相关节点, -function LeaveTitle(Dom, ChangeDom) { - ChangeDom.html("").hide(); - ChangeDom.parent().css({ - opacity: 0, - left: 0, - top: 0 - }).hide(); -} - -$(function() { - //平台tip的样式优化js - var $desc = $("
" + "
" + "
" + "
" + "
" + "
").appendTo("body"); - //Dom:绑定事件的节点对象,ChangeDOM:操作的相关节点, - function LeaveTitle(Dom, ChangeDom) { - Dom.live("mouseleave", function() { - ChangeDom.html("").hide(); - $desc.css({ - opacity: 0, - left: 0, - top: 0 - }).hide(); - }) - } - LeaveTitle($("[data-tip-top]"), $(".data-tip-top")); - LeaveTitle($("[data-tip-down]"), $(".data-tip-down")); - LeaveTitle($("[data-tip-right]"), $(".data-tip-left")); - LeaveTitle($("[data-tip-left]"), $(".data-tip-right")); - $("[data-tip-top]").live("mouseenter", function() { - var $tool = $(this).attr("data-tip-top"); - if ($tool != "") { - $(".data-tip-top").show().html($tool); - $desc.show().css({ - left: $(this).offset().left - ($desc.width() - $(this).outerWidth()) / 2, - opacity: 1, - top: $(this).offset().top - 30 - }); - } - }); - $("[data-tip-down]").live("mouseenter", function() { - var $tool = $(this).attr("data-tip-down"); - if ($tool != "") { - $(".data-tip-down").show().html($tool); - $desc.show().css({ - left: $(this).offset().left - ($desc.width() - $(this).outerWidth()) / 2, - opacity: 1, - top: $(this).offset().top + $(this).height() + 6 - }); - } - }); - $("[data-tip-right]").live("mouseenter", function() { - var $tool = $(this).attr("data-tip-right"); - if ($tool != "") { - console.log($(this).offset().left + " " + $(this).width()); - $(".data-tip-left").show().html($tool); - $desc.show().css({ - left: $(this).offset().left + $(this).outerWidth() + 6, - opacity: 1, - top: $(this).offset().top - ($desc.height() - $(this).height()) / 2 - }); - } - }); - $("[data-tip-left]").live("mouseenter", function() { - var $tool = $(this).attr("data-tip-left"); - if ($tool != "") { - $(".data-tip-right").show().html($tool); - $desc.show().css({ - left: $(this).offset().left - $desc.width() - 6, - opacity: 1, - top: $(this).offset().top - ($desc.height() - $(this).height()) / 2 - }); - } - }); - unitDownOption(); -}); - -function unitDownOption() { - //下拉框 - $("[select-for]").append(""); - $("[select-for]").hover(function() { - $(this).find(".down-select").show(); - }, function() { - $(this).find(".down-select").hide(); - }) - $("[select-for] .down-select p").bind("click", function() { - //alert($(this).attr("data-shixun-value")); - if ($(this).attr("id") == "diy_script") { - return; - // 实训新建-选择自定义脚本diy - } - $(this).parents(".down-select").siblings("input[type=hidden]").attr("value", $(this).attr("data-shixun-value")); - - $(this).parents(".down-select").siblings("input[type=text]").val($(this).html().trim()); - $(this).parents(".down-select").hide(); - }) -} - -//初始化省份 -function showprovince(id) { - var arrary = ["北京", "上海", "广东", "江苏", "浙江", "重庆", "安徽", "福建", "甘肃", "广西", "贵州", "海南", "河北", "黑龙江", "河南", "湖北", "湖南", "江西", "吉林", "辽宁", "内蒙古", "宁夏", "青海", "山东", "山西", "陕西", "四川", "天津", "新疆", "西藏", "云南", "香港特别行政区", "澳门特别行政区", "台湾", "海外"]; - var html = "" - for (var i = 0; i < arrary.length; i++) { - var item = arrary[i]; - html += ""; - } - $("#" + id).html(html); -} - -//省市下拉框 -function showcity(province, cityField) { - switch (province) { - case "北京": - var cityOptions = new Array("东城","西城","朝阳","丰台","石景山","海淀","门头沟","房山","通州","顺义","昌平","大兴","平谷","怀柔","密云","延庆"); - break; - case "上海": - var cityOptions = new Array("崇明","黄浦","卢湾","徐汇","长宁","静安","普陀","闸北","虹口","杨浦","闵行","宝山","嘉定","浦东","金山","松江","青浦","南汇","奉贤"); - break; - case "广东": - var cityOptions = new Array("广州","深圳","珠海","东莞","中山","佛山","惠州","河源","潮州","江门","揭阳","茂名","梅州","清远","汕头","汕尾","韶关","顺德","阳江","云浮","湛江","肇庆"); - break; - case "江苏": - var cityOptions = new Array("南京","常熟","常州","海门","淮安","江都","江阴","昆山","连云港","南通","启东","沭阳","宿迁","苏州","太仓","泰州","同里","无锡","徐州","盐城","扬州","宜兴","仪征","张家港","镇江","周庄"); - break; - case "重庆": - var cityOptions = new Array("万州","涪陵","渝中","大渡口","江北","沙坪坝","九龙坡","南岸","北碚","万盛","双挢","渝北","巴南","黔江","长寿","綦江","潼南","铜梁","大足","荣昌","壁山","梁平","城口","丰都","垫江","武隆","忠县","开县","云阳","奉节","巫山","巫溪","石柱","秀山","酉阳","彭水","江津","合川","永川","南川"); - break; - case "安徽": - var cityOptions = new Array("合肥","安庆","蚌埠","亳州","巢湖","滁州","阜阳","贵池","淮北","淮化","淮南","黄山","九华山","六安","马鞍山","宿州","铜陵","屯溪","芜湖","宣城"); - break; - case "福建": - var cityOptions = new Array("福州","厦门","泉州","漳州","龙岩","南平","宁德","莆田","三明"); - break; - case "甘肃": - var cityOptions = new Array("兰州","白银","定西","敦煌","甘南","金昌","酒泉","临夏","平凉","天水","武都","武威","西峰","张掖"); - break; - case "广西": - var cityOptions = new Array("南宁","百色","北海","桂林","防城港","贵港","河池","贺州","柳州","钦州","梧州","玉林"); - break; - case "贵州": - var cityOptions = new Array("贵阳","安顺","毕节","都匀","凯里","六盘水","铜仁","兴义","玉屏","遵义"); - break; - case "海南": - var cityOptions = new Array("海口","儋县","陵水","琼海","三亚","通什","万宁"); - break; - case "河北": - var cityOptions = new Array("石家庄","保定","北戴河","沧州","承德","丰润","邯郸","衡水","廊坊","南戴河","秦皇岛","唐山","新城","邢台","张家口"); - break; - case "黑龙江": - var cityOptions = new Array("哈尔滨","北安","大庆","大兴安岭","鹤岗","黑河","佳木斯","鸡西","牡丹江","齐齐哈尔","七台河","双鸭山","绥化","伊春"); - break; - case "河南": - var cityOptions = new Array("郑州","安阳","鹤壁","潢川","焦作","济源","开封","漯河","洛阳","南阳","平顶山","濮阳","三门峡","商丘","新乡","信阳","许昌","周口","驻马店"); - break; - case "香港": - var cityOptions = new Array("香港","九龙","新界"); - break; - case "湖北": - var cityOptions = new Array("武汉","恩施","鄂州","黄冈","黄石","荆门","荆州","潜江","十堰","随州","武穴","仙桃","咸宁","襄阳","襄樊","孝感","宜昌"); - break; - case "湖南": - var cityOptions = new Array("长沙","常德","郴州","衡阳","怀化","吉首","娄底","邵阳","湘潭","益阳","岳阳","永州","张家界","株洲"); - break; - case "江西": - var cityOptions = new Array("南昌","抚州","赣州","吉安","景德镇","井冈山","九江","庐山","萍乡","上饶","新余","宜春","鹰潭"); - break; - case "吉林": - var cityOptions = new Array("长春","吉林","白城","白山","珲春","辽源","梅河","四平","松原","通化","延吉"); - break; - case "辽宁": - var cityOptions = new Array("沈阳","鞍山","本溪","朝阳","大连","丹东","抚顺","阜新","葫芦岛","锦州","辽阳","盘锦","铁岭","营口"); - break; - case "澳门": - var cityOptions = new Array("澳门"); - break; - case "内蒙古": - var cityOptions = new Array("呼和浩特","阿拉善盟","包头","赤峰","东胜","海拉尔","集宁","临河","通辽","乌海","乌兰浩特","锡林浩特"); - break; - case "宁夏": - var cityOptions = new Array("银川","固源","石嘴山","吴忠"); - break; - case "青海": - var cityOptions = new Array("西宁","德令哈","格尔木","共和","海东","海晏","玛沁","同仁","玉树"); - break; - case "山东": - var cityOptions = new Array("济南","滨州","兖州","德州","东营","菏泽","济宁","莱芜","聊城","临沂","蓬莱","青岛","曲阜","日照","泰安","潍坊","威海","烟台","枣庄","淄博"); - break; - case "山西": - var cityOptions = new Array("太原","长治","大同","候马","晋城","离石","临汾","宁武","朔州","忻州","阳泉","榆次","运城"); - break; - case "陕西": - var cityOptions = new Array("西安","安康","宝鸡","汉中","渭南","商州","绥德","铜川","咸阳","延安","榆林"); - break; - case "四川": - var cityOptions = new Array("成都","巴中","达川","德阳","都江堰","峨眉山","涪陵","广安","广元","九寨沟","康定","乐山","泸州","马尔康","绵阳","眉山","南充","内江","攀枝花","遂宁","汶川","西昌","雅安","宜宾","自贡","资阳"); - break; - case "台湾": - var cityOptions = new Array("台北","基隆","台南","台中","高雄","屏东","南投","云林","新竹","彰化","苗栗","嘉义","花莲","桃园","宜兰","台东","金门","马祖","澎湖"); - break; - case "天津": - var cityOptions = new Array("天津","和平","东丽","河东","西青","河西","津南","南开","北辰","河北","武清","红挢","塘沽","汉沽","大港","宁河","静海","宝坻","蓟县"); - break; - case "新疆": - var cityOptions = new Array("乌鲁木齐","阿克苏","阿勒泰","阿图什","博乐","昌吉","东山","哈密","和田","喀什","克拉玛依","库车","库尔勒","奎屯","石河子","塔城","吐鲁番","伊宁"); - break; - case "西藏": - var cityOptions = new Array("拉萨","阿里","昌都","林芝","那曲","日喀则","山南"); - break; - case "云南": - var cityOptions = new Array("昆明","大理","保山","楚雄","大理","东川","个旧","景洪","开远","临沧","丽江","六库","潞西","曲靖","思茅","文山","西双版纳","玉溪","中甸","昭通"); - break; - case "浙江": - var cityOptions = new Array("杭州","安吉","慈溪","定海","奉化","海盐","黄岩","湖州","嘉兴","金华","临安","临海","丽水","宁波","瓯海","平湖","千岛湖","衢州","江山","瑞安","绍兴","嵊州","台州","温岭","温州","余姚","舟山"); - break; - case "海外": - var cityOptions = new Array("美国","日本","英国","法国","德国","其他"); - break; - default: - var cityOptions = new Array("请选择所在城市"); - break; - } - - cityField.options.length = 0; - for (var i = 0; i < cityOptions.length; i++) { - cityField.options[i] = new Option(cityOptions[i],cityOptions[i]); - /* - if (cityField.options[i].value==city) - { - //alert("here put City ok!"); - document.oblogform["city"].selectedIndex = i; - }*/ - } -} - -/*弹框*/ -// 公共弹框样式 -// 建议左右栏的:Width:460,Height:190 -// 建议宽屏对应值:Width:760,Height:500 -function pop_box_new(value, Width, Height, close) { - - if ($("#popupAll").length > 0) { - $("#popupAll").remove(); - } - w = ($(window).width() - Width) / 2; - h = ($(window).height() - Height) / 2; - var html = "
"; - if (close) { - value = "" + value; - } - $(document.body).append(html); - $("#popupWrap").html(value); - $('#popupWrap').css({ - "top": h + "px", - "left": w + "px", - "padding": "0", - "border": "none", - "position": "fixed", - "z-index": "99999", - "background-color": "#fff", - "border-radius": "10px" - }); - if (close) { - $('#closeIcon').css({ - "top": "-26px", - "left": Width + "px", - "z-index": "100000" - }); - } - - $("#popupWrap").parent().parent().show(); - $('#popupAll').find("#closeIcon").click(function() { - $("#popupAll").hide(); - }); - $('#popupAll').find("a[class*='pop_close']").click(function() { - $("#popupAll").hide(); - }); - // w = ($(window).width() - Width)/2; - // h = ($(window).height() - Height)/2; - // $("#ajax-modal").html(value); - // showModal('ajax-modal', Width + 'px'); - // $('#ajax-modal').siblings().remove(); - // $('#ajax-modal').parent().css({"top": h+"px","left": w+"px","padding":"0","border":"none","position":"fixed"}); - // $('#ajax-modal').parent().removeClass("resourceUploadPopup popbox_polls popbox"); - // $('#ajax-modal').css({"padding":"0","overflow":"hidden"}); - // $('#ajax-modal').parent().attr("id","popupWrap"); - - //拖拽 - function Drag(id) { - this.div = document.getElementById(id); - if (this.div) { - this.div.style.cursor = "move"; - this.div.style.position = "fixed"; - } - this.disX = 0; - this.disY = 0; - var _this = this; - this.div.onmousedown = function(evt) { - _this.getDistance(evt); - document.onmousemove = function(evt) { - _this.setPosition(evt); - } - ; - _this.div.onmouseup = function() { - _this.clearEvent(); - } - } - } - Drag.prototype.getDistance = function(evt) { - var oEvent = evt || event; - this.disX = oEvent.clientX - this.div.offsetLeft; - this.disY = oEvent.clientY - this.div.offsetTop; - } - ; - Drag.prototype.setPosition = function(evt) { - var oEvent = evt || event; - var l = oEvent.clientX - this.disX; - var t = oEvent.clientY - this.disY; - if (l <= 0) { - l = 0; - } else if (l >= document.documentElement.clientWidth - this.div.offsetWidth) { - l = document.documentElement.clientWidth - this.div.offsetWidth; - } - if (t <= 0) { - t = 0; - } else if (t >= document.documentElement.clientHeight - this.div.offsetHeight) { - t = document.documentElement.clientHeight - this.div.offsetHeight; - } - this.div.style.left = l + "px"; - this.div.style.top = t + "px"; - } - ; - Drag.prototype.clearEvent = function() { - this.div.onmouseup = null; - document.onmousemove = null; - } - ; - - new Drag("popupWrap"); - - $("#popupAll input, #popupAll textarea, #popupAll select, #popupAll ul, #popupAll a,#shixun_search_form_div").mousedown(function(event) { - event.stopPropagation(); - new Drag("popupWrap"); - }); - -} - -function hideModal(el) { - if ($("#popupAll").length > 0) { - $("#popupAll").remove(); - } else { - var modal; - if (el) { - modal = $(el).parents('.ui-dialog-content'); - } else { - modal = $('#ajax-modal'); - } - modal.dialog("close"); - } -} - -//提示框:只有一个确定按钮,点击跳转 -// -function notice_box_redirect(url, str) { - var htmlvalue = '
提示
' + '

' + str + '

' + '确定
'; - pop_box_new(htmlvalue, 480, 160); -} -//按钮内容自定义(自定义按钮需要remote=true,且有取消按钮) -function notice_operation_box(url, str, btnstr) { - var htmlvalue = '
提示
' + '

' + str + '

' + '取消' + btnstr + '
'; - pop_box_new(htmlvalue, 480, 160); -} -//点击删除时的确认弹框: 不走destroy方法 -function delete_confirm_box(url, str) { - var htmlvalue = '
提示
' + '

' + str + '

取消' + '确定
'; - pop_box_new(htmlvalue, 480, 160); -} -//点击删除时的确认弹框: 走destroy方法,remote为true -function delete_confirm_box_2(url, str) { - var htmlvalue = '
提示
' + '
' + str + '
取消' + '确定
'; - pop_box_new(htmlvalue, 480, 160); -} - -// 点击确定的时候ajax请求,两个按钮 点击确认跳转, 提示信息可以多行 -function op_confirm_box_remote(url, str) { - var htmlvalue = '
提示
' + '

' + str + '

取消' + '确定
'; - pop_box_new(htmlvalue, 578, 205); -} - -//点击删除时的确认弹框: post,remote为true -function post_confirm_box(url, str) { - var htmlvalue = '

提示

' + '

' + str + '

取消' + '确定
'; - pop_box_new(htmlvalue, 480, 160); -} - -//提示框:只有一个确定按钮,点击关闭弹框 -// -function notice_box(str) { - var htmlvalue = '
提示
' + '

' + str + '

' + '确定
'; - pop_box_new(htmlvalue, 480, 160); -} - -//点击删除时的确认弹框: 走destroy方法 -function delete_confirm_box_3(url, str) { - var htmlvalue = '
提示
' + '

' + str + '

取消' + '确定
'; - pop_box_new(htmlvalue, 480, 160); -} - -//取消和确定,确定会调用自定义方法 -function op_confirm_tip(str, func) { - var htmlvalue = '
提示
' + '

' + str + '

取消' + '确定
'; - pop_box_new(htmlvalue, 500, 205); -} - -//取消和确定,确定会调用自定义方法(带参数) -function op_confirm_tip_1(str, func) { - var htmlvalue = '
提示
' + '

' + str + '

取消' + '确定
'; - pop_box_new(htmlvalue, 500, 205); -} - -function op_confirm_box_loading(url, str) { - var htmlvalue = '
提示
' + '

' + str + '

取消' + '确定
'; - pop_box_new(htmlvalue, 578, 205); -} - -// 两个按钮 点击确认跳转, 提示信息有两行 -function s_op_confirm_box(url, str) { - var htmlvalue = '
提示
' + '

' + str + '

取消' + '确定
'; - pop_box_new(htmlvalue, 480, 205); -} - -function suofang() { - var html = '

可能会影响某些功能的正常使用

'; - sure_confirm_box("页面缩放比例不正确", 600, 310, html); -} - -//一个“知道了”按钮,title和宽度都作为参数 -function sure_confirm_box(title, width, height, str) { - var htmlvalue = '
' + title + '
' + '
' + str + '
' + '知道了
'; - pop_box_new(htmlvalue, width, height); -} - -function throttle(method, context, e) { - clearTimeout(method.tId); - method.tId = setTimeout(function() { - method.call(context, e); - }, 500); -} - -function apply_publish_shixun(url) { - if ($("#apply_publish_shixun").attr("data-option") == '1') { - $("#apply_publish_shixun").attr("data-option", 0); - $("#apply_publish_shixun").addClass("disabled-grey-bg"); - $.ajax({ - url: url, - type: 'get' - }); - } -} - -var autoTextarea = function(elem, extra, maxHeight) { - extra = extra || 0; - var isFirefox = !!document.getBoxObjectFor || 'mozInnerScreenX'in window - , isOpera = !!window.opera && !!window.opera.toString().indexOf('Opera') - , addEvent = function(type, callback) { - elem.addEventListener ? elem.addEventListener(type, callback, false) : elem.attachEvent('on' + type, callback); - } - , getStyle = elem.currentStyle ? function(name) { - var val = elem.currentStyle[name]; - - if (name === 'height' && val.search(/px/i) !== 1) { - var rect = elem.getBoundingClientRect(); - return rect.bottom - rect.top - parseFloat(getStyle('paddingTop')) - parseFloat(getStyle('paddingBottom')) + 'px'; - } - ; - return val; - } - : function(name) { - return getComputedStyle(elem, null)[name]; - } - , minHeight = parseFloat(getStyle('height')); - - elem.style.resize = 'none'; - - var change = function() { - var scrollTop, height, padding = 0, style = elem.style; - - if (elem._length === elem.value.length) - return; - elem._length = elem.value.length; - - if (!isFirefox && !isOpera) { - padding = parseInt(getStyle('paddingTop')) + parseInt(getStyle('paddingBottom')); - } - ;scrollTop = document.body.scrollTop || document.documentElement.scrollTop; - - elem.style.height = minHeight + 'px'; - if (elem.scrollHeight > minHeight) { - if (maxHeight && elem.scrollHeight > maxHeight) { - height = maxHeight - padding; - style.overflowY = 'auto'; - } else { - height = elem.scrollHeight - padding + 10; - style.overflowY = 'hidden'; - } - ;style.height = height + extra + 'px'; - scrollTop += parseInt(style.height) - elem.currHeight; - //document.body.scrollTop = scrollTop; - //document.documentElement.scrollTop = scrollTop; - elem.currHeight = parseInt(style.height); - } - ; - }; - - addEvent('propertychange', change); - addEvent('input', change); - addEvent('focus', change); - change(); -}; - -// 点击按钮复制功能 -function jsCopy() { - var e = document.getElementById("copy_rep_content"); - e.select(); - document.execCommand("Copy"); -} - -// 使用resize事件监听窗口的zoom,如果zoom变化了,弹框提示;初始化时也检查zoom是否是100%。 -function _initZoomCheck() { - if (!IsPC()) { - // 手机端不需要提示 - return; - } - var isNormalZoom = Math.round(window.devicePixelRatio * 100) === 100 - if (!isNormalZoom) { - suofang(); - } - - $(window).resize(function() { - var isNormalZoom = Math.round(window.devicePixelRatio * 100) === 100 - if (!isNormalZoom) { - suofang(); - } else { - $('.task-btn.task-btn-orange:visible').click() - } - }) - -} - -var win_resize = function() { - var _w = $(window).width() - 1200; - if (_w < 0) { - $('.newHeader>.educontent').width('auto') - } else { - $('.newHeader>.educontent').width('1200px') - } -}; - -function initWindowResize() { - if (location.pathname === '/login') { - // 登录页不需要 - return; - } - - $(function() { - setTimeout(function() { - win_resize(); - }, 1000) - }) - - $(window).resize(function() { - win_resize() - }) -} -initWindowResize(); - -// 登录刷新 https://stackoverflow.com/questions/28230845/communication-between-tabs-or-windows -if (window['BroadcastChannel']) { - var bc = new BroadcastChannel('ec_reload'); - bc.onmessage = function(ev) { - if (window['ec_reload_msg_send_window']) { - window['ec_reload_msg_send_window'] = false; - } else { - location.reload(); - } - } -} -function _sendReloadMsg() { - var bc = new BroadcastChannel('ec_reload'); - window['ec_reload_msg_send_window'] = true; - // 消息发出的窗口,不需要处理该消息 - bc.postMessage('ec_reload'); - /* send */ -} -// IE11 没有 startsWith -if (!String.prototype.startsWith) { - String.prototype.startsWith = function(searchString, position) { - position = position || 0; - return this.substr(position, searchString.length) === searchString; - } - ; - String.prototype.endsWith = function(search, this_len) { - if (this_len === undefined || this_len > this.length) { - this_len = this.length; - } - return this.substring(this_len - search.length, this_len) === search; - } - ; -} - -function clickNewsubscript() { - $(".newsubscript").hide(); - $(".newedbox").addClass("newminheight"); - $(".newedbox").removeClass("newedboxheight"); -} - -/** tpm实训开启按钮,不允许多次点击 START */ -//点击模拟实战或者开启实战等,按钮变灰内容变成“开启中” -var operationItem = null; -var operationButtonOldValue = null; -function opClickString(item) { - var value = $(item).html(); - $(item).css({ - 'background': 'gray', - 'border': '1px solid grey', - 'pointer-events': 'none' - }); - $(item).html('开启中'); - - operationButtonOldValue = value - operationItem = item - // setTimeout(function(){ $(item).css('background', '#4CACFF');$(item).html(value); }, 4000) -} -// -// var isOperationSending = false; -$(document).bind('ajaxStop', function(event, xhr, settings) { - if (settings && settings.url && (settings.url.match(/operation\?/))) { - if (operationItem) { - $(operationItem).css('background', '#4CACFF').css('pointer-events', 'inherit'); - $(operationItem).html(operationButtonOldValue); - } - } -}); -$(document).bind('ajaxError', function(event, xhr, settings) { - if (settings && settings.url && (settings.url.match(/operation\?/))) { - if (operationItem) { - $(operationItem).css('background', '#4CACFF').css('pointer-events', 'inherit'); - $(operationItem).html(operationButtonOldValue); - } - } -}); -/** tpm实训开启按钮,不允许多次点击 END */ +document.write(""); + +/*! + * JavaScript Cookie v2.2.0 + * https://github.com/js-cookie/js-cookie + * + * Copyright 2006, 2015 Klaus Hartl & Fagner Brack + * Released under the MIT license + */ +!function(e) { + var n; + if ("function" == typeof define && define.amd && (define(e), + n = !0), + "object" == typeof exports && (module.exports = e(), + n = !0), + !n) { + var t = window.Cookies + , o = window.Cookies = e(); + o.noConflict = function() { + return window.Cookies = t, + o + } + } +}(function() { + function e() { + for (var e = 0, n = {}; e < arguments.length; e++) { + var t = arguments[e]; + for (var o in t) + n[o] = t[o] + } + return n + } + function n(e) { + return e.replace(/(%[0-9A-Z]{2})+/g, decodeURIComponent) + } + return function t(o) { + function r() {} + function i(n, t, i) { + if ("undefined" != typeof document) { + "number" == typeof (i = e({ + path: "/" + }, r.defaults, i)).expires && (i.expires = new Date(1 * new Date + 864e5 * i.expires)), + i.expires = i.expires ? i.expires.toUTCString() : ""; + try { + var c = JSON.stringify(t); + /^[\{\[]/.test(c) && (t = c) + } catch (e) {} + t = o.write ? o.write(t, n) : encodeURIComponent(String(t)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent), + n = encodeURIComponent(String(n)).replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent).replace(/[\(\)]/g, escape); + var f = ""; + for (var u in i) + i[u] && (f += "; " + u, + !0 !== i[u] && (f += "=" + i[u].split(";")[0])); + return document.cookie = n + "=" + t + f + } + } + function c(e, t) { + if ("undefined" != typeof document) { + for (var r = {}, i = document.cookie ? document.cookie.split("; ") : [], c = 0; c < i.length; c++) { + var f = i[c].split("=") + , u = f.slice(1).join("="); + t || '"' !== u.charAt(0) || (u = u.slice(1, -1)); + try { + var a = n(f[0]); + if (u = (o.read || o)(u, a) || n(u), + t) + try { + u = JSON.parse(u) + } catch (e) {} + if (r[a] = u, + e === a) + break + } catch (e) {} + } + return e ? r[e] : r + } + } + return r.set = i, + r.get = function(e) { + return c(e, !1) + } + , + r.getJSON = function(e) { + return c(e, !0) + } + , + r.remove = function(n, t) { + i(n, "", e(t, { + expires: -1 + })) + } + , + r.defaults = {}, + r.withConverter = t, + r + }(function() {}) +}); + +$(function() { + var result = location.search.match(/\?search=(\w*)&?/i) + if (result && result[1]) { + var searchText = result[1] + $('#search-input').val(searchText) + } + // 未报名用户登录时弹框 + // console.log(Cookies.get('enroll_status')); + // if(Cookies.get('enroll_status') == 0){ + // Cookies.remove('enroll_status'); + // var html='
'+ + // '
'+ + // ''+ + // ''+ + // '立即报名'+ + // '
'; + // $(".newContainer").append(html); + // } +}); + +function CloseBox() { + $(".CompetitionEnrollBox").remove(); +} + +//根据页面大小决定侧边栏的位置 +$(window).resize(function() { + rightSlider(); +}); +function rightSlider() { + var poi = parseInt((parseInt($(window).width()) - 1200) / 2) - 81; + // console.log(parseInt($(window).width())+" "+poi); + if (poi > 0) { + $(".-task-sidebar").css("right", poi); + } else { + $(".-task-sidebar").css("right", "0px"); + } + $(".-task-sidebar").show(); +} +function open_course(id, allowVisit) { + if (allowVisit) { + window.open("/courses/" + id); + } +} +function open_project(id, allowVisit) { + if (allowVisit) { + window.open("/projects/" + id); + } +} + +function conver_size(limit) { + var size = ""; + if (limit < 1024) { + //如果小于1KB转化成B + size = limit.toFixed(2) + "B"; + } else if (limit < 1024 * 1024) { + //如果小于1MB转化成KB + size = (limit / 1024).toFixed(2) + "KB"; + } else if (limit < 1024 * 1024 * 1024) { + //如果小于1GB转化成MB + size = (limit / (1024 * 1024)).toFixed(2) + "MB"; + } else { + //其他转化成GB + size = (limit / (1024 * 1024 * 1024)).toFixed(2) + "GB"; + } + + var sizestr = size + ""; + var len = sizestr.indexOf("\."); + var dec = sizestr.substr(len + 1, 2); + if (dec == "00") { + //当小数点后为00时 去掉小数部分 + return sizestr.substring(0, len) + sizestr.substr(len + 3, 2); + } + return sizestr; +} + +function _initSider() { + var $descSide = $("
").appendTo("body"); + $(".-task-sidebar>div").hover(function() { + //移入显示二维码 + if ($(this).hasClass("scan")) { + $(".scan_ewm").show().css({ + right: "75px", + opacity: 0 + }).stop().animate({ + right: "45px", + opacity: 1 + }) + return; + } + var $tool = $(this).attr("tooltips"); + $descSide.html($tool + "
"); + $descSide.data('_dom', this) + $descSide.show().css({ + left: $(this).offset().left - $descSide.width() - 30, + opacity: 0, + top: $(this).offset().top + }).stop().animate({ + left: $(this).offset().left - $descSide.width() - 5, + opacity: 1 + }, 400); + }, function() { + if ($(this).hasClass("scan")) { + $(".scan_ewm").stop().animate({ + right: "75px", + opacity: 0 + }, 200).hide(); + } + $descSide.stop().animate({ + left: $(this).offset().left - $descSide.width() - 30, + opacity: 0 + }, 200).hide(); + }); + rightSlider(); + + $(window).scroll(function() { + if ($descSide.height()) { + var hoverIcon = $descSide.data('_dom') + $descSide.css('top', $(hoverIcon).offset().top) + } + }) +} +$(function() { + loadHeader(); + _initSider(); + + $(window).scroll(function() { + if ($(".gotop").length > 0) { + if ($(document).scrollTop() > 0) { + $(".-task-sidebar .gotop").show(); + $(".gotop").click(function() { + $("html,body").scrollTop(0); + }); + } + if ($(document).scrollTop() == 0) { + $(".-task-sidebar .gotop").hide(); + } + } + }); + + // 翻页的GO + $(".page_GO").live("keydown", function(event) { + var code; + if (!event) { + event = window.event; + //针对ie浏览器 + code = event.keyCode; + } else { + code = event.keyCode; + } + if (code == 13) { + var prev = $(this).prev().find("a").html().trim(); + var page = $(this).val().trim(); + if (parseInt(prev) >= parseInt(page)) { + if (typeof ($(this).prev().children("a").attr("href")) == "undefined") { + var href = $(this).parent().find("li:first-child").children("a").attr("href"); + } else { + var href = $(this).prev().children("a").attr("href"); + } + var new_href = href.replace(/page=(\d*)/, 'page=' + page); + console.log(new_href); + $.get(new_href); + return false; + } + } + }); + + // 试用申请弹框 + $("#apply_trail_submit_btn").live('click', function() { + if ($("#apply_reason").val().trim() == "") { + $("#hint_message").show(); + } else { + $("#hint_message").hide(); + $("#apply_trail_form").submit(); + hideModal(); + } + }); + +}); + +// editor 存在了jquery对象上,应用不需要自己写md_rec_data方法了 +function md_rec_data(k, mdu, id) { + if (window.sessionStorage.getItem(k + mdu) !== null) { + editor = $("#e_tips_" + id).data('editor'); + editor.setValue(window.sessionStorage.getItem(k + mdu)); + + md_clear_data(k, mdu, id); + } +} +// markdown的自动保存 +function md_elocalStorage(editor, mdu, id) { + if (window.sessionStorage) { + var oc = window.sessionStorage.getItem('content' + mdu); + if (oc !== null) { + $("#e_tips_" + id).data('editor', editor); + var h = '您上次有已保存的数据,是否恢复 ? / 不恢复'; + $("#e_tips_" + id).html(h); + } + setInterval(function() { + d = new Date(); + var h = d.getHours(); + var m = d.getMinutes(); + var s = d.getSeconds(); + h = h < 10 ? '0' + h : h; + m = m < 10 ? '0' + m : m; + s = s < 10 ? '0' + s : s; + if (editor.getValue().trim() != "") { + md_add_data("content", mdu, editor.getValue()); + var id1 = "#e_tip_" + id; + var id2 = "#e_tips_" + id; + $(id1).html(" 数据已于 " + h + ':' + m + ':' + s + " 保存 "); + $(id2).html(""); + } + }, 10000); + + } else { + $("#e_tip_" + id).after('您的浏览器不支持localStorage.无法开启自动保存草稿服务,请升级浏览器!'); + } +} +// 保存数据 +function md_add_data(k, mdu, d) { + window.sessionStorage.setItem(k + mdu, d); +} +// 恢复数据 +//function md_rec_data(k,mdu,id, editor){ +// if(window.sessionStorage.getItem(k+mdu) !== null){ +// editor.setValue(window.sessionStorage.getItem(k+mdu)); +// md_clear_data(k,mdu,id); +// } +//} +// 清空保存的数据 +function md_clear_data(k, mdu, id) { + window.sessionStorage.removeItem(k + mdu); + var id1 = "#e_tip_" + id; + var id2 = "#e_tips_" + id; + if (k == 'content') { + $(id2).html(""); + } else { + $(id1).html(""); + } +} + +// editorMD to create +/** + * + * @param id 渲染DOM的id + * @param width 宽度 + * @param high 高度 + * @param placeholder + * @param imageUrl 上传图片的url + * @returns {*} 返回一个editorMD实例 + */ +function create_editorMD(id, width, high, placeholder, imageUrl, readonly) { + var readonly = readonly == undefined ? false : readonly; + var editorName = editormd(id, { + width: width, + height: high, + syncScrolling: "single", + //你的lib目录的路径,我这边用JSP做测试的 + path: "/editormd/lib/", + tex: true, + tocm: true, + emoji: true, + taskList: true, + codeFold: true, + searchReplace: true, + htmlDecode: "style,script,iframe", + sequenceDiagram: true, + autoFocus: false, + readonly: readonly, + toolbarIcons: function() { + // Or return editormd.toolbarModes[name]; // full, simple, mini + // Using "||" set icons align right. + return ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "|", "testIcon", "testIcon1", '|', "image", "table", '|', "watch", "clear"] + }, + toolbarCustomIcons: { + testIcon: "
", + testIcon1: "
" + }, + //这个配置在simple.html中并没有,但是为了能够提交表单,使用这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中,方便post提交表单。 + saveHTMLToTextarea: true, + // 用于增加自定义工具栏的功能,可以直接插入HTML标签,不使用默认的元素创建图标 + dialogMaskOpacity: 0.6, + placeholder: placeholder, + imageUpload: true, + imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp", "JPG", "JPEG", "GIF", "PNG", "BMP", "WEBP"], + imageUploadURL: imageUrl, + //url + onload: function(cMirror) { + $("#" + id + " [type=\"latex\"]").bind("click", function() { + editorName.cm.replaceSelection("```latex"); + editorName.cm.replaceSelection("\n"); + editorName.cm.replaceSelection("\n"); + editorName.cm.replaceSelection("```"); + var __Cursor = editorName.cm.getDoc().getCursor(); + editorName.cm.setCursor(__Cursor.line - 1, 0); + }); + + $("#" + id + " [type=\"inline\"]").bind("click", function() { + editorName.cm.replaceSelection("$$$$"); + var __Cursor = editorName.cm.getDoc().getCursor(); + editorName.cm.setCursor(__Cursor.line, __Cursor.ch - 2); + editorName.cm.focus(); + }); + $("[type=\"inline\"]").attr("title", "行内公式"); + $("[type=\"latex\"]").attr("title", "多行公式"); + setTimeout(function() { + editorName.resize(); + editorName.cm.refresh(); + window.new_md = editorName; + }, 300); + } + }); + return editorName; +} + +// editormd to html +/** + * + * @param id 渲染的id + * @param callback onload回調 暫時未用 + */ +function editormd_to_html(id, callback) { + editormd.loadKaTeX(function() { + editormd.markdownToHTML(id, { + htmlDecode: "style,script,iframe", + // you can filter tags decode + onload: function() { + callback && callback() + }, + taskList: true, + tex: true, + // 默认不解析 + flowChart: true, + // 默认不解析 + sequenceDiagram: true// 默认不解析 + }); + }); +} + +function loadHeader() { + //头部导航条的----------显示搜索框 + $("#search-open").on("click", function(e) { + $(this).hide(); + // $("#header-nav").animate({opacity:"0"},1000); + $(".posi-search").show() + // .animate({opacity:"1"},1000); + $("#header-nav").css("z-index", "2"); + $(".posi-search").css("z-index", "3"); + // $(".search-input").val(""); // 不清空 + $(".search-input").focus(); + $(".search-all .search-content").hide(); + e.stopPropagation(); + //阻止冒泡 + }); + $(".search-input").on("click", function(e) { + e.stopPropagation(); + //阻止冒泡 + }); + //搜索框输入内容 + $(".search-input").on("input", function(e) { + if ($(".search-input").val() == "") { + $(".search-all .search-content").hide(); + } else { + $(".search-all .search-content").show(); + } + e.stopPropagation(); + //阻止冒泡 + }); + //搜索 + $("#header_keyword_search").on("click", header_search); + $("input[name='search_keyword']").on("keydown", function(event) { + var code; + if (!event) { + event = window.event; + //针对ie浏览器 + code = event.keyCode; + } else { + code = event.keyCode; + } + if (code == 13) { + header_search(); + return false; + } + }); + $(".search-clear").click(function(e) { + e.stopPropagation(); + }); + //切换搜索条件 + $("#searchkey li").click(function(e) { + var key = $($(this).children("a")[0]).html(); + switch (key) { + case '实训': + $("#search_type").val('1'); + break; + case '课堂': + $("#search_type").val('2'); + break; + case '用户': + $("#search_type").val('3'); + break; + } + $("#searchkey").siblings(".searchkey").html(key); + // $("#searchkey").hide(); + e.stopPropagation(); + //阻止冒泡 + }); + //切换选择导航条 + $("#header-nav li").click(function() { + $("#header-nav li").removeClass("active"); + $(this).addClass("active"); + }); + //点击页面其它(与搜索框无关的地方)都会将搜索框隐藏,所以与搜索框有关的地方需要阻止冒泡 + $("body").on("click", function() { + closeSearch(); + }); + + $(".search_history").on("click", function() { + $("input[name='search_keyword']").val($(this).html()); + header_search(); + }); +} + +function header_search() { + var keyword = $("input[name='search_keyword']").val(); + // 搜索关键字 + var index = $("#search_type").val(); + // 搜索课程/项目 + keyword = encodeURIComponent(keyword); + // $.get('/users/search_shixuns_or_course', + // { search: keyword, + // index: index}); + window.location.href = "/users/search_shixuns_or_courses" + "?search=" + keyword + "&index=" + index; + //e.stopPropagation();//阻止冒泡 +} + +//头部导航条的隐藏 +function closeSearch() { + $('#posi-search').hide(); + $("#search-open").show(); + // $(".posi-search").animate({opacity:"0"},800); + $("#header-nav").animate({ + opacity: "1" + }, 1000); + $(".posi-search").css("z-index", "2"); + $("#header-nav").css("z-index", "3"); +} +(function($) { + $.fn.drag = function(options) { + var x, drag = this, isMove = false, defaults = {}; + var options = $.extend(defaults, options); + //添加背景,文字,滑块 + var html = '
' + '
请拖住滑块,拖动到最右边
' + '
'; + this.append(html); + + var handler = drag.find('.handler'); + var drag_bg = drag.find('.drag_bg'); + var text = drag.find('.drag_text'); + var maxWidth = text.width() - handler.width(); + //能滑动的最大间距 + //鼠标按下时候的x轴的位置 + handler.mousedown(function(e) { + isMove = true; + x = e.pageX - parseInt(handler.css('left'), 10); + }); + + //鼠标指针在上下文移动时,移动距离大于0小于最大间距,滑块x轴位置等于鼠标移动距离 + $(document).mousemove(function(e) { + var _x = e.pageX - x; + var handler_offset = handler.offset(); + var lastX = e.clientX - x; + lastX = Math.max(0, Math.min(maxWidth, lastX)); + if (isMove) { + if (_x > 0 && _x <= maxWidth) { + handler.css({ + 'left': lastX + }); + drag_bg.css({ + 'width': lastX + }); + } else if (lastX > maxWidth - 5 && lastX < maxWidth + 5) { + //鼠标指针移动距离达到最大时清空事件 + dragOk(); + } + } + }); + handler.mouseup(function(e) { + isMove = false; + var _x = e.pageX - x; + if (_x < maxWidth) { + //鼠标松开时,如果没有达到最大距离位置,滑块就返回初始位置 + handler.css({ + 'left': 0 + }); + drag_bg.css({ + 'width': 0 + }); + } + }); + + //清空事件 + function dragOk() { + var kuaiwidth = drag.width() - handler.width(); + handler.removeClass('handler_bg').addClass('handler_ok_bg'); + handler.css({ + 'left': kuaiwidth + 'px' + }) + text.css({ + 'width': kuaiwidth + 'px' + }); + text.text('验证通过'); + drag.css({ + 'color': '#fff' + }); + drag_bg.css({ + 'width': kuaiwidth + 'px' + }) + handler.unbind('mousedown'); + $(document).unbind('mousemove'); + $(document).unbind('mouseup'); + handler.parent().next().find("p").html("").hide(); + } + } + ; +} +)(jQuery); + +//判断是手机端还是电脑端 +function IsPC() { + var userAgentInfo = navigator.userAgent; + var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"]; + var flag = true; + for (var v = 0; v < Agents.length; v++) { + if (userAgentInfo.indexOf(Agents[v]) > 0) { + flag = false; + break; + } + } + return flag; +} + +//Dom:绑定事件的节点对象,ChangeDOM:操作的相关节点, +function LeaveTitle(Dom, ChangeDom) { + ChangeDom.html("").hide(); + ChangeDom.parent().css({ + opacity: 0, + left: 0, + top: 0 + }).hide(); +} + +$(function() { + //平台tip的样式优化js + var $desc = $("
" + "
" + "
" + "
" + "
" + "
").appendTo("body"); + //Dom:绑定事件的节点对象,ChangeDOM:操作的相关节点, + function LeaveTitle(Dom, ChangeDom) { + Dom.live("mouseleave", function() { + ChangeDom.html("").hide(); + $desc.css({ + opacity: 0, + left: 0, + top: 0 + }).hide(); + }) + } + LeaveTitle($("[data-tip-top]"), $(".data-tip-top")); + LeaveTitle($("[data-tip-down]"), $(".data-tip-down")); + LeaveTitle($("[data-tip-right]"), $(".data-tip-left")); + LeaveTitle($("[data-tip-left]"), $(".data-tip-right")); + $("[data-tip-top]").live("mouseenter", function() { + var $tool = $(this).attr("data-tip-top"); + if ($tool != "") { + $(".data-tip-top").show().html($tool); + $desc.show().css({ + left: $(this).offset().left - ($desc.width() - $(this).outerWidth()) / 2, + opacity: 1, + top: $(this).offset().top - 30 + }); + } + }); + $("[data-tip-down]").live("mouseenter", function() { + var $tool = $(this).attr("data-tip-down"); + if ($tool != "") { + $(".data-tip-down").show().html($tool); + $desc.show().css({ + left: $(this).offset().left - ($desc.width() - $(this).outerWidth()) / 2, + opacity: 1, + top: $(this).offset().top + $(this).height() + 6 + }); + } + }); + $("[data-tip-right]").live("mouseenter", function() { + var $tool = $(this).attr("data-tip-right"); + if ($tool != "") { + console.log($(this).offset().left + " " + $(this).width()); + $(".data-tip-left").show().html($tool); + $desc.show().css({ + left: $(this).offset().left + $(this).outerWidth() + 6, + opacity: 1, + top: $(this).offset().top - ($desc.height() - $(this).height()) / 2 + }); + } + }); + $("[data-tip-left]").live("mouseenter", function() { + var $tool = $(this).attr("data-tip-left"); + if ($tool != "") { + $(".data-tip-right").show().html($tool); + $desc.show().css({ + left: $(this).offset().left - $desc.width() - 6, + opacity: 1, + top: $(this).offset().top - ($desc.height() - $(this).height()) / 2 + }); + } + }); + unitDownOption(); +}); + +function unitDownOption() { + //下拉框 + $("[select-for]").append(""); + $("[select-for]").hover(function() { + $(this).find(".down-select").show(); + }, function() { + $(this).find(".down-select").hide(); + }) + $("[select-for] .down-select p").bind("click", function() { + //alert($(this).attr("data-shixun-value")); + if ($(this).attr("id") == "diy_script") { + return; + // 实训新建-选择自定义脚本diy + } + $(this).parents(".down-select").siblings("input[type=hidden]").attr("value", $(this).attr("data-shixun-value")); + + $(this).parents(".down-select").siblings("input[type=text]").val($(this).html().trim()); + $(this).parents(".down-select").hide(); + }) +} + +//初始化省份 +function showprovince(id) { + var arrary = ["北京", "上海", "广东", "江苏", "浙江", "重庆", "安徽", "福建", "甘肃", "广西", "贵州", "海南", "河北", "黑龙江", "河南", "湖北", "湖南", "江西", "吉林", "辽宁", "内蒙古", "宁夏", "青海", "山东", "山西", "陕西", "四川", "天津", "新疆", "西藏", "云南", "香港特别行政区", "澳门特别行政区", "台湾", "海外"]; + var html = "" + for (var i = 0; i < arrary.length; i++) { + var item = arrary[i]; + html += ""; + } + $("#" + id).html(html); +} + +//省市下拉框 +function showcity(province, cityField) { + switch (province) { + case "北京": + var cityOptions = new Array("东城","西城","朝阳","丰台","石景山","海淀","门头沟","房山","通州","顺义","昌平","大兴","平谷","怀柔","密云","延庆"); + break; + case "上海": + var cityOptions = new Array("崇明","黄浦","卢湾","徐汇","长宁","静安","普陀","闸北","虹口","杨浦","闵行","宝山","嘉定","浦东","金山","松江","青浦","南汇","奉贤"); + break; + case "广东": + var cityOptions = new Array("广州","深圳","珠海","东莞","中山","佛山","惠州","河源","潮州","江门","揭阳","茂名","梅州","清远","汕头","汕尾","韶关","顺德","阳江","云浮","湛江","肇庆"); + break; + case "江苏": + var cityOptions = new Array("南京","常熟","常州","海门","淮安","江都","江阴","昆山","连云港","南通","启东","沭阳","宿迁","苏州","太仓","泰州","同里","无锡","徐州","盐城","扬州","宜兴","仪征","张家港","镇江","周庄"); + break; + case "重庆": + var cityOptions = new Array("万州","涪陵","渝中","大渡口","江北","沙坪坝","九龙坡","南岸","北碚","万盛","双挢","渝北","巴南","黔江","长寿","綦江","潼南","铜梁","大足","荣昌","壁山","梁平","城口","丰都","垫江","武隆","忠县","开县","云阳","奉节","巫山","巫溪","石柱","秀山","酉阳","彭水","江津","合川","永川","南川"); + break; + case "安徽": + var cityOptions = new Array("合肥","安庆","蚌埠","亳州","巢湖","滁州","阜阳","贵池","淮北","淮化","淮南","黄山","九华山","六安","马鞍山","宿州","铜陵","屯溪","芜湖","宣城"); + break; + case "福建": + var cityOptions = new Array("福州","厦门","泉州","漳州","龙岩","南平","宁德","莆田","三明"); + break; + case "甘肃": + var cityOptions = new Array("兰州","白银","定西","敦煌","甘南","金昌","酒泉","临夏","平凉","天水","武都","武威","西峰","张掖"); + break; + case "广西": + var cityOptions = new Array("南宁","百色","北海","桂林","防城港","贵港","河池","贺州","柳州","钦州","梧州","玉林"); + break; + case "贵州": + var cityOptions = new Array("贵阳","安顺","毕节","都匀","凯里","六盘水","铜仁","兴义","玉屏","遵义"); + break; + case "海南": + var cityOptions = new Array("海口","儋县","陵水","琼海","三亚","通什","万宁"); + break; + case "河北": + var cityOptions = new Array("石家庄","保定","北戴河","沧州","承德","丰润","邯郸","衡水","廊坊","南戴河","秦皇岛","唐山","新城","邢台","张家口"); + break; + case "黑龙江": + var cityOptions = new Array("哈尔滨","北安","大庆","大兴安岭","鹤岗","黑河","佳木斯","鸡西","牡丹江","齐齐哈尔","七台河","双鸭山","绥化","伊春"); + break; + case "河南": + var cityOptions = new Array("郑州","安阳","鹤壁","潢川","焦作","济源","开封","漯河","洛阳","南阳","平顶山","濮阳","三门峡","商丘","新乡","信阳","许昌","周口","驻马店"); + break; + case "香港": + var cityOptions = new Array("香港","九龙","新界"); + break; + case "湖北": + var cityOptions = new Array("武汉","恩施","鄂州","黄冈","黄石","荆门","荆州","潜江","十堰","随州","武穴","仙桃","咸宁","襄阳","襄樊","孝感","宜昌"); + break; + case "湖南": + var cityOptions = new Array("长沙","常德","郴州","衡阳","怀化","吉首","娄底","邵阳","湘潭","益阳","岳阳","永州","张家界","株洲"); + break; + case "江西": + var cityOptions = new Array("南昌","抚州","赣州","吉安","景德镇","井冈山","九江","庐山","萍乡","上饶","新余","宜春","鹰潭"); + break; + case "吉林": + var cityOptions = new Array("长春","吉林","白城","白山","珲春","辽源","梅河","四平","松原","通化","延吉"); + break; + case "辽宁": + var cityOptions = new Array("沈阳","鞍山","本溪","朝阳","大连","丹东","抚顺","阜新","葫芦岛","锦州","辽阳","盘锦","铁岭","营口"); + break; + case "澳门": + var cityOptions = new Array("澳门"); + break; + case "内蒙古": + var cityOptions = new Array("呼和浩特","阿拉善盟","包头","赤峰","东胜","海拉尔","集宁","临河","通辽","乌海","乌兰浩特","锡林浩特"); + break; + case "宁夏": + var cityOptions = new Array("银川","固源","石嘴山","吴忠"); + break; + case "青海": + var cityOptions = new Array("西宁","德令哈","格尔木","共和","海东","海晏","玛沁","同仁","玉树"); + break; + case "山东": + var cityOptions = new Array("济南","滨州","兖州","德州","东营","菏泽","济宁","莱芜","聊城","临沂","蓬莱","青岛","曲阜","日照","泰安","潍坊","威海","烟台","枣庄","淄博"); + break; + case "山西": + var cityOptions = new Array("太原","长治","大同","候马","晋城","离石","临汾","宁武","朔州","忻州","阳泉","榆次","运城"); + break; + case "陕西": + var cityOptions = new Array("西安","安康","宝鸡","汉中","渭南","商州","绥德","铜川","咸阳","延安","榆林"); + break; + case "四川": + var cityOptions = new Array("成都","巴中","达川","德阳","都江堰","峨眉山","涪陵","广安","广元","九寨沟","康定","乐山","泸州","马尔康","绵阳","眉山","南充","内江","攀枝花","遂宁","汶川","西昌","雅安","宜宾","自贡","资阳"); + break; + case "台湾": + var cityOptions = new Array("台北","基隆","台南","台中","高雄","屏东","南投","云林","新竹","彰化","苗栗","嘉义","花莲","桃园","宜兰","台东","金门","马祖","澎湖"); + break; + case "天津": + var cityOptions = new Array("天津","和平","东丽","河东","西青","河西","津南","南开","北辰","河北","武清","红挢","塘沽","汉沽","大港","宁河","静海","宝坻","蓟县"); + break; + case "新疆": + var cityOptions = new Array("乌鲁木齐","阿克苏","阿勒泰","阿图什","博乐","昌吉","东山","哈密","和田","喀什","克拉玛依","库车","库尔勒","奎屯","石河子","塔城","吐鲁番","伊宁"); + break; + case "西藏": + var cityOptions = new Array("拉萨","阿里","昌都","林芝","那曲","日喀则","山南"); + break; + case "云南": + var cityOptions = new Array("昆明","大理","保山","楚雄","大理","东川","个旧","景洪","开远","临沧","丽江","六库","潞西","曲靖","思茅","文山","西双版纳","玉溪","中甸","昭通"); + break; + case "浙江": + var cityOptions = new Array("杭州","安吉","慈溪","定海","奉化","海盐","黄岩","湖州","嘉兴","金华","临安","临海","丽水","宁波","瓯海","平湖","千岛湖","衢州","江山","瑞安","绍兴","嵊州","台州","温岭","温州","余姚","舟山"); + break; + case "海外": + var cityOptions = new Array("美国","日本","英国","法国","德国","其他"); + break; + default: + var cityOptions = new Array("请选择所在城市"); + break; + } + + cityField.options.length = 0; + for (var i = 0; i < cityOptions.length; i++) { + cityField.options[i] = new Option(cityOptions[i],cityOptions[i]); + /* + if (cityField.options[i].value==city) + { + //alert("here put City ok!"); + document.oblogform["city"].selectedIndex = i; + }*/ + } +} + +/*弹框*/ +// 公共弹框样式 +// 建议左右栏的:Width:460,Height:190 +// 建议宽屏对应值:Width:760,Height:500 +function pop_box_new(value, Width, Height, close) { + + if ($("#popupAll").length > 0) { + $("#popupAll").remove(); + } + w = ($(window).width() - Width) / 2; + h = ($(window).height() - Height) / 2; + var html = "
"; + if (close) { + value = "" + value; + } + $(document.body).append(html); + $("#popupWrap").html(value); + $('#popupWrap').css({ + "top": h + "px", + "left": w + "px", + "padding": "0", + "border": "none", + "position": "fixed", + "z-index": "99999", + "background-color": "#fff", + "border-radius": "10px" + }); + if (close) { + $('#closeIcon').css({ + "top": "-26px", + "left": Width + "px", + "z-index": "100000" + }); + } + + $("#popupWrap").parent().parent().show(); + $('#popupAll').find("#closeIcon").click(function() { + $("#popupAll").hide(); + }); + $('#popupAll').find("a[class*='pop_close']").click(function() { + $("#popupAll").hide(); + }); + // w = ($(window).width() - Width)/2; + // h = ($(window).height() - Height)/2; + // $("#ajax-modal").html(value); + // showModal('ajax-modal', Width + 'px'); + // $('#ajax-modal').siblings().remove(); + // $('#ajax-modal').parent().css({"top": h+"px","left": w+"px","padding":"0","border":"none","position":"fixed"}); + // $('#ajax-modal').parent().removeClass("resourceUploadPopup popbox_polls popbox"); + // $('#ajax-modal').css({"padding":"0","overflow":"hidden"}); + // $('#ajax-modal').parent().attr("id","popupWrap"); + + //拖拽 + function Drag(id) { + this.div = document.getElementById(id); + if (this.div) { + this.div.style.cursor = "move"; + this.div.style.position = "fixed"; + } + this.disX = 0; + this.disY = 0; + var _this = this; + this.div.onmousedown = function(evt) { + _this.getDistance(evt); + document.onmousemove = function(evt) { + _this.setPosition(evt); + } + ; + _this.div.onmouseup = function() { + _this.clearEvent(); + } + } + } + Drag.prototype.getDistance = function(evt) { + var oEvent = evt || event; + this.disX = oEvent.clientX - this.div.offsetLeft; + this.disY = oEvent.clientY - this.div.offsetTop; + } + ; + Drag.prototype.setPosition = function(evt) { + var oEvent = evt || event; + var l = oEvent.clientX - this.disX; + var t = oEvent.clientY - this.disY; + if (l <= 0) { + l = 0; + } else if (l >= document.documentElement.clientWidth - this.div.offsetWidth) { + l = document.documentElement.clientWidth - this.div.offsetWidth; + } + if (t <= 0) { + t = 0; + } else if (t >= document.documentElement.clientHeight - this.div.offsetHeight) { + t = document.documentElement.clientHeight - this.div.offsetHeight; + } + this.div.style.left = l + "px"; + this.div.style.top = t + "px"; + } + ; + Drag.prototype.clearEvent = function() { + this.div.onmouseup = null; + document.onmousemove = null; + } + ; + + new Drag("popupWrap"); + + $("#popupAll input, #popupAll textarea, #popupAll select, #popupAll ul, #popupAll a,#shixun_search_form_div").mousedown(function(event) { + event.stopPropagation(); + new Drag("popupWrap"); + }); + +} + +function hideModal(el) { + if ($("#popupAll").length > 0) { + $("#popupAll").remove(); + } else { + var modal; + if (el) { + modal = $(el).parents('.ui-dialog-content'); + } else { + modal = $('#ajax-modal'); + } + modal.dialog("close"); + } +} + +//提示框:只有一个确定按钮,点击跳转 +// +function notice_box_redirect(url, str) { + var htmlvalue = '
提示
' + '

' + str + '

' + '确定
'; + pop_box_new(htmlvalue, 480, 160); +} +//按钮内容自定义(自定义按钮需要remote=true,且有取消按钮) +function notice_operation_box(url, str, btnstr) { + var htmlvalue = '
提示
' + '

' + str + '

' + '取消' + btnstr + '
'; + pop_box_new(htmlvalue, 480, 160); +} +//点击删除时的确认弹框: 不走destroy方法 +function delete_confirm_box(url, str) { + var htmlvalue = '
提示
' + '

' + str + '

取消' + '确定
'; + pop_box_new(htmlvalue, 480, 160); +} +//点击删除时的确认弹框: 走destroy方法,remote为true +function delete_confirm_box_2(url, str) { + var htmlvalue = '
提示
' + '
' + str + '
取消' + '确定
'; + pop_box_new(htmlvalue, 480, 160); +} + +// 点击确定的时候ajax请求,两个按钮 点击确认跳转, 提示信息可以多行 +function op_confirm_box_remote(url, str) { + var htmlvalue = '
提示
' + '

' + str + '

取消' + '确定
'; + pop_box_new(htmlvalue, 578, 205); +} + +//点击删除时的确认弹框: post,remote为true +function post_confirm_box(url, str) { + var htmlvalue = '

提示

' + '

' + str + '

取消' + '确定
'; + pop_box_new(htmlvalue, 480, 160); +} + +//提示框:只有一个确定按钮,点击关闭弹框 +// +function notice_box(str) { + var htmlvalue = '
提示
' + '

' + str + '

' + '确定
'; + pop_box_new(htmlvalue, 480, 160); +} + +//点击删除时的确认弹框: 走destroy方法 +function delete_confirm_box_3(url, str) { + var htmlvalue = '
提示
' + '

' + str + '

取消' + '确定
'; + pop_box_new(htmlvalue, 480, 160); +} + +//取消和确定,确定会调用自定义方法 +function op_confirm_tip(str, func) { + var htmlvalue = '
提示
' + '

' + str + '

取消' + '确定
'; + pop_box_new(htmlvalue, 500, 205); +} + +//取消和确定,确定会调用自定义方法(带参数) +function op_confirm_tip_1(str, func) { + var htmlvalue = '
提示
' + '

' + str + '

取消' + '确定
'; + pop_box_new(htmlvalue, 500, 205); +} + +function op_confirm_box_loading(url, str) { + var htmlvalue = '
提示
' + '

' + str + '

取消' + '确定
'; + pop_box_new(htmlvalue, 578, 205); +} + +// 两个按钮 点击确认跳转, 提示信息有两行 +function s_op_confirm_box(url, str) { + var htmlvalue = '
提示
' + '

' + str + '

取消' + '确定
'; + pop_box_new(htmlvalue, 480, 205); +} + +function suofang() { + var html = '

可能会影响某些功能的正常使用

'; + sure_confirm_box("页面缩放比例不正确", 600, 310, html); +} + +//一个“知道了”按钮,title和宽度都作为参数 +function sure_confirm_box(title, width, height, str) { + var htmlvalue = '
' + title + '
' + '
' + str + '
' + '知道了
'; + pop_box_new(htmlvalue, width, height); +} + +function throttle(method, context, e) { + clearTimeout(method.tId); + method.tId = setTimeout(function() { + method.call(context, e); + }, 500); +} + +function apply_publish_shixun(url) { + if ($("#apply_publish_shixun").attr("data-option") == '1') { + $("#apply_publish_shixun").attr("data-option", 0); + $("#apply_publish_shixun").addClass("disabled-grey-bg"); + $.ajax({ + url: url, + type: 'get' + }); + } +} + +var autoTextarea = function(elem, extra, maxHeight) { + extra = extra || 0; + var isFirefox = !!document.getBoxObjectFor || 'mozInnerScreenX'in window + , isOpera = !!window.opera && !!window.opera.toString().indexOf('Opera') + , addEvent = function(type, callback) { + elem.addEventListener ? elem.addEventListener(type, callback, false) : elem.attachEvent('on' + type, callback); + } + , getStyle = elem.currentStyle ? function(name) { + var val = elem.currentStyle[name]; + + if (name === 'height' && val.search(/px/i) !== 1) { + var rect = elem.getBoundingClientRect(); + return rect.bottom - rect.top - parseFloat(getStyle('paddingTop')) - parseFloat(getStyle('paddingBottom')) + 'px'; + } + ; + return val; + } + : function(name) { + return getComputedStyle(elem, null)[name]; + } + , minHeight = parseFloat(getStyle('height')); + + elem.style.resize = 'none'; + + var change = function() { + var scrollTop, height, padding = 0, style = elem.style; + + if (elem._length === elem.value.length) + return; + elem._length = elem.value.length; + + if (!isFirefox && !isOpera) { + padding = parseInt(getStyle('paddingTop')) + parseInt(getStyle('paddingBottom')); + } + ;scrollTop = document.body.scrollTop || document.documentElement.scrollTop; + + elem.style.height = minHeight + 'px'; + if (elem.scrollHeight > minHeight) { + if (maxHeight && elem.scrollHeight > maxHeight) { + height = maxHeight - padding; + style.overflowY = 'auto'; + } else { + height = elem.scrollHeight - padding + 10; + style.overflowY = 'hidden'; + } + ;style.height = height + extra + 'px'; + scrollTop += parseInt(style.height) - elem.currHeight; + //document.body.scrollTop = scrollTop; + //document.documentElement.scrollTop = scrollTop; + elem.currHeight = parseInt(style.height); + } + ; + }; + + addEvent('propertychange', change); + addEvent('input', change); + addEvent('focus', change); + change(); +}; + +// 点击按钮复制功能 +function jsCopy() { + var e = document.getElementById("copy_rep_content"); + e.select(); + document.execCommand("Copy"); +} + +// 使用resize事件监听窗口的zoom,如果zoom变化了,弹框提示;初始化时也检查zoom是否是100%。 +function _initZoomCheck() { + if (!IsPC()) { + // 手机端不需要提示 + return; + } + var isNormalZoom = Math.round(window.devicePixelRatio * 100) === 100 + if (!isNormalZoom) { + suofang(); + } + + $(window).resize(function() { + var isNormalZoom = Math.round(window.devicePixelRatio * 100) === 100 + if (!isNormalZoom) { + suofang(); + } else { + $('.task-btn.task-btn-orange:visible').click() + } + }) + +} + +var win_resize = function() { + var _w = $(window).width() - 1200; + if (_w < 0) { + $('.newHeader>.educontent').width('auto') + } else { + $('.newHeader>.educontent').width('1200px') + } +}; + +function initWindowResize() { + if (location.pathname === '/login') { + // 登录页不需要 + return; + } + + $(function() { + setTimeout(function() { + win_resize(); + }, 1000) + }) + + $(window).resize(function() { + win_resize() + }) +} +initWindowResize(); + +// 登录刷新 https://stackoverflow.com/questions/28230845/communication-between-tabs-or-windows +if (window['BroadcastChannel']) { + var bc = new BroadcastChannel('ec_reload'); + bc.onmessage = function(ev) { + if (window['ec_reload_msg_send_window']) { + window['ec_reload_msg_send_window'] = false; + } else { + location.reload(); + } + } +} +function _sendReloadMsg() { + var bc = new BroadcastChannel('ec_reload'); + window['ec_reload_msg_send_window'] = true; + // 消息发出的窗口,不需要处理该消息 + bc.postMessage('ec_reload'); + /* send */ +} +// IE11 没有 startsWith +if (!String.prototype.startsWith) { + String.prototype.startsWith = function(searchString, position) { + position = position || 0; + return this.substr(position, searchString.length) === searchString; + } + ; + String.prototype.endsWith = function(search, this_len) { + if (this_len === undefined || this_len > this.length) { + this_len = this.length; + } + return this.substring(this_len - search.length, this_len) === search; + } + ; +} + +function clickNewsubscript() { + $(".newsubscript").hide(); + $(".newedbox").addClass("newminheight"); + $(".newedbox").removeClass("newedboxheight"); +} + +/** tpm实训开启按钮,不允许多次点击 START */ +//点击模拟实战或者开启实战等,按钮变灰内容变成“开启中” +var operationItem = null; +var operationButtonOldValue = null; +function opClickString(item) { + var value = $(item).html(); + $(item).css({ + 'background': 'gray', + 'border': '1px solid grey', + 'pointer-events': 'none' + }); + $(item).html('开启中'); + + operationButtonOldValue = value + operationItem = item + // setTimeout(function(){ $(item).css('background', '#4CACFF');$(item).html(value); }, 4000) +} +// +// var isOperationSending = false; +$(document).bind('ajaxStop', function(event, xhr, settings) { + if (settings && settings.url && (settings.url.match(/operation\?/))) { + if (operationItem) { + $(operationItem).css('background', '#4CACFF').css('pointer-events', 'inherit'); + $(operationItem).html(operationButtonOldValue); + } + } +}); +$(document).bind('ajaxError', function(event, xhr, settings) { + if (settings && settings.url && (settings.url.match(/operation\?/))) { + if (operationItem) { + $(operationItem).css('background', '#4CACFF').css('pointer-events', 'inherit'); + $(operationItem).html(operationButtonOldValue); + } + } +}); +/** tpm实训开启按钮,不允许多次点击 END */ diff --git a/public/react/config/webpack.config.dev.js b/public/react/config/webpack.config.dev.js index ceddbd81b..12d5e437f 100644 --- a/public/react/config/webpack.config.dev.js +++ b/public/react/config/webpack.config.dev.js @@ -9,7 +9,7 @@ const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin'); const eslintFormatter = require('react-dev-utils/eslintFormatter'); const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); -// const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); +const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); const getClientEnvironment = require('./env'); const paths = require('./paths'); @@ -28,8 +28,8 @@ const env = getClientEnvironment(publicUrl); // The production configuration is different and lives in a separate file. module.exports = { // You may want 'eval' instead if you prefer to see the compiled output in DevTools. - // See the discussion in https://github.com/facebookincubator/create-react-app/issues/343. - devtool: 'cheap-module-source-map', + // See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.s + devtool: "eval", // 开启调试 // These are the "entry points" to our application. // This means they will be the "root" imports that are included in JS bundle. // The first two entry points enable "hot" CSS and auto-refreshes for JS. @@ -113,7 +113,6 @@ module.exports = { // First, run the linter. // It's important to do this before Babel processes the JS. - // 上线然后要注释回来 // { // test: /\.(js|jsx|mjs)$/, // enforce: 'pre', @@ -250,7 +249,8 @@ module.exports = { // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack // You can remove this if you don't use Moment.js: new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), - ], + new MonacoWebpackPlugin(), + ], // Some libraries import Node modules but don't use them in the browser. // Tell Webpack to provide empty mocks for them so importing them works. node: { diff --git a/public/react/config/webpack.config.prod.js b/public/react/config/webpack.config.prod.js index 92feea0a4..0abd707af 100644 --- a/public/react/config/webpack.config.prod.js +++ b/public/react/config/webpack.config.prod.js @@ -1,349 +1,365 @@ -'use strict'; - -const autoprefixer = require('autoprefixer'); -const path = require('path'); -const webpack = require('webpack'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const ExtractTextPlugin = require('extract-text-webpack-plugin'); -const ManifestPlugin = require('webpack-manifest-plugin'); -const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); -const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin'); -const eslintFormatter = require('react-dev-utils/eslintFormatter'); -const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); -const paths = require('./paths'); -const getClientEnvironment = require('./env'); - -// Webpack uses `publicPath` to determine where the app is being served from. -// It requires a trailing slash, or the file assets will get an incorrect path. -const publicPath = paths.servedPath; -// Some apps do not use client-side routing with pushState. -// For these, "homepage" can be set to "." to enable relative asset paths. -const shouldUseRelativeAssetPaths = publicPath === './'; -// Source maps are resource heavy and can cause out of memory issue for large source files. -const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false'; -// `publicUrl` is just like `publicPath`, but we will provide it to our app -// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript. -// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz. -const publicUrl = publicPath.slice(0, -1); -// Get environment variables to inject into our app. -const env = getClientEnvironment(publicUrl); - -// Assert this just to be safe. -// Development builds of React are slow and not intended for production. -if (env.stringified['process.env'].NODE_ENV !== '"production"') { - throw new Error('Production builds must have NODE_ENV=production.'); -} - -// Note: defined here because it will be used more than once. -const cssFilename = './static/css/[name].[contenthash:8].css'; - -// ExtractTextPlugin expects the build output to be flat. -// (See https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/27) -// However, our output is structured with css, js and media folders. -// To have this structure working with relative paths, we have to use custom options. -const extractTextPluginOptions = shouldUseRelativeAssetPaths - ? // Making sure that the publicPath goes back to to build folder. - { publicPath: Array(cssFilename.split('/').length).join('../') } - : {}; - -// This is the production configuration. -// It compiles slowly and is focused on producing a fast and minimal bundle. -// The development configuration is different and lives in a separate file. - -// console.log('publicPath ', publicPath) -module.exports = { - // Don't attempt to continue if there are any errors. - bail: true, - // We generate sourcemaps in production. This is slow but gives good results. - // You can exclude the *.map files from the build during deployment. - // devtool: shouldUseSourceMap ? 'nosources-source-map' : false, //正式版 - devtool: shouldUseSourceMap ? 'source-map' : false,//测试版 - // In production, we only want to load the polyfills and the app code. - entry: [require.resolve('./polyfills'), paths.appIndexJs], - output: { - // The build folder. - path: paths.appBuild, - // Generated JS file names (with nested folders). - // There will be one main bundle, and one file per asynchronous chunk. - // We don't currently advertise code splitting but Webpack supports it. - filename: './static/js/[name].[chunkhash:8].js', - chunkFilename: './static/js/[name].[chunkhash:8].chunk.js', - // We inferred the "public path" (such as / or /my-project) from homepage. - // cdn - // publicPath: 'https://shixun.educoder.net/react/build/', //publicPath, https://cdn.educoder.net - // publicPath: 'https://cdn-testeduplus2.educoder.net/react/build/', //publicPath, https://cdn.educoder.net - publicPath: '/react/build/', //publicPath, https://cdn.educoder.net - - // Point sourcemap entries to original disk location (format as URL on Windows) - devtoolModuleFilenameTemplate: info => - path - .relative(paths.appSrc, info.absoluteResourcePath) - .replace(/\\/g, '/'), - }, - resolve: { - // This allows you to set a fallback for where Webpack should look for modules. - // We placed these paths second because we want `node_modules` to "win" - // if there are any conflicts. This matches Node resolution mechanism. - // https://github.com/facebookincubator/create-react-app/issues/253 - modules: ['node_modules', paths.appNodeModules].concat( - // It is guaranteed to exist because we tweak it in `env.js` - process.env.NODE_PATH.split(path.delimiter).filter(Boolean) - ), - // These are the reasonable defaults supported by the Node ecosystem. - // We also include JSX as a common component filename extension to support - // some tools, although we do not recommend using it, see: - // https://github.com/facebookincubator/create-react-app/issues/290 - // `web` extension prefixes have been added for better support - // for React Native Web. - extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'], - alias: { - "educoder": __dirname + "/../src/common/educoder.js", - // Support React Native Web - // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ - 'react-native': 'react-native-web', - }, - plugins: [ - // Prevents users from importing files from outside of src/ (or node_modules/). - // This often causes confusion because we only process files within src/ with babel. - // To fix this, we prevent you from importing files out of src/ -- if you'd like to, - // please link the files into your node_modules/ and let module-resolution kick in. - // Make sure your source files are compiled, as they will not be processed in any way. - new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]), - ], - }, - module: { - strictExportPresence: true, - rules: [ - // TODO: Disable require.ensure as it's not a standard language feature. - // We are waiting for https://github.com/facebookincubator/create-react-app/issues/2176. - // { parser: { requireEnsure: false } }, - - // First, run the linter. - // It's important to do this before Babel processes the JS. - // { - // test: /\.(js|jsx|mjs)$/, - // enforce: 'pre', - // use: [ - // { - // options: { - // formatter: eslintFormatter, - // eslintPath: require.resolve('eslint'), - // - // }, - // loader: require.resolve('eslint-loader'), - // }, - // ], - // include: paths.appSrc, - // }, - { - // "oneOf" will traverse all following loaders until one will - // match the requirements. When no loader matches it will fall - // back to the "file" loader at the end of the loader list. - oneOf: [ - // "url" loader works just like "file" loader but it also embeds - // assets smaller than specified size as data URLs to avoid requests. - { - test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/], - loader: require.resolve('url-loader'), - options: { - limit: 10000, - name: 'static/media/[name].[hash:8].[ext]', - }, - }, - // Process JS with Babel. - { - test: /\.(js|jsx|mjs)$/, - include: paths.appSrc, - loader: require.resolve('babel-loader'), - options: { - - compact: true, - }, - }, - // The notation here is somewhat confusing. - // "postcss" loader applies autoprefixer to our CSS. - // "css" loader resolves paths in CSS and adds assets as dependencies. - // "style" loader normally turns CSS into JS modules injecting + + + ); + } +} + +export default Loading; diff --git a/public/react/src/common/Const.js b/public/react/src/common/Const.js index ed90302e3..f680cb78d 100644 --- a/public/react/src/common/Const.js +++ b/public/react/src/common/Const.js @@ -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_SHIXUN_MANAGER = 2 // 实训管理员 -export const EDU_SHIXUN_MEMBER = 3 // 实训成员 -export const EDU_CERTIFICATION_TEACHER = 4 // 平台认证的老师 -export const EDU_GAME_MANAGER = 5 // TPI的创建者 -export const EDU_TEACHER = 6 // 平台老师,但是未认证 -export const EDU_NORMAL = 7 // 普通用户 \ No newline at end of file +export const EDU_BUSINESS = 2 // # 运营人员 +export const EDU_SHIXUN_MANAGER = 3 // 实训管理员 +export const EDU_SHIXUN_MEMBER = 4 // 实训成员 +export const EDU_CERTIFICATION_TEACHER = 5 // 平台认证的老师 +export const EDU_GAME_MANAGER = 6 // TPI的创建者 +export const EDU_TEACHER = 7 // 平台老师,但是未认证 +export const EDU_NORMAL = 8 // 普通用户 \ No newline at end of file diff --git a/public/react/src/common/SnackbarHOC.js b/public/react/src/common/SnackbarHOC.js index 1fd1407dd..d66b697ca 100644 --- a/public/react/src/common/SnackbarHOC.js +++ b/public/react/src/common/SnackbarHOC.js @@ -1,63 +1,81 @@ -import React, { Component } from 'react'; -import Snackbar from 'material-ui/Snackbar'; -import Fade from 'material-ui/transitions/Fade'; - -export function SnackbarHOC(options = {}) { - return function wrap(WrappedComponent) { - return class Wrapper extends Component { - constructor(props) { - super(props); - this.showSnackbar = this.showSnackbar.bind(this) - this.state = { - snackbarText: '', - snackbarOpen: false, - } - } - - handleSnackbarClose() { - this.setState({ - snackbarOpen: false, - snackbarVertical: '', - snackbarHorizontal: '', - }) - } - - // 全局的snackbar this.props.showSnackbar调用即可 - showSnackbar(text, vertical, horizontal) { - this.setState({ - snackbarOpen: true, - snackbarText: text, - snackbarVertical: vertical, - snackbarHorizontal: horizontal, - }) - } - render() { - const { snackbarOpen, snackbarText, snackbarHorizontal, snackbarVertical } = this.state; - - - return ( - - this.handleSnackbarClose()} - transition={Fade} - SnackbarContentProps={{ - 'aria-describedby': 'message-id', - }} - resumeHideDuration={2000} - message={{this.state.snackbarText}} - /> - - - - - ) - } - } - } +import React, { Component } from 'react'; +import Snackbar from 'material-ui/Snackbar'; +import Fade from 'material-ui/transitions/Fade'; +import { notification } from 'antd' +export function SnackbarHOC(options = {}) { + return function wrap(WrappedComponent) { + return class Wrapper extends Component { + constructor(props) { + super(props); + this.showSnackbar = this.showSnackbar.bind(this) + this.state = { + snackbarText: '', + snackbarOpen: false, + } + } + + handleSnackbarClose() { + this.setState({ + snackbarOpen: false, + snackbarVertical: '', + snackbarHorizontal: '', + }) + } + + // 全局的snackbar this.props.showSnackbar调用即可 + // showSnackbar(description, message = "提示",icon) { + // // this.setState({ + // // snackbarOpen: true, + // // snackbarText: text, + // // snackbarVertical: vertical, + // // snackbarHorizontal: horizontal, + // // }) + // const data = { + // message, + // description + // } + // if (icon) { + // data.icon = icon; + // } + // notification.open(data); + // } + + showSnackbar(text, vertical, horizontal) { + this.setState({ + snackbarOpen: true, + snackbarText: text, + snackbarVertical: vertical, + snackbarHorizontal: horizontal, + }) + } + + render() { + const { snackbarOpen, snackbarText, snackbarHorizontal, snackbarVertical } = this.state; + + + return ( + + this.handleSnackbarClose()} + transition={Fade} + SnackbarContentProps={{ + 'aria-describedby': 'message-id', + }} + resumeHideDuration={2000} + message={{this.state.snackbarText}} + /> + + + + + ) + } + } + } } \ No newline at end of file diff --git a/public/react/src/common/TextUtil.js b/public/react/src/common/TextUtil.js index 4a3145c19..c711143d2 100644 --- a/public/react/src/common/TextUtil.js +++ b/public/react/src/common/TextUtil.js @@ -1,5 +1,9 @@ import { bytesToSize } from 'educoder'; -export function markdownToHTML(oldContent) { +export function isImageExtension(fileName) { + return fileName ? !!(fileName.match(/.(jpg|jpeg|png|gif)$/i)) : false +} + +export function markdownToHTML(oldContent, selector) { window.$('#md_div').html('') // markdown to html var markdwonParser = window.editormd.markdownToHTML("md_div", { @@ -12,6 +16,9 @@ export function markdownToHTML(oldContent) { sequenceDiagram: true // 默认不解析 }); const content = window.$('#md_div').html() + if (selector) { + window.$(selector).html(content) + } return content } diff --git a/public/react/src/common/UnitUtil.js b/public/react/src/common/UnitUtil.js index bb3267aa4..8b115dffb 100644 --- a/public/react/src/common/UnitUtil.js +++ b/public/react/src/common/UnitUtil.js @@ -1,6 +1,6 @@ -export function bytesToSize(bytes) { - var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; - if (bytes == 0) return '0 Byte'; - var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); - return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i]; +export function bytesToSize(bytes) { + var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; + if (bytes == 0) return '0 Byte'; + var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); + return parseFloat(bytes / Math.pow(1024, i), 2).toFixed(1) + ' ' + sizes[i]; } \ No newline at end of file diff --git a/public/react/src/common/UrlTool.js b/public/react/src/common/UrlTool.js index 2c307d13e..3953f1f34 100644 --- a/public/react/src/common/UrlTool.js +++ b/public/react/src/common/UrlTool.js @@ -1,40 +1,62 @@ -const isDev = window.location.port == 3007; -export function getImageUrl(path) { - // https://www.educoder.net - // https://testbdweb.trustie.net - // const local = 'http://localhost:3000' - const local = 'https://testeduplus2.educoder.net' - if (isDev) { - return `${local}/${path}` - } - return `/${path}`; -} - -export function getUrl(path, goTest) { - // https://www.educoder.net - // https://testbdweb.trustie.net - - // 如果想所有url定位到测试版,可以反注释掉下面这行 - //goTest = true - // testbdweb.educoder.net testbdweb.trustie.net - // const local = goTest ? 'https://testeduplus2.educoder.net' : 'http://localhost:3000' - const local = 'https://testeduplus2.educoder.net' - if (isDev) { - return `${local}${path?path:''}` - } - return `${path ? path: ''}`; -} -export function getUploadActionUrl(path, goTest) { - return `${getUrl()}/api/attachments.json${isDev ? `?debug=${window._debugType || 'admin'}` : ''}` -} - -export function test(path) { - return `${path}`; -} - -export function toPath(path) { - window.open(path, '_blank'); -} - - +const isDev = window.location.port == 3007; +export function getImageUrl(path) { + // https://www.educoder.net + // https://testbdweb.trustie.net + // const local = 'http://localhost:3000' + const local = 'http://47.96.87.25:48080' + if (isDev) { + return `${local}/${path}` + } + return `/${path}`; +} + +export function setImagesUrl(path){ + const local = 'http://47.96.87.25:48080' + let firstStr=path.substr(0,1); + console.log(firstStr); + if(firstStr=="/"){ + return isDev?`${local}${path}`:`${path}`; + }else{ + return isDev?`${local}/${path}`:`/${path}`; + } +} + +export function getUrl(path, goTest) { + // https://www.educoder.net + // https://testbdweb.trustie.net + + // 如果想所有url定位到测试版,可以反注释掉下面这行 + //goTest = true + // testbdweb.educoder.net testbdweb.trustie.net + // const local = goTest ? 'https://testeduplus2.educoder.net' : 'http://localhost:3000' + // const local = 'https://testeduplus2.educoder.net' + const local = 'http://47.96.87.25:48080' + if (isDev) { + return `${local}${path?path:''}` + } + return `${path ? path: ''}`; +} +export function getUrl2(path, goTest) { + const local = 'http://localhost:3000' + if (isDev) { + return `${local}${path?path:''}` + } + return `${path ? path: ''}`; +} +export function getUploadActionUrl(path, goTest) { + return `${getUrl()}/api/attachments.json${isDev ? `?debug=${window._debugType || 'admin'}` : ''}` +} +export function getUploadActionUrlOfAuth(id) { + return `${getUrl()}/api/users/accounts/${id}/auth_attachments.json${isDev ? `?debug=${window._debugType || 'admin'}` : ''}` +} + +export function test(path) { + return `${path}`; +} + +export function toPath(path) { + window.open(path, '_blank'); +} + + // export default queryString \ No newline at end of file diff --git a/public/react/src/common/components/Cropper.js b/public/react/src/common/components/Cropper.js new file mode 100644 index 000000000..632950434 --- /dev/null +++ b/public/react/src/common/components/Cropper.js @@ -0,0 +1,288 @@ +import React, { Component } from 'react'; + +import { getUrl2, isDev } from 'educoder' +const $ = window.$ + +let _url_origin = getUrl2() +// let _url_origin = `http://47.96.87.25:48080`; + + + +function save_avatar(){ + + // if($(img_lg).html().trim() == ""){ + // $("#avatar-name").html("请先选择图片上传").css("color", 'red'); + // } else { + // $("#avatar-name").html("").css("color", '#333'); + const previewId = this.props.previewId + var img_lg = document.getElementById(previewId || 'img-preview'); + // 截图小的显示框内的内容 + window.html2canvas(img_lg).then(function(canvas) { + // for test + // document.getElementById('canvasWrap').appendChild(canvas); + + var dataUrl = canvas.toDataURL("image/jpeg"); + console.log(dataUrl) + // TODO upload base64 image data to server + }); + return + + // 老版接口: + // html2canvas(img_lg, { + // allowTaint: true, + // taintTest: false, + // onrendered: function(canvas) { + // canvas.id = "mycanvas"; + // //生成base64图片数据 + // var dataUrl = canvas.toDataURL("image/jpeg"); + // console.log(dataUrl) + + // var newImg = document.getElementById("showImg"); + // newImg.src = dataUrl; + // return; + + // imagesAjax(dataUrl); + // $(".avatar-save").attr("disabled","true"); + // } + // }); + // } +} +/** + props 说明: + imageId 源图片标签的id + previewId crop后预览dom的id + imageSrc 源图片src + width 数字格式 + height 数字格式 +*/ +class Cropper extends Component { + state = { + }; + + handleChange = (info) => { + } + + componentDidMount() { + this.options = { + aspectRatio: 1, + crop(event) { + // console.log(event.detail.x); + // console.log(event.detail.y); + // console.log(event.detail.width); + // console.log(event.detail.height); + // console.log(event.detail.rotate); + // console.log(event.detail.scaleX); + // console.log(event.detail.scaleY); + }, + preview: this.props.previewId ? `#${this.props.previewId}` : '.img-preview', + } + + if (!window.Cropper) { + $.ajaxSetup({ + cache: true + }); + const _isDev = isDev() + let _path = _isDev ? 'public' : 'build' + + $('head').append($('') + .attr('href', `${_url_origin}/react/${_path}/js/cropper/cropper.min.css`)); + + $.getScript( + `${_url_origin}/react/${_path}/js/cropper/cropper.js`, + (data, textStatus, jqxhr) => { + + }); + $.getScript( + `${_url_origin}/react/${_path}/js/cropper/html2canvas.min.js`, + (data, textStatus, jqxhr) => { + + }); + } + + setTimeout(() => { + const image = document.getElementById(this.props.imageId || '__image'); + this.cropper = new window.Cropper(image, this.options); + }, 1000) + } + + renew = (image) => { + this.cropper && this.cropper.destroy(); + this.cropper = new window.Cropper(image, this.options); + + } + render() { + + const { width, height, previewId, imageSrc } = this.props; + + return ( +
+ {/* This rule is very important, please do not ignore this! */} + +
+ {/* http://localhost:3007/images/footNavLogo.png 图片转了后不对 + || "/images/testPicture.jpg" + || "/images/shixun0.jpg" + */} + +
+ {/* background: 'aquamarine', + 'border-radius': '128px' + */} + {!previewId &&
+
} + + {/* */} + + {/*
*/} + {/* */} +
+ ); + } +} + +export default Cropper; + + +// function aaa () { +// function showedit_headphoto() { +// var html = ` +//