Merge branch 'master' into dev_aliyun

dev_forum
hjm 5 years ago
commit bd4fc5c734

4
.gitignore vendored

@ -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

@ -91,3 +91,5 @@ gem 'bulk_insert'
# elasticsearch
gem 'searchkick'
gem 'aasm'

@ -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

@ -0,0 +1,24 @@
class BiddingUsersController < ApplicationController
before_action :require_login, :check_auth
def create
ProjectPackages::BiddingService.call(current_package, current_user)
render_ok
rescue ProjectPackages::BiddingService::Error => ex
render_error(ex.message)
end
def win
package = current_user.project_packages.find(params[:project_package_id])
ProjectPackages::WinBiddingService.call(package, params)
render_ok
rescue ProjectPackages::WinBiddingService::Error => ex
render_error(ex.message)
end
private
def current_package
@_current_package ||= ProjectPackage.find(params[:project_package_id])
end
end

@ -0,0 +1,6 @@
class ProjectPackageCategoriesController < ApplicationController
def index
categories = ProjectPackageCategory.cached_data
render_ok(count: categories.size, categories: categories)
end
end

@ -0,0 +1,78 @@
class ProjectPackagesController < ApplicationController
include PaginateHelper
before_action :require_login, :check_auth, only: %i[create update destroy]
helper_method :current_package, :package_manageable?
def index
packages = ProjectPackage.where(status: %w(published bidding_ended bidding_finished))
packages = packages.where(project_package_category_id: params[:category_id]) if params[:category_id].present?
keyword = params[:keyword].to_s.strip
packages = packages.where('title LIKE ?', "%#{keyword}%") if keyword.present?
@count = packages.count
direction = params[:sort_direction] == 'asc' ? 'asc' : 'desc'
sort = params[:sort_by] == 'price' ? 'min_price' : 'published_at'
packages = packages.order("#{sort} #{direction}")
@packages = paginate packages.includes(:creator, :attachments, :project_package_category, bidding_users: :user)
end
def show
return render_forbidden unless current_package.visitable? || package_manageable?
current_package.increment!(:visit_count)
end
def create
package = current_user.project_packages.new
ProjectPackages::SaveService.call(package, save_params)
package.increment!(:visit_count)
render_ok(id: package.id)
rescue ProjectPackages::SaveService::Error => ex
render_error(ex.message)
end
def update
package = current_user.project_packages.find(params[:id])
return render_error('该状态下不能编辑') unless package.editable?
ProjectPackages::SaveService.call(package, save_params)
package.increment!(:visit_count)
render_ok(id: package.id)
rescue ProjectPackages::SaveService::Error => ex
render_error(ex.message)
end
def destroy
package = ProjectPackage.find(params[:id])
return render_forbidden unless package.deletable? && package_manageable?
package.destroy!
Tiding.create!(user_id: package.creator_id, trigger_user_id: 1, container_id: package.id,
container_type: 'ProjectPackage', tiding_type: 'Destroyed', extra: package.title)
render_ok
end
private
def current_package
@_current_package ||= ProjectPackage.find(params[:id])
end
def package_manageable?
current_user&.id == current_package.creator_id || admin_or_business?
end
def save_params
params.permit(*%i[category_id title content attachment_ids deadline_at min_price max_price
contact_name contact_phone code publish])
end
end

@ -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

@ -0,0 +1,5 @@
module ProjectPackageDecorator
extend ApplicationDecorator
display_time_method :updated_at, :deadline_at, :published_at
end

@ -0,0 +1,15 @@
class ProjectPackages::SaveForm
include ActiveModel::Model
attr_accessor :category_id, :title, :content, :attachment_ids, :deadline_at,
:min_price, :max_price, :contact_name, :contact_phone, :code, :publish
validates :category_id, presence: true
validates :title, presence: true, length: { maximum: 60 }
validates :content, presence: true
validates :deadline_at, presence: true
validates :min_price, numericality: { greater_than: 0 }, allow_blank: true
validates :max_price, numericality: { greater_than: ->(obj){ obj.min_price.to_i } }, allow_blank: true
validates :contact_name, presence: true, length: { maximum: 20 }
validates :contact_phone, presence: true, format: { with: /1\d{10}/ }
end

@ -0,0 +1,24 @@
class BiddingUser < ApplicationRecord
include AASM
belongs_to :user
belongs_to :project_package, counter_cache: true
aasm(:status) do
state :pending, initiali: true
state :bidding_won
state :bidding_lost
event :win do
transitions from: [:pending], to: :bid_won
end
event :lose do
transitions from: [:pending], to: :bid_lost
end
end
def status_text
I18n.t("bidding_user.status.#{status}")
end
end

@ -0,0 +1,78 @@
class ProjectPackage < ApplicationRecord
include AASM
belongs_to :creator, class_name: 'User'
belongs_to :project_package_category
has_many :project_package_applies, dependent: :destroy
has_one :process_project_package_apply, -> { where(status: :pending) }, class_name: 'ProjectPackageApply'
has_many :bidding_users, dependent: :delete_all
has_many :win_bidding_users, -> { where(status: :bidding_won) }, class_name: 'BiddingUser'
has_many :lose_bidding_users, -> { where(status: :bidding_lost) }, class_name: 'BiddingUser'
has_many :attachments, as: :container, dependent: :destroy
aasm(:status) do
state :pending, initiali: true
state :applying
state :refused
state :published
state :bidding_ended
state :bidding_finished
event :apply do
transitions from: [:pending, :refused], to: :applying
end
event :refuse do
transitions from: :applying, to: :refused
end
event :publish do
transitions from: :applying, to: :published
end
event :end_bidding do
transitions from: :published, to: :bidding_ended
end
event :finish_bidding do
transitions from: [:bidding_ended], to: :bidding_finished
end
end
def category_name
project_package_category.name
end
def visitable?
!editable?
end
def editable?
pending? || applying? || refused?
end
def deletable?
pending? || refused?
end
def deadline?
deadline_at < Time.now
end
def bidding_end?
flag = deadline?
ProjectPackages::EndBiddingService.call(self) if flag && may_end_bidding?
flag
end
def can_bidding?(user)
published? && !bidding_end? && user.id != creator_id && !bidding_users.exists?(user_id: user.id)
end
def status_text
I18n.t("project_package.status.#{status}")
end
end

@ -0,0 +1,19 @@
class ProjectPackageApply < ApplicationRecord
include AASM
belongs_to :project_package
aasm(:status) do
state :pending, initiali: true
state :refused
state :agreed
event :refuse do
transitions from: :pending, to: :refused
end
event :agree do
transitions from: :pending, to: :agreed
end
end
end

@ -0,0 +1,23 @@
class ProjectPackageCategory < ApplicationRecord
default_scope { order(position: :asc) }
has_many :project_packages, dependent: :destroy
after_commit :reset_cache_data
def self.cached_data
Rails.cache.fetch(data_cache_key, expires_in: 1.days) do
ProjectPackageCategory.select(:id, :name).as_json
end
end
def self.data_cache_key
'project_package_category/cached_data'
end
private
def reset_cache_data
Rails.cache.delete(self.class.data_cache_key)
end
end

@ -0,0 +1,36 @@
class ProjectPackages::AgreeApplyService < ApplicationService
Error = Class.new(StandardError)
attr_reader :apply, :package
def initialize(apply)
@apply = apply
@package = apply.project_package
end
def call
raise Error, '该状态下不能进行此操作' unless apply.may_agree? && package.may_publish?
ActiveRecord::Base.transaction do
apply.agree!
# 发布
package.publish
package.published_at = Time.now
package.save!
# 消息
send_agree_notify!
end
end
private
def send_agree_notify!
Tiding.where(container_id: package.id, container_type: 'ProjectPackage',
tiding_type: 'Apply', status: 0).update_all(status: 1)
Tiding.create!(user_id: package.creator_id, trigger_user_id: 1,
container_id: package.id, container_type: 'ProjectPackage',
tiding_type: 'System', status: 1)
end
end

@ -0,0 +1,31 @@
class ProjectPackages::ApplyPublishService < ApplicationService
Error = Class.new(StandardError)
attr_reader :package
def initialize(package)
@package = package
end
def call
return if package.applying?
raise Error, '该状态下不能申请发布' unless package.may_apply?
ActiveRecord::Base.transaction do
package.apply!
package.project_package_applies.create!
send_project_package_apply_notify!
end
end
private
def send_project_package_apply_notify!
Tiding.create!(user_id: 1, trigger_user_id: package.creator_id,
container_id: package.id, container_type: 'ProjectPackage',
tiding_type: 'Apply', status: 0)
end
end

@ -0,0 +1,29 @@
class ProjectPackages::BiddingService < ApplicationService
Error = Class.new(StandardError)
attr_reader :package, :user
def initialize(package, user)
@package = package
@user = user
end
def call
raise Error, '竞标已截止' if package.bidding_end?
raise Error, '不能参与自己发布的竞标' if package.creator_id == user.id
raise Error, '您已参与竞标' if package.bidding_users.exists?(user_id: user.id)
ActiveRecord::Base.transaction do
package.bidding_users.create!(user_id: user.id)
send_bidding_notify!
end
end
private
def send_bidding_notify!
Tiding.create!(user_id: package.creator_id, trigger_user_id: user.id,
container_id: package.id, container_type: 'ProjectPackage', tiding_type: 'Bidding')
end
end

@ -0,0 +1,26 @@
class ProjectPackages::EndBiddingService < ApplicationService
attr_reader :package
def initialize(package)
@package = package
end
def call
return unless package_deadline?
package.end_bidding!
send_bidding_end_notify!
end
private
def send_bidding_end_notify!
Tiding.create!(user_id: package.creator_id, trigger_user_id: 1,
container_id: package.id, container_type: 'ProjectPackage', tiding_type: 'BiddingEnd')
end
def package_deadline?
package.may_end_bidding? && package.deadline_at < Time.now
end
end

@ -0,0 +1,38 @@
class ProjectPackages::RefuseApplyService < ApplicationService
Error = Class.new(StandardError)
attr_reader :apply, :package, :params
def initialize(apply, params)
@apply = apply
@package = apply.project_package
@params = params
end
def call
raise Error, '该状态下不能进行此操作' unless apply.may_refuse? && package.may_refuse?
ActiveRecord::Base.transaction do
apply.refuse
apply.reason = params[:reason].to_s.strip
apply.save!
# 发布
package.refuse!
# 消息
send_refuse_notify!
end
end
private
def send_refuse_notify!
Tiding.where(container_id: package.id, container_type: 'ProjectPackage',
tiding_type: 'Apply', status: 0).update_all(status: 1)
Tiding.create!(user_id: package.creator_id, trigger_user_id: 1,
container_id: package.id, container_type: 'ProjectPackage',
tiding_type: 'System', status: 2, extra: apply.reason)
end
end

@ -0,0 +1,79 @@
class ProjectPackages::SaveService < ApplicationService
Error = Class.new(StandardError)
attr_reader :package, :params
def initialize(package, params)
@package = package
@params = params
end
def call
ProjectPackages::SaveForm.new(params).validate!
check_code_valid! if need_check_code?
is_create = package.new_record?
raise Error, '类型不存在' unless ProjectPackageCategory.where(id: params[:category_id]).exists?
params[:project_package_category_id] = params[:category_id].to_i
raise Error, '竞标截止时间不能小于当前时间' if params[:deadline_at].present? && params[:deadline_at].to_time < Time.now
if params[:min_price].blank? && params[:max_price].present?
params[:min_price] = params[:max_price]
params[:max_price] = nil
end
ActiveRecord::Base.transaction do
package.assign_attributes(params)
package.save!
# 处理附件
deal_attachments
send_create_notify! if is_create
ProjectPackages::ApplyPublishService.call(package) if with_publish?
end
package
rescue ProjectPackages::ApplyPublishService::Error => ex
raise Error, ex.message
end
private
def need_check_code?
(package.new_record? && params[:contact_phone] != package.creator.phone) ||
(!package.new_record? && package.contact_phone != params[:contact_phone])
end
def check_code_valid!
raise Error, '验证码不能为空' if params[:code].blank?
code = VerificationCode.where(phone: params[:contact_phone], code_type: 9, code: params[:code]).last
raise Error, '无效的验证码' if code.blank? || !code.valid_code?
end
def deal_attachments
attachment_ids = Array.wrap(params[:attachment_ids]).compact.map(&:to_i) || []
old_attachment_ids = package.attachments.pluck(:id)
destroy_ids = old_attachment_ids - attachment_ids
package.attachments.where(id: destroy_ids).delete_all
new_ids = attachment_ids - old_attachment_ids
if new_ids.present?
Attachment.where(id: new_ids, container_id: nil).update_all(container_id: package.id, container_type: 'ProjectPackage')
end
end
def send_create_notify!
Tiding.create!(user_id: package.creator_id, trigger_user_id: 1,
container_id: package.id, container_type: 'ProjectPackage', tiding_type: 'Created')
end
def with_publish?
params[:publish].to_s == 'true'
end
end

@ -0,0 +1,50 @@
class ProjectPackages::WinBiddingService < ApplicationService
Error = Class.new(StandardError)
attr_reader :package, :params
def initialize(package, params)
@package = package
@params = params
end
def call
raise Error, '竞标报名还未结束' unless package.bidding_end?
raise Error, '该状态下不能选择中标者' unless package.may_finish_bidding?
win_user_ids = Array.wrap(params[:user_ids]).compact.map(&:to_i)
bidding_user_ids = package.bidding_users.pluck(:user_id)
win_user_ids = bidding_user_ids & win_user_ids
raise Error, '请选择中标者' if win_user_ids.blank?
ActiveRecord::Base.transaction do
package.finish_bidding!
# win bidding users
package.bidding_users.where(user_id: win_user_ids).update_all(status: :bidding_won)
# lose bidding users
lost_user_ids = bidding_user_ids - win_user_ids
package.bidding_users.where(user_id: lost_user_ids).update_all(status: :bidding_lost)
send_bidding_result_notify!('BiddingWon', win_user_ids)
send_bidding_result_notify!('BiddingLost', lost_user_ids)
end
package
end
private
def send_bidding_result_notify!(type, user_ids)
columns = %i[user_id trigger_user_id container_id container_type tiding_type created_at updated_at]
Tiding.bulk_insert(*columns) do |worker|
base_attr = { trigger_user_id: package.creator_id, container_id: package.id,
container_type: 'ProjectPackage', tiding_type: type }
user_ids.each do |user_id|
worker.add(base_attr.merge(user_id: user_id))
end
end
end
end

@ -0,0 +1,12 @@
class CheckProjectPackageDeadlineTask
def call
ProjectPackage.where(status: :published).where('deadline_at < ?', Time.now).find_each do |package|
begin
ProjectPackages::EndBiddingService.new(package).call
rescue => ex
Rails.logger.error ex.message
Rails.logger.error ex.backtrace.join('\n')
end
end
end
end

@ -0,0 +1,13 @@
json.count @count
json.project_packages do
json.array! @packages.each do |package|
json.extract! package, :id, :title, :content, :category_name, :status,
:visit_count, :bidding_users_count, :min_price, :max_price
json.category_id package.project_package_category_id
json.updated_at package.display_updated_at
json.deadline_at package.display_deadline_at
json.published_at package.display_published_at
end
end

@ -0,0 +1,43 @@
package = current_package
json.extract! package, :id, :title, :content, :category_name, :status,
:visit_count, :bidding_users_count, :min_price, :max_price
json.category_id package.project_package_category_id
# 只有自己和管理员才返回私人信息
if package_manageable?
json.contact_name package.contact_name
json.contact_phone package.contact_phone
end
json.updated_at package.display_updated_at
json.deadline_at package.display_deadline_at
json.published_at package.display_published_at
json.creator do
json.partial! 'users/user_simple', user: package.creator
end
json.attachments do
json.array! package.attachments, partial: 'attachments/attachment_simple', as: :attachment
end
json.bidding_users do
json.array! package.bidding_users.includes(:user).each do |bidding_user|
json.partial! 'users/user_simple', user: bidding_user.user
json.status bidding_user.status
end
end
json.operation do
if current_user
manageable = package_manageable?
json.can_bidding package.can_bidding?(current_user)
json.can_select_bidding_user package.bidding_end? && package.bidding_ended? && manageable
json.can_edit package.editable? && manageable
json.can_delete package.deletable? && manageable
end
end

@ -0,0 +1,6 @@
'zh-CN':
bidding_user:
status:
pending: 竞标中
bidding_won: 已中标
bidding_lost: 未中标

@ -0,0 +1,13 @@
'zh-CN':
activemodel:
attributes:
project_packages/save_form:
category_id: 类型
title: 标题
content: 描述
deadline_at: 截止日期
min_price: 最小价格
max_price: 最大价格
contact_name: 联系人姓名
contact_phone: 联系人电话
code: 验证码

@ -0,0 +1,9 @@
zh-CN:
project_package:
status:
pending: 已创建
applying: 审核中
refused: 已拒绝
published: 竞标中
bidding_ended: 待选标
bidding_finished: 已完成

@ -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 认证回调

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

File diff suppressed because it is too large Load Diff

@ -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: {

@ -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 <style>,
// but unlike in development configuration, we do something different.
// `ExtractTextPlugin` first applies the "postcss" and "css" loaders
// (second argument), then grabs the result CSS and puts it into a
// separate file in our build process. This way we actually ship
// a single CSS file in production instead of JS code injecting <style>
// tags. If you use code splitting, however, any async bundles will still
// use the "style" loader inside the async code so CSS from them won't be
// in the main CSS file.
{
test: /\.css$/,
loader: ExtractTextPlugin.extract(
Object.assign(
{
fallback: {
loader: require.resolve('style-loader'),
options: {
hmr: false,
},
},
use: [
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
minimize: true,
sourceMap: shouldUseSourceMap,
},
},
{
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
flexbox: 'no-2009',
}),
],
},
},
],
},
extractTextPluginOptions
)
),
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
},
// "file" loader makes sure assets end up in the `build` folder.
// When you `import` an asset, you get its filename.
// This loader doesn't use a "test" so it will catch all modules
// that fall through the other loaders.
{
loader: require.resolve('file-loader'),
// Exclude `js` files to keep "css" loader working as it injects
// it's runtime that would otherwise processed through "file" loader.
// Also exclude `html` and `json` extensions so they get processed
// by webpacks internal loaders.
exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/],
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
},
// ** STOP ** Are you adding a new loader?
// Make sure to add the new loader(s) before the "file" loader.
],
},
],
},
plugins: [
// Makes some environment variables available in index.html.
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
// In production, it will be an empty string unless you specify "homepage"
// in `package.json`, in which case it will be the pathname of that URL.
new InterpolateHtmlPlugin(env.raw),
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
// Makes some environment variables available to the JS code, for example:
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
// It is absolutely essential that NODE_ENV was set to production here.
// Otherwise React will be compiled in the very slow development mode.
new webpack.DefinePlugin(env.stringified),
// Minify the code.
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
// Disabled because of an issue with Uglify breaking seemingly valid code:
// https://github.com/facebookincubator/create-react-app/issues/2376
// Pending further investigation:
// https://github.com/mishoo/UglifyJS2/issues/2011
comparisons: false,
},
mangle: {
safari10: true,
},
output: {
comments: false,
// Turned on because emoji and regex is not minified properly using default
// https://github.com/facebookincubator/create-react-app/issues/2488
ascii_only: true,
},
sourceMap: shouldUseSourceMap,
}),
// Note: this won't work without ExtractTextPlugin.extract(..) in `loaders`.
new ExtractTextPlugin({
filename: cssFilename,
}),
// Generate a manifest file which contains a mapping of all asset filenames
// to their corresponding output file so that tools can pick it up without
// having to parse `index.html`.
new ManifestPlugin({
fileName: 'asset-manifest.json',
}),
// Generate a service worker script that will precache, and keep up to date,
// the HTML & assets that are part of the Webpack build.
new SWPrecacheWebpackPlugin({
// By default, a cache-busting query parameter is appended to requests
// used to populate the caches, to ensure the responses are fresh.
// If a URL is already hashed by Webpack, then there is no concern
// about it being stale, and the cache-busting can be skipped.
dontCacheBustUrlsMatching: /\.\w{8}\./,
filename: 'service-worker.js',
logger(message) {
if (message.indexOf('Total precache size is') === 0) {
// This message occurs for every build and is a bit too noisy.
return;
}
if (message.indexOf('Skipping static resource') === 0) {
// This message obscures real errors so we ignore it.
// https://github.com/facebookincubator/create-react-app/issues/2612
return;
}
// console.log(message);
},
minify: true,
// For unknown URLs, fallback to the index page
navigateFallback: publicUrl + '/index.html',
// Ignores URLs starting from /__ (useful for Firebase):
// https://github.com/facebookincubator/create-react-app/issues/2237#issuecomment-302693219
navigateFallbackWhitelist: [/^(?!\/__).*/],
// Don't precache sourcemaps (they're large) and build asset manifest:
staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/],
}),
// Moment.js is an extremely popular library that bundles large locale files
// by default due to how Webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales.
// 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$/),
],
// 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: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
},
};
'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 ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
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 <style>,
// but unlike in development configuration, we do something different.
// `ExtractTextPlugin` first applies the "postcss" and "css" loaders
// (second argument), then grabs the result CSS and puts it into a
// separate file in our build process. This way we actually ship
// a single CSS file in production instead of JS code injecting <style>
// tags. If you use code splitting, however, any async bundles will still
// use the "style" loader inside the async code so CSS from them won't be
// in the main CSS file.
{
test: /\.css$/,
loader: ExtractTextPlugin.extract(
Object.assign(
{
fallback: {
loader: require.resolve('style-loader'),
options: {
hmr: false,
},
},
use: [
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
minimize: true,
sourceMap: shouldUseSourceMap,
},
},
{
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
flexbox: 'no-2009',
}),
],
},
},
],
},
extractTextPluginOptions
)
),
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
},
// "file" loader makes sure assets end up in the `build` folder.
// When you `import` an asset, you get its filename.
// This loader doesn't use a "test" so it will catch all modules
// that fall through the other loaders.
{
loader: require.resolve('file-loader'),
// Exclude `js` files to keep "css" loader working as it injects
// it's runtime that would otherwise processed through "file" loader.
// Also exclude `html` and `json` extensions so they get processed
// by webpacks internal loaders.
exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/],
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
},
// ** STOP ** Are you adding a new loader?
// Make sure to add the new loader(s) before the "file" loader.
],
},
],
},
plugins: [
// Makes some environment variables available in index.html.
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
// In production, it will be an empty string unless you specify "homepage"
// in `package.json`, in which case it will be the pathname of that URL.
new InterpolateHtmlPlugin(env.raw),
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
// Makes some environment variables available to the JS code, for example:
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
// It is absolutely essential that NODE_ENV was set to production here.
// Otherwise React will be compiled in the very slow development mode.
new webpack.DefinePlugin(env.stringified),
// Minify the code.
// new webpack.optimize.UglifyJsPlugin({
// compress: {
// warnings: false,
// // Disabled because of an issue with Uglify breaking seemingly valid code:
// // https://github.com/facebookincubator/create-react-app/issues/2376
// // Pending further investigation:
// // https://github.com/mishoo/UglifyJS2/issues/2011
// comparisons: false,
// },
// mangle: {
// safari10: true,
// },
// output: {
// comments: false,
// // Turned on because emoji and regex is not minified properly using default
// // https://github.com/facebookincubator/create-react-app/issues/2488
// ascii_only: true,
// },
// sourceMap: shouldUseSourceMap,
// }),
//正式版上线后打开去掉debuger和console
new ParallelUglifyPlugin({
cacheDir: '.cache/',
uglifyJS:{
output: {
comments: false
},
warnings: false,
compress: {
drop_debugger: false,
drop_console: false
}
}
}),
// Note: this won't work without ExtractTextPlugin.extract(..) in `loaders`.
new ExtractTextPlugin({
filename: cssFilename,
}),
// Generate a manifest file which contains a mapping of all asset filenames
// to their corresponding output file so that tools can pick it up without
// having to parse `index.html`.
new ManifestPlugin({
fileName: 'asset-manifest.json',
}),
// Generate a service worker script that will precache, and keep up to date,
// the HTML & assets that are part of the Webpack build.
new SWPrecacheWebpackPlugin({
// By default, a cache-busting query parameter is appended to requests
// used to populate the caches, to ensure the responses are fresh.
// If a URL is already hashed by Webpack, then there is no concern
// about it being stale, and the cache-busting can be skipped.
dontCacheBustUrlsMatching: /\.\w{8}\./,
filename: 'service-worker.js',
logger(message) {
if (message.indexOf('Total precache size is') === 0) {
// This message occurs for every build and is a bit too noisy.
return;
}
if (message.indexOf('Skipping static resource') === 0) {
// This message obscures real errors so we ignore it.
// https://github.com/facebookincubator/create-react-app/issues/2612
return;
}
// console.log(message);
},
minify: true,
// For unknown URLs, fallback to the index page
navigateFallback: publicUrl + '/index.html',
// Ignores URLs starting from /__ (useful for Firebase):
// https://github.com/facebookincubator/create-react-app/issues/2237#issuecomment-302693219
navigateFallbackWhitelist: [/^(?!\/__).*/],
// Don't precache sourcemaps (they're large) and build asset manifest:
staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/],
}),
// Moment.js is an extremely popular library that bundles large locale files
// by default due to how Webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales.
// 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$/),
],
// 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: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
},
};

@ -1,95 +1,95 @@
'use strict';
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
const ignoredFiles = require('react-dev-utils/ignoredFiles');
const config = require('./webpack.config.dev');
const paths = require('./paths');
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const host = process.env.HOST || '0.0.0.0';
module.exports = function(proxy, allowedHost) {
return {
// WebpackDevServer 2.4.3 introduced a security fix that prevents remote
// websites from potentially accessing local content through DNS rebinding:
// https://github.com/webpack/webpack-dev-server/issues/887
// https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
// However, it made several existing use cases such as development in cloud
// environment or subdomains in development significantly more complicated:
// https://github.com/facebookincubator/create-react-app/issues/2271
// https://github.com/facebookincubator/create-react-app/issues/2233
// While we're investigating better solutions, for now we will take a
// compromise. Since our WDS configuration only serves files in the `public`
// folder we won't consider accessing them a vulnerability. However, if you
// use the `proxy` feature, it gets more dangerous because it can expose
// remote code execution vulnerabilities in backends like Django and Rails.
// So we will disable the host check normally, but enable it if you have
// specified the `proxy` setting. Finally, we let you override it if you
// really know what you're doing with a special environment variable.
disableHostCheck:
!proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true',
// Enable gzip compression of generated files.
compress: true,
// Silence WebpackDevServer's own logs since they're generally not useful.
// It will still show compile warnings and errors with this setting.
clientLogLevel: 'none',
// By default WebpackDevServer serves physical files from current directory
// in addition to all the virtual build products that it serves from memory.
// This is confusing because those files wont automatically be available in
// production build folder unless we copy them. However, copying the whole
// project directory is dangerous because we may expose sensitive files.
// Instead, we establish a convention that only files in `public` directory
// get served. Our build script will copy `public` into the `build` folder.
// In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
// In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
// Note that we only recommend to use `public` folder as an escape hatch
// for files like `favicon.ico`, `manifest.json`, and libraries that are
// for some reason broken when imported through Webpack. If you just want to
// use an image, put it in `src` and `import` it from JavaScript instead.
contentBase: paths.appPublic,
// By default files from `contentBase` will not trigger a page reload.
watchContentBase: true,
// Enable hot reloading server. It will provide /sockjs-node/ endpoint
// for the WebpackDevServer client so it can learn when the files were
// updated. The WebpackDevServer client is included as an entry point
// in the Webpack development configuration. Note that only changes
// to CSS are currently hot reloaded. JS changes will refresh the browser.
hot: true,
// It is important to tell WebpackDevServer to use the same "root" path
// as we specified in the config. In development, we always serve from /.
publicPath: config.output.publicPath,
// WebpackDevServer is noisy by default so we emit custom message instead
// by listening to the compiler events with `compiler.plugin` calls above.
quiet: true,
// Reportedly, this avoids CPU overload on some systems.
// https://github.com/facebookincubator/create-react-app/issues/293
// src/node_modules is not ignored to support absolute imports
// https://github.com/facebookincubator/create-react-app/issues/1065
watchOptions: {
ignored: ignoredFiles(paths.appSrc),
},
// Enable HTTPS if the HTTPS environment variable is set to 'true'
https: protocol === 'https',
host: host,
overlay: false,
historyApiFallback: {
// Paths with dots should still use the history fallback.
// See https://github.com/facebookincubator/create-react-app/issues/387.
disableDotRule: true,
},
public: allowedHost,
proxy,
before(app) {
// This lets us open files from the runtime error overlay.
app.use(errorOverlayMiddleware());
// This service worker file is effectively a 'no-op' that will reset any
// previous service worker registered for the same host:port combination.
// We do this in development to avoid hitting the production cache if
// it used the same host and port.
// https://github.com/facebookincubator/create-react-app/issues/2272#issuecomment-302832432
app.use(noopServiceWorkerMiddleware());
},
};
};
'use strict';
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
const ignoredFiles = require('react-dev-utils/ignoredFiles');
const config = require('./webpack.config.dev');
const paths = require('./paths');
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const host = process.env.HOST || '0.0.0.0';
module.exports = function(proxy, allowedHost) {
return {
// WebpackDevServer 2.4.3 introduced a security fix that prevents remote
// websites from potentially accessing local content through DNS rebinding:
// https://github.com/webpack/webpack-dev-server/issues/887
// https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
// However, it made several existing use cases such as development in cloud
// environment or subdomains in development significantly more complicated:
// https://github.com/facebookincubator/create-react-app/issues/2271
// https://github.com/facebookincubator/create-react-app/issues/2233
// While we're investigating better solutions, for now we will take a
// compromise. Since our WDS configuration only serves files in the `public`
// folder we won't consider accessing them a vulnerability. However, if you
// use the `proxy` feature, it gets more dangerous because it can expose
// remote code execution vulnerabilities in backends like Django and Rails.
// So we will disable the host check normally, but enable it if you have
// specified the `proxy` setting. Finally, we let you override it if you
// really know what you're doing with a special environment variable.
disableHostCheck:
!proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true',
// Enable gzip compression of generated files.
compress: true,
// Silence WebpackDevServer's own logs since they're generally not useful.
// It will still show compile warnings and errors with this setting.
clientLogLevel: 'none',
// By default WebpackDevServer serves physical files from current directory
// in addition to all the virtual build products that it serves from memory.
// This is confusing because those files wont automatically be available in
// production build folder unless we copy them. However, copying the whole
// project directory is dangerous because we may expose sensitive files.
// Instead, we establish a convention that only files in `public` directory
// get served. Our build script will copy `public` into the `build` folder.
// In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
// In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
// Note that we only recommend to use `public` folder as an escape hatch
// for files like `favicon.ico`, `manifest.json`, and libraries that are
// for some reason broken when imported through Webpack. If you just want to
// use an image, put it in `src` and `import` it from JavaScript instead.
contentBase: paths.appPublic,
// By default files from `contentBase` will not trigger a page reload.
watchContentBase: true,
// Enable hot reloading server. It will provide /sockjs-node/ endpoint
// for the WebpackDevServer client so it can learn when the files were
// updated. The WebpackDevServer client is included as an entry point
// in the Webpack development configuration. Note that only changes
// to CSS are currently hot reloaded. JS changes will refresh the browser.
hot: true,
// It is important to tell WebpackDevServer to use the same "root" path
// as we specified in the config. In development, we always serve from /.
publicPath: config.output.publicPath,
// WebpackDevServer is noisy by default so we emit custom message instead
// by listening to the compiler events with `compiler.plugin` calls above.
quiet: true,
// Reportedly, this avoids CPU overload on some systems.
// https://github.com/facebookincubator/create-react-app/issues/293
// src/node_modules is not ignored to support absolute imports
// https://github.com/facebookincubator/create-react-app/issues/1065
watchOptions: {
ignored: ignoredFiles(paths.appSrc),
},
// Enable HTTPS if the HTTPS environment variable is set to 'true'
https: protocol === 'https',
host: host,
overlay: false,
historyApiFallback: {
// Paths with dots should still use the history fallback.
// See https://github.com/facebookincubator/create-react-app/issues/387.
disableDotRule: true,
},
public: allowedHost,
proxy,
before(app) {
// This lets us open files from the runtime error overlay.
app.use(errorOverlayMiddleware());
// This service worker file is effectively a 'no-op' that will reset any
// previous service worker registered for the same host:port combination.
// We do this in development to avoid hitting the production cache if
// it used the same host and port.
// https://github.com/facebookincubator/create-react-app/issues/2272#issuecomment-302832432
app.use(noopServiceWorkerMiddleware());
},
};
};

File diff suppressed because it is too large Load Diff

@ -3,9 +3,9 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@flatten/array": "^1.1.7",
"@icedesign/base": "^0.2.5",
"antd": "^3.6.5",
"@novnc/novnc": "^1.1.0",
"antd": "^3.20.1",
"array-flatten": "^2.1.2",
"autoprefixer": "7.1.6",
"axios": "^0.18.0",
@ -39,14 +39,15 @@
"fs-extra": "3.0.1",
"html-webpack-plugin": "2.29.0",
"immutability-helper": "^2.6.6",
"install": "^0.12.2",
"jest": "20.0.4",
"js-file-download": "^0.4.7",
"lodash": "^4.17.5",
"loglevel": "^1.6.1",
"material-ui": "^1.0.0-beta.40",
"moment": "^2.23.0",
"monaco-editor": "^0.15.6",
"monaco-editor-webpack-plugin": "^1.7.0",
"npm": "^6.10.1",
"object-assign": "4.1.1",
"postcss-flexbugs-fixes": "3.2.0",
"postcss-loader": "2.0.8",
@ -60,13 +61,13 @@
"rc-select": "^8.0.12",
"rc-tree": "^1.7.11",
"rc-upload": "^2.5.1",
"react": "^16.3.0",
"react": "^16.8.0",
"react-beautiful-dnd": "^10.0.4",
"react-codemirror": "^1.0.0",
"react-codemirror2": "^6.0.0",
"react-content-loader": "^3.1.1",
"react-dev-utils": "^5.0.0",
"react-dom": "^16.3.0-alpha.2",
"react-dom": "^16.8.0",
"react-hot-loader": "^4.0.0",
"react-infinite-scroller": "^1.2.4",
"react-loadable": "^5.3.1",
@ -86,11 +87,12 @@
"webpack": "3.8.1",
"webpack-dev-server": "2.9.4",
"webpack-manifest-plugin": "1.3.2",
"webpack-parallel-uglify-plugin": "^1.1.0",
"whatwg-fetch": "2.0.3",
"wrap-md-editor": "^0.2.20"
},
"scripts": {
"start": "node --max_old_space_size=8072 scripts/start.js",
"start": "node --max_old_space_size=15360 scripts/start.js",
"build": "node --max_old_space_size=15360 scripts/build.js",
"concat": "node scripts/concat.js",
"gen_stats": "NODE_ENV=production webpack --profile --config=./config/webpack.config.prod.js --json > stats.json",
@ -157,11 +159,10 @@
"port": "3007",
"devDependencies": {
"@babel/runtime": "7.0.0-beta.51",
"antd": "^3.6.5",
"babel-plugin-import": "^1.11.0",
"concat": "^1.0.3",
"happypack": "^5.0.1",
"videojs-for-react": "^0.0.3",
"webpack-bundle-analyzer": "^3.0.3"
"webpack-bundle-analyzer": "^3.0.3",
"webpack-parallel-uglify-plugin": "^1.1.0"
}
}

@ -1799,7 +1799,20 @@ ol.linenums{margin-top:0;margin-bottom:0}li.L1,li.L3,li.L5,li.L7,li.L9{backgroun
.page--body {
margin-top: 54px;
}
#myshixun_top {
display: flex;
height: 54px;
}
.yslflexhome {
display: flex;
flex-direction: row;
}
.yslflexhomes {
display: flex;
flex-direction: row;
}
#games_repository_contents,
.cm-s-railscasts .CodeMirror-gutters,
.split-panel--second {

@ -296,7 +296,7 @@ label.infolabel{display: block;float: left;width: 56px;text-align: right;margin-
.subshaicontent a{float: left;margin-right: 20px;color: #999;cursor: pointer}
.search-new{width: 248px;height:32px;position: relative}
.search-new{width: 248px;height:32px;position: relative;margin-right: 35px;}
.search-span{display: block;position: absolute;width: 100%;height: 100%;left:0px;top:0px;background-color: #F4F4F4;border: 1px solid #EAEAEA; border-radius: 4px;z-index: 1}
.search-new-input{height: 32px;padding-left: 5px;width: 225px;border: none;box-sizing: border-box;background: none;outline: none;position: absolute;left:0px;top:1px;z-index: 2}
.search-new img,.search-new a,.search-new .searchicon{cursor: pointer;position: absolute;right:2px;top:2px;z-index: 2}
@ -455,7 +455,7 @@ li.li-width7{width: 7%;text-align: left}
.top-black-trangle{display: block;border-width: 8px;position: absolute;top: -16px;right: 4px;border-style: dashed solid dashed dashed;border-color: transparent transparent rgba(5,16,26,0.6) transparent;font-size: 0;line-height: 0;}
.right-black-trangle{display: block;border-width: 8px;position: absolute;top: 10px;right: -16px;border-style: dashed solid dashed dashed;border-color: transparent transparent transparent rgba(5,16,26,0.6);font-size: 0;line-height: 0;}
.activity-nav.active{color: #4CACFF!important;}
.yslinvitetip{right: 140px;color: #fff; box-sizing: border-box;width: 170px;text-align: center;border-radius: 2px;background-color: rgba(5,16,26,0.6)}
.courseNewNum{display: block;background: #FF6800;border-radius:30px;padding:0px 2px;color: #fff!important;font-size: 11px;
height: 16px;line-height: 15px;min-width: 12px;text-align: center;margin-top: 17px;}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

@ -19,7 +19,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Educoder</title>
<title>EduCoder</title>
<script type="text/javascript">
window.__isR = true;
@ -31,7 +31,7 @@
location.pathname.indexOf("/compatibility") == -1) {
// location.href = './compatibility'
location.href = './compatibility.html'
location.href = '/compatibility.html'
}
</script>
@ -55,7 +55,7 @@
<!-- <link href="/react/build/css/iconfont.css" rel="stylesheet" type="text/css"> -->
<link href="https://testeduplus2.educoder.net/stylesheets/educoder/edu-all.css" rel="stylesheet" type="text/css">
<!--<link href="http://47.96.87.25:48080/stylesheets/educoder/edu-all.css" rel="stylesheet" type="text/css">-->
<!--<link href="https://pandao.github.io/editor.md/examples/css/style.css" rel="stylesheet" type="text/css">-->
@ -67,7 +67,9 @@
<!-- <link rel="stylesheet" type="text/css" href="https://www.educoder.net/stylesheets/css/font-awesome.css?1510652321"> -->
<link rel="stylesheet" type="text/css" href="/stylesheets/educoder/iconfont/iconfont.css">
<!--<link rel="stylesheet" type="text/css" href="http://47.96.87.25:48080/stylesheets/educoder/iconfont/iconfont.css">-->
<!--需要去build js配置-->
<link rel="stylesheet" type="text/css" href="/css/iconfont.css">
<style>
/*<!--去除浏览器点击操作后有蓝色的底块-->*/
@ -146,16 +148,30 @@
<!-- 在tpi js里加载这3个脚本 -->
<script>
(function() { // Scoping function to avoid globals
var href = location.href;
if (href.indexOf('/tasks/') != -1) {
document.write('<script type="text/javascript" src="https://testeduplus2.educoder.net/assets/kindeditor/kindeditor.js"><\/script>');
// build.js中会将这个url附加一个前缀 react/build
document.write('<script type="text/javascript" src="/js/create_kindeditor.js"><\/script>');
document.write('<script type="text/javascript" src="https://testeduplus2.educoder.net/javascripts/educoder/edu_application.js"><\/script>');
} else if (href.indexOf('/paths/') != -1) {
document.write('<script type="text/javascript" src="https://testeduplus2.educoder.net/javascripts/educoder/edu_application.js"><\/script>');
}
var href = location.href;
if(window.location.port === "3007"){
if (href.indexOf('/tasks/') != -1) {
document.write('<script type="text/javascript" src="https://newweb.educoder.net/assets/kindeditor/kindeditor.js"><\/script>');
// build.js中会将这个url附加一个前缀 react/build
document.write('<script type="text/javascript" src="/js/create_kindeditor.js"><\/script>');
document.write('<script type="text/javascript" src="https://newweb.educoder.net/javascripts/educoder/edu_application.js"><\/script>');
} else if (href.indexOf('/paths/') != -1) {
document.write('<script type="text/javascript" src="https://newweb.educoder.net/javascripts/educoder/edu_application.js"><\/script>');
}
}else{
if (href.indexOf('/tasks/') != -1) {
document.write('<script type="text/javascript" src="/assets/kindeditor/kindeditor.js"><\/script>');
// build.js中会将这个url附加一个前缀 react/build
document.write('<script type="text/javascript" src="/js/create_kindeditor.js"><\/script>');
document.write('<script type="text/javascript" src="/javascripts/educoder/edu_application.js"><\/script>');
} else if (href.indexOf('/paths/') != -1) {
document.write('<script type="text/javascript" src="/javascripts/educoder/edu_application.js"><\/script>');
}
}
})();
</script>
<!-- <script type="text/javascript" src="https://testeduplus2.educoder.net/assets/kindeditor/kindeditor.js"></script>

@ -38,7 +38,7 @@
<link rel="stylesheet" type="text/css" href="/css/css_min_all.css">
<link href="https://testeduplus2.educoder.net/stylesheets/educoder/edu-all.css" rel="stylesheet" type="text/css">
<link href="/stylesheets/educoder/edu-all.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" type="text/css" href="//at.alicdn.com/t/font_653600_qa9lwwv74z.css">

File diff suppressed because it is too large Load Diff

@ -0,0 +1,9 @@
/*!
* Cropper.js v1.5.2
* https://fengyuanchen.github.io/cropperjs
*
* Copyright 2015-present Chen Fengyuan
* Released under the MIT license
*
* Date: 2019-06-30T06:01:02.389Z
*/.cropper-container{direction:ltr;font-size:0;line-height:0;position:relative;-ms-touch-action:none;touch-action:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.cropper-container img{display:block;height:100%;image-orientation:0deg;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;width:100%}.cropper-canvas,.cropper-crop-box,.cropper-drag-box,.cropper-modal,.cropper-wrap-box{bottom:0;left:0;position:absolute;right:0;top:0}.cropper-canvas,.cropper-wrap-box{overflow:hidden}.cropper-drag-box{background-color:#fff;opacity:0}.cropper-modal{background-color:#000;opacity:.5}.cropper-view-box{display:block;height:100%;outline:1px solid #39f;outline-color:rgba(51,153,255,.75);overflow:hidden;width:100%}.cropper-dashed{border:0 dashed #eee;display:block;opacity:.5;position:absolute}.cropper-dashed.dashed-h{border-bottom-width:1px;border-top-width:1px;height:33.33333%;left:0;top:33.33333%;width:100%}.cropper-dashed.dashed-v{border-left-width:1px;border-right-width:1px;height:100%;left:33.33333%;top:0;width:33.33333%}.cropper-center{display:block;height:0;left:50%;opacity:.75;position:absolute;top:50%;width:0}.cropper-center:after,.cropper-center:before{background-color:#eee;content:" ";display:block;position:absolute}.cropper-center:before{height:1px;left:-3px;top:0;width:7px}.cropper-center:after{height:7px;left:0;top:-3px;width:1px}.cropper-face,.cropper-line,.cropper-point{display:block;height:100%;opacity:.1;position:absolute;width:100%}.cropper-face{background-color:#fff;left:0;top:0}.cropper-line{background-color:#39f}.cropper-line.line-e{cursor:ew-resize;right:-3px;top:0;width:5px}.cropper-line.line-n{cursor:ns-resize;height:5px;left:0;top:-3px}.cropper-line.line-w{cursor:ew-resize;left:-3px;top:0;width:5px}.cropper-line.line-s{bottom:-3px;cursor:ns-resize;height:5px;left:0}.cropper-point{background-color:#39f;height:5px;opacity:.75;width:5px}.cropper-point.point-e{cursor:ew-resize;margin-top:-3px;right:-3px;top:50%}.cropper-point.point-n{cursor:ns-resize;left:50%;margin-left:-3px;top:-3px}.cropper-point.point-w{cursor:ew-resize;left:-3px;margin-top:-3px;top:50%}.cropper-point.point-s{bottom:-3px;cursor:s-resize;left:50%;margin-left:-3px}.cropper-point.point-ne{cursor:nesw-resize;right:-3px;top:-3px}.cropper-point.point-nw{cursor:nwse-resize;left:-3px;top:-3px}.cropper-point.point-sw{bottom:-3px;cursor:nesw-resize;left:-3px}.cropper-point.point-se{bottom:-3px;cursor:nwse-resize;height:20px;opacity:1;right:-3px;width:20px}@media (min-width:768px){.cropper-point.point-se{height:15px;width:15px}}@media (min-width:992px){.cropper-point.point-se{height:10px;width:10px}}@media (min-width:1200px){.cropper-point.point-se{height:5px;opacity:.75;width:5px}}.cropper-point.point-se:before{background-color:#39f;bottom:-50%;content:" ";display:block;height:200%;opacity:0;position:absolute;right:-50%;width:200%}.cropper-invisible{opacity:0}.cropper-bg{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC")}.cropper-hide{display:block;height:0;position:absolute;width:0}.cropper-hidden{display:none!important}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-drag-box,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed}

File diff suppressed because one or more lines are too long

@ -1,103 +1,103 @@
function Base64() {
// private property
_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
// public method for encoding
this.encode = function (input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = _utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
_keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
_keyStr.charAt(enc3) + _keyStr.charAt(enc4);
}
return output;
}
// public method for decoding
this.decode = function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = _keyStr.indexOf(input.charAt(i++));
enc2 = _keyStr.indexOf(input.charAt(i++));
enc3 = _keyStr.indexOf(input.charAt(i++));
enc4 = _keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = _utf8_decode(output);
return output;
}
// private method for UTF-8 encoding
_utf8_encode = function (string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
// private method for UTF-8 decoding
_utf8_decode = function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;
while ( i < utftext.length ) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
} else if((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i+1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = utftext.charCodeAt(i+1);
c3 = utftext.charCodeAt(i+2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
// private property
_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
// public method for encoding
this.encode = function (input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = _utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
_keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
_keyStr.charAt(enc3) + _keyStr.charAt(enc4);
}
return output;
}
// public method for decoding
this.decode = function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = _keyStr.indexOf(input.charAt(i++));
enc2 = _keyStr.indexOf(input.charAt(i++));
enc3 = _keyStr.indexOf(input.charAt(i++));
enc4 = _keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = _utf8_decode(output);
return output;
}
// private method for UTF-8 encoding
_utf8_encode = function (string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
// private method for UTF-8 decoding
_utf8_decode = function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;
while ( i < utftext.length ) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
} else if((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i+1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = utftext.charCodeAt(i+1);
c3 = utftext.charCodeAt(i+2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1,224 +1,225 @@
'use strict';
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'production';
process.env.NODE_ENV = 'production';
// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', err => {
throw err;
});
// Ensure environment variables are read.
require('../config/env');
const path = require('path');
const chalk = require('chalk');
const fs = require('fs-extra');
const webpack = require('webpack');
const config = require('../config/webpack.config.prod');
const paths = require('../config/paths');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
const printBuildError = require('react-dev-utils/printBuildError');
var CombinedStream = require('combined-stream');
var fs2 = require('fs');
const measureFileSizesBeforeBuild =
FileSizeReporter.measureFileSizesBeforeBuild;
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
const useYarn = fs.existsSync(paths.yarnLockFile);
// These sizes are pretty large. We'll warn for bundles exceeding them.
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1);
}
function removeExceptGitDir(dir) {
// readdirSync
const list = fs2.readdirSync(dir)
// if (err) return done(err);
var pending = list.length;
// if (!pending) return done(null, results);
list.forEach(function(file) {
if (file.indexOf('.git') == -1) {
file = path.resolve(dir, file);
fs.removeSync(file)
}
});
}
// First, read the current file sizes in build directory.
// This lets us display how much they changed later.
measureFileSizesBeforeBuild(paths.appBuild)
.then(previousFileSizes => {
// Remove all content but keep the directory so that
// if you're in it, you don't end up in Trash
// fs.emptyDirSync(paths.appBuild);
console.log('removeExceptGitDir')
removeExceptGitDir(paths.appBuild)
console.log('copyPublicFolder')
// Merge with the public folder
copyPublicFolder();
// Start the webpack build
return build(previousFileSizes);
})
.then(
({ stats, previousFileSizes, warnings }) => {
if (warnings.length) {
console.log(chalk.yellow('Compiled with warnings.\n'));
console.log(warnings.join('\n\n'));
console.log(
'\nSearch for the ' +
chalk.underline(chalk.yellow('keywords')) +
' to learn more about each warning.'
);
console.log(
'To ignore, add ' +
chalk.cyan('// eslint-disable-next-line') +
' to the line before.\n'
);
} else {
console.log(chalk.green('Compiled successfully.\n'));
}
console.log('File sizes after gzip:\n');
printFileSizesAfterBuild(
stats,
previousFileSizes,
paths.appBuild,
WARN_AFTER_BUNDLE_GZIP_SIZE,
WARN_AFTER_CHUNK_GZIP_SIZE
);
console.log();
const appPackage = require(paths.appPackageJson);
const publicUrl = paths.publicUrl;
const publicPath = config.output.publicPath;
const buildFolder = path.relative(process.cwd(), paths.appBuild);
printHostingInstructions(
appPackage,
publicUrl,
publicPath,
buildFolder,
useYarn
);
},
err => {
console.log(chalk.red('Failed to compile.\n'));
printBuildError(err);
process.exit(1);
}
);
// Create the production build and print the deployment instructions.
function build(previousFileSizes) {
console.log('Creating an optimized production build...');
let compiler = webpack(config);
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
if (err) {
return reject(err);
}
const messages = formatWebpackMessages(stats.toJson({}, true));
if (messages.errors.length) {
// Only keep the first error. Others are often indicative
// of the same problem, but confuse the reader with noise.
if (messages.errors.length > 1) {
messages.errors.length = 1;
}
return reject(new Error(messages.errors.join('\n\n')));
}
if (
process.env.CI &&
(typeof process.env.CI !== 'string' ||
process.env.CI.toLowerCase() !== 'false') &&
messages.warnings.length
) {
console.log(
chalk.yellow(
'\nTreating warnings as errors because process.env.CI = true.\n' +
'Most CI servers set it automatically.\n'
)
);
return reject(new Error(messages.warnings.join('\n\n')));
}
generateNewIndexJsp();
return resolve({
stats,
previousFileSizes,
warnings: messages.warnings,
});
});
});
}
function copyPublicFolder() {
fs.copySync(paths.appPublic, paths.appBuild, {
dereference: true,
filter: file => file !== paths.appHtml,
});
}
function generateNewIndexJsp() {
// var combinedStream = CombinedStream.create();
var filePath = paths.appBuild + '/index.html';
// var htmlContent = fs2.createReadStream( filePath )
// stream没有replace方法
// htmlContent = htmlContent.replace('/js/js_min_all.js', '/react/build/js/js_min_all.js')
// htmlContent = htmlContent.replace('/css/css_min_all.css', '/react/build/css/css_min_all.css')
// combinedStream.append(htmlContent);
// combinedStream.pipe(fs2.createWriteStream( filePath ));
var outputPath = paths.appBuild + '/../../../public/react/build/index.html'
fs2.readFile(filePath, 'utf8', function (err,data) {
if (err) {
return console.log(err);
}
const newVersion = '1.1.1'
let cdnHost = 'https://shixun.educoder.net'
cdnHost = 'https://cdn-testeduplus2.educoder.net'
cdnHost = ''
var result = data.replace('/js/js_min_all.js', `${cdnHost}/react/build/js/js_min_all.js?v=${newVersion}`)
.replace('/js/js_min_all_2.js', `${cdnHost}/react/build/js/js_min_all_2.js?v=${newVersion}`)
.replace('/css/css_min_all.css', `${cdnHost}/react/build/css/css_min_all.css?v=${newVersion}`)
.replace('/js/create_kindeditor.js', `${cdnHost}/react/build/js/create_kindeditor.js?v=${newVersion}`)
// .replace('/react/build/./static/css/main', `${cdnHost}/react/build/./static/css/main`)
// .replace('/react/build/./static/js/main', `${cdnHost}/react/build/./static/js/main`)
.replace(/https:\/\/testeduplus2.educoder.net/g, '');
// .replace(/http:\/\/testbdweb.educoder.net/g, '');
// .replace('/css/css_min_all.css', '/react/build/css/css_min_all.css');
fs2.writeFile(outputPath, result, 'utf8', function (err) {
if (err) return console.log(err);
commitAndPush();
});
});
}
function commitAndPush() {
var exec = require('child_process').exec;
function puts(error, stdout, stderr) { console.log(stdout) }
var options = {cwd:"./build"};
exec("git status && git commit -am 'b' && git push", options, puts);
'use strict';
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'production';
process.env.NODE_ENV = 'production';
// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', err => {
throw err;
});
// Ensure environment variables are read.
require('../config/env');
const path = require('path');
const chalk = require('chalk');
const fs = require('fs-extra');
const webpack = require('webpack');
const config = require('../config/webpack.config.prod');
const paths = require('../config/paths');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
const printBuildError = require('react-dev-utils/printBuildError');
var CombinedStream = require('combined-stream');
var fs2 = require('fs');
const measureFileSizesBeforeBuild =
FileSizeReporter.measureFileSizesBeforeBuild;
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
const useYarn = fs.existsSync(paths.yarnLockFile);
// These sizes are pretty large. We'll warn for bundles exceeding them.
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1);
}
function removeExceptGitDir(dir) {
// readdirSync
const list = fs2.readdirSync(dir)
// if (err) return done(err);
var pending = list.length;
// if (!pending) return done(null, results);
list.forEach(function(file) {
if (file.indexOf('.git') == -1) {
file = path.resolve(dir, file);
fs.removeSync(file)
}
});
}
// First, read the current file sizes in build directory.
// This lets us display how much they changed later.
measureFileSizesBeforeBuild(paths.appBuild)
.then(previousFileSizes => {
// Remove all content but keep the directory so that
// if you're in it, you don't end up in Trash
// fs.emptyDirSync(paths.appBuild);
console.log('removeExceptGitDir')
removeExceptGitDir(paths.appBuild)
console.log('copyPublicFolder')
// Merge with the public folder
copyPublicFolder();
// Start the webpack build
return build(previousFileSizes);
})
.then(
({ stats, previousFileSizes, warnings }) => {
if (warnings.length) {
console.log(chalk.yellow('Compiled with warnings.\n'));
console.log(warnings.join('\n\n'));
console.log(
'\nSearch for the ' +
chalk.underline(chalk.yellow('keywords')) +
' to learn more about each warning.'
);
console.log(
'To ignore, add ' +
chalk.cyan('// eslint-disable-next-line') +
' to the line before.\n'
);
} else {
console.log(chalk.green('Compiled successfully.\n'));
}
console.log('File sizes after gzip:\n');
printFileSizesAfterBuild(
stats,
previousFileSizes,
paths.appBuild,
WARN_AFTER_BUNDLE_GZIP_SIZE,
WARN_AFTER_CHUNK_GZIP_SIZE
);
console.log();
const appPackage = require(paths.appPackageJson);
const publicUrl = paths.publicUrl;
const publicPath = config.output.publicPath;
const buildFolder = path.relative(process.cwd(), paths.appBuild);
printHostingInstructions(
appPackage,
publicUrl,
publicPath,
buildFolder,
useYarn
);
},
err => {
console.log(chalk.red('Failed to compile.\n'));
printBuildError(err);
process.exit(1);
}
);
// Create the production build and print the deployment instructions.
function build(previousFileSizes) {
console.log('Creating an optimized production build...');
let compiler = webpack(config);
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
if (err) {
return reject(err);
}
const messages = formatWebpackMessages(stats.toJson({}, true));
if (messages.errors.length) {
// Only keep the first error. Others are often indicative
// of the same problem, but confuse the reader with noise.
if (messages.errors.length > 1) {
messages.errors.length = 1;
}
return reject(new Error(messages.errors.join('\n\n')));
}
if (
process.env.CI &&
(typeof process.env.CI !== 'string' ||
process.env.CI.toLowerCase() !== 'false') &&
messages.warnings.length
) {
console.log(
chalk.yellow(
'\nTreating warnings as errors because process.env.CI = true.\n' +
'Most CI servers set it automatically.\n'
)
);
return reject(new Error(messages.warnings.join('\n\n')));
}
generateNewIndexJsp();
return resolve({
stats,
previousFileSizes,
warnings: messages.warnings,
});
});
});
}
function copyPublicFolder() {
fs.copySync(paths.appPublic, paths.appBuild, {
dereference: true,
filter: file => file !== paths.appHtml,
});
}
function generateNewIndexJsp() {
// var combinedStream = CombinedStream.create();
var filePath = paths.appBuild + '/index.html';
// var htmlContent = fs2.createReadStream( filePath )
// stream没有replace方法
// htmlContent = htmlContent.replace('/js/js_min_all.js', '/react/build/js/js_min_all.js')
// htmlContent = htmlContent.replace('/css/css_min_all.css', '/react/build/css/css_min_all.css')
// combinedStream.append(htmlContent);
// combinedStream.pipe(fs2.createWriteStream( filePath ));
var outputPath = paths.appBuild + '/../../../public/react/build/index.html'
fs2.readFile(filePath, 'utf8', function (err,data) {
if (err) {
return console.log(err);
}
const newVersion = '1.1.1'
let cdnHost = 'https://shixun.educoder.net'
cdnHost = 'http://cdn.educoder.net'
cdnHost = ''
var result = data.replace('/js/js_min_all.js', `${cdnHost}/react/build/js/js_min_all.js?v=${newVersion}`)
.replace('/js/js_min_all_2.js', `${cdnHost}/react/build/js/js_min_all_2.js?v=${newVersion}`)
.replace('/css/css_min_all.css', `${cdnHost}/react/build/css/css_min_all.css?v=${newVersion}`)
.replace('/css/iconfont.css', `${cdnHost}/react/build/css/iconfont.css?v=${newVersion}`)
.replace(/\/js\/create_kindeditor.js/g, `${cdnHost}/react/build/js/create_kindeditor.js?v=${newVersion}`)
// .replace('/react/build/./static/css/main', `${cdnHost}/react/build/./static/css/main`)
// .replace('/react/build/./static/js/main', `${cdnHost}/react/build/./static/js/main`)
.replace(/https:\/\/testeduplus2.educoder.net/g, '');
// .replace(/http:\/\/testbdweb.educoder.net/g, '');
// .replace('/css/css_min_all.css', '/react/build/css/css_min_all.css');
fs2.writeFile(outputPath, result, 'utf8', function (err) {
if (err) return console.log(err);
commitAndPush();
});
});
}
function commitAndPush() {
var exec = require('child_process').exec;
function puts(error, stdout, stderr) { console.log(stdout) }
var options = {cwd:"./build"};
exec("git status && git commit -am 'b' && git push", options, puts);
}

@ -35,6 +35,10 @@
.editormd .CodeMirror-linenumbers {
padding: 0;
}
.editormd-html-preview hr, .editormd-preview-container hr {
/* 颜色加深 */
border-top: 1px solid #ccc;
}
/* 重置掉antd的一些样式 */
html, body {

@ -14,6 +14,9 @@ import '@icedesign/base/dist/ICEDesignBase.css';
import '@icedesign/base/index.scss';
import LoginDialog from './modules/login/LoginDialog'
import Notcompletedysl from './modules/user/Notcompletedysl'
import Trialapplicationysl from './modules/login/Trialapplicationysl'
import Trialapplicationreview from './modules/user/Trialapplicationreview'
import Trialapplication from './modules/login/Trialapplication'
import NotFoundPage from './NotFoundPage'
@ -110,6 +113,11 @@ const CoursesIndex = Loadable({
loader: () => import('./modules/courses/Index'),
loading: Loading,
})
const SearchPage = Loadable({
loader: () => import('./search/SearchPage'),
loading: Loading,
})
// 课堂讨论
// const BoardIndex = Loadable({
// loader: () => import('./modules/courses/boards/BoardIndex'),
@ -207,6 +215,12 @@ const UsersInfo = Loadable({
loading: Loading,
})
// 兴趣页面
const Interestpage = Loadable({
loader: () => import('./modules/login/EducoderInteresse'),
loading: Loading,
})
class App extends Component {
constructor(props) {
super(props)
@ -218,7 +232,12 @@ class App extends Component {
componentDidMount() {
// force an update if the URL changes
history.listen(() => this.forceUpdate());
history.listen(() => {
this.forceUpdate()
const $ = window.$
// https://www.trustie.net/issues/21919 可能会有问题
$("html").animate({ scrollTop: $('html').scrollTop() - 0 })
});
initAxiosInterceptors(this.props)
@ -241,12 +260,16 @@ class App extends Component {
render() {
// let {isRenders} = this.state;
return (
<LocaleProvider locale={zhCN}>
<MuiThemeProvider theme={theme}>
<LoginDialog></LoginDialog>
<LoginDialog {...this.props} {...this.state}></LoginDialog>
<Notcompletedysl {...this.props} {...this.state}></Notcompletedysl>
<Trialapplicationysl {...this.props} {...this.state}></Trialapplicationysl>
<Trialapplicationreview {...this.props} {...this.state}></Trialapplicationreview>
{/*{*/}
{/* isRender === true?*/}
{/* <LoginDialog></LoginDialog> : ""*/}
@ -254,7 +277,7 @@ class App extends Component {
{/*{*/}
{/* isRenders === true?*/}
<Trialapplication></Trialapplication>
{/*<Trialapplication></Trialapplication>*/}
{/*:""*/}
{/*}*/}
@ -262,7 +285,7 @@ class App extends Component {
<Switch>
{/*<Route path="/login" component={LoginRegisterPage}/>*/}
{/*认证*/}
{/*<Route path="/account" component={AccountPage}/>*/}
<Route path="/account" component={AccountPage}/>
{/*403*/}
<Route path="/403" component={Shixunauthority}/>
@ -289,7 +312,10 @@ class App extends Component {
<Route
path="/changepassword" component={EducoderLogin}
/>
<Route
path="/interesse" component={Interestpage}
/>
<Route path="/shixuns/new" component={Newshixuns}>
</Route>
@ -307,15 +333,22 @@ class App extends Component {
<Route path="/fork_list" component={TPMshixunfork_listIndexComponent}>
</Route> */}
<Route path="/forums" component={ForumsIndexComponent}>
</Route>
{/*<Route path="/forums" component={ForumsIndexComponent}>*/}
{/*</Route>*/}
{/*实训课程(原实训路径)*/}
<Route path="/paths" component={ShixunPaths}></Route>
<Route path="/search"
render={
(props)=>(<SearchPage {...this.props} {...props} {...this.state}></SearchPage>)
}
></Route>
{/*课堂*/}
<Route path="/courses" component={CoursesIndex} {...this.props}></Route>
{/* 课堂讨论 */}
{/* <Route path="/board" component = {BoardIndex} {...this.props}></Route> */}
@ -323,7 +356,8 @@ class App extends Component {
</Route> */}
{/* <Route path="/myshixuns/:shixunId/stages/:stageId" component={Index}/> */}
{/* 兴趣页面*/}
{/*<Route path="/interest" component={Interestpage}/>*/}
<Route path="/comment" component={CommentComponent}/>
<Route path="/testMaterial" component={TestMaterialDesignComponent}/>

@ -2,22 +2,23 @@ import React from "react";
import axios from 'axios';
import { requestProxy } from "./indexEduplus2RequestProxy";
import { broadcastChannelOnmessage } from 'educoder'
import { broadcastChannelOnmessage ,SetAppModel} from 'educoder';
import { notification } from 'antd';
import './index.css'
broadcastChannelOnmessage('refreshPage', () => {
window.location.reload()
})
function locationurl(list){
if (window.location.port === "3007") {
if (window.location.port === "3007") {
} else {
window.location.replace(list)
}
} else {
window.location.replace(list)
}
}
// TODO 开发期多个身份切换
const debugType = window.location.search.indexOf('debug=t') != -1 ? 'teacher' :
const debugType =window.location.search.indexOf('debug=t') != -1 ? 'teacher' :
window.location.search.indexOf('debug=s') != -1 ? 'student' : 'admin'
window._debugType = debugType;
export function initAxiosInterceptors(props) {
@ -29,7 +30,8 @@ export function initAxiosInterceptors(props) {
var proxy = "http://localhost:3000"
// proxy = "http://testbdweb.trustie.net"
// proxy = "http://testbdweb.educoder.net"
proxy = "https://testeduplus2.educoder.net"
// proxy = "https://testeduplus2.educoder.net"
proxy="http://47.96.87.25:48080"
// 在这里使用requestMap控制避免用户通过双击等操作发出重复的请求
@ -100,6 +102,7 @@ export function initAxiosInterceptors(props) {
if(response===undefined){
return
}
const config = response.config
if (response.data.status === -1) {
// console.error('error:', response.data.message)
// throw new Error()
@ -114,6 +117,9 @@ export function initAxiosInterceptors(props) {
notification.open({
message:"提示",
description: response.data.message || '服务器异常,请联系管理员。',
style: {
zIndex: 99999999
},
});
// notification['error']({
// message:"提示",
@ -138,9 +144,16 @@ export function initAxiosInterceptors(props) {
locationurl('/500');
}
if (response.data.status === 402) {
locationurl(response.data.url);
}
// if (response.data.status === 402) {
// console.log(response.data.status);
// console.log(response.data);
// // locationurl(402);
// }
if (response.data.status === 401) {
return config;
}
// if (response.data.status === 407) {
// 在app js 中解决 Trialapplication
// // </Trialapplication>

@ -1,16 +1,27 @@
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
class Loading extends Component {
render() {
// Loading
return (
<div className="App" style={{minHeight: '800px'}}>
</div>
);
}
}
export default Loading;
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import { Spin } from 'antd';
class Loading extends Component {
render() {
// Loading
return (
<div className="App" style={{minHeight: '800px',width:"100%"}}>
<style>
{
`
.margintop{
margin-top:20%;
}
`
}
</style>
<Spin size="large" className={"margintop"}/>
</div>
);
}
}
export default Loading;

@ -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 // 普通用户
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 // 普通用户

@ -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 (
<React.Fragment>
<Snackbar
className={"rootSnackbar"}
style={{zIndex:30000}}
open={this.state.snackbarOpen}
autoHideDuration={3000}
anchorOrigin={{ vertical: this.state.snackbarVertical || 'top'
, horizontal: this.state.snackbarHorizontal || 'center' }}
onClose={() => this.handleSnackbarClose()}
transition={Fade}
SnackbarContentProps={{
'aria-describedby': 'message-id',
}}
resumeHideDuration={2000}
message={<span id="message-id">{this.state.snackbarText}</span>}
/>
<WrappedComponent {...this.props} showSnackbar={ this.showSnackbar } >
</WrappedComponent>
</React.Fragment>
)
}
}
}
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 (
<React.Fragment>
<Snackbar
className={"rootSnackbar"}
style={{zIndex:30000}}
open={this.state.snackbarOpen}
autoHideDuration={3000}
anchorOrigin={{ vertical: this.state.snackbarVertical || 'top'
, horizontal: this.state.snackbarHorizontal || 'center' }}
onClose={() => this.handleSnackbarClose()}
transition={Fade}
SnackbarContentProps={{
'aria-describedby': 'message-id',
}}
resumeHideDuration={2000}
message={<span id="message-id">{this.state.snackbarText}</span>}
/>
<WrappedComponent {...this.props} showSnackbar={ this.showSnackbar } >
</WrappedComponent>
</React.Fragment>
)
}
}
}
}

@ -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
}

@ -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];
}

@ -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

@ -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($('<link rel="stylesheet" type="text/css" />')
.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 (
<div>
{/* This rule is very important, please do not ignore this! */}
<style>{`
.wrapper {
width: ${ width ? (width+'px') : '500px'};
height: ${ height ? (height+'px') : '500px'};
border: 1px solid #eee;
}
img {
max-width: 100%;
}
.preview-lg {
overflow: hidden;
background-color: #fff;
border-radius: 50%;
text-align: center;
}
`}</style>
<div className="wrapper">
{/* http://localhost:3007/images/footNavLogo.png 图片转了后不对
|| "/images/testPicture.jpg"
|| "/images/shixun0.jpg"
*/}
<img id={this.props.imageId || "__image"} src={`${imageSrc }`}></img>
</div>
{/* background: 'aquamarine',
'border-radius': '128px'
*/}
{!previewId && <div id="img-preview" className="img-preview preview-lg" style={{width: '128px', height: '128px', }}>
</div>}
{/* <img id="showImg" src="http://localhost:3007/images/testPicture.jpg"></img> */}
{/* <div id="canvasWrap"></div> */}
{/* <button onClick={save_avatar.bind(this)}>save </button> */}
</div>
);
}
}
export default Cropper;
// function aaa () {
// function showedit_headphoto() {
// var html = `
// <script src=\"../head/jquery.min.js\"><\/script>\n
// <link href=\"../head/cropper.min.css\" rel=\"stylesheet\">\n
// <link href=\"../head/sitelogo.css\" rel=\"stylesheet\">\n
// <script src=\"../head/bootstrap.min.js\"><\/script>\n
// <script src=\"../head/cropper.js\"><\/script>\n
// <script src=\"../head/sitelogo.js\"><\/script>\n
// <script src=\"../head/html2canvas.min.js\" type=\"text/javascript\" charset=\"utf-8\"><\/script>\n\n
// <div class=\"task-popup\" style=\"width: 550px;\">\n <div class=\"task-popup-title clearfix task-popup-bggrey\">上传头像<\/div>\n <div class=\"clearfix\">\n
// <div class=\"modal fade\" style=\"outline: none;\" id=\"avatar-modal\" aria-hidden=\"true\" aria-labelledby=\"avatar-modal-label\" role=\"dialog\" tabindex=\"-1\">\n
// <div class=\"modal-dialog modal-lg\">\n <div class=\"modal-content\">\n <form class=\"avatar-form\">\n <div class=\"modal-body\">\n
// <div class=\"padding20\">\n <div class=\"avatar-upload\">\n <input class=\"avatar-src\" name=\"avatar_src\" type=\"hidden\">\n
// <input class=\"avatar-data\" name=\"avatar_data\" type=\"hidden\">\n\n <span id=\"avatar-name\"><\/span>\n
// <input class=\"avatar-input\" style=\"display:none;\" id=\"avatarInput\" value=\"avatars/User/116\" name=\"avatar_file\" type=\"file\">\n
// <input type=\"hidden\" id=\"source_id\" value=\"116\"/>\n <input type=\"hidden\" id=\"source_type\" value=\"User\"/>\n <\/div>\n
// <div class=\"row clearfix mt20 pl20\">\n <div class=\"task-form-45 fl panel-box-sizing uplaodImg\">\n
// <div class=\"avatar-wrapper\" id=\"wrapper_image_show\">\n <!--<span style=\"display: block;\">\n 选择你要上传的图片<br/>
// \n 仅支持JPG、GIF、PNG且文件小于2M\n <\/span>-->\n <\/div>\n
// <div class=\"row avatar-btns clearfix\">\n <a href=\"javascript:void(0);\" class=\"fl\" type=\"button\" onClick=\"$(\'input[id=avatarInput]\').click();\">重新上传<\/a>\n
// <!--<div class=\"btn-group\">\n <a href=\"javascript:void(0);\" class=\"fa fa-repeat fr mt5 color-grey-9\" data-method=\"rotate\" data-option=\"90\"
// type=\"button\" title=\"Rotate 90 degrees\">\n <\/a>\n <\/div>-->\n <\/div>\n <\/div>\n
// <div class=\"task-form-50 panel-box-sizing fr color-grey pr20\" style=\"width: 128px;\">\n <div class=\"edu-txt-center\">\n
// <div class=\"avatar-preview preview-lg radius\" id=\"imageHead\">\n
// <img alt=\"头像\" height=\"128\" nhname=\"avatar_image\" src=\"/images/avatars/User/116?1556802838\" width=\"128\" />\n <\/div>\n
// <span>头像预览<\/span>\n <\/div>\n
// <p class=\"color-grey-9 font-12 mt110 justify\">仅支持JPG、GIF、PNG且文件小于2M<\/p>\n <\/div>\n <\/div>\n
// <\/div>\n <div class=\"clearfix edu-txt-center mb20 mt10\">\n
// <a href=\"javascript:void(0);\" class=\"task-btn mr20\" onclick=\"hideModal()\">取消<\/a>\n
// <a href=\"javascript:void(0);\" class=\"avatar-save task-btn task-btn-orange\" data-dismiss=\"modal\">确定<\/a>\n
// <\/div>\n <\/div>\n <\/form>\n <\/div>\n <\/div>\n <\/div>\n <\/div>\n<\/div>\n\n<script>\n
// $(function () {\n new CropAvatar($(\'#crop-avatar\'), 1/1);\n\n //---------------------------头像上传-----------------------------//\n //做个下简易的验证 大小 格式\n $(\'#avatarInput\').on(\'change\', function(e) {\n var filemaxsize = 1024 * 5;//5M\n var target = $(e.target);\n var Size = target[0].files[0].size / 1024;\n if(Size > filemaxsize) {\n alert(\'图片过大,请重新选择!\');\n $(\".avatar-wrapper\").children().remove;\n return false;\n }\n if(!this.files[0].type.match(/image.*/)) {\n alert(\'请选择正确的图片!\')\n } else {\n /*var filename = document.querySelector(\"#avatar-name\");*/\n var texts = document.querySelector(\"#avatarInput\").value;\n var teststr = texts; //你这里的路径写错了\n testend = teststr.match(/[^\\\\]+\\.[^\\(]+/i); //直接完整文件名的\n /*filename.innerHTML = testend;\n $(filename).css(\"color\", \'#333\');*/\n $(\".avatar-save\").removeClass(\"task-btn-grey\").addClass(\"task-btn-orange\");\n $(\".avatar-save\").on(\"click\", save_avatar);\n }\n\n });\n });\n\n function save_avatar(){\n var img_lg = document.getElementById(\'imageHead\');\n if($(img_lg).html().trim() == \"\"){\n $(\"#avatar-name\").html(\"请先选择图片上传\").css(\"color\", \'red\');\n } else {\n $(\"#avatar-name\").html(\"\").css(\"color\", \'#333\');\n // 截图小的显示框内的内容\n html2canvas(img_lg, {\n allowTaint: true,\n taintTest: false,\n onrendered: function(canvas) {\n canvas.id = \"mycanvas\";\n //生成base64图片数据\n var dataUrl = canvas.toDataURL(\"image/jpeg\");\n var newImg = document.createElement(\"img\");\n newImg.src = dataUrl;\n imagesAjax(dataUrl);\n $(\".avatar-save\").attr(\"disabled\",\"true\");\n }\n });\n }\n }\n\n function imagesAjax(src) {\n var data = {};\n data.img = src;\n data.source_id = $(\'#source_id\').val();\n data.source_type = $(\'#source_type\').val();\n data.is_direct = 0;\n $.ajax({\n url: \"/upload_avatar\",\n beforeSend: function(xhr) {xhr.setRequestHeader(\'X-CSRF-Token\', $(\'meta[name=\"csrf-token\"]\').attr(\'content\'))},\n data: data,\n type: \"POST\",\n success: function (re) {\n console.log(re);\n console.log(1562050370);\n if(re){\n var o = JSON.parse(re);\n if (o.status !=0 ){\n console.log(o.message);\n } else {\n var imgSpan = $(\"img[nhname=\'avatar_image\']\");\n imgSpan.attr({\"src\": o.url + \'?1562050370\'});\n $(\"#user_code\").html(o.grade);\n notice_box_redirect(\"/users/shitou\", \"上传成功\");\n }\n } else {\n notice_box(\"上传出错\");\n }\n\n },\n error: function (e) {\n alert(e);\n }\n });\n }\n
// <\/script>`;
// pop_box_new(html, 550, 510);
// $("#imageHead img").attr({"src": $("#user_avatar_show").attr("src")});
// $("#wrapper_image_show img").attr({"src": $("#user_avatar_show").attr("src")});
// }
// $(function () {
// new CropAvatar($('#crop-avatar'), 1/1);
// //---------------------------头像上传-----------------------------//
// //做个下简易的验证 大小 格式
// $('#avatarInput').on('change', function(e) {
// var filemaxsize = 1024 * 5;//5M
// var target = $(e.target);
// var Size = target[0].files[0].size / 1024;
// if(Size > filemaxsize) {
// alert('图片过大,请重新选择!');
// $(".avatar-wrapper").children().remove;
// return false;
// }
// if(!this.files[0].type.match(/image.*/)) {
// alert('请选择正确的图片!')
// } else {
// /*var filename = document.querySelector("#avatar-name");*/
// var texts = document.querySelector("#avatarInput").value;
// var teststr = texts; //你这里的路径写错了
// testend = teststr.match(/[^\\\\]+\\.[^\\(]+/i); //直接完整文件名的
// /*filename.innerHTML = testend; $(filename).css("color", '#333');*/
// $(".avatar-save").removeClass("task-btn-grey").addClass("task-btn-orange");
// $(".avatar-save").on("click", save_avatar);
// }
// });
// });
// function save_avatar(){
// var img_lg = document.getElementById('imageHead');
// if($(img_lg).html().trim() == ""){
// $("#avatar-name").html("请先选择图片上传").css("color", 'red');
// } else {
// $("#avatar-name").html("").css("color", '#333');
// // 截图小的显示框内的内容
// html2canvas(img_lg, {
// allowTaint: true,
// taintTest: false,
// onrendered: function(canvas) {
// canvas.id = "mycanvas";
// //生成base64图片数据
// var dataUrl = canvas.toDataURL("image/jpeg");
// var newImg = document.createElement("img");
// newImg.src = dataUrl;
// imagesAjax(dataUrl);
// $(".avatar-save").attr("disabled","true");
// }
// });
// }
// }
// function imagesAjax(src) {
// var data = {};
// data.img = src;
// data.source_id = $('#source_id').val();
// data.source_type = $('#source_type').val();
// data.is_direct = 0;
// $.ajax({
// url: "/upload_avatar",
// beforeSend: function(xhr) {
// xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
// },
// data: data,
// type: "POST",
// success: function (re) {
// console.log(re);
// // console.log(1562050370);
// if(re){
// var o = JSON.parse(re);
// if (o.status !=0 ){
// console.log(o.message);
// } else {
// var imgSpan = $("img[nhname='avatar_image']");
// imgSpan.attr({"src": o.url + '?1562050370'});
// $("#user_code").html(o.grade);
// notice_box_redirect("/users/shitou", "上传成功");
// }
// } else {
// notice_box("上传出错");
// }
// },
// error: function (e) {
// alert(e);
// }
// });
// }
// }

@ -0,0 +1,114 @@
import React, { Component } from 'react';
const $ = window.jQuery
const jQuery = $;
if (!$.drag) {
(function($){
$.fn.dragValidator = function(options){
var x, drag = this, isMove = false, defaults = {
};
var options = $.extend(defaults, options);
//添加背景,文字,滑块
var html = '<div class="drag_bg"></div>'+
'<div class="drag_text" onselectstart="return false;" unselectable="on">拖动滑块验证</div>'+
'<div class="handler handler_bg"></div>';
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(text.text() != '验证通过' && _x < maxWidth){ //鼠标松开时,如果没有达到最大距离位置,滑块就返回初始位置
handler.animate({'left': 0});
drag_bg.animate({'width': 0});
}
});
//清空事件
function dragOk(){
options.dragOkCallback && options.dragOkCallback()
var kuaiwidth=drag.width() - handler.width() - 2;
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');
$("#user_verification_notice").html("");
$('#user_verification_notice').parent().hide();
}
};
})(jQuery);
}
class DragValidator extends Component {
componentDidMount () {
// if($("#reg-drag").length>0 && IsPC()){
$("#reg-drag").dragValidator({
height: this.props.height,
dragOkCallback: () => {
this.props.dragOkCallback && this.props.dragOkCallback()
}
});
// }else{
// $("#reg-drag").empty();
// }
}
empty() {
$("#reg-drag").empty();
}
render() {
const height = this.props.height || 45;
const className = this.props.className
const successGreenColor = this.props.successGreenColor || '#29bd8b'
// newMain clearfix
return (
<div id="reg-drag" className={`drag_slider ${className}`}>
<style>{`
.drag_slider .handler {
height: 100%;
}
.drag_slider {
height: ${height}px;
line-height: ${height}px;
}
.drag_slider .drag_bg {
height: ${height}px;
background-color: ${successGreenColor};
}
`}</style>
</div>
);
}
}
export default ( DragValidator );

@ -0,0 +1,48 @@
import React, { Component } from 'react';
import { Modal} from 'antd';
import axios from 'axios';
import '../../modules/user/common.css';
//完善个人资料
class Notcompleted extends Component {
constructor(props) {
super(props);
}
modalCancel=()=>{
window.location.href = "/";
}
setDownload=()=>{
window.location.href ='/account/profile';
}
render() {
console.log(this.props)
return(
<Modal
keyboard={false}
closable={false}
footer={null}
destroyOnClose={true}
title="提示"
centered={true}
visible={this.props.modalsType===undefined?false:this.props.modalsType}
width="530px"
>
<div className="educouddiv">
<div className={"tabeltext-alignleft mt10"}><p>您尚未完善个人资料</p></div>
<div className={"tabeltext-alignleft mt10"}><p>请在完成资料后提交试用申请</p></div>
<div className="clearfix mt30 edu-txt-center">
<a className="task-btn mr30" onClick={()=>this.modalCancel()}>取消</a>
<a className="task-btn task-btn-orange" onClick={()=>this.setDownload()}>立即完善资料</a>
</div>
</div>
</Modal>
)
}
}
export default Notcompleted;

@ -0,0 +1,51 @@
import React, { Component } from 'react';
import { Modal } from 'antd';
export function SetAppModel(options={}) {
return function wrap(WrappedComponent) {
return class Wrapper extends Component {
constructor(props) {
super(props);
this.state = {
}
}
modalCancel=()=>{
window.location.href = "/";
}
setDownload=()=>{
window.location.href ='/account/profile';
}
componentDidMount(){
console.log(this.props)
}
render() {
const { titlemessage, Modallisttype, Modallist, singleButton } = this.state;
return (
<Modal
keyboard={false}
closable={false}
footer={null}
destroyOnClose={true}
title="提示"
centered={true}
visible={true}
width="530px"
>
<div className="educouddiv">
<div className={"tabeltext-alignleft mt10"}><p>您尚未完善个人资料</p></div>
<div className={"tabeltext-alignleft mt10"}><p>请在完成资料后提交试用申请</p></div>
<div className="clearfix mt30 edu-txt-center">
<a className="task-btn mr30" onClick={()=>this.modalCancel()}>取消</a>
<a className="task-btn task-btn-orange" onClick={()=>this.setDownload()}>立即完善资料</a>
</div>
</div>
</Modal>
)
}
}
}
}

@ -1719,6 +1719,10 @@ const options = [{
label: '其他'
}],
}];
function filter(inputValue, path) {
return path.some(option => option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
}
class City extends Component{
constructor(props){
super(props);
@ -1739,11 +1743,16 @@ class City extends Component{
}
}
render(){
const { defaultValue } = this.props
const { defaultValue, matchInputWidth, className, popupClassName } = this.props
const { value } = this.state
// 这里用请选择所在省市的话会触发chrome的地址选择
return(
<Cascader allowClear size="large" options={options} placeholder="请选择所在省市" onChange={this.onChange}
<Cascader allowClear size="large" options={options} placeholder="请选择所在地" onChange={this.onChange}
matchInputWidth={matchInputWidth}
value={value}
showSearch={{ filter }}
className={className}
popupClassName={popupClassName}
></Cascader>
)
}

@ -1,72 +1,83 @@
import React,{ Component } from "react";
import TPMMDEditor from '../../../modules/tpm/challengesnew/TPMMDEditor'
import {markdownToHTML} from 'educoder'
import './DMDEditor.css'
// 需要父组件通过toShowMode、toMDMode 来控制一次只能打开一个DMDEditor
class DMDEditor extends Component{
constructor(props){
super(props);
this.mdRef = React.createRef()
this.state={
mdMode: false,
// value: this.props.initValue
}
}
componentDidUpdate(prevProps, prevState) {
}
componentDidMount() {
// if(this.props.initValue != this.mdRef.current.getValue()) {
// this.mdRef.current.setValue(this.props.initValue)
// }
}
toMDMode = () => {
this.setState({mdMode: true}, () => {
this.mdRef.current.resize()
this.mdRef.current.setValue(this.props.initValue)
})
this.props.toMDMode(this)
}
toShowMode = () => {
this.setState({mdMode: false})
this.props.toShowMode && this.props.toShowMode(this)
}
onCMBlur = () => {
this.toShowMode()
}
onChange = (val) => {
// this.setState({ value: val })
this.props.onChange(val)
}
render(){
const { mdMode } = this.state;
const { initValue } = this.props;
return(
<React.Fragment>
<style>{`
`}</style>
<div id="content_editorMd_show" className="new_li content_editorMd_show"
style={{display: mdMode == true ? 'none' : '', color: initValue? '': '#999', alignItems: 'center', wordBreak: 'break-all'}}
dangerouslySetInnerHTML={{__html: initValue ? markdownToHTML(initValue):this.props.placeholder}}
onClick={this.toMDMode}
>
</div>
{/*
onCMBlur={this.onCMBlur} */}
<TPMMDEditor
ref={this.mdRef}
{...this.props}
initValue={initValue}
className={`${this.props.className} ${mdMode == true ? '' : 'hideMd'}`}
onChange={this.onChange}
></TPMMDEditor>
</React.Fragment>
)
}
}
import React,{ Component } from "react";
import TPMMDEditor from '../../../modules/tpm/challengesnew/TPMMDEditor'
import {markdownToHTML} from 'educoder'
import './DMDEditor.css'
// 需要父组件通过toShowMode、toMDMode 来控制一次只能打开一个DMDEditor
class DMDEditor extends Component{
constructor(props){
super(props);
this.mdRef = React.createRef()
this.state={
mdMode: false,
// value: this.props.initValue
}
}
componentDidUpdate(prevProps, prevState) {
}
componentDidMount() {
// if(this.props.initValue != this.mdRef.current.getValue()) {
// this.mdRef.current.setValue(this.props.initValue)
// }
}
toMDMode = () => {
this.setState({mdMode: true}, () => {
this.mdRef.current.resize()
this.mdRef.current.setValue(this.props.initValue)
})
this.props.toMDMode(this)
}
toShowMode = () => {
this.setState({mdMode: false})
this.props.toShowMode && this.props.toShowMode(this)
}
onCMBlur = () => {
this.toShowMode()
}
onChange = (val) => {
// this.setState({ value: val })
this.props.onChange(val)
if (this.state.showError == true) {
this.setState({showError: false})
}
}
showError = () => {
this.mdRef.current.showError()
this.setState({showError: true})
}
render(){
const { mdMode, showError } = this.state;
const { initValue } = this.props;
let _style = {}
if (showError) {
_style.border = '1px solid red'
}
_style = Object.assign(_style, {display: mdMode == true ? 'none' : '', color: initValue? '': '#999', alignItems: 'center', wordBreak: 'break-all'})
return(
<React.Fragment>
<style>{`
`}</style>
<div id="content_editorMd_show" className="new_li content_editorMd_show markdown-body"
style={_style}
dangerouslySetInnerHTML={{__html: initValue ? markdownToHTML(initValue):this.props.placeholder}}
onClick={this.toMDMode}
>
</div>
{/*
onCMBlur={this.onCMBlur} */}
<TPMMDEditor
ref={this.mdRef}
{...this.props}
initValue={initValue}
className={`${this.props.className} ${mdMode == true ? '' : 'hideMd'}`}
onChange={this.onChange}
></TPMMDEditor>
</React.Fragment>
)
}
}
export default DMDEditor;

@ -6,40 +6,23 @@ class MarkdownToHtml extends Component{
this.state={
}
}
// componentDidUpdate = (prevProps) => {
// if (this.props.content) {
// if ( prevProps.content != this.props.content ) {
// if (!this.shixunDescr) {
// this.shixunDescr = window.editormd.markdownToHTML("memo_content_editorMd", {
// markdown: this.props.content,
// htmlDecode: "style,script,iframe", // you can filter tags decode
// taskList: true,
// tex: true, // 默认不解析
// flowChart: true, // 默认不解析
// sequenceDiagram: true // 默认不解析
// });
// } else {
// this.shixunDescr.html(this.props.content)
// }
// }
// }
// }
// componentDidMount () {
// if (!this.shixunDescr) {
// this.shixunDescr = window.editormd.markdownToHTML("memo_content_editorMd", {
// markdown: this.props.content,
// htmlDecode: "style,script,iframe", // you can filter tags decode
// taskList: true,
// tex: true, // 默认不解析
// flowChart: true, // 默认不解析
// sequenceDiagram: true // 默认不解析
// });
// }
// }
componentDidUpdate = (prevProps) => {
if (this.props.content) {
if ( prevProps.content != this.props.content ) {
markdownToHTML(this.props.content, `.markdown_to_html_${this.props.selector || ''}`)
}
}
}
componentDidMount () {
this.props.content && markdownToHTML(this.props.content, `.markdown_to_html_${this.props.selector || ''}`)
}
render(){
return(
<div id="memo_content_editorMd" className="new_li markdown-body" dangerouslySetInnerHTML={{__html: markdownToHTML(this.props.content)}}>
<div id="memo_content_editorMd" className={`new_li markdown-body ${this.props.className} markdown_to_html_${this.props.selector || ''}`}
// dangerouslySetInnerHTML={{__html: markdownToHTML(this.props.content)}}
>
</div>
)
}

@ -0,0 +1,17 @@
import React from 'react'
export const themes = {
light: {
foreground: '#000000',
background: '#eeeeee',
foreground_select: '#4CACFF'
},
dark: {
foreground: '#ffffff',
background: '#222222',
},
};
export const ThemeContext = React.createContext(
themes.light // default value
);

@ -1,7 +1,9 @@
import React, { Component } from 'react';
import {Link} from 'react-router-dom'
const map={"blue":"blueFull","greyBack":"greyBack","grey":"greyWidthFixed","green":"greenBack"}
const map={"blue":"blueFull","greyBack":"greyBack","grey":"greyWidthFixed","green":"greenBack",
'colorBlue': 'colorBlue', // 蓝字白底
}
class ActionBtn extends Component {
constructor(props) {
super(props);

@ -1,49 +1,57 @@
import { from } from '_array-flatten@2.1.2@array-flatten';
// export { default as OrderStateUtil } from '../routes/Order/components/OrderStateUtil';
export { getImageUrl as getImageUrl, getUrl as getUrl, getUploadActionUrl as getUploadActionUrl } from './UrlTool';
export { default as queryString } from './UrlTool2';
export { SnackbarHOC as SnackbarHOC } from './SnackbarHOC';
export { trigger as trigger, on as on, off as off
, broadcastChannelPostMessage, broadcastChannelOnmessage } from './EventUtil';
export { updatePageParams as updatePageParams } from './RouterUtil';
export { bytesToSize as bytesToSize } from './UnitUtil';
export { markdownToHTML, uploadNameSizeSeperator, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll } from './TextUtil'
export { handleDateString, getNextHalfHourOfMoment,formatDuring } from './DateUtil'
export { isDev as isDev } from './Env'
export { toStore as toStore, fromStore as fromStore } from './Store'
export { trace_collapse, trace, debug, info, warn, error, trace_c, debug_c, info_c, warn_c, error_c } from './LogUtil'
export { EDU_ADMIN, EDU_SHIXUN_MANAGER, EDU_SHIXUN_MEMBER, EDU_CERTIFICATION_TEACHER
, EDU_GAME_MANAGER, EDU_TEACHER, EDU_NORMAL} from './Const'
export { ModalHOC } from './components/ModalHOC'
export { default as ConditionToolTip } from './components/ConditionToolTip'
export { default as DragValidator } from './components/DragValidator'
export { default as PopInstruction } from './components/instruction/PopInstruction'
export { default as City } from './components/form/City'
// course
export { default as WordsBtn } from './course/WordsBtn'
export { default as ActionBtn } from './course/ActionBtn'
export { default as MarkdownToHtml } from './components/markdown/MarkdownToHtml'
export { default as DMDEditor } from './components/markdown/DMDEditor'
import { from } from '_array-flatten@2.1.2@array-flatten';
// export { default as OrderStateUtil } from '../routes/Order/components/OrderStateUtil';
export { getImageUrl as getImageUrl, getUrl as getUrl, getUrl2 as getUrl2, setImagesUrl as setImagesUrl
, getUploadActionUrl as getUploadActionUrl, getUploadActionUrlOfAuth as getUploadActionUrlOfAuth } from './UrlTool';
export { default as queryString } from './UrlTool2';
export { SnackbarHOC as SnackbarHOC } from './SnackbarHOC';
export { trigger as trigger, on as on, off as off
, broadcastChannelPostMessage, broadcastChannelOnmessage } from './EventUtil';
export { updatePageParams as updatePageParams } from './RouterUtil';
export { bytesToSize as bytesToSize } from './UnitUtil';
export { markdownToHTML, uploadNameSizeSeperator, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll, isImageExtension } from './TextUtil'
export { handleDateString, getNextHalfHourOfMoment,formatDuring } from './DateUtil'
export { isDev as isDev } from './Env'
export { toStore as toStore, fromStore as fromStore } from './Store'
export { trace_collapse, trace, debug, info, warn, error, trace_c, debug_c, info_c, warn_c, error_c } from './LogUtil'
export { EDU_ADMIN, EDU_BUSINESS, EDU_SHIXUN_MANAGER, EDU_SHIXUN_MEMBER, EDU_CERTIFICATION_TEACHER
, EDU_GAME_MANAGER, EDU_TEACHER, EDU_NORMAL} from './Const'
export { themes, ThemeContext } from './context/ThemeContext'
export { ModalHOC } from './components/ModalHOC'
export { SetAppModel } from './components/SetAppModel'
export { default as Cropper } from './components/Cropper'
export { default as ConditionToolTip } from './components/ConditionToolTip'
export { default as DragValidator } from './components/DragValidator'
export { default as PopInstruction } from './components/instruction/PopInstruction'
export { default as City } from './components/form/City'
// course
export { default as WordsBtn } from './course/WordsBtn'
export { default as ActionBtn } from './course/ActionBtn'
export { default as MarkdownToHtml } from './components/markdown/MarkdownToHtml'
export { default as DMDEditor } from './components/markdown/DMDEditor'
export { default as ImageLayerHook } from './hooks/ImageLayerHook'

@ -0,0 +1,48 @@
import React, { useState, useEffect, memo } from 'react';
import ImageLayer from '../../modules/page/layers/ImageLayer';
import { isImageExtension } from 'educoder';
const $ = window.$;
function ImageLayerHook(props) {
const [showImage, setShowImage] = useState(false)
const [imageSrc, setImageSrc] = useState('')
const { parentSel, childSel, watchPropsArray } = props
const onImageLayerClose = () => {
setShowImage(false)
setImageSrc('')
}
const onDelegateClick = (event) => {
const imageSrc = event.target.src || event.target.getAttribute('src') || event.target.getAttribute('href')
// 判断imageSrc是否是图片
const fileName = event.target.innerHTML.trim()
if (isImageExtension(imageSrc.trim()) || isImageExtension(fileName) || event.target.tagName == 'IMG' || imageSrc.indexOf('base64,') != -1) {
// 非回复里的头像图片; 非emoticons
if (imageSrc.indexOf('/images/avatars/User') === -1 &&
imageSrc.indexOf('kindeditor/plugins/emoticons') === -1 ) {
setShowImage(true)
setImageSrc(imageSrc)
}
event.stopPropagation()
event.preventDefault && event.preventDefault()
event.originalEvent.preventDefault()
// event.originalEvent.stopPropagation()
// event.originalEvent.cancelBubble = true
return false;
}
}
useEffect(() => {
$(parentSel)
.delegate(childSel, "click", onDelegateClick);
return () => {
$(parentSel).undelegate(childSel, "click", onDelegateClick )
}
})
return (
<ImageLayer showImage={showImage} imageSrc={imageSrc} onImageLayerClose={onImageLayerClose}></ImageLayer>
)
}
export default memo(ImageLayerHook)

@ -17,7 +17,16 @@ class EvaluateSuccessEffectDisplay extends Component {
}
componentDidMount() {
if (this.props.type == 'html') {
const iframe = document.getElementById('_displayIframe')
if (iframe && iframe.contentWindow) {
iframe.contentWindow.open()
iframe.contentWindow.document.write(this.props.iframe_src);
iframe.contentWindow.document.close();
} else {
console.error('not mounted')
}
}
}
hidepicture = () => {
window.$('#picture_display').hide();
@ -26,7 +35,7 @@ class EvaluateSuccessEffectDisplay extends Component {
// qrcode
// const type = 'image' // 'qrcode'
const { type, qrcode_str,
answer_picture, orignal_picture, user_picture } = this.props;
answer_picture, orignal_picture, user_picture, contents } = this.props;
if (type == 'qrcode') {
// 单张图片比如安卓评测完显示qrcode
return (
@ -51,16 +60,16 @@ class EvaluateSuccessEffectDisplay extends Component {
</div>
<div className="clearfix" id="picture-content">
<div className="fl with33 mr03precent pt10 mb50">
<img alt="Icon"
src={orignal_picture[0].pic_url}/>
{orignal_picture[0] && <img alt="Icon"
src={ orignal_picture[0].pic_url}/>}
</div>
<div className="fl with33 mr03precent pt10 mb50">
<img alt="Icon"
src={user_picture[0].pic_url}/>
{user_picture[0] && <img alt="Icon"
src={ user_picture[0].pic_url }/>}
</div>
<div className="fl with33 mr03precent pt10 mb50">
<img alt="Icon"
src={answer_picture[0].pic_url}/>
{ answer_picture[0] && <img alt="Icon"
src={ answer_picture[0].pic_url}/> }
</div>
</div>
</div>
@ -69,12 +78,16 @@ class EvaluateSuccessEffectDisplay extends Component {
if (type == "txt") {
return (
<div className="task-popup-content clearfix">
<div className="with49" style={{margin: '0 auto'}}>
<div className="with80" style={{margin: '0 auto'}}>
<p className="color-blue font-18 mb20 edu-txt-center">实际输出</p>
<textarea className="output-txt" readonly="" defaultValue={content}></textarea>
<textarea className="output-txt" readonly="" defaultValue={contents}></textarea>
</div>
</div>
)
} else if (type == "html") {
return (
<iframe id="_displayIframe"></iframe>
)
}
/* <div className="with49 fr">
@ -95,12 +108,17 @@ class EvaluateSuccessEffectDisplay extends Component {
height: 100%;
box-sizing: border-box;
}
iframe#_displayIframe {
width: 100%;
height: 100%;
}
`}
</style>
<div className="photo_display">
<div className="task-popup">
<div className="task-popup-title clearfix">
<h3 className="fl color-grey3">查看效果</h3>
<h3 className="fl color-grey3 mt4">查看效果</h3>
<a href="javascript:void(0);" onClick={this.hidepicture}
data-tip-left="关闭查看效果" className="pop_close fr">
<i className="fa fa-times-circle font-18 link-color-grey mt5"></i>

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

@ -33,4 +33,8 @@ body {
}
.anticon anticon-paper-clip{
color: #29bd8b !important;
}
.MuiModal-root-15{
z-index: 1000 !important;
}

@ -1,46 +1,46 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import './indexPlus.css';
import App from './App';
// 加之前main.js 18.1MB
// import { message } from 'antd';
import message from 'antd/lib/message';
import 'antd/lib/message/style/css';
import { AppContainer } from 'react-hot-loader';
import registerServiceWorker from './registerServiceWorker';
import { configureUrlQuery } from 'react-url-query';
import history from './history';
// link the history used in our app to url-query so it can update the URL with it.
configureUrlQuery({ history });
// ----------------------------------------------------------------------------------- 请求配置
window.__useKindEditor = false;
const render = (Component) => {
ReactDOM.render(
<AppContainer>
<Component />
</AppContainer>,
document.getElementById('root')
);
}
// ReactDOM.render(
// ,
// document.getElementById('root'));
// registerServiceWorker();
render(App);
if (module.hot) {
module.hot.accept('./App', () => { render(App) });
}
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import './indexPlus.css';
import App from './App';
// 加之前main.js 18.1MB
// import { message } from 'antd';
import message from 'antd/lib/message';
import 'antd/lib/message/style/css';
import { AppContainer } from 'react-hot-loader';
import registerServiceWorker from './registerServiceWorker';
import { configureUrlQuery } from 'react-url-query';
import history from './history';
// link the history used in our app to url-query so it can update the URL with it.
configureUrlQuery({ history });
// ----------------------------------------------------------------------------------- 请求配置
window.__useKindEditor = false;
const render = (Component) => {
ReactDOM.render(
<AppContainer {...this.props} {...this.state}>
<Component {...this.props} {...this.state}/>
</AppContainer>,
document.getElementById('root')
);
}
// ReactDOM.render(
// ,
// document.getElementById('root'));
// registerServiceWorker();
render(App);
if (module.hot) {
module.hot.accept('./App', () => { render(App) });
}

@ -1,639 +0,0 @@
import React, {Component} from 'react';
import {Link} from "react-router-dom";
import axios from 'axios';
import {getImageUrl, trigger} from 'educoder';
import { Tooltip, message,Popover} from 'antd';
import CoursesListType from '../coursesPublic/CoursesListType';
import Addcourses from '../coursesPublic/Addcourses';
import '../css/Courses.css';
import Modals from "../../modals/Modals";
import AddStudentModal from '../members/modal/AddStudentModal'
import AddTeacherModal from '../members/modal/AddTeacherModal'
// 点击按钮复制功能
// function jsCopy(){
// var e = document.getElementById("copy_invite_code");
// e.select();
// document.execCommand("Copy");
// codesuccess()
// }
// 点击按钮复制功能
function jsCopy() {
var e = document.getElementById("copy_invite_code");
e.select();
document.execCommand("Copy");
codesuccess()
}
function codesuccess() {
message.success('复制成功');
};
class CoursesBanner extends Component {
constructor(props) {
super(props)
this.state = {
show: false,
Addcoursestypes: false,
modalsType: false,
modalsTopval: "",
loadtype: false,
metype: 0,
modalsBottomval: "",
antIcon:false,
coursedata:undefined
}
}
componentDidMount() {
this.updatabanner()
}
updatabanner=()=>{
let courseId = this.props.match.params.coursesId;
let url = "/courses/" + courseId + "/top_banner.json"
axios.get(url).then((result) => {
let data = result.data;
this.setState({
coursedata: data
})
// console.log(data)
})
}
showeditmenu = () => {
this.setState({
show: true,
})
}
hideeditmenu = () => {
this.setState({
show: false
})
}
tojoinclass = (val) => {
if (val === 1) {
this.setState({
Addcoursestypes: true,
})
} else {
this.setState({
Addcoursestypes: false,
})
}
}
showActionPoll=(i,s,ss)=>{
this.setState({
modalsType: true,
modalsTopval: s,
loadtype: false,
metype: i,
modalsBottomval: ss,
})
}
ActionPoll = (i) => {
let {coursedata}=this.state;
var s = "";
var ss = "";
if (i === 1) {
s = "课堂删除后数据将无法恢复,是否确定删除?";
this.showActionPoll(i,s)
}
if (i === 2) {
s = "您确定要设置为私有?";
this.showActionPoll(i,s)
}
if (i === 3) {
s = "您确定要设置为公开?";
this.showActionPoll(i,s)
}
if(i===4){
if(coursedata.code_halt === true){
var url = `/courses/${this.props.match.params.coursesId}/set_invite_code_halt.json`
axios.post(url, {}).then((result) => {
try {
if (result.data.status === 0){
this.updatabanner()
}
} catch (e) {
}
})
}else{
s = "邀请码停用后,用户不能主动加入该课堂了";
ss = "您是否确认停用?";
this.showActionPoll(i,s,ss)
}
}
if (i ===5) {
s = "复制将在后台执行,作业、资源、试卷都将复制到新课堂平台";
ss = "将为你创建一个新的同名课堂,请问是否继续?";
this.showActionPoll(i,s,ss)
}
}
//取消
modalCancel = () => {
this.setState({
modalsType: false,
modalsTopval: "",
modalsBottomval:"",
loadtype: false,
antIcon:false
})
}
// 确定
ModalAction = () => {
let {coursedata}=this.state;
var push = this.props.history;
let id = this.props.match.params.coursesId;
//删除
if (this.state.metype === 1) {
console.log("删除");
this.modalCancel();
var url = `/courses/${id}.json`
axios
.delete(url, {})
.then(function (response) {
console.log(response.data.status)
if (response.data.status === 0) {
message.success("删除成功", 1)
push.push(`/courses`)
}
})
}
//设为私有的
if (this.state.metype === 2) {
this.modalCancel();
var state = this.state;
var url = `/courses/${id}/set_public_or_private.json`
axios.post(url, {}).then((result) => {
if (result.data.status === 0) {
message.success("设为私有的成功", 1);
state.coursedata.is_public = false;
this.setState({
coursedata: state.coursedata,
})
}
})
}
//设为公有的
if (this.state.metype === 3) {
this.modalCancel();
var state = this.state;
var url = `/courses/${id}/set_public_or_private.json`
axios.post(url, {}).then((result) => {
if (result.data.status === 0) {
message.success("设为公有的成功", 1);
state.coursedata.is_public = true;
this.setState({
coursedata: state.coursedata,
})
}
})
}
//停用邀请码
if (this.state.metype === 4) {
this.modalCancel();
var url = `/courses/${id}/set_invite_code_halt.json`
axios.post(url, {}).then((result) => {
try {
if (result.data.status === 0) {
message.success(coursedata.code_halt === true?"启用用邀请码成功":"停用邀请码成功", 1);
this.updatabanner()
}
} catch (e) {
}
})
}
if (this.state.metype ===5) {
this.setState({
antIcon: true,
})
var url = `/courses/${id}/duplicate_course.json`
axios.post(url).then((response) => {
// window.location.href = "/courses/" + response.data.new_course_id;
window.location.href = "/courses/" + response.data.new_course_id+"/students";
})
}
if(this.state.metype===6){
this.setState({
antIcon: true,
})
var url =`/courses/${id}/exit_course.json`;
axios.post(url).then((response) => {
if(response.data.status===0){
window.location.href = "/users/" + this.props.current_user.login;
}
})
}
}
addTeacher = (isTeacher) => {
this.setState({ isTeacher }, () => {
this.refs.addTeacherModal.setVisible(true)
})
}
addStudent = () => {
this.refs.addStudentModal.setVisible(true)
}
addTeacherSuccess = (params) => {
trigger('addTeacherSuccess', JSON.stringify(params))
this.updatabanner()
}
addStudentSuccess = (params) => {
trigger('addStudentSuccess', JSON.stringify(params))
this.updatabanner()
}
//退出课堂按钮
exitclass=()=>{
this.setState({
modalsType: true,
modalsTopval: "退出后您将不再是本课题的成员,作品将全部被删除,",
modalsBottomval:"确定要退出该课堂吗?",
metype:6
})
}
//切换身份
switchidentity=(sum)=>{
let newurl=this.props.match.url;
let id = this.props.match.params.coursesId;
if(sum===1){
let url =`/courses/${id}/switch_to_student.json`;
axios.post(url).then((response) => {
if(response.data.status===0){
// window.location.href = "/users/" + this.props.current_user.login;
// this.props.history.replace(newurl);
window.location.href=newurl
}
})
}
if(sum===2){
let url =`/courses/${id}/switch_to_teacher.json`;
axios.post(url).then((response) => {
if(response.data.status===0){
// window.location.href = "/users/" + this.props.current_user.login;
// this.props.history.replace(newurl);
window.location.href=newurl
}
})
}
if(sum===3){
let url =`/courses/${id}/switch_to_assistant.json`;
axios.post(url).then((response) => {
if(response.data.status===0){
// window.location.href = "/users/" + this.props.current_user.login;
// this.props.history.replace(newurl);
window.location.href=newurl
}
})
}
}
postsettings=()=>{
window.location.href = "/courses/" + this.props.match.params.coursesId + "/settings";
}
render() {
let { Addcoursestypes, coursedata, modalsType, modalsTopval, loadtype,modalsBottomval,antIcon} = this.state;
return (
coursedata === undefined || coursedata.status===401? <div id="course_info_1309" className="courseHead" style={{height: '206px'}}></div>:<div id="course_info_1309" className="courseHead" style={{height: '206px'}}>
{Addcoursestypes === true ? <Addcourses
Addcoursestype={Addcoursestypes}
hideAddcoursestype={() => this.tojoinclass(2)}
/> : ""}
<Modals
modalsType={modalsType}
modalsTopval={modalsTopval}
loadtype={loadtype}
modalsBottomval={modalsBottomval}
modalCancel={this.modalCancel}
modalSave={this.ModalAction}
antIcon={antIcon}
></Modals>
<AddTeacherModal ref="addTeacherModal"
{...this.props}
isTeacher={this.state.isTeacher}
moduleName={this.state.isTeacher ? "教师" : "助教"}
addTeacherSuccess={this.addTeacherSuccess}
></AddTeacherModal>
<AddStudentModal ref="addStudentModal"
{...this.props}
moduleName="学生"
addStudentSuccess={this.addStudentSuccess}
></AddStudentModal>
<div className="educontent clearfix educontentTop">
<div className="color-white clearfix mb10">
{
coursedata===undefined || coursedata.status===401 || coursedata.status===407?"":
<Tooltip placement="bottom" title={coursedata&&coursedata.name.length<38?"":coursedata.name}>
<span className="font-24 fl bannername">{coursedata.name}</span>
</Tooltip>
}
{/*访
公开公开课堂非课堂成员可以访问*/}
<span className={"TabsWarp"}>
<CoursesListType
typelist={coursedata.course_end === true ? ["已结束"] : coursedata.is_public === true ? ["公开"] : ["私有"]}
typesylename={"mt10"} tipval={coursedata.is_public === true?"":"私有课堂,非课堂成员不能访问"}/>
</span>
</div>
<div className="clearfix ">
<div className="fl fl mr40 mb20">
<a href={"/users/" + coursedata.teacher_login} className="fl">
<img alt="头像" className="radius fl mt3 bannerimgname"
src={getImageUrl(`images/` + coursedata.teacher_img)}/>
</a>
<div className="fl mt13">
<p className="color-white">
<a href={"/users/" + coursedata.teacher_login}
className="color-white bannnerusername">{coursedata.teacher_name}</a>
</p>
</div>
<div className="fl mt13">
<p className="color-white bannnerusernames">{coursedata.teacher_school}</p>
</div>
</div>
<div>
{coursedata.switch_to_student === true ?
<Tooltip placement="bottom" title={"切换至学生可进行提交作品、答题等操作"}>
<a className="fr user_default_btn user_blue_btn mr20 font-18"
onClick={()=>this.switchidentity(1)}
> 切换为学生 </a>
</Tooltip>
:""}
{coursedata.switch_to_teacher === true ?
<Tooltip placement="bottom" title={"由学生身份切换至教师"}>
<a className="fr user_default_btn user_blue_btn mr20 font-18"
onClick={()=>this.switchidentity(2)}
> 切换为老师 </a>
</Tooltip>:""}
{coursedata.switch_to_assistant === true ?
<Tooltip placement="bottom" title={"由学生身份切换至助教"}>
<a className="fr user_default_btn user_blue_btn mr20 font-18"
onClick={()=>this.switchidentity(3)}
> 切换为助教 </a>
</Tooltip>:""}
{coursedata.course_identity === 6&&coursedata.educoder_teacher===false?
<a className="fr user_default_btn task-btn-orange font-18 mr20" id="shixun_operation"
onClick={() => this.tojoinclass(1)}>加入课堂</a>: ""}
{coursedata.course_identity === 6&&coursedata.educoder_teacher===true?
<a className="fr user_default_btn task-btn-orange font-18 mr20" id="shixun_operation"
onClick={() => this.tojoinclass(1)}>加入课堂</a>: ""}
{coursedata.course_identity === 6&&coursedata.educoder_teacher===true?
<a className="fr user_default_btn user_blue_btn mr20 font-18" onClick={() => this.ActionPoll(5)}> 复制课堂 </a>: ""}
{this.props.isStudent()?<a className="fr user_default_btn user_blue_btn mr20 font-18"
onClick={() => this.exitclass()}
> 退出课堂 </a>:""}
</div>
<div className="clearfix clearfixborder">
<ul className="fl color-grey-eb pathInfo pathInfobox mt10">
<li className={"mt7"}>
<Link to={"/courses/"+this.props.match.params.coursesId+"/teachers"}>
<span className="color-grey-c fl font-16">教师</span>
<span
className="color-white fl font-16 bannerurli">{coursedata.teacher_count}</span>
</Link>
</li>
<li className={"mt7"}>
<Link to={"/courses/"+this.props.match.params.coursesId+"/students"}>
<span className="color-grey-c fl font-16">学生</span>
<span
className={coursedata.credit===null?"color-white fl font-16":"color-white fl font-16 bannerurli"}>{coursedata.student_count}</span>
</Link>
</li>
{/*<li className={"mt7"}>*/}
{/*<a>*/}
{/*<span className="color-grey-c fl font-16">分班</span>*/}
{/*<span className="color-white fl font-16 bannerurli">{coursedata.course_group_count}</span>*/}
{/*</a>*/}
{/*</li>*/}
{coursedata.credit===null?"":<li className={"mt7"}>
<a>
<span className="color-grey-c fl font-16 mr10">学分</span>
<span className="color-white fl font-16 "
>{coursedata.credit}</span>
</a>
</li>}
{/*{coursedata.course_end===true? <li className={"mt7"}>*/}
{/*<span className="color-grey-c fl font-16">已结束</span>*/}
{/*</li>:<li className={"mt7"}>*/}
{/*<span className="color-grey-c fl font-16"> 结束</span>*/}
{/*<span className="color-white fl font-16"*/}
{/*style={{*/}
{/*marginLeft:coursedata.deadline===null?"":'8px'*/}
{/*}}*/}
{/*>{coursedata.deadline===null?"--":coursedata.deadline}</span>*/}
{/*</li>}*/}
</ul>
{this.props.isAdmin()? <ul className="fr color-grey-eb pathInfo pathInfobox mt10"
style={{
position: "relative"
}}
>
<li className={"mt7 mr10im"}>
<a onClick={()=>this.addTeacher(true)}>
<span className="color-white fl font-16 bannerurli width100f">添加老师</span>
</a>
</li>
<li className={"mt7 mr10im"}>
<a onClick={()=>this.addTeacher(false)}>
<span className="color-white fl font-16 bannerurli width100f">添加助教</span>
</a>
</li>
<li className={"mt7 mr10im"}>
<a onClick={()=>this.addStudent()}>
<span className="color-white fl font-16 bannerurli width100f">添加学生</span>
</a>
</li>
<li className={"mt7 mr10im ml10"} style={{overflow:"hidden"}}>
<a>
<span className="color-grey-c fl font-16">邀请码</span>
<span
className={coursedata.code_halt === true? "color-white fl font-16 bannerurli width75f" : "color-white fl font-16 bannerurli width107f marleftf10 color-orange-tip"}>
{coursedata.code_halt === true? "已停用" : coursedata.invite_code}
{coursedata.code_halt === true ? "" :
<Tooltip placement="bottom" title={
<pre>
成员可以通过邀请码主动加入课堂<br/>
点击立刻复制邀请码
</pre>
}>
<i className="iconfont icon-fuzhi color-white font-14 ml10"
onClick={() => {
jsCopy()
}}></i>
</Tooltip>
}
<input id="copy_invite_code" value={coursedata.invite_code}/>
</span>
</a>
</li>
<li className={"mt7 ml10 mr0 "}>
<style>
{
`
.defaults{cursor:default}
`
}
</style>
<Popover placement="bottom" content={
<ul className="sandianbox" style={{
display: 'block',
right: "-113px",
top: "20px"
}}>
{coursedata.is_public === true?coursedata.course_identity <3?
<div className={"defaults"} onClick={() => this.ActionPoll(2)}>设为私有</div>: "" : ""}
{coursedata.is_public === false?coursedata.course_identity <3?
<div className={"defaults"} onClick={() => this.ActionPoll(3)}>设为公开</div> : "": ""}
{coursedata.course_identity <3? <div className={"defaults"} onClick={() => this.ActionPoll(4)}>
{coursedata.code_halt === true ?"启用邀请码":"停用邀请码"}
</div>:""}
<div className={"defaults"} onClick={this.postsettings}>设置</div>
<div className={"defaults"} onClick={() => this.ActionPoll(5)}>复制
</div>
{coursedata.is_admin===true?coursedata.course_identity <3?<div className={"defaults"} onClick={() => this.ActionPoll(1)}>删除</div>
:"":""}
</ul>
} trigger="hover">
<i className="iconfont icon-weibiaoti12 color-white font-14 relative"></i>
</Popover>
</li>
</ul>:""}
</div>
</div>
</div>
</div>
)
}
}
export default CoursesBanner;
// let id=this.props.match.params.coursesId;
//
// let url="/courses/"+id+"/top_banner.json"
// axios.get(url).then((result)=>{
// if(result.status===200){
// let data=result.data;
// this.setState({
// coursebannerlist:result.data,
// name: result.data.name,
// teacher_name: result.data.teacher_name,
// teacher_login: result.data.teacher_login,
// teacher_img: result.data.teacher_img,
// teacher_school: result.data.teacher_school,
// teacher_count: result.data.teacher_count,
// student_count: result.data.student_count,
// course_group_count: result.data.course_group_count,
// credit: result.data.credit,
// course_end: result.data.course_end,
// deadline: result.data.deadline,
// is_teacher: result.data.is_teacher,
// is_student: result.data.is_student,
// is_admin: result.data.is_admin,
// is_public: result.data.is_public,
// code_halt: result.data.code_halt,
// invite_code: result.data.invite_code,
// switch_to_student: result.data.switch_to_student,
// switch_to_teacher: result.data.switch_to_teacher,
// join_course: result.data.join_course,
// copy_course: result.data.copy_course,
// })
// }
// coursebannerlist:undefined,
// name: undefined,
// teacher_name: undefined,
// teacher_login: undefined,
// teacher_img: undefined,
// teacher_school: undefined,
// teacher_count: undefined,
// student_count: undefined,
// course_group_count: undefined,
// credit: undefined,
// course_end: undefined,
// deadline: undefined,
// is_teacher: undefined,
// is_student: undefined,
// is_admin: undefined,
// is_public: undefined,
// code_halt: undefined,
// invite_code:undefined,
// switch_to_student: undefined,
// switch_to_teacher: undefined,
// join_course: undefined,
// copy_course: undefined,
// }).catch((error)=>{
// console.log(error);
// })

@ -1,355 +0,0 @@
import React,{Component} from "React";
import { Form, Select, Input, Button,Checkbox,Upload,Icon,message,Modal} from "antd";
import {Link} from 'react-router-dom';
import TPMMDEditor from '../../../tpm/challengesnew/TPMMDEditor';
import { WordsBtn,getUrl} from 'educoder';
import axios from 'axios';
import Modals from '../../../modals/Modals';
import '../../css/Courses.css';
const { Option} = Select;
const CheckboxGroup = Checkbox.Group;
const confirm = Modal.confirm;
let GraduationTasksnewtype=true;
class GraduationTasksnew extends Component{
constructor(props){
super(props)
this.state={
coursename:"",
coursesearch:"",
title_num:60,
title_value:"",
fileList: [],
contents: [{val:"",id:1}],
type:true
}
}
componentDidMount(){
// const query =this.props.location.search;
//
// const type = query.split('&');
// let name = type[1].split("name=")
// name = String(name).split(",")
// name = decodeURI(name[1])
//
// this.setState({
// coursename:name,
// coursesearch:this.props.location.search
// })
}
scrollToAnchors=(anchorName)=>{
this.setState({
anchor:anchorName
})
}
handleSubmit=(e) => {
let {fileList}=this.state;
let listid=[]
for(var list of fileList){
listid.push(list.response.id)
}
e.preventDefault();
if( GraduationTasksnewtype===true){
this.props.form.validateFields((err, values) => {
if (!err) {
if(values.tasktype===undefined){
this.scrollToAnchors("tasktypes");
return
}
if(values.name===undefined){
this.scrollToAnchors("nametypes");
return
}
if(values.description===undefined){
this.scrollToAnchors("descriptiontypes");
return
}else if(values.description.length>5000){
this.scrollToAnchors("descriptiontypes");
return
}
console.log('Received values of form: ', values);
console.log(fileList);
const course_id=this.props.match.params.coursesId;
let url="/courses/"+course_id+"/graduation_tasks.json"
axios.post(url, {
task_type:parseInt(values.tasktype),
name:values.name,
description:values.description,
attachment_ids:listid,
}
).then((response) => {
if(response.status===200) {
GraduationTasksnewtype=false;
// this.goback();
this.props.history.push("/courses/"+this.props.match.params.coursesId+"/graduation_tasks/"+this.props.match.params.category_id+"/"+response.data.task_id+"/setting");
}
}).catch((error) => {
console.log(error)
})
}
});
}
}
goback=()=>{
let courseId=this.props.match.params.coursesId;
let category_id=this.props.match.params.category_id;
window.location.href="/courses/"+courseId+"/graduation_tasks/"+category_id;
}
// 输入title
changeTitle=(e)=>{
this.setState({
title_num:60-parseInt(e.target.value.length),
title_value:e.target.value
})
}
// 附件相关 START
handleChange = (info) => {
let fileList = info.fileList;
// for(var list of fileList ){
// console.log(list)
// }
this.setState({ fileList });
}
onAttachmentRemove = (file) => {
// confirm({
// title: '确定要删除这个附件吗?',
// okText: '确定',
// cancelText: '取消',
// // content: 'Some descriptions',
// onOk: () => {
// this.deleteAttachment(file)
// },
// onCancel() {
// console.log('Cancel');
// },
// });
// return false;
this.setState({
Modalstype:true,
Modalstopval:'确定要删除这个附件吗?',
ModalSave: ()=>this.deleteAttachment(file),
ModalCancel:this.cancelAttachment
})
return false;
}
cancelAttachment=()=>{
this.setState({
Modalstype:false,
Modalstopval:'确定要删除这个附件吗?',
ModalSave:"",
ModalCancel:""
})
}
deleteAttachment = (file) => {
const url = `/attachments/${file.response ? file.response.id : file.uid}.json`
axios.delete(url, {
})
.then((response) => {
if (response.data) {
const { status } = response.data;
if (status == 0) {
console.log('--- success')
this.setState((state) => {
const index = state.fileList.indexOf(file);
const newFileList = state.fileList.slice();
newFileList.splice(index, 1);
return {
fileList: newFileList,
};
});
}
}
})
.catch(function (error) {
console.log(error);
});
}
//滚动
ifHasAnchorJustScorll() {
// let anchor = this.getURLStuff("anchor");
let anchor = this.state.anchor;
console.log("anchor ", anchor);
// 对应id的话, 滚动到相应位置
if (!!anchor) {
let anchorElement = document.getElementById(anchor);
if (anchorElement) {
window.scrollTo(0, anchorElement.offsetTop - window.innerHeight / 2);
}
}
// 没有的话,滚动到头部
else {
document.body.scrollTop = document.documentElement.scrollTop = 0;
}
}
render(){
const { getFieldDecorator } = this.props.form;
let {coursename,coursesearch,title_num,title_value,pageType,fileList,contents,type,
Modalstype,Modalstopval,ModalCancel,ModalSave} =this.state;
let {coursedata}=this.props;
let courseId=this.props.match.params.coursesId;
let position=this.props.match.params.position;
let category_id=this.props.match.params.category_id;
const uploadProps = {
width: 600,
fileList,
multiple: true,
// https://github.com/ant-design/ant-design/issues/15505
// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
// showUploadList: false,
action: `${getUrl()}/api/attachments.json`,
onChange: this.handleChange,
onRemove: this.onAttachmentRemove,
beforeUpload: (file) => {
console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 < 150;
if (!isLt150M) {
message.error('文件大小必须小于150MB!');
}
return isLt150M;
},
};
this.ifHasAnchorJustScorll();
// console.log(this.props.current_user.course_name)
return(
<React.Fragment>
<div>
{/*提示*/}
<Modals
modalsType={Modalstype}
modalsTopval={Modalstopval}
modalCancel={ModalCancel}
modalSave={ModalSave}
/>
<div className="newMain clearfix">
<div className={"educontent mb20"}>
<p className="clearfix mt10">
<WordsBtn style="grey" className="fl"> <Link to={"/courses/"+courseId+"/students"} className="color-grey-6">{this.props.current_user&&this.props.current_user.course_name}</Link></WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<WordsBtn style="grey" className="fl"> <Link to={"/courses/"+courseId+"/graduation_tasks/"+category_id} className="color-grey-6">毕设任务</Link></WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<span>{"新建"}</span>
</p>
<div style={{ width:'100%',height:'75px'}} >
<p className=" fl color-black mt25 summaryname">新建毕设任务</p>
<a className="color-grey-6 fr font-16 ml30 mt10 mr20" onClick={this.goback}>返回</a>
</div>
<Form labelCol={{ span: 5 }} wrapperCol={{ span: 12 }} onSubmit={GraduationTasksnewtype===true?this.handleSubmit:""} >
{/*内容*/}
<div className="stud-class-set bor-bottom-greyE pd20 edu-back-white">
<Form.Item label="类型">
{getFieldDecorator('tasktype', {
rules: [{ required: true, message: "请选择任务类型" }],
})(<Select className={"greyInput"}
style={{width:'20%'}}
placeholder="请选择任务类型">
<Option value="1">普通</Option>
<Option value="2">分组</Option>
</Select>)}
<input type="hidden" id='tasktypes' />
<span className={"newcoursestitle"}>选择确认后无法修改</span>
</Form.Item>
<Form.Item label="任务标题" >
{getFieldDecorator('name', {
rules: [{ required: true, message: "不能为空" }],
})(<Input placeholder="请输入任务名称最大限制60个字符" value={title_value} onInput={this.changeTitle} className="searchView searchViewAfter" style={{"width":"100%"}} maxLength="60" addonAfter={String(title_num)}/>)}
</Form.Item>
<input type="hidden" id='nametypes' />
</div>
<div className="stud-class-set pd20 coursenavbox edu-back-white">
<style>{`
.uploadBtn.ant-btn {
border: none;
color: #4CACFF;
box-shadow: none;
background: transparent;
padding: 0 6px;
}
.ant-upload-list-item:hover .ant-upload-list-item-info{
background-color:#fff;
}
.upload_1 .ant-upload-list {
width: 350px;
}
.ant-form-item{
margin-bottom:0px
}
`}</style>
<Form.Item
label="内容"
>
{getFieldDecorator('description', {
rules: [{
required: true, message: '请输入帖子内容',
}, {
max: 5000, message: '最大限制为5000个字符',
}],
})(
<TPMMDEditor ref={this.mdRef} placeholder={'请输入任务内容说明最大限制5000个字符'}
mdID={'courseMessageMD'} className="courseMessageMD"></TPMMDEditor>
)}
</Form.Item>
<input type="hidden" id='descriptiontypes' />
<Upload {...uploadProps} className="upload_1 ml5">
<Button className="uploadBtn">
<Icon type="upload" /> 上传附件
</Button>
(单个文件150M以内)
</Upload>
</div>
<Form.Item>
<div className="clearfix mt30 mb30">
<Button type="primary" htmlType="submit" className="defalutSubmitbtn fl mr20">提交</Button>
<a onClick={this.goback} className="defalutCancelbtn fl">取消</a>
</div>
</Form.Item>
</Form>
</div>
</div>
</div>
</React.Fragment>
)
}
}
const GraduationTasksnewApp = Form.create({ name: 'coursesNew' })(GraduationTasksnew);
export default GraduationTasksnewApp;

@ -1,967 +0,0 @@
import React, {Component} from "react";
import CoursesListType from '../coursesPublic/CoursesListType';
import {WordsBtn, ActionBtn} from 'educoder';
import {
Table,
Pagination,
} from "antd";
import {Link} from 'react-router-dom';
import axios from 'axios';
import moment from 'moment';
import '../css/members.css'
import '../css/busyWork.css'
import '../poll/pollStyle.css'
import {getImageUrl} from 'educoder';
import TraineetraininginformationModal from "./TraineetraininginformationModal";
import Startshixuntask from "../coursesPublic/Startshixuntask";
//GraduationTaskssetting.js
//作品列表(学生)
class Listofworksstudentone extends Component {
//unifiedsetting 统一设置
//allowreplenishment 允许补交
//completionefficiencyscore 完成效率评分占比
//level级别
//proportion 比例
constructor(props) {
super(props);
// course_group_info 老师身份显示的分班情况
//task_status 老师身份显示的提交情况
//要提交的作品状态checkedValuesine
//要提交的分班状态checkedValuesineinfo
//searchtext 输入的姓名和学号
//order 排序时间
this.state = {
jobsettingsdata: undefined,
endTime: "2018/11/10 17:10:00",
type: "day",
props: props,
data: [],
datas: [],
page: 1,
pages: 1,
limit: 20,
loadingstate: true,
order: "updated_at",
search: null,
day: 0,
hour: 0,
minute: 0,
second: 0,
userids: 0,
viewtrainingdata: {},
teacherdata: undefined,
task_status: [],
visibles: false,
course_group_info: [],
styletable: {
"display": "none"
},
student_works: [],
startbtn: false
}
//
}
componentDidMount() {
var homeworkid = this.props.match.params.homeworkid;
this.Getalistofworks(homeworkid);
let query = this.props.location.pathname;
const type = query.split('/');
this.setState({
shixuntypes: type[3]
})
}
//卸载组件取消倒计时
componentWillUnmount() {
// clearInterval(this.timer);
}
// 获取作品列表
Getalistofworks = (homeworkid) => {
// console.log("获取作品列表");
let urll = `/homework_commons/${homeworkid}/works_list.json`;
var data = {
search: "",
order: "",
b_order: "",
work_status: "",
course_group: "",
}
axios.post(urll, data).then((result) => {
if (result.status === 200) {
this.setState({
teacherdata: result.data,
task_status: result.data.task_status,
course_group_info: result.data.course_group_info,
student_works: result.data.student_works,
jobsettingsdata: result,
publish_immediately: result.data.publish_immediately,
end_immediately: result.data.end_immediately,
id: result.data.id,
})
if (result.data.student_works === undefined || result.data.student_works === null || JSON.stringify(result.data.student_works) === "[]") {
this.seacthdata();
} else {
this.seacthdatas();
}
}
}).catch((error) => {
console.log(error)
})
}
// 获取作品列表
Getalistofworkstwo = (ordervlue, checkedValuesine, checkedValuesineinfo, searchtext, page, limit) => {
// console.log("获取作品列表");
var homeworkid = this.props.match.params.homeworkid;
let urll = `/homework_commons/${homeworkid}/works_list.json`;
// console.log(homeworkid);
// search:搜索关键字
// order:排序方式更新时间update_time最终成绩work_score学号student_id
// b_order:desc倒序 asc顺序
// work_status:0:未提交, 1:按时提交, 2:延时提交,“”不限
// course_group:分班情况 [232, 231] []:不限(空数组)
var order = "asc";
if (ordervlue === "updated_at") {
order = "desc";
}
var data = {
search: "",
order: ordervlue,
b_order: order,
work_status: "",
course_group: "",
page: page,
limit: limit,
}
axios.post(urll, data).then((result) => {
if (result.status === 200) {
// console.log("学生未截至未公开的作品列表")
// console.log(JSON.stringify(result))
this.setState({
teacherdata: result.data,
task_status: result.data.task_status,
course_group_info: result.data.course_group_info,
student_works: result.data.student_works,
})
if (result.data.student_works === undefined || result.data.student_works === null || JSON.stringify(result.data.student_works) === "[]") {
this.seacthdata();
} else {
this.seacthdatas();
}
}
}).catch((error) => {
console.log(error)
this.setState({
loadingstate:false
})
})
}
TablePagination = (e) => {
// console.log(e.current);
this.setState({
page: e.current
})
}
seacthdatas = () => {
let {page, limit} = this.state;
let datalist = [];
let datalists = [];
var styletable = {"display": "none"}
// console.log("seacthdata设置数据3")
var teacherlist = undefined;
var teacherlists = undefined;
if (this.state.teacherdata !== undefined) {
// console.log("seacthdata设置数据")
// console.log(this.state.teacherdata.id )
if (this.state.teacherdata.id === undefined) {
this.setState({
data: [],
teacherlist: undefined,
loadingstate: false,
styletable: styletable,
})
} else {
console.log(this.state.teacherdata)
if (this.state.teacherdata.id !== undefined || this.state.teacherdata.id !== null || this.state.teacherdata.id !== "") {
// console.log("seacthdata设置数据1")
// console.log(this.state.teacherdata )
var timedata = moment(this.state.teacherdata.update_time).format('YYYY-MM-DD HH:mm');
datalist.push({
myid: this.state.teacherdata.id,
number: 1,
name: this.state.teacherdata.user_name,
stduynumber: this.state.teacherdata.student_id,
classroom: this.state.teacherdata.group_name,
submitstate: this.state.teacherdata.work_status === 0 ? "未提交" : this.state.teacherdata.work_status === 1 ? "按时提交" : this.state.teacherdata.work_status === 2 ? "延时提交" : "未提交",
// updatetime:this.state.teacherdata.student_works[i].update_time,
// updatetime:"",
updatetime: timedata === "Invalid date" ? "--" : timedata,
completion: this.state.teacherdata.complete_count === 4 ? "4/4" : this.state.teacherdata.complete_count === 3 ? "3/4" : this.state.teacherdata.complete_count === 2 ? "2/4" : this.state.teacherdata.complete_count === 1 ? "1/4" : this.state.teacherdata.complete_count === 0 ? "0" : 0,
levelscore: this.state.teacherdata.final_score,
efficiencyscore: this.state.teacherdata.eff_score,
finalscore: this.state.teacherdata.work_score,
operating: "查看",
})
// }
var teacherlist = { //分页
total: this.state.teacherdata.student_works.length, //数据总数量
pageSize: this.state.teacherdata.student_works.length, //一页显示几条
current: this.state.page,
}
styletable = {"display": "block",}
} else {
// console.log("seacthdata设置数据2")
teacherlist = undefined;
}
}
}
if (this.state.student_works !== undefined) {
for (var i = 0; i < this.state.student_works.length; i++) {
var timedata = moment(this.state.student_works[i].update_time).format('YYYY-MM-DD HH:mm');
datalists.push({
myid: this.state.student_works[i].id,
number: (parseInt(page) - 1) * parseInt(limit) + (i + 1),
name: this.state.student_works[i].user_name,
stduynumber: this.state.student_works[i].student_id,
classroom: this.state.student_works[i].group_name,
submitstate: this.state.student_works[i].work_status === 0 ? "未提交" : this.state.student_works[i].work_status === 1 ? "按时提交" : this.state.student_works[i].work_status === 2 ? "延时提交" : "未提交",
// updatetime:this.state.teacherdata.student_works[i].update_time,
// updatetime:"",
updatetime: timedata === "Invalid date" ? "--" : timedata,
completion: this.state.student_works[i].complete_count === 4 ? "4/4" : this.state.student_works[i].complete_count === 3 ? "3/4" : this.state.student_works[i].complete_count === 2 ? "2/4" : this.state.student_works[i].complete_count === 1 ? "1/4" : this.state.student_works[i].complete_count === 0 ? "0" : 0,
levelscore: this.state.student_works[i].final_score,
efficiencyscore: this.state.student_works[i].eff_score,
finalscore: this.state.student_works[i].work_score,
operating: "查看",
})
}
/*teacherlists = { //分页
total: this.state.student_works.length, //数据总数量
pageSize: 10, //一页显示几条
current: this.state.pages,
}*/
}
this.setState({
data: datalist,
datas: datalists,
teacherlist: teacherlist,
teacherlists: teacherlists,
loadingstate: false,
styletable: styletable,
})
}
// 设置数据
seacthdata = () => {
let datalist = [];
if (this.state.teacherdata !== undefined) {
// console.log("seacthdata设置数据")
// console.log(this.state.teacherdata.id )
if (this.state.teacherdata.id === undefined) {
this.setState({
data: [],
teacherlist: undefined,
loadingstate: false,
})
return
}
// console.log(this.state.teacherdata )
if (this.state.teacherdata.id !== undefined || this.state.teacherdata.id !== null || this.state.teacherdata.id !== "") {
// console.log("seacthdata设置数据1")
// console.log(this.state.teacherdata )
var timedata = moment(this.state.teacherdata.update_time).format('YYYY-MM-DD HH:mm');
datalist.push({
myid: this.state.teacherdata.id,
number: 1,
name: this.state.teacherdata.user_name,
stduynumber: this.state.teacherdata.student_id,
classroom: this.state.teacherdata.group_name,
submitstate: this.state.teacherdata.work_status === 0 ? "未提交" : this.state.teacherdata.work_status === 1 ? "按时提交" : this.state.teacherdata.work_status === 2 ? "延时提交" : "未提交",
// updatetime:this.state.teacherdata.student_works[i].update_time,
// updatetime:"",
updatetime: timedata === "Invalid date" ? "--" : timedata,
completion: this.state.teacherdata.complete_count === 4 ? "4/4" : this.state.teacherdata.complete_count === 3 ? "3/4" : this.state.teacherdata.complete_count === 2 ? "2/4" : this.state.teacherdata.complete_count === 1 ? "1/4" : this.state.teacherdata.complete_count === 0 ? "0" : 0,
levelscore: this.state.teacherdata.final_score,
efficiencyscore: this.state.teacherdata.eff_score,
finalscore: this.state.teacherdata.work_score,
operating: "查看",
})
// }
var teacherlist = { //分页
total: this.state.teacherdata.student_works.length, //数据总数量
pageSize: this.state.teacherdata.student_works.length, //一页显示几条
current: this.state.page,
}
} else {
// console.log("seacthdata设置数据2")
var teacherlist = undefined;
}
} else {
// console.log("seacthdata设置数据3")
var teacherlist = undefined;
}
console.log("datalistdatalist文件");
console.log(datalist);
this.setState({
data: datalist,
teacherlist: teacherlist,
loadingstate: false,
})
}
// 查看学员实训信息
Viewstudenttraininginformation = (e) => {
// console.log("Listofworksstudentone.js");
// console.log(e);
this.setState({
userids: e.myid,
})
this.viewtraining(e.myid);
}
viewtraining = (userids) => {
// console.log("viewtraining")
// console.log(userids)
// console.log(JSON.stringify(this.props.user))
var url = `/student_works/${userids}/shixun_work.json`;
axios.get(url).then((result) => {
if (result.status === 200) {
// console.log("Listofworksstudentone.js")
// console.log(JSON.stringify(result))
let datalist = [];
var game_list = result.data.game_list
for (var i = 0; i < game_list.length; i++) {
datalist.push({
number: "第" + game_list[i].position + "关",
name: moment(game_list[i].end_time).format('YYYY-MM-DD HH:mm'),
stduynumber: game_list[i].cost_time,
classroom: game_list[i].score,
complete_status: game_list[i].complete_status,
})
}
// console.log("viewtraining");
// console.log(datalist);
// console.log(JSON.stringify(datalist));
this.setState({
viewtrainingdata: result.data,
visibles: true,
game_list: datalist
})
}
}).catch((error) => {
})
}
// 关闭调分
cancelModulationModels = () => {
this.setState({visibles: false})
}
//排序
funorder = (e) => {
if (e === "updated_at") {
// 时间
// 时间排序是从小到大
this.setState({
order: "updated_at",
loadingstate: true,
})
this.Getalistofworkstwo(e, "", "", "", this.state.page, this.state.limit);
}
if (e === "work_score") {
// 成绩
//成绩排序是从大到小
this.setState({
order: "work_score",
loadingstate: true,
})
this.Getalistofworkstwo(e, "", "", "", this.state.page, this.state.limit);
}
if (e === "student_id") {
//学号
//学号排序是从大到小
this.setState({
order: "student_id",
loadingstate: true,
})
this.Getalistofworkstwo(e, "", "", "", this.state.page, this.state.limit);
}
}
Startsorting = (ordervlue, checkedValuesine, checkedValuesineinfo, searchtext, page, limit) => {
// console.log("获取作品列表Listofworksstudenttwo");
var homeworkid = this.props.match.params.homeworkid;
let urll = `/homework_commons/${homeworkid}/works_list.json`;
// console.log(homeworkid);
// search:搜索关键字
// order:排序方式更新时间update_time最终成绩work_score学号student_id
// b_order:desc倒序 asc顺序
// work_status:0:未提交, 1:按时提交, 2:延时提交,“”不限
// course_group:分班情况 [232, 231] []:不限(空数组)
var order = "asc";
if (ordervlue === "updated_at") {
order = "desc";
}
var data = {
search: "",
order: ordervlue,
b_order: order,
work_status: "",
course_group: "",
page: page,
limit: limit,
}
axios.post(urll, data).then((result) => {
if(result !== undefined){
// console.log("学生公开的作品列表")
// console.log(JSON.stringify(result))
this.setState({
teacherdata: result.data,
task_status: result.data.task_status,
course_group_info: result.data.course_group_info,
student_works: result.data.student_works,
loadingstate: false
})
this.seacthdata();
}
}).catch((error) => {
console.log(error)
this.setState({
loadingstate: false
})
})
}
TablePagination = (e) => {
// console.log(e.current);
var teacherlists = { //分页
total: this.state.student_works.length, //数据总数量
pageSize: 10, //一页显示几条
current: e.current,
}
this.setState({
page: e.current,
teacherlists: teacherlists
})
}
paginationonChanges = (pageNumber) => {
this.setState({
page: pageNumber,
loadingstate: true,
})
this.Getalistofworkstwo(this.state.order, "", "", "", pageNumber, this.state.limit);
}
render() {
let {visibles, game_list, limit, viewtrainingdata, teacherdata,page, data, jobsettingsdata,styletable, datas, order, loadingstate} = this.state;
let columns = [
{
title: '序号',
dataIndex: 'number',
key: 'number',
align: "center",
width:'96px',
render: (text, record) => (
<span>
<a style={{
"color": '#07111B',
"text-align": "center"
}}>{record.number === undefined ? "--" : record.number === "" ? "--" : record.number}</a>
</span>
),
},
{
title: '姓名',
dataIndex: 'name',
key: 'name',
align: "center",
width:'147px',
render: (text, record) => (
<span>
<a style={{
"color": '#07111B',
"text-align": "center"
}}>{record.name === undefined ? "--" : record.name === "" ? "--" : record.name === null ? "--" : record.name}</a>
</span>
),
},
{
title: '学号',
dataIndex: 'stduynumber',
key: 'stduynumber',
align: "center",
width:'200px',
render: (text, record) => (
<span>
<a style={{
"color": '#9A9A9A',
"text-align": "center"
}}>{record.stduynumber === undefined ? "--" : record.stduynumber === null ? "--" : record.stduynumber === "" ? "--" : record.stduynumber}</a>
</span>
),
},
{
title: '分班',
key: 'classroom',
dataIndex: 'classroom',
align: "center",
render: (text, record) => (
<span>
<a style={{
"color": '#9A9A9A',
"text-align": "center"
}}>{record.classroom === undefined ? "--" : record.classroom === "" ? "--" : record.classroom === null ? "--" : record.classroom}</a>
</span>
),
},
{
title: '提交状态',
dataIndex: 'submitstate',
key: 'submitstate',
align: "center",
render: (text, record) => (
<span>
<a style={record.submitstate === "延时提交" ? {
"color": '#DD1717',
"text-align": "center"
} : record.submitstate === "按时提交" ? {"color": '#29BD8B', "text-align": "center"} : {
"color": '#747A7F',
"text-align": "center"
}}>{record.submitstate === undefined ? "--" : record.submitstate === "" ? "--" : record.submitstate === null ? "--" : record.submitstate}</a>
</span>
),
},
{
title: '更新时间',
dataIndex: 'updatetime',
key: 'updatetime',
align: "center",
render: (text, record) => (
<span>
<a style={{
"color": '#9A9A9A',
"text-align": "center"
}}>{record.updatetime === undefined ? "--" : record.updatetime === "" ? "--" : record.updatetime}</a>
</span>
),
},
{
title: '完成情况',
dataIndex: 'completion',
key: 'completion',
align: "center",
render: (text, record) => (
<span>
<a style={{
"color": '#9A9A9A',
"text-align": "center"
}}>{record.completion === undefined ? "--" : record.completion === "" ? "--" : record.completion}</a>
</span>
),
},
{
title: '最终成绩',
dataIndex: 'levelscore',
key: 'levelscore',
align: "center",
render: (text, record) => (
<span>
<a style={parseInt(record.levelscore) > 90 ? {
"color": '#DD1717',
"text-align": "center"
} : parseInt(record.levelscore) <= 90 ? {
"color": '#FF6800',
"text-align": "center",
"margin-left": "20px"
} : parseInt(record.levelscore) <= 60 ? {
"color": '#747A7F',
"text-align": "center"
} : {"color": '#747A7F', "text-align": "center"}}>{record.levelscore}</a>
</span>
),
},
{
title: '操作',
dataIndex: 'operating',
key: 'operating',
align: "center",
width:'96px',
render: (text, record) => (
<span>
{
record.submitstate === "未提交" ? "--"
:
<a style={{"text-align": "center"}}
className="color-blue"
onClick={() => this.Viewstudenttraininginformation(record)}>{record.operating}</a>
}
</span>
),
},
];
// console.log("teacherdatateacherdatateacherdata");
// console.log(teacherdata);
// console.log("teacherdata.work_count");
// console.log(teacherdata&&teacherdata.work_count);
return (
teacherdata === undefined || teacherdata.student_works === undefined || teacherdata.student_works === null || JSON.stringify(teacherdata.student_works) === "[]" ?
// 学生不能查看别人的
<div className="newMain clearfix ">
{visibles === true ?
<div>
<style>
{
`
body {
overflow: hidden !important;
}
`
}
</style>
<TraineetraininginformationModal
homeworkid={this.props.match.params.homeworkid}
coursesId={this.props.match.params.coursesId}
userids={this.state.userids}
viewtrainingdata={viewtrainingdata}
game_list={game_list}
visible={visibles}
Cancel={() => this.cancelModulationModels()}
/>
</div>
: ""
}
<div className={"educontent mb20"}>
<div className="educontent mb30">
<p className="clearfix mb20 mt10">
<ActionBtn className=" btn colorgrey fl hovercolorblue "
to={`/courses/${this.props.match.params.coursesId}/students`}>{jobsettingsdata === undefined ? "" : jobsettingsdata.data.course_name}</ActionBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<ActionBtn
className=" btn colorgrey fl hovercolorblue "
to={`/courses/${this.props.match.params.coursesId}/${this.state.shixuntypes}/${jobsettingsdata === undefined ? "" : jobsettingsdata.data.category.category_id===undefined?"" :jobsettingsdata.data.category.category_id}`}>{jobsettingsdata === undefined ? "" : jobsettingsdata.data.category.category_name}</ActionBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<WordsBtn className="fl">作业详情</WordsBtn>
</p>
</div>
<div className="educontent mb30">
<p className=" fl color-black summaryname">
{teacherdata === undefined ? "" : teacherdata.homework_name}
</p>
<CoursesListType
typelist={teacherdata === undefined ? [""] : teacherdata.homework_status}
/>
<a className="color-grey-9 fr font-16 summaryname ml20 mr20"
href={`/courses/${this.state.props.match.params.coursesId}/${this.state.shixuntypes}/${jobsettingsdata === undefined ? "" :jobsettingsdata.data.category.category_id}`}>返回</a>
<p className="color-grey-9 fr font-16"></p>
</div>
<div className="edu-back-white">
<div className="stud-class-set bor-bottom-greyE ">
<div className=" clearfix edu-back-white poll_list">
<Link className="active"
to={`/courses/${this.state.props.match.params.coursesId}/${this.state.shixuntypes}/${this.state.props.match.params.homeworkid}/openlist`}>作品列表</Link>
<Link
to={`/courses/${this.state.props.match.params.coursesId}/${this.state.shixuntypes}/${this.state.props.match.params.homeworkid}/questions`}>作业问答</Link>
<Link
to={`/courses/${this.state.props.match.params.coursesId}/${this.state.shixuntypes}/${this.state.props.match.params.homeworkid}/settings`}
>设置</Link>
<Link className="fr color-blue font-16" target={"_blank"}
to={`/courses/${this.state.props.match.params.coursesId}/${this.state.shixuntypes}/${teacherdata === undefined ? "" : teacherdata.id}/shixun_work_report`}>
查看实训报告
</Link>
{/*<a className="fr color-blue font-16">查看实训报告</a>*/}
{
teacherdata === undefined ? ""
: teacherdata.commit_des === null || teacherdata.commit_des === undefined ? "" :
<a className="fr color-blue font-16"
href={`/courses/${this.state.props.match.params.coursesId}/${this.state.shixuntypes}/${teacherdata === undefined ? "" : teacherdata.id}/commitsummary/${this.state.props.match.params.homeworkid}`}>{teacherdata.commit_des}</a>
}
{teacherdata === undefined ? "" : <Startshixuntask
{...this.props}
data={teacherdata}
/>}
</div>
</div>
{JSON.stringify(data) !== "[]" ?
<div>
<div id="graduation_work_list"
style={{
"padding": '0px 20px 10px 20px',
"margin-top": "20px",
"margin-bottom": "10px"
}}>
<div className="clearfix">
<span className="fl color-grey-6 font-12"><span
className="color-orange-tip">{teacherdata === undefined ? "" : teacherdata.commit_count === undefined ? "" : teacherdata.commit_count}</span><span
className="ml10">{teacherdata === undefined ? "" : teacherdata.uncommit_count}</span><span></span>
{teacherdata === undefined ? "" : teacherdata.left_time === undefined ? "" :teacherdata.left_time === null ? "" :
<span className="ml20">{teacherdata.left_time.status}</span>
}
{teacherdata === undefined ? "0" : teacherdata.left_time === undefined ? "0" : teacherdata.left_time === null ? "0" :
<span className="ml20" style={{"color": '#FF6800'}}>{teacherdata.left_time.time}</span>
}
</span>
</div>
</div>
<div className={"justify break_full_word new_li edu-back-white"}
style={{minHeight: "480px"}}>
<style>{`
.ant-spin-nested-loading > div > .ant-spin .ant-spin-dot {
top: 72%;}
}
`}</style>
<div className="edu-table edu-back-white ">
{data === undefined ? "" : <Table
dataSource={data}
columns={columns}
pagination={false}
loading={false}
/>}
</div>
</div>
</div>
:
<div id="forum_list" className="forum_table">
<div className="mh650 edu-back-white">
<div className="edu-tab-con-box clearfix edu-txt-center">
<img className="edu-nodata-img mb20"
src={getImageUrl("images/educoder/nodata.png")}/>
<p className="edu-nodata-p mb30">没有数据可以显示</p>
</div>
</div>
</div>
}
</div>
</div>
</div>
:
// 学生能查看别人的
<div className="newMain clearfix ">
{visibles === true ?
<TraineetraininginformationModal
homeworkid={this.props.match.params.homeworkid}
coursesId={this.props.match.params.coursesId}
userids={this.state.userids}
viewtrainingdata={viewtrainingdata}
game_list={game_list}
visible={visibles}
Cancel={() => this.cancelModulationModels()}
/> : ""
}
<div className={"educontent mb20"}>
<div className="educontent mb30">
<p className="clearfix mb20 mt10">
<ActionBtn className=" fl "
to={`/courses/${this.props.match.params.coursesId}`}>{jobsettingsdata === undefined ? "" : jobsettingsdata.data.course_name}</ActionBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<ActionBtn
className=" fl "
to={`/courses/${this.props.match.params.coursesId}/${this.state.shixuntypes}/${jobsettingsdata === undefined ? "" : jobsettingsdata.data.category.category_id}`}>{jobsettingsdata === undefined ? "" : jobsettingsdata.data.category.category_name}</ActionBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<WordsBtn className="fl">作业详情</WordsBtn>
</p>
</div>
<div className="educontent mb30">
<p className=" fl color-black summaryname">
{jobsettingsdata === undefined ? "" : jobsettingsdata.data.homework_name}
</p>
<CoursesListType
typelist={jobsettingsdata === undefined ? [""] : jobsettingsdata.data.homework_status}
/>
<p className="color-grey-6 fr font-16 summaryname ml20"
onClick={() => this.props.history.goBack()}>返回</p>
<p className="color-grey-6 fr font-16"></p>
</div>
<div className="edu-back-white">
<div className="stud-class-set bor-bottom-greyE ">
<div className=" clearfix edu-back-white poll_list">
<Link className="active"
to={`/courses/${this.state.props.match.params.coursesId}/${this.state.shixuntypes}/${this.state.props.match.params.homeworkid}/openlist`}>作品列表</Link>
<Link
to={`/courses/${this.state.props.match.params.coursesId}/${this.state.shixuntypes}/${this.state.props.match.params.homeworkid}/questions`}>作业问答</Link>
<Link
to={`/courses/${this.state.props.match.params.coursesId}/${this.state.shixuntypes}/${this.state.props.match.params.homeworkid}/settings`}
>设置</Link>
<Link className="fr color-blue font-16" target={"_blank"}
to={`/courses/${this.state.props.match.params.coursesId}/${this.state.shixuntypes}/${this.state.props.match.params.homeworkid}/shixun_work_report`}>
查看实训报告
</Link>
{
teacherdata === undefined ? ""
: teacherdata.commit_des === null || teacherdata.commit_des === undefined ? "" :
<a className="fr color-blue font-16"
href={`/courses/${this.state.props.match.params.coursesId}/${this.state.shixuntypes}/${teacherdata === undefined ? "" : teacherdata.id}/commitsummary/${this.state.props.match.params.homeworkid}`}>{teacherdata.commit_des}</a>
}
{teacherdata === undefined ? "" : <Startshixuntask
{...this.props}
data={teacherdata}
/>}
</div>
</div>
{/*<div id="graduation_work_list" style={{padding: '0px 40px 10px 40px'}}>*/}
{/* <div className="clearfix">*/}
{/* <span className="fl color-grey-6 font-12"><span className="color-orange-tip">89</span>已交<span*/}
{/* className="ml10">9000</span><span>未交</span> <span className="ml20">剩余提交时间:</span><span*/}
{/* className="ml20">{this.state.day}天 {this.state.hour}:{this.state.minute}:{this.state.second}</span></span>*/}
{/* </div>*/}
{/*</div>*/}
<div className="edu-table edu-back-white ">
{data === undefined ? "" : <Table
style={styletable}
dataSource={data}
columns={columns}
pagination={false}
loading={false}
showHeader={false}
scroll={{y: 240}}
/>}
</div>
{JSON.stringify(datas) !== "[]" ?
<div>
<div id="graduation_work_list" style={{
"padding": '0px 20px 10px 20px',
"margin-top": "20px",
"margin-bottom": "10px"
}}>
<div className="clearfix">
<span className="fl color-grey-6 font-12"><span
className="color-orange-tip">{teacherdata === undefined ? "" : teacherdata.commit_count === undefined ? "" : teacherdata.commit_count}</span><span
className="ml10">{teacherdata === undefined ? "" : teacherdata.uncommit_count}</span><span></span>
{teacherdata === undefined ? "" : teacherdata.left_time === undefined ? "" : teacherdata.left_time === null ? "" :<span className="ml20">{teacherdata.left_time.status}</span>}
{teacherdata === undefined ? "0" : teacherdata.left_time === undefined ? "0" :teacherdata.left_time === null ? "0" :<span className="ml20" style={{"color": '#FF6800'}}>{teacherdata.left_time.time}</span> }
</span>
<div className="fr color-grey-6 edu-menu-panel">
<ul>
<li className="edu-position edu-position-hidebox">
<a className="font-12 color-grey-6">
{order === "updated_at" ? "时间" : order === "work_score" ? "成绩" : order === "student_id" ? "学号" : ""}排序</a>
<i className="iconfont icon-xiajiantou ml5 font-12 color-grey-6"></i>
<ul className="edu-position-hide undis mt10">
<li> <a onClick={(e) => this.funorder("updated_at")} data-remote="true" className="color-grey-6 font-12" style={{"text-align": "center "}}>更新时间</a></li>
<li> <a onClick={(e) => this.funorder("work_score")} data-remote="true" className="color-grey-6 font-12" style={{"text-align": "center "}}>最终成绩</a></li>
<li> <a onClick={(e) => this.funorder("student_id")} data-remote="true" className="color-grey-6 font-12" style={{"text-align": "center "}}>学生学号</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
<div className={"justify break_full_word new_li edu-back-white"}
style={{minHeight: "480px"}}>
<style>{`
.ant-spin-nested-loading > div > .ant-spin .ant-spin-dot {
top: 72%;}
}
`}</style>
<div className="edu-table edu-back-white ">
{datas === undefined ? "" : <Table
dataSource={datas}
columns={columns}
pagination={false}
loading={loadingstate}
onChange={this.TablePagination}
/>}
</div>
{
teacherdata && teacherdata.work_count && teacherdata.work_count > limit ?
<div className="edu-txt-center mt30 mb20">
<Pagination showQuickJumper current={page}
onChange={this.paginationonChanges} pageSize={limit}
total={teacherdata.work_count}></Pagination>
</div>
: ""
}
</div>
</div>
:
<div id="forum_list" className="forum_table">
<div className="mh650 edu-back-white">
<div className="edu-tab-con-box clearfix edu-txt-center">
<img className="edu-nodata-img mb20"
src={getImageUrl("images/educoder/nodata.png")}/>
<p className="edu-nodata-p mb30">没有数据可以显示</p>
</div>
</div>
</div>
}
{/*///*/}
</div>
</div>
</div>
)
}
}
export default Listofworksstudentone;

@ -1,272 +0,0 @@
import React,{ Component } from "react";
import { Modal,Checkbox,notification} from "antd";
import axios from 'axios';
class ShixunWorkModal extends Component{
constructor(props){
super(props);
this.state={
course_groups:undefined,
limit:10,
page:1,
group_ids:undefined,
group_list:undefined
}
}
componentDidMount() {
let url="/homework_commons/"+this.props.match.params.homeworkid+"/group_list.json";
axios.get(url,{params:{
limit:10,
page:1,
}
}).then((response) => {
if(response.data.group_list===undefined){
this.setState({
course_groups:response.data,
group_list:undefined
})
}else{
this.setState({
course_groups:response.data,
group_list:response.data.group_list
})
}
}).catch((error) => {
console.log(error)
});
}
//勾选实训
shixunhomeworkedit=(checkedValues)=>{
let{group_list}=this.state;
if(checkedValues.length===group_list.length){
this.setState({
onChangetype:true,
group_ids:checkedValues
})
}else{
this.setState({
group_ids:checkedValues,
onChangetype:false
})
}
}
contentViewScroll=(e)=>{
//滑动到底判断
if(e.currentTarget.scrollHeight-e.currentTarget.scrollTop===e.currentTarget.clientHeight){
let {page,limit,group_list}=this.state;
let newpage=page+1;
let newgroup_list=group_list;
let url="/homework_commons/"+this.props.match.params.homeworkid+"/group_list.json";
axios.get(url,{params:{
limit:limit,
page:newpage,
}
}).then((response) => {
response.data. course_groups.group_list&&response.data.group_list.map((item,key)=>{
newgroup_list.push(item)
})
this.setState({
course_groups:response.data,
group_list:newgroup_list,
page:newpage
})
}).catch((error) => {
console.log(error)
});
}
}
onChange=(e)=>{
let{group_list}=this.state;
let {data}=this.props;
if(e.target.checked===true){
if(data&&data.length===0){
let id=[]
group_list.forEach((item,key)=>{
id.push(item.id)
})
this.setState({
group_ids:id,
onChangetype:e.target.checked
})
}else{
let id=[]
group_list.forEach((item,key)=>{
id.push(item.id)
})
this.setState({
group_ids:id,
onChangetype:e.target.checked
})
}
}else{
this.setState({
group_ids:[],
onChangetype:e.target.checked
})
}
}
isSave=()=>{
let{group_ids}=this.state;
let url="/homework_commons/"+this.props.match.params.homeworkid+"/homework_code_repeat.json";
axios.post(url, {
group_ids: group_ids,
})
.then((response) => {
if (response.data.status === 0) {
this.props.updatas()
this.props.issCancel()
notification.open({
message:"提示",
description: response.data.message
});
console.log(this.props)
}else if(response.data.status === -1){
notification.open({
message:"提示",
description: response.data.message
});
}else if(response.data.status === -2){
notification.open({
message:"提示",
description: response.data.message
});
}else if(response.data.status === -3){
notification.open({
message:"提示",
description: response.data.message
});
}else if(response.data.status === -4){
notification.open({
message:"提示",
description: response.data.message
});
}
})
.catch(function (error) {
console.log(error);
});
}
issCancel=()=>{
this.props.issCancel()
}
render(){
let {course_groups,group_ids,onChangetype,group_list}=this.state;
// let {data}=this.props;
// console.log(group_list)
// console.log(course_groups)
return(
<div>
<Modal
keyboard={false}
className={"HomeworkModal"}
title={this.props.modalname}
visible={this.props.visible}
closable={false}
footer={null}
destroyOnClose={true}
>
<div className="task-popup-content">
<style>{`
.greybackHead{
padding:0px 30px;
}
.fontlefts{text-align: left;}
`}</style>
<ul className="clearfix edu-txt-center">
<li className="fl paddingleft22 fontlefts" style={{width:'150px'}}>分班名称</li>
<li className="fl edu-txt-left" style={{width:'150px'}}>有效作品数</li>
<li className="fl" style={{width:'100px'}}>上次查重时间</li>
</ul>
{course_groups===undefined?"":
<ul className="upload_select_box fl clearfix mt10 mb10" tyle={{"overflow-y":"auto"}}id="search_not_members_list"
onScroll={this.contentViewScroll}
>
<Checkbox.Group style={{ width: '100%' }} onChange={this.shixunhomeworkedit} value={group_ids}>
{
group_list===undefined?
<div className="clearfix edu-txt-center lineh-40 bor-bottom-greyE">
<li className="fl" style={{width: '100px'}}>
<Checkbox
className="fl task-hide edu-txt-left"
name="shixun_homework[]"
value={course_groups.ungroup_list.id}
>
<label style={{"textAlign": "left", "color": "#05101A"}}
className="task-hide color-grey-name" title="frerere">{course_groups.ungroup_list.name}</label>
</Checkbox>
</li>
<li className="fl" style={{width: '150px'}}>
{course_groups.ungroup_list.works_count}
</li>
<li className="fl" style={{width: '160px'}}>
{course_groups.ungroup_list.last_review_time}
</li>
</div>
:
group_list&&group_list.map((item,key)=>{
return(
<div className="clearfix edu-txt-center lineh-40 bor-bottom-greyE" key={key}>
<li className="fl" style={{width: '100px'}}>
<Checkbox
className="fl task-hide edu-txt-left"
name="shixun_homework[]"
value={item.id}
>
<label style={{"textAlign": "left", "color": "#05101A"}}
className="task-hide color-grey-name" title="frerere">{item.name}</label>
</Checkbox>
</li>
<li className="fl" style={{width: '150px'}}>
{item.works_count}
</li>
<li className="fl" style={{width: '160px'}}>
{item.last_review_time}
</li>
</div>
)
})
}
</Checkbox.Group>
</ul>
}
<div className={"clearfix"}>
<Checkbox checked={onChangetype} onChange={this.onChange} className={"ml10"}>{onChangetype===true?"清除":"全选"}</Checkbox>
</div>
<div className="clearfix mt30 edu-txt-center mb10">
<a className="task-btn color-white mr30" onClick={this.issCancel}>取消</a>
<a className="task-btn task-btn-orange" onClick={this.isSave}>确认</a>
</div>
</div>
</Modal>
</div>
)
}
}
export default ShixunWorkModal;

@ -1,195 +0,0 @@
import React, {Component} from "react";
import axios from 'axios';
import moment from 'moment';
import {Link} from 'react-router-dom';
import 'moment/locale/zh-cn';
import delay from '../../../images/delay.png'
import invalid from '../../../images/invalid.png'
import {
Form,
Select,
Input,
Button,
Checkbox,
Upload,
Icon,
message,
Modal,
Table,
Divider,
InputNumber,
Tag,
DatePicker,
Radio,
Tooltip
} from "antd";
import '../css/members.css'
import "../common/formCommon.css"
import '../css/Courses.css'
import './style.css'
import ShixunModal from "../coursesPublic/ShixunModal";
class TraineetraininginformationModal extends Component {
constructor(props) {
super(props);
this.setState({
props: this.props,
userids: this.props.userids,
game_list:this.props.game_list,
})
// console.log("TraineetraininginformationModal")
// console.log(JSON.stringify(this.props))
}
Cancel = () => {
this.props.Cancel()
}
componentDidMount() {
// this.seacthdata();
}
// 设置数据
// seacthdata = () => {
//
// console.log(datalist)
// }
render() {
let columns = [
{
title: '关卡',
dataIndex: 'number',
key: 'number',
width: 150,
align: "center",
render: (text, record) => (
<span >
<a style={{"color":'#07111B', "text-align": "center"}}>{record.number}</a>
</span>
),
},
{
title: '完成时间',
dataIndex: 'name',
key: 'name',
width: 150,
align: "center",
render: (text, record) => (
<span >
<a style={{"color":'#989898', "text-align": "center"}} >{record.name==='Invalid date'?"--":record.name}</a>
</span>
),
},
{
title: '',
dataIndex: 'complete_status',
key: 'complete_status',
width: 150,
align: "center",
render: (text, record) => (
<span>
{record.complete_status===2?<img src={delay}/>:record.complete_status===3?<img src={invalid}/>:""}
</span>
),
},
{
title: '耗时',
dataIndex: 'stduynumber',
key: 'stduynumber',
width: 150,
align: "center",
render: (text, record) => (
<span>
<a style={{"color":'#989898', "text-align": "center"}}>{record.stduynumber}</a>
</span>
),
},
{
title: '经验值',
key: 'classroom',
width: 150,
dataIndex: 'classroom',
align: "center",
render: (text, record) => (
<span>
<a style={{"color":'#29BD8B', "text-align": "center"}}>{record.classroom}</a>
</span>
),
}
];
return (
<div>
<Modal
keyboard={false}
className="HomeworkModal"
title={"学员实训信息"}
visible={this.props.visible}
footer={null}
onCancel={this.Cancel}
>
<div style={{width:"100%" }}className="login_register_content" >
<div style={{"width": "100%","text-align": "center"}}>
<a style={{"text-align": "center"}}>{this.props.viewtrainingdata === undefined ? "" : this.props.viewtrainingdata.shixun_name}</a>
<a className="ml20 " style={{"text-align": "center","color":'#29BD8B'}} >经验值<span style={{"color":'#29BD8B'}}> {this.props.viewtrainingdata === undefined ? "" : this.props.viewtrainingdata.shixun_score}</span></a>
{/*<Button type="primary" className="ml30" >实训报告</Button>*/}
<a className="ml30 color-blue " style={{"text-align": "center"}} target={"_blank"} href={`/courses/${this.props.coursesId}/shixun_homeworks/${this.props.viewtrainingdata.work_id}/shixun_work_report`}>
实训报告
</a>
{/*这里到时候要做判断*/}
</div>
</div>
<div className=" bor-bottom-greyE mt10"></div>
<div className="fl edu-back-white ml10 ">
<img alt="头像" className="radius mt10" height="70" id="nh_user_logo" name="avatar_image"
src={this.props.viewtrainingdata === undefined ? "" : "https://www.educoder.net/images/" + `${this.props.viewtrainingdata.image_url}`}
width="70"/>
</div>
<div className="fl edu-back-white ml20 mt10">
<li>{this.props.viewtrainingdata === undefined ? "" :this.props.viewtrainingdata.username}<span style={{"color":"#323232"}}> 通关</span><span style={{"color": '#FF6800'}}>{this.props.viewtrainingdata === undefined ? "" :this.props.viewtrainingdata.complete_count}/{this.props.viewtrainingdata === undefined ? "" :this.props.viewtrainingdata.challenges_count}</span></li>
<li><span style={{"color":"#989898"}} >完成效率</span><span style={{"color":"#4C4C4C"}}>{this.props.viewtrainingdata === undefined ? "" :this.props.viewtrainingdata.efficiency}</span></li>
<li><span style={{"color":"#989898"}}>通关时间</span> <span style={{"color":"#4C4C4C"}}>{this.props.viewtrainingdata === undefined ? "":moment(this.props.viewtrainingdata.passed_time).format('YYYY-MM-DD HH:mm')==="Invalid date"?"--":moment(this.props.viewtrainingdata.passed_time).format('YYYY-MM-DD HH:mm')}</span> </li>
</div>
<div className="fl edu-back-white ml20 mt10">
<li className="mt20"> <span style={{"color":"#989898"}}>课堂最高完成效率</span> <span style={{"color":"#4C4C4C"}}>{this.props.viewtrainingdata === undefined ? "" :this.props.viewtrainingdata.max_efficiency} </span> </li>
<li><span style={{"color":"#989898"}}>总耗时:</span> <span style={{"color":"#4C4C4C"}}> {this.props.viewtrainingdata === undefined ? "" :this.props.viewtrainingdata.total_spend_time}</span> </li>
</div>
{
this.props.game_list === undefined?"" : this.props.game_list.length<4?
<style>
{
` .ant-table-body{
overflow: hidden !important;
}`
}
</style>
:""
}
<div className={"both"}></div>
{this.props.game_list === undefined ? "" : <Table
className="mt20"
dataSource={this.props.game_list}
columns={columns}
pagination={{ //分页
total: this.props.game_list.length, //数据总数量
pageSize: this.props.game_list.length, //一页显示几条
current: 1,
}}
loading={false}
pagination={false}
onChange={this.TablePagination}
scroll={{ y: 300 }}
/>}
</Modal>
</div>
)
}
}
export default TraineetraininginformationModal;

@ -1,511 +0,0 @@
import React, {Component} from "react";
import CoursesListType from '../coursesPublic/CoursesListType';
import {WordsBtn,ActionBtn,markdownToHTML} from 'educoder';
import GraduateTopicReply from '../../courses/graduation/topics/GraduateTopicReply'
import MemoDetailMDEditortwo from '../../forums/MemoDetailMDEditortwo'
import {
Form,
Select,
Input,
Button,
Checkbox,
Upload,
Icon,
message,
Modal,
Table,
Divider,
InputNumber,
Tag,
DatePicker,
Radio,
Tooltip,
notification
} from "antd";
import {Link, Switch, Route, Redirect} from 'react-router-dom';
import axios from 'axios';
import '../css/members.css'
import "../common/formCommon.css"
import '../css/Courses.css'
import './style.css'
import 'antd/lib/pagination/style/index.css';
import './Challenges.css'
import CommonReply from "../common/comments/CommonReply";
import Homeworddescription from "../shixunHomework/Homeworddescription";
import ShixunWorkModal from './Shixunworkdetails/ShixunWorkModal';
import HomeworkModal from "../coursesPublic/HomeworkModal";
// import Homeworddescription from "../shixunHomework/Homeworddescription";
const TextArea = Input.TextArea
//GraduationTaskssetting.js
//作业问答页面
class Workquestionandanswer extends Component {
//unifiedsetting 统一设置
//allowreplenishment 允许补交
//completionefficiencyscore 完成效率评分占比
//level级别
//proportion 比例
constructor(props) {
super(props);
// this.props.form.setFieldsValue({
// radiogroup:1,
//
// });
this.state ={
// namestring:"JFinal是基于Java语言的极速web开发框架其核心设计目标是开发迅速、代码量少、学习简单、功能强大、轻量级、易扩展、`Restful`。在拥有Java语言所有优势的同时再拥有`ruby`、`python`等动态语言的开发效率。\r\n\r\n在本实训中我们将基于JFinal框架搭建一个在线商城。我们会学习到如何合理设计`Model`来进行数据表映射和数据操作、如何设计`Controller`对请求进行合理的处理…… 通过本实训,您不仅能收获到具体框架和技术的使用经验,也能对项目架构设计等知识有所了解或巩固。\r\n\r\n<div align=\"center\">\r\n\t<img src=\"/attachments/download/170838\" />\r\n</div>\r\n\r\n本实训聚焦一个在线商城项目的核心业务逻辑实现。你将在这一过程中收获Java Web项目设计的基本思想也能得到编程能力的一次跃升。",
props: props,
starttimetype: false,
endtimetype: false,
latetimetype: false,
allowlate: 1,
latepenaltytype: false,
unifiedsetting: false,
allowreplenishment: undefined,
completionefficiencyscore: false,
proportion: undefined,
level: undefined,
ealuation: false,
latededuction: undefined,
latedeductiontwo: undefined,
Showupdateinstructions:false,
database: false,
datasheet: false,
databasetwo: undefined,
datasheettwo: undefined,
publicwork: undefined,
memo: {id: "94", user_id: 1},
polls_descriptiontest: "作业说明...",
jobdescriptiondisplay:"none",
score_open:false,
code_review:false
}
}
componentDidMount() {
var homeworkid = this.props.match.params.homeworkid;
// console.log(homeworkid)
this.Gettitleinformation(homeworkid)
let query = this.props.location.pathname;
const type = query.split('/');
this.setState({
shixuntypes:type[3]
})
}
isupdatas=()=>{
var homeworkid = this.props.match.params.homeworkid;
this.Gettitleinformation(homeworkid)
}
//获取题目信息
Gettitleinformation = (homeworkid) => {
// console.log("获取题目信息");
let url = `/homework_commons/${homeworkid}.json`;
axios.get(url).then((result) => {
if (result.status === 200) {
// console.log(url)
// console.log("设置页")
// console.log(JSON.stringify(result))
this.setState({
jobsettingsdata:result,
...result.data
})
}
}).catch((error) => {
console.log(error)
})
}
jobdescriptiondisplaybj =()=>{
this.setState({
Showupdateinstructions:true,
})
}
//统一设置
onChange = (e) => {
this.setState({
unifiedsetting: e.target.checked,
})
// console.log(e.target.checked);
}
//立即发布
homeworkstart = () => {
let homeworkid=this.props.match.params.homeworkid;
let url="/homework_commons/"+homeworkid+"/publish_groups.json";
axios.get(url).then((response) => {
if (response.status === 200) {
let starttime = this.props.getNowFormatDates(1);
let endtime = this.props.getNowFormatDates(2);
this.setState({
modalname: "立即发布",
modaltype: response.data.course_groups === null || response.data.course_groups.length === 0 ? 2 : 1,
visible: true,
Topval: "本操作只对“未发布”的对象生效",
Botvalleft: "暂不发布",
Botval: "则通过后续手动设置,定时发布",
starttime: "发布时间:" + starttime,
endtime: "截止时间:" + endtime,
Cancelname: "暂不发布",
Savesname: "立即发布",
Cancel: this.homeworkhide,
Saves: this.homeworkstartend,
course_groups: response.data.course_groups,
starttimes:starttime,
typs:"start",
})
}
}).catch((error) => {
console.log(error)
});
}
//立即截止
homeworkends = () => {
let homeworkid=this.props.match.params.homeworkid;
let url="/homework_commons/"+homeworkid+"/end_groups.json";
axios.get(url).then((response) => {
if (response.status === 200) {
this.setState({})
this.setState({
modalname: "立即截止",
modaltype: response.data.course_groups === null || response.data.course_groups.length === 0 ? 2 : 1,
visible: true,
Topval: "本操作只对“提交中”的对象生效",
Botvalleft: "暂不截止",
Botval: "则将根据已设置的截止时间,定时截止",
Cancelname: "暂不截止",
Savesname: "立即截止",
Cancel: this.homeworkhide,
Saves: this.coursetaskend,
starttime: undefined,
endtime: undefined,
course_groups: response.data.course_groups,
typs:"end",
})
}
}).catch((error) => {
console.log(error)
});
}
// 立即发布
homeworkstartend = (ds,endtime) => {
var homeworkid = this.props.match.params.homeworkid;
let {course_groupslist} = this.state;
let coursesId = this.props.match.params.coursesId;
let url = "/courses/" + coursesId + "/homework_commons/publish_homework.json";
axios.post(url, {
homework_ids: [homeworkid],
group_ids: course_groupslist,
end_time:endtime,
}).then((result) => {
if (result.status === 200) {
if (result.data.status === 0) {
notification.open({
message:"提示",
description:result.data.message
});
this.homeworkhide();
}
}
}).catch((error) => {
console.log(error);
})
}
//立即截止确定按钮
coursetaskend = () => {
var homeworkid = this.props.match.params.homeworkid;
let {course_groupslist} = this.state;
const cid = this.props.match.params.coursesId;
let url = "/courses/" + cid + "/homework_commons/end_homework.json";
axios.post(url, {
group_ids: course_groupslist,
homework_ids: [homeworkid],
})
.then((response) => {
if (response.data.status == 0) {
notification.open({
message:"提示",
description:response.data.message
});
this.homeworkhide()
}
})
.catch(function (error) {
console.log(error);
});
}
ReleaseNotes=(explanations)=>{
var homeworkid = this.props.match.params.homeworkid;
let url=`/homework_commons/${homeworkid}/update_explanation.json`;
axios.post((url), {
explanation: explanations
})
.then((result)=>{
if(result){
this.props.showNotification(`${result.data.message}`);
this.setState({
Showupdateinstructions:false
})
var homeworkid = this.props.match.params.homeworkid;
// console.log(homeworkid)
this.Gettitleinformation(homeworkid)
}
}).catch((error)=>{
console.log(error);
})
}
NOReleaseNotes=()=>{
this.setState({
Showupdateinstructions:false,
})
}
workshowmodel=()=>{
this.setState({
showmodel:true
})
}
hideshowmodel=()=>{
this.setState({
showmodel:false
})
}
homeworkhide=()=>{
this.isupdatas()
this.setState({
modalname:undefined,
modaltype:undefined,
visible:false,
Topval:undefined,
Topvalright:undefined,
Botvalleft:undefined,
Botval:undefined,
starttime:undefined,
endtime:undefined,
Cancelname:undefined,
Savesname:undefined,
Cancel:undefined,
Saves:undefined,
StudentList_value:undefined,
addname:undefined,
addnametype:false,
addnametab:undefined,
typs:undefined,
starttimes:undefined,
})
}
getcourse_groupslist=(id)=>{
this.setState({
course_groupslist:id
})
}
render() {
const dateFormat = 'YYYY-MM-DD HH:mm:ss';
let {starttimetype, endtimetype, latetimetype, allowlate, latepenaltytype, jobsettingsdata,score_open,Showupdateinstructions
, homework_id} = this.state;
const radioStyle = {
display: 'block',
height: '30px',
lineHeight: '30px',
};
const formItemLayout = {
labelCol: {span: 6},
wrapperCol: {span: 14},
};
let coursesId = this.props.match.params.coursesId;
return (
<div className="newMain clearfix ">
{this.state.showmodel===true?<ShixunWorkModal
{...this.props}
visible={this.state.showmodel}
modalname={"代码查重"}
data={[]}
issCancel={()=>this.hideshowmodel()}
updatas={()=>this.isupdatas()}
/>:""}
{/*立即发布*/}
<HomeworkModal
modaltype={this.state.modaltype}
modalname={this.state.modalname}
visible={this.state.visible}
Topval={this.state.Topval}
Topvalright={this.state.Topvalright}
Botvalleft={this.state.Botvalleft}
Botval={this.state.Botval}
starttime={this.state.starttime}
endtime={this.state.endtime}
Cancelname={this.state.Cancelname}
Savesname={this.state.Savesname}
Cancel={this.state.Cancel}
Saves={this.state.Saves}
course_groups={this.state.course_groups}
getcourse_groupslist={(id)=>this.getcourse_groupslist(id)}
starttimes={this.state.starttimes}
typs={this.state.typs}
/>
<div className={"educontent mb20"}>
<div className="educontent mb30">
<p className="clearfix mb20 mt10">
<ActionBtn className=" btn colorgrey fl hovercolorblue "
to={"/courses/"+this.props.match.params.coursesId+"/students"}>{jobsettingsdata === undefined ? "" : jobsettingsdata.data.course_name}</ActionBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<ActionBtn
className=" btn colorgrey fl hovercolorblue "
to={`/courses/${this.props.match.params.coursesId}/${this.state.shixuntypes}/${jobsettingsdata===undefined?"":jobsettingsdata.data.category.category_id}`}>{jobsettingsdata === undefined ? "" : jobsettingsdata.data.category.category_name}</ActionBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<WordsBtn className="fl">作业详情</WordsBtn>
</p>
</div>
<div className="educontent mb30">
<p className=" fl color-black summaryname">
{jobsettingsdata === undefined ? "" : jobsettingsdata.data.homework_name}
</p>
<CoursesListType
typelist={jobsettingsdata === undefined ? [""] : jobsettingsdata.data.homework_status}
/>
<a className="color-grey-9 fr font-16 summaryname ml20 mr20" href={`/courses/${this.state.props.match.params.coursesId}/${this.state.shixuntypes}/${jobsettingsdata === undefined ? "" :jobsettingsdata.data.category.category_id}`}>返回</a>
<a className="color-grey-9 fr font-16 mr20" href={`/shixuns/${jobsettingsdata===undefined?"":jobsettingsdata.data.shixun_identifier}/challenges`} target={"_blank"}>实训详情</a>
</div>
<div className="edu-back-white">
<div className="stud-class-set bor-bottom-greyE ">
<div className=" clearfix edu-back-white poll_list">
{this.props.isAdmin() === true? <Link
to={`/courses/${this.state.props.match.params.coursesId}/${this.state.shixuntypes}/${this.state.props.match.params.homeworkid}/list`}>作品列表</Link>:
<Link to={`/courses/${this.state.props.match.params.coursesId}/${this.state.shixuntypes}/${this.state.props.match.params.homeworkid}/openlist`}>作品列表</Link>
// :<Link
// to={`/courses/${this.state.props.match.params.coursesId}/${this.state.shixuntypes}/${this.state.props.match.params.homeworkid}/noopenlist`}>作品列表(学生完成)</Link>
}
<Link
className="active"
to={`/courses/${this.state.props.match.params.coursesId}/${this.state.shixuntypes}/${this.state.props.match.params.homeworkid}/questions`}>作业问答</Link>
{this.props.isAdmin()?
this.state.code_review===false?"":<Link
// to={`/courses/${this.state.props.match.params.coursesId}/${this.state.props.match.params.homeworkid}/student_work`}
to={`/courses/${this.state.props.match.params.coursesId}/${this.state.shixuntypes}/${this.state.props.match.params.homeworkid}/student_work`}>
代码查重</Link>:""}
<Link
to={`/courses/${this.state.props.match.params.coursesId}/${this.state.shixuntypes}/${this.state.props.match.params.homeworkid}/settings`}
>设置</Link>
{/*<a className="fr color-blue font-16" href={`/api/homework_commons/${this.props.match.params.coursesId}/works_list.xls`}>导出成绩</a>*/}
{this.props.isAdmin() ? <a
className="fr color-blue font-16"
href={`/api/homework_commons/${this.props.match.params.coursesId}/works_list.xls`}
>导出</a> : ""}
{this.props.isAdmin() ?jobsettingsdata&&jobsettingsdata.data.end_immediately===true?
<a className="fr color-blue font-16" onClick={this.homeworkends}>立即截止</a>:"" : ""}
{this.props.isAdmin() ?jobsettingsdata&&jobsettingsdata.data.publish_immediately===true?
<a className="fr color-blue font-16" onClick={this.homeworkstart}>立即发布</a> :"": ""}
{this.props.isAdmin()?
this.state.code_review===true?
<a className="fr color-blue font-16" onClick={this.workshowmodel}>代码查重</a>
:"":""}
</div>
</div>
{jobsettingsdata === undefined|| jobsettingsdata.data.description===null? "" : <div className=" clearfix edu-back-white poll_list mt20 mr20 ">
<span>
<div className={"font-16 color-dark fl pl20 "}>
<div dangerouslySetInnerHTML={{__html: markdownToHTML(jobsettingsdata.data.description).replace(/▁/g,"▁▁▁")}}></div>
{/* /!*{}}></div>
{/* /!*{jobsettingsdata.data.description}*!/*/}
{/* <textarea>*/}
{/* {*/}
{/* this.state.namestring*/}
{/* }*/}
{/* </textarea>*/}
</div>
{/* <div className="justify break_full_word new_li markdown-body"*/}
{/* id="challenge_editorMd_description">*/}
{/* <p id="ReactMarkdown" style={{overflow:'hidden'}}>*/}
{/* /!*{ChallengesDataList === undefined ? "" :ChallengesDataList.description===null?""*!/*/}
{/* <textarea className="w200">*/}
{/* {jobsettingsdata.data.description}*/}
{/* </textarea>*/}
{/* /!*}*!/*/}
{/* </p>*/}
{/*</div>*/}
</span>
</div>}
<div className=" clearfix edu-back-white poll_list pd10">
<div className="font-16 color-green fl pl20 mt10">作业说明:</div>
{
this.props.isAdmin()&&this.props.isAdmin()===true ?
<Tooltip placement="top" title="编辑"><i
className="color-green font-18 iconfont icon-bianjidaibeijing fr pr20 " onClick={()=>this.jobdescriptiondisplaybj()} ></i></Tooltip>
:""
}
</div>
<span>
<div className="font-16 color-dark break_word flex1 pl20 " style={{"padding":"10px 10px 10px 20px"}}>
{jobsettingsdata === undefined ? "" : jobsettingsdata === null ? "" : jobsettingsdata === "null" ? "" :
<div dangerouslySetInnerHTML={{__html:markdownToHTML(jobsettingsdata.data.explanation).replace(/▁/g,"▁▁▁")}}></div>
}
</div>
</span>
</div>
{/* <style>*/}
{/* {*/}
{/* `.mockInputWrapper {*/}
{/* display: flex;*/}
{/* padding: 30px 20px 30px 20px;*/}
{/*}`*/}
{/* }*/}
{/* </style>*/}
{/* <div className="edu-back-white mb20 graduateTopic course-message"> */}
{/*<GraduateTopicReply memo={{id: graduation_topic_id, user_id: topicInfo && topicInfo.user_id}} course_id={course_id} {...this.props}></GraduateTopicReply>*/}
{/* </div> */}
{ Showupdateinstructions&& Showupdateinstructions === true?
<div >
<Homeworddescription {...this.props} ReleaseNotes={this.ReleaseNotes} NOReleaseNotes ={this.NOReleaseNotes} ></Homeworddescription>
</div>
:""
}
{ homework_id && <CommonReply
memo={{
id: homework_id,
}}
course_id={coursesId} {...this.props}
apiRouteName={'homework_commons'}
jour_type={'HomeworkCommon'}
></CommonReply>
}
</div>
</div>
)
}
}
export default Workquestionandanswer;

@ -106,6 +106,7 @@
position: absolute;
top: -30px;
left: -30px;
left: -24px;
z-index: 999;
}
@ -236,6 +237,8 @@
/* line-height: 14px; */
width: 100%;
word-wrap: break-word;
margin-bottom: 4px;
margin-top: 4px;
}
.childComment .break_word_comments{
line-height: 22px;

@ -24,6 +24,22 @@
.panel-comment_item .editor__resize {
transform: translateX(-2%);
position: absolute;
width: 120px;
height: 4px;
left: 54%;
transform: translateX(-50%);
margin-top: 2px;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
cursor: row-resize;
text-indent: 110%;
white-space: nowrap;
overflow: hidden;
text-transform: capitalize;
box-sizing: border-box;
}
/*帖子回復按鈕*/
@ -31,6 +47,11 @@ a.commentsbtn.task-btn-blue {
background: #4CACFF !important;
}
.commentTab a.commentsbtn.task-btn-blue {
margin-right: 22px;
margin-top: 6px;
}
.editormd-grid-table-row a.editormd-emoji-btn.selected {
border-bottom: 2px solid #4CACFF!important;

@ -96,7 +96,7 @@ class CommentItemMDEditor extends Component {
}
render() {
const { match, history, item, user } = this.props
const { match, history, item, user, buttonText } = this.props
if (!item) {
return <div></div>
}
@ -111,7 +111,7 @@ class CommentItemMDEditor extends Component {
</a>
</div>
<div id={`reply_message_${item.id}`} className="reply_to_message commentItemMDEditor"
style={{ paddingTop: '0px', paddingBottom: '20px', marginTop: '36px' }}
style={{ paddingTop: '0px', paddingBottom: '0px', marginTop: '36px' }}
>
<div id={`reply_message_editorMd_${item.id}`} className="editorMD" style={{ marginBottom: '0px'}}>
<textarea style={{'display': 'none'}}>
@ -119,11 +119,14 @@ class CommentItemMDEditor extends Component {
</div>
<div className="editor__resize" href="javascript:void(0);" style={{display: ''}}>调整高度</div>
<a id={`commitBtn_${item.id}`} href="javascript:void(0)"
onClick={this.onCommit} style={{ marginRight: '44px' }}
className="commentsbtn task-btn task-btn-blue fr " style={{display: ''}}>
发送
</a>
<div class="clearfix">
<a id={`commitBtn_${item.id}`} href="javascript:void(0)"
onClick={this.onCommit} style={{ marginRight: '44px' }}
className="commentsbtn task-btn task-btn-blue fr " style={{display: ''}}>
{ buttonText || '发送'}
</a>
</div>
</div>
</div>
);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,88 +1,89 @@
import React, { Component } from 'react';
import Dialog, {
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
} from 'material-ui/Dialog';
import Button from 'material-ui/Button';
import { FormControl, FormHelperText } from 'material-ui/Form';
import Input, { InputLabel } from 'material-ui/Input';
class RewardDialog extends Component {
constructor(props) {
super(props)
this.handleGoldRewardDialogClose = this.handleGoldRewardDialogClose.bind(this)
this.state = {
// goldRewardDialogOpen: false,
goldRewardInput: '',
}
}
showGoldRewardDialog(comment, childComment) {
if (comment.admin === true) {
this.comment = comment;
this.childComment = childComment;
this.setState({goldRewardDialogOpen: true})
}
}
handleGoldRewardDialogClose() {
this.props.setRewardDialogVisible(false)
}
onGoldRewardDialogOkBtnClick() {
console.log('onGoldRewardDialogOkBtnClick')
const { goldRewardInput } = this.state;
if (!goldRewardInput || goldRewardInput === '0' || goldRewardInput.indexOf('-') !== -1) {
this.setState({ goldRewardInputError: true})
return;
} else {
this.props.setRewardDialogVisible( false )
this.props.rewardCode(goldRewardInput)
}
}
onGoldRewardInputChange(event) {
this.setState({ goldRewardInput: event.target.value, goldRewardInputError: false });
}
render() {
const { goldRewardDialogOpen } = this.props;
const { goldRewardInputError } = this.state;
const goldRewardInputErrorObj = goldRewardInputError ? {'error': 'error'} : {}
return (
<Dialog
open={goldRewardDialogOpen}
onClose={this.handleGoldRewardDialogClose}
>
<DialogTitle id="alert-dialog-title">{"奖励设置"}</DialogTitle>
<DialogContent>
<FormControl { ...goldRewardInputErrorObj } aria-describedby="name-error-text">
<InputLabel htmlFor="goldReward">请输入奖励的金币数量</InputLabel>
<Input id="goldReward" type="number" value={this.state.goldRewardInput} onChange={(e) => this.onGoldRewardInputChange(e)} />
{ goldRewardInputError ? <FormHelperText id="name-error-text">奖励金币不能为空或负数</FormHelperText> : ''}
</FormControl>
{/*<DialogContentText id="alert-dialog-description" style={{textAlign: 'center'}}> </DialogContentText>*/}
</DialogContent>
<DialogActions>
<Button onClick={this.handleGoldRewardDialogClose} color="primary">
取消
</Button>
<Button variant="raised"
onClick={() => this.onGoldRewardDialogOkBtnClick() } color="primary" autoFocus>
确定
</Button>
</DialogActions>
</Dialog>
);
}
}
export default RewardDialog;
import React, { Component } from 'react';
import Dialog, {
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
} from 'material-ui/Dialog';
import Button from 'material-ui/Button';
import { FormControl, FormHelperText } from 'material-ui/Form';
import Input, { InputLabel } from 'material-ui/Input';
class RewardDialog extends Component {
constructor(props) {
super(props)
this.handleGoldRewardDialogClose = this.handleGoldRewardDialogClose.bind(this)
this.state = {
// goldRewardDialogOpen: false,
goldRewardInput: '',
}
}
showGoldRewardDialog(comment, childComment) {
if (comment.admin === true) {
this.comment = comment;
this.childComment = childComment;
this.setState({goldRewardDialogOpen: true})
}
}
handleGoldRewardDialogClose() {
this.props.setRewardDialogVisible(false)
}
onGoldRewardDialogOkBtnClick() {
console.log('onGoldRewardDialogOkBtnClick')
const { goldRewardInput } = this.state;
if (!goldRewardInput || goldRewardInput === '0' || goldRewardInput.indexOf('-') !== -1) {
this.setState({ goldRewardInputError: true})
return;
} else {
this.props.setRewardDialogVisible( false )
this.props.rewardCode(goldRewardInput)
}
}
onGoldRewardInputChange(event) {
this.setState({ goldRewardInput: event.target.value, goldRewardInputError: false });
}
render() {
const { goldRewardDialogOpen } = this.props;
const { goldRewardInputError } = this.state;
const goldRewardInputErrorObj = goldRewardInputError ? {'error': 'error'} : {}
return (
<Dialog
open={goldRewardDialogOpen}
disableEscapeKeyDown={true}
onClose={this.handleGoldRewardDialogClose}
>
<DialogTitle id="alert-dialog-title">{"奖励设置"}</DialogTitle>
<DialogContent>
<FormControl { ...goldRewardInputErrorObj } aria-describedby="name-error-text">
<InputLabel htmlFor="goldReward">请输入奖励的金币数量</InputLabel>
<Input id="goldReward" type="number" value={this.state.goldRewardInput} onChange={(e) => this.onGoldRewardInputChange(e)} />
{ goldRewardInputError ? <FormHelperText id="name-error-text">奖励金币不能为空或负数</FormHelperText> : ''}
</FormControl>
{/*<DialogContentText id="alert-dialog-description" style={{textAlign: 'center'}}> </DialogContentText>*/}
</DialogContent>
<DialogActions>
<Button onClick={this.handleGoldRewardDialogClose} color="primary">
取消
</Button>
<Button variant="raised"
onClick={() => this.onGoldRewardDialogOkBtnClick() } color="primary" autoFocus>
确定
</Button>
</DialogActions>
</Dialog>
);
}
}
export default RewardDialog;

@ -6,6 +6,8 @@ import Loading from '../../Loading';
import axios from 'axios';
import { TPMIndexHOC } from '../tpm/TPMIndexHOC';
import { CNotificationHOC } from './common/CNotificationHOC'
import {ImageLayerOfCommentHOC} from '../page/layers/ImageLayerOfCommentHOC'
import "./css/Courses.css"
//引入对应跳转的组件
@ -71,6 +73,10 @@ const WrappedCoursesNewApp= Loadable({
loading: Loading,
})
const ShixunHomeworkPage =Loadable({
loader: () => import('./shixunHomework/ShixunHomeworkPage'),
loading: Loading,
})
//实训作业
const ShixunHomework= Loadable({
loader: () => import('./shixunHomework/shixunHomework'),
@ -83,17 +89,12 @@ const Trainingjobsetting= Loadable({
loading: Loading,
})
//实训作业列表教师
const Listofworks= Loadable({
loader: () => import('./shixunHomework/Listofworks'),
loading: Loading,
})
// //实训作业列表学生未完成的
// const Studentnotcompleted= Loadable({
// loader: () => import('./shixunHomework/Listofworksstudentone'),
// loading: Loading,
// })
//实训作业列表完成的
const Studenthascompleted= Loadable({
loader: () => import('./shixunHomework/Listofworksstudentone'),
@ -442,16 +443,21 @@ class CoursesIndex extends Component{
<Switch {...this.props}>
{/* 资源列表页 */}
<Route path="/courses/:coursesId/file/:Id"
<Route path="/courses/:coursesId/file/:Id" exact
render={
(props) => (<Resourcelist {...this.props} {...props} {...this.state} />)
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} />)
}
></Route>
<Route path="/courses/:coursesId/files/:main_id"
render={
(props) => (<Resourcelist {...this.props} {...props} {...this.state} />)
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} />)
}
></Route>
<Route exact path="/courses/:coursesId/boards/:boardId"
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} />)
}
></Route>
{/*课堂讨论*/}
<Route path="/courses/:coursesId/boards/:boardId"
render={
@ -486,9 +492,9 @@ class CoursesIndex extends Component{
></Route>
{/* 毕设选题列表 */}
<Route path="/courses/:coursesId/graduation_topics/:Id"
<Route path="/courses/:coursesId/graduation_topics/:Id" exact
render={
(props) => (<GraduationTopics {...this.props} {...props} {...this.state} />)
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} />)
}></Route>
@ -554,9 +560,9 @@ class CoursesIndex extends Component{
{/* 毕设任务列表 https://www.trustie.net/issues/19981 */}
<Route path="/courses/:coursesId/graduation_tasks/:Id"
<Route path="/courses/:coursesId/graduation_tasks/:Id" exact
render={
(props) => (<GraduationTasks {...this.props} {...props} {...this.state} />)
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} />)
}
></Route>
@ -591,16 +597,29 @@ class CoursesIndex extends Component{
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 普通作业 */}
<Route path="/courses/:coursesId/common_homeworks/:category_id" exact
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 分组作业 */}
<Route path="/courses/:coursesId/group_homeworks/:category_id" exact
render={
(props) => (<ListPageIndex {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 普通作业 */}
<Route path="/courses/:coursesId/common_homeworks"
<Route path="/courses/:coursesId/common_homeworks/" strict
render={
(props) => (<CommonWork {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 分组作业 */}
<Route path="/courses/:coursesId/group_homeworks"
<Route path="/courses/:coursesId/group_homeworks/" strict
render={
(props) => (<GroupWork {...this.props} {...props} {...this.state} />)
}
@ -700,12 +719,12 @@ class CoursesIndex extends Component{
{/*实训查重列表*/}
<Route path="/courses/:coursesId/shixun_homeworks/:homeworkid/student_work"
render={
(props) => (<ShixunStudentWork {...this.props} {...props} {...this.state} />)
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} />)
}
></Route>
<Route path="/courses/:coursesId/shixun_homework/:homeworkid/student_work"
render={
(props) => (<ShixunStudentWork {...this.props} {...props} {...this.state} />)
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} />)
}
></Route>
@ -724,32 +743,32 @@ class CoursesIndex extends Component{
{/*教师列表*/}
<Route path="/courses/:coursesId/shixun_homeworks/:homeworkid/list"
render={
(props) => (<Listofworks {...this.props} {...props} {...this.state} />)
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} />)
}
></Route>
<Route path="/courses/:coursesId/shixun_homework/:homeworkid/list"
render={
(props) => (<Listofworks {...this.props} {...props} {...this.state} />)
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} />)
}
></Route>
{/*实训作业page*/}
<Route path="/courses/:coursesId/shixun_homeworks/:homeworkid/Page"
render={
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} />)
}
></Route>
{/*实训作业设置*/}
<Route path="/courses/:coursesId/shixun_homeworks/:homeworkid/settings"
render={
(props) => (<Trainingjobsetting {...this.props} {...props} {...this.state} />)
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} />)
}
></Route>
<Route path="/courses/:coursesId/shixun_homework/:homeworkid/settings"
render={
(props) => (<Trainingjobsetting {...this.props} {...props} {...this.state} />)
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} />)
}
></Route>
{/*/!*实训作品列表教师*!/*/}
{/*<Route path="/courses/:coursesId/:homeworkid/list"*/}
{/* render={*/}
{/* (props) => (<Listofworks {...this.props} {...props} {...this.state} />)*/}
{/* }*/}
{/*></Route>*/}
{/*提交总结*/}
<Route path="/courses/:coursesId/shixun_homeworks/:id/commitsummary/:homeworkid"
render={
@ -770,13 +789,13 @@ class CoursesIndex extends Component{
{/*实训学生作品列表已公布*/}
<Route path="/courses/:coursesId/shixun_homeworks/:homeworkid/openlist"
render={
(props) => (<Studenthascompleted {...this.props} {...props} {...this.state} />)
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} />)
}
></Route>
{/*实训学生作品列表已公布*/}
<Route path="/courses/:coursesId/shixun_homework/:homeworkid/openlist"
render={
(props) => (<Studenthascompleted {...this.props} {...props} {...this.state} />)
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} />)
}
></Route>
@ -785,14 +804,14 @@ class CoursesIndex extends Component{
{/*实训作业问答主目录*/}
<Route path="/courses/:coursesId/shixun_homeworks/:homeworkid/questions"
render={
(props) => (<Workquestionandanswer {...this.props} {...props} {...this.state} />)
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} />)
}
></Route>
{/*实训作业问答子目录*/}
<Route path="/courses/:coursesId/shixun_homework/:homeworkid/questions"
render={
(props) => (<Workquestionandanswer {...this.props} {...props} {...this.state} />)
(props) => (<ShixunHomeworkPage {...this.props} {...props} {...this.state} />)
}
></Route>
@ -844,4 +863,4 @@ class CoursesIndex extends Component{
}
}
export default CNotificationHOC() ( SnackbarHOC() ( TPMIndexHOC(CoursesIndex) ));
export default ImageLayerOfCommentHOC({imgSelector: '.imageLayerParent img, .imageLayerParent .imageTarget', parentSelector: '.newMain'}) (CNotificationHOC() ( SnackbarHOC() ( TPMIndexHOC(CoursesIndex) )));

@ -56,6 +56,14 @@ const ShixunHomework= Loadable({
loading: Loading,
})
const GraduationTopics= Loadable({
loader: () => import('./graduation/topics'),
loading: Loading,
})
const GraduationTasks= Loadable({
loader: () => import('./graduation/tasks'),
loading: Loading,
})
class ListPageIndex extends Component{
constructor(props) {
super(props)
@ -169,12 +177,34 @@ class ListPageIndex extends Component{
}
></Route>
<Route path="/courses/:coursesId/files/:main_id"
render={
(props) => (<Resourcelist {...this.props} {...props} {...this.state} />)
}
></Route>
<Route path="/courses/:coursesId/file/:Id"
render={
(props) => (<Resourcelist {...this.props} {...props} {...this.state} />)
}
></Route>
<Route path="/courses/:coursesId/graduation_topics/:Id"
render={
(props) => (<GraduationTopics {...this.props} {...props} {...this.state} />)
}></Route>
<Route path="/courses/:coursesId/graduation_tasks/:Id"
render={
(props) => (<GraduationTasks {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 默认 */}
<Route path="/courses/:coursesId"
render={
(props) => (<StudentsList {...this.props} {...props} {...this.state} />)
}
></Route>
</Switch>
</div>
</div>

@ -5,9 +5,10 @@ import {Link} from 'react-router-dom';
import {getImageUrl} from 'educoder';
import axios from 'axios'
import {getUrl} from 'educoder';
import moment from 'moment';
import moment from 'moment'
import CoursesListType from '../coursesPublic/CoursesListType';
import Showoldfiles from "../coursesPublic/Showoldfiles";
import Modals from '../../modals/Modals';
class Fileslistitem extends Component{
constructor(props){
@ -25,45 +26,32 @@ class Fileslistitem extends Component{
})
this.props.Settingtypes(discussMessage.id)
}
showfiles=()=>{
showfiles=(value)=>{
let {discussMessage,coursesId}=this.props
let file_id=discussMessage.id
let url="/files/"+file_id+"/histories.json"
axios.get(url,{
params:{
course_id:coursesId
}
},
}).then((result)=>{
if(result.data.attachment_histories.length===0){
let link = document.createElement('a');
// link.href = window.URL.createObjectURL(new Blob([result.data.url]));
link.href = result.data.url;
link.download = result.data.title;
//兼容火狐浏览器
document.body.appendChild(link);
let evt = document.createEvent("MouseEvents");
evt.initEvent("click", false, false);
link.dispatchEvent(evt);
document.body.removeChild(link);
// // window.location.href=result.data.url;
// let url = window.URL.createObjectURL(new Blob([result.data.url]));
// let link = document.createElement('a');
// link.style.display = 'none';
// link.href = url;
// link.setAttribute('download',result.data.title);
// document.body.appendChild(link);
// link.click();
}else{
let link = document.createElement('a');
document.body.appendChild(link);
link.href = result.data.url;
link.download = result.data.title;
//兼容火狐浏览器
let evt = document.createEvent("MouseEvents");
evt.initEvent("click", false, false);
link.dispatchEvent(evt);
document.body.removeChild(link);
}else{
this.setState({
Showoldfiles:true,
allfiles:result.data
})
}
// console.log(result)
}).catch((error)=>{
console.log(error)
})
@ -74,7 +62,68 @@ class Fileslistitem extends Component{
Showoldfiles:false,
})
}
render(){
onDelete = (id) => {
this.setState({
Modalstype:true,
Modalstopval:"是否确认删除?",
ModalCancel:this.cancelmodel,
ModalSave:()=>this.savedelete(id),
})
}
cancelmodel=()=>{
this.setState({
Modalstype:false,
Loadtype:false,
Modalstopval:"",
ModalCancel:"",
ModalSave:"",
checkBoxValues:[],
})
}
savedelete=(id)=>{
this.setState({
Modalstype:false,
})
const cid = this.props.match.params.coursesId
const url = `/files/bulk_delete.json`;
axios.delete(url, { data: {
course_id:cid,
ids: [id],
}})
.then((response) => {
if (response.data.status == 0) {
//Modalstopval:response.data.message,
this.props.updatafiledfun()
this.setState({
// Modalstype:true,
// Modalstopval:"删除成功",
ModalsBottomval:"",
// ModalSave:this.cancelmodel,
// Loadtype:true,
checkBoxValues:[],
checkAllValue:false
})
this.props.showNotification("删除成功");
}
})
.catch(function (error) {
console.log(error);
});
}
render(){
const { checkBox,
discussMessage,
@ -84,6 +133,16 @@ class Fileslistitem extends Component{
return(
<div className="graduateTopicList boardsList">
{/*提示*/}
{this.state.Modalstype&&this.state.Modalstype===true?<Modals
modalsType={this.state.Modalstype}
modalsTopval={this.state.Modalstopval}
modalCancel={this.state.ModalCancel}
modalSave={this.state.ModalSave}
modalsBottomval={this.state.ModalsBottomval}
loadtype={this.state.Loadtype}
/>:""}
<Showoldfiles
visible={this.state.Showoldfiles}
allfiles={this.state.allfiles}
@ -97,20 +156,21 @@ class Fileslistitem extends Component{
margin-top: 0px;
}
`}</style>
{checkBox}
<style>{`
.boardsList .panel-list-img {
width: 50px;
height: 50px;
}
`}</style>
<a href={"/users/"+this.props.user.login} alt="用户"
style={{"width": "50px", "height": "50px", "display": "block", margin: "0 10px"}}>
<img
alt="1?1529221779" className="panel-list-img mr15" height="50"
src={getImageUrl("images/"+discussMessage.author.image_url)} width="50"
></img>
</a>
{/*<a href={"/users/"+this.props.user.login} alt="用户"*/}
{/*style={{"width": "50px", "height": "50px", "display": "block", margin: "0 10px"}}>*/}
{/*<img*/}
{/*alt="1?1529221779" className="panel-list-img mr15" height="50"*/}
{/*src={getImageUrl("images/"+discussMessage.author.image_url)} width="50"*/}
{/*></img>*/}
{/*</a>*/}
<style>{`
.boardsList .contentSection{
@ -125,44 +185,42 @@ class Fileslistitem extends Component{
`}</style>
<div className="clearfix ds pr contentSection">
<h6>
{
this.props.isAdmin ? <a
// href={"/courses/" + coursesId + "/graduation/graduation_tasks/" + categoryid + "/" + taskid + "/list"}
onClick={this.showfiles}
title={discussMessage.title}
className="fl mt3 font-16 font-bd color-dark maxwidth580">{discussMessage.title}</a> : ""
}
<span className="fl mr12 mt3">
{checkBox}
</span>
{
this.props.isAdmin ? <a
// href={"/courses/" + coursesId + "/graduation/graduation_tasks/" + categoryid + "/" + taskid + "/list"}
onClick={()=>this.showfiles(discussMessage.title)}
title={discussMessage.title}
className="fl mt3 font-16 font-bd color-dark maxwidth580">{discussMessage.title}</a> : ""
}
{
this.props.isStudent? <a
onClick={this.showfiles}
onClick={()=>this.showfiles(discussMessage.title)}
title={discussMessage.title}
className="fl mt3 font-16 font-bd color-dark maxwidth580">{discussMessage.title}</a> :""
}
{
this.props.isNotMember ? discussMessage.is_lock === true ?
this.props.isNotMember===true?
discussMessage.is_lock === true ?
<span className="fl mt3 font-16 font-bd color-dark maxwidth580">{discussMessage.title}</span>
:
<a
onClick={this.showfiles}
// href={"/courses/" + coursesId + "/graduation/graduation_tasks/" + categoryid + "/" + taskid + "/list"}
className="fl mt3 font-16 font-bd color-dark maxwidth580">{discussMessage.title}</a> :""
}
:<a
onClick={()=>this.showfiles(discussMessage.title)}
title={discussMessage.title}
className="fl mt3 font-16 font-bd color-dark maxwidth580">{discussMessage.title}</a>:""
}
{
discussMessage.is_lock === true ?
<Tooltip title={ this.props.isNotMember?"私有属性,非课堂成员不能访问":"私有属性"} placement="bottom">
<Tooltip title={ this.props.isNotMember===true?"私有属性,非课堂成员不能访问":"私有属性"} placement="bottom">
<i className="iconfont icon-guansuo color-grey-c ml10 font-16 fl mt4"></i>
</Tooltip>
:""
}
{discussMessage.is_publish===true?"":<CoursesListType typelist={["未发布"]} typesylename={""}/>}
{discussMessage.is_publish===false?<CoursesListType typelist={["未发布"]} typesylename={""}/>:""}
</h6>
<style>
{
@ -176,39 +234,36 @@ class Fileslistitem extends Component{
}
</style>
{this.props.isAdmin ||this.props.current_user.username===discussMessage.author.name?
<span className={"sttingbox"}>
<WordsBtn style="blue" className="colorblue font-16 mr20 fr">
<a className="btn colorblue"
onClick={()=>this.settingList()}>设置</a>
</WordsBtn>
</span>:""}
<div className="cl"></div>
<style>
{
`
.lightgreybox{
min-width: 260px;
}
.mrf2{
margin-top: -2px;
}
`
}
</style>
{discussMessage.course_groups.length===0?"":
<p className="color-grey panel-lightgrey mt8 fl lightgreybox">
<p className="color-grey panel-lightgrey mt8 fl lightgreybox ml30" style={{width:'100%'}}>
{discussMessage.course_groups.map((item,key)=>{
return(
<div className="mr50">
<span className="mr15 color-dark">{item.course_group_name}</span>
<span className="mr15 color-grey9 fr">发布于 { moment(item.course_group_publish_time).format('YYYY-MM-DD hh:mm')}</span>
<span className="mr15 color-grey9 ">发布于 { moment(item.course_group_publish_time).format('YYYY-MM-DD HH:mm')}</span>
</div>
)
})}
</p>}
<p className="color-grey panel-lightgrey mt8 fl" style={{width:'70%'}}>
<p className="color-grey panel-lightgrey mt8 fl ml30" style={{width:'100%'}}>
<span className="mr50">
<span className="mr15 color-dark">{discussMessage.author.name}</span>
<span className="mr15 color-dark">{discussMessage.author.login}</span>
<span className="mr15 color-grey9">大小 {discussMessage.filesize}</span>
<span className="mr15 color-grey9">下载 {discussMessage.downloads_count}</span>
<span className="mr15 color-grey9">引用 {discussMessage.quotes}</span>
@ -217,19 +272,45 @@ class Fileslistitem extends Component{
{/*{moment(discussMessage.publish_time).fromNow()}*/}
{ discussMessage.publish_time===null?"":
discussMessage.is_publish===true?"":"发布于"}
{ discussMessage.publish_time===null?"":discussMessage.is_publish===true?moment(discussMessage.publish_time).fromNow():moment(discussMessage.publish_time).format('YYYY-MM-DD hh:mm')}
{ discussMessage.publish_time===null?"":discussMessage.is_publish===true?moment(discussMessage.publish_time).fromNow():moment(discussMessage.publish_time).format('YYYY-MM-DD HH:mm')}
</span>
</span>
{this.props.isAdmin?
<span className={"fr mrf2 mr10"}>
<WordsBtn style="blue" className="colorblue font-16 mr20 fr">
<a className="btn colorblue"
onClick={()=>this.settingList()}>设置</a>
</WordsBtn>
</span>:""}
{this.props.isStudent===true&&this.props.current_user.login===discussMessage.author.login?
<span className={"fr mrf2 mr10"}>
<WordsBtn style="blue" className="colorblue font-16 mr20 fr">
<a className="btn colorblue"
onClick={()=>this.settingList()}>设置</a>
</WordsBtn>
<WordsBtn style="blue" className="colorblue font-16 mr20 fr">
<a className="btn colorblue"
onClick={()=>this.onDelete(discussMessage.id)}>删除</a>
</WordsBtn>
</span>:""}
</p>
<p className="color-grey panel-lightgrey mt8 fl" style={{width:'100%'}}>
<span className="mr50">
<span className="mr15 color-dark">资源描述 :</span>
<span className="mr15 color-dark">{discussMessage.description}</span>
{this.props.isAdmin ?<span><i className="iconfont icon-bianjidaibeijing font-22 color-green" onClick={()=>this.settingList()}></i></span>:""}
</span>
<p className="color-grey panel-lightgrey mt8 fl ml30" style={{width:'100%'}}>
<pre className="color-dark">资源描述 :{discussMessage.description===null?"暂无描述":discussMessage.description}</pre>
{/*<span className="mr50">*/}
{/*/!*<span className="mr15 color-dark"></span>*!/*/}
{/*<span className="mr15 color-dark">*/}
{/*</span>*/}
{/*/!*{this.props.isAdmin ?<span><i className="iconfont icon-bianjidaibeijing font-22 color-green" onClick={()=>this.settingList()}></i></span>:""}*!/*/}
{/*</span>*/}
</p>

@ -7,11 +7,12 @@ import Sendtofilesmodal from "../coursesPublic/SendToFilesModal";
import Selectresource from "../coursesPublic/SelectResource";
import Sendresource from "../coursesPublic/sendResource";
import Selectsetting from "../coursesPublic/SelectSetting";
import CourseLayoutcomponent from '../common/CourseLayoutComponent';
import HomeworkModal from "../coursesPublic/HomeworkModal";
import Fileslistitem from './Fileslistitem';
import Titlesearchsection from '../common/titleSearch/TitleSearchSection';
import './style.css';
import '../css/members.css';
import moment from 'moment';
class Fileslists extends Component{
constructor(props){
@ -36,12 +37,15 @@ class Fileslists extends Component{
course_modules:undefined,
has_course_groups:false,
course_is_public:undefined,
isSpin:false
isSpin:false,
course_second_categories:[]
}
}
componentDidMount=()=>{
this.setState({
isSpin:true
isSpin:true,
checkBoxValues:[],
checkAllValue:false
})
if(this.props.match.params.main_id){
this.seactall();
@ -49,21 +53,37 @@ class Fileslists extends Component{
this.seactall(parseInt(this.props.match.params.Id),1)
}
this.updadatalist();
on('updateNavSuccess', this.updateNavSuccess)
on('updateNavSuccess',this.updadatalist)
}
updateNavSuccess=()=>{
this.setState({
isSpin:true
})
if(this.props.match.params.main_id){
this.seactall();
}else if(this.props.match.params.Id){
this.seactall(parseInt(this.props.match.params.Id),1)
}
}
componentDidUpdate = (prevProps) => {
if(prevProps.match.params.main_id != this.props.match.params.main_id){
this.setState({
isSpin:true
})
this.setState({
isSpin:true,
checkBoxValues:[],
checkAllValue:false
})
if(this.props.match.params.main_id!=undefined){
this.seactall();
}
}
if(prevProps.match.params.Id != this.props.match.params.Id){
this.setState({
isSpin:true
})
this.setState({
isSpin:true,
checkBoxValues:[],
checkAllValue:false
})
if(this.props.match.params.Id!=undefined){
this.seactall(parseInt(this.props.match.params.Id),1)
}
@ -96,16 +116,33 @@ class Fileslists extends Component{
axios.get(url, {
}).then((response) => {
if(response!=undefined){
if(response.data&&response.data){
let list=response.data.course_modules;
let course_second_categoriess;
list.map((item,key)=>{
course_second_categoriess=item.course_second_categories
})
this.setState({
course_modules:response.data,
has_course_groups:response.data.has_course_groups
has_course_groups:response.data.has_course_groups,
course_second_categories:course_second_categoriess
})
}
}
})
}
updatafiled=()=>{
if(this.props.match.params.main_id){
this.seactall();
}else if(this.props.match.params.Id){
this.seactall(parseInt(this.props.match.params.Id),1)
}
}
seactall=(coursesecondcategoryid,type)=>{
this.setState({
@ -150,8 +187,8 @@ class Fileslists extends Component{
let list=result.data.data;
this.setState({
total_count:list.total_count,
public_count:list.public_count,
private_count:list.private_count,
publish_count:list.publish_count,
unpublish_count:list.unpublish_count,
files:list.files,
filesId:list.id,
name:list.name,
@ -207,7 +244,9 @@ class Fileslists extends Component{
this.props.showNotification("请先在列表中选择要发送的资源");
}else if(value===3){
this.props.showNotification("请先在列表中选择要公开的资源");
}
}else if(value===4){
this.props.showNotification("请先在列表中选择要发布的资源");
}
}
onDelete = () => {
@ -243,8 +282,7 @@ class Fileslists extends Component{
.then((response) => {
if (response.data.status == 0) {
//Modalstopval:response.data.message,
this.seactall();
this.updatafiled()
this.setState({
// Modalstype:true,
// Modalstopval:"删除成功",
@ -255,6 +293,7 @@ class Fileslists extends Component{
checkAllValue:false
})
this.props.showNotification("删除成功");
this.props.updataleftNavfun()
}
})
.catch(function (error) {
@ -297,14 +336,14 @@ class Fileslists extends Component{
this.setState({
Modalstype:true,
Modalstopval:"公开后非课堂成员也可以访问查看",
ModalsBottomval:"是否确认公开?",
Modalstopval:"设为公开后非课堂成员也可以访问查看",
ModalsBottomval:"是否确认设为公开?",
ModalCancel:this.cancelmodel,
ModalSave:this.saveonOpen,
})
}
saveonOpen=()=>{
saveonOpen=()=>{
this.setState({
Modalstype:false,
})
@ -507,8 +546,6 @@ class Fileslists extends Component{
})
.then((response) => {
if (response.data.status == 0) {
this.seactall();
this.setState({
// Modalstype:true,
// Modalstopval:response.data.message,
@ -516,22 +553,95 @@ class Fileslists extends Component{
// ModalSave:this.cancelmodel,
// Loadtype:true,
checkBoxValues:[],
// checkAllValue:false
checkAllValue:false
})
this.props.showNotification('资源移动成功')
this.props.updataleftNavfun()
this.updatafiled()
}
})
}
homeworkstart=()=>{
let selectnum= this.testonSelect();
if(selectnum===true){
this.noSelect(4);
return
}
let starttime= this.props.getNowFormatDates(1);
let endtime=this.props.getNowFormatDates(2);
this.setState({
modalname:"立即发布",
visible:true,
typs:"start",
Topval:"学生将能立即查看和下载发布资源",
// Botvalleft:"暂不发布",
// Botval:`本操作只对"未发布"的分班有效`,
// starttime:"发布时间:"+moment(moment(new Date())).format("YYYY-MM-DD HH:mm"),
// starttimes:starttime,
// endtime:"截止时间:"+endtime,
Cancelname:"暂不发布",
Savesname:"立即发布",
Cancel:this.homeworkhide,
Saves:this.homeworkstartend,
})
}
// 立即发布
homeworkstartend=(ds,endtime)=>{
let {checkBoxValues}=this.state;
let coursesId=this.props.match.params.coursesId;
let url ="/files/bulk_publish.json";
axios.put(url, {
course_id:coursesId,
ids :checkBoxValues,
}).then((result)=>{
if(result.status===200){
this.props.showNotification("发布成功")
this.setState({
checkBoxValues:[],
checkAllValue:false
})
this.homeworkhide()
this.props.updataleftNavfun()
this.updatafiled()
}
}).catch((error)=>{
console.log(error);
})
}
render(){
homeworkhide=()=>{
this.setState({
modalname:undefined,
modaltype:undefined,
visible:false,
Topval:undefined,
Topvalright:undefined,
Botvalleft:undefined,
Botval:undefined,
starttime:undefined,
endtime:undefined,
Cancelname:undefined,
Savesname:undefined,
Cancel:undefined,
Saves:undefined,
StudentList_value:undefined,
addname:undefined,
addnametype:false,
addnametab:undefined,
typs:undefined,
starttimes:undefined,
})
}
render(){
let { searchValue,
checkBoxValues,
checkAllValue,
total_count,
public_count,
private_count,
publish_count,
unpublish_count,
files,
sorttype,
Modalstype,
@ -548,14 +658,35 @@ class Fileslists extends Component{
discussMessageid,
course_modules,
shixunmodal,
course_is_public
course_is_public,
filesId
} = this.state;
let category_id= this.props.match.params.category_id;
return(
<div>
<React.Fragment >
{/*立即发布*/}
{this.state.visible===true?<HomeworkModal
category_id={this.props.match.params.category_id}
modaltype={this.state.modaltype}
modalname={this.state.modalname}
visible={this.state.visible}
Topval={this.state.Topval}
Topvalright={this.state.Topvalright}
Botvalleft={this.state.Botvalleft}
Botval={this.state.Botval}
starttime={this.state.starttime}
starttimes={this.state.starttimes}
typs={this.state.typs}
endtime={this.state.endtime}
Cancelname={this.state.Cancelname}
Savesname={this.state.Savesname}
Cancel={this.state.Cancel}
Saves={this.state.Saves}
course_groups={this.state.course_groups}
/>:""}
{/*发送*/}
<Sendtofilesmodal
@ -628,7 +759,6 @@ class Fileslists extends Component{
has_course_groups={this.state.has_course_groups}
/>:""}
<CourseLayoutcomponent {...this.props}>
<Titlesearchsection
title={name}
searchValue={ searchValue }
@ -647,8 +777,8 @@ class Fileslists extends Component{
secondRowLeft={
<div style={{"display":"inline-block", "marginTop": "22px"}}>
<span> {total_count} 个资源</span>
<span style={{"marginLeft":"16px"}}>公共资源{public_count}</span>
{this.props.isAdmin()||this.props.isStudent()?<span style={{"marginLeft":"16px"}}>私有资源{private_count}</span>:""}
<span style={{"marginLeft":"16px"}}>已发布{publish_count}</span>
<span style={{"marginLeft":"16px"}}>未发布{unpublish_count}</span>
</div>
}
onPressEnter={this.onPressEnter}
@ -656,12 +786,15 @@ class Fileslists extends Component{
showSearchInput={true}
></Titlesearchsection>
<div className="mt20 edu-back-white padding20-30" style={{display:this.props.isAdmin()||this.props.isStudent()?"":"none"}}>
{this.props.isAdmin()? <div className="mt20 edu-back-white padding20-30" style={{display:this.props.isAdmin()||this.props.isStudent()?"":"none"}}>
<div className="clearfix">
{this.props.isAdmin()? <Checkbox className="fl" onChange={this.onCheckAll} checked={checkAllValue}>已选 {checkBoxValues.length} </Checkbox>:""}
<div className="studentList_operation_ul">
{this.props.isAdmin()?<li className="li_line"><a href="javascript:void(0)" className="color-grey-9" onClick={this.onDelete}>删除</a></li>:""}
{this.props.isAdmin()?<li className="li_line"><a href="javascript:void(0)" className="color-grey-9" onClick={this.onSend}>发送</a></li>:""}
{this.props.isAdmin()?
<li className="li_line"><a href="javascript:void(0)" className="color-grey-9" onClick={this.homeworkstart}>立即发布</a></li>
:""}
{this.props.isAdmin()?course_is_public===true?
<li className="li_line"><a href="javascript:void(0)" className="color-grey-9" onClick={this.onOpen}>设为公开</a></li>
:"":""}
@ -676,31 +809,37 @@ class Fileslists extends Component{
overflow-y: auto;
}
.drop_down_menu li {
overflow: visible;
}
overflow: visible;
}
.courseSecond{
margin-left: 10px;
padding: 10px;
}
`}
</style>
<ul className="drop_down_menu" style={{"right":"0px","left":"unset"}}>
<p className="drop_down_search">
<ul className="drop_down_menu" style={{"right":"0px","left":"unset","min-width":'150px'}}>
{this.state.course_second_categories.length>10? <p className="drop_down_search">
<Input placeholder="搜索" value={this.state.dirSearchValue} onChange={(e) => {this.setState({dirSearchValue: e.target.value})}}/>
</p>
</p>:""}
{/*{course_modules&&course_modules.course_modules.map((item,key)=>{*/}
{/*return(*/}
{/*<li key={key} id={item.id} onClick={() => this.moveTos(0)}>{item.module_name}</li>*/}
{/*)*/}
{/*})}*/}
{course_modules&&course_modules.course_modules.map((item,key)=>{
return(
filesId&&filesId===item.id?"":<li key={key} id={item.id} onClick={() => this.moveTos(0)}>{item.module_name}</li>
)
})}
{ course_modules&&course_modules.course_modules.map( (item,key) => {
return item.course_second_categories.filter((item)=> {
return (!this.state.dirSearchValue || item.name.indexOf(this.state.dirSearchValue) != -1)
}).map((itm,k)=>{
return(
<li key={k} id={itm.id} onClick={() => this.moveTos(itm.id )}>{itm.name}</li>
filesId&&filesId===itm.id?"":<li key={k} id={itm.id} onClick={() => this.moveTos(itm.id )}>{itm.name}</li>
)
})
})}
{this.state.course_second_categories.length===0?
<div className={"courseSecond"}>暂无数据</div>:""}
{/*{course_modules&&course_modules.course_modules.map((item,key)=>{*/}
{/*return(*/}
@ -711,9 +850,9 @@ class Fileslists extends Component{
{/*})*/}
{/*)*/}
{/*})}*/}
{this.props.isAdmin()?parseInt(this.props.match.params.main_id)===parseInt(this.props.coursesids)?
{this.props.isAdmin()?parseInt(this.props.match.params.main_id)===filesId&&filesId?
<p className="drop_down_btn">
<a className="color-grey-6" onClick={()=>this.addDir()}>添加目录...</a>
<a className="color-grey-6" onClick={()=>this.addDir()}>添加目录</a>
</p>
:"":""}
</ul>
@ -734,7 +873,7 @@ class Fileslists extends Component{
</li>:""}
</div>
</div>
</div>
</div> :""}
<style>{`
.padding02010{
@ -766,9 +905,10 @@ class Fileslists extends Component{
isAdmin={this.props.isAdmin()}
isStudent={this.props.isStudent()}
isNotMember={this.props.isNotMember()}
checkBox={<Checkbox value={item.id}></Checkbox>}
checkBox={this.props.isAdmin()?<Checkbox value={item.id} key={item.id}></Checkbox>:""}
Settingtypes={(id)=>this.Settingtypes(id)}
coursesId={this.props.match.params.coursesId}
updatafiledfun={()=>this.updatafiled()}
></Fileslistitem>
</div>
</div>
@ -810,18 +950,17 @@ class Fileslists extends Component{
<div className="alltask edu-back-white"
style={
{
display: total_count===undefined?'block' :total_count===0? 'block' : 'none'
display: files===undefined?'block' :files.length===0? 'block' : 'none'
}
}
>
<div className="edu-tab-con-box clearfix edu-txt-center"><img className="edu-nodata-img mb20"
src="https://www.educoder.net/images/educoder/nodata.png" />
<div className="edu-tab-con-box clearfix edu-txt-center">
<img className="edu-nodata-img mb20" src="/images/educoder/nodata.png" />
<p className="edu-nodata-p mb20">暂无数据哦~</p></div>
</div>
</CourseLayoutcomponent>
</div>
</React.Fragment>
)
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save