Merge branch 'dev_aliyun' into develop

dev_cs_new
daiao 6 years ago
commit 0633061426

@ -494,17 +494,10 @@ class GamesController < ApplicationController
path = path.try(:strip)
uid_logger("--rep_content: path is #{path}")
begin
if @myshixun.repo_name.nil?
g = Gitlab.client
repo_name = g.project(@myshixun.gpid).path_with_namespace
@myshixun.update_column(:repo_name, repo_name)
@content = git_fle_content("#{repo_name}.git", path) || ""
else
@content = git_fle_content(@myshixun.repo_path, path) || ""
end
@content = git_fle_content(@myshixun.repo_path, path) || ""
rescue Exception => e
# 思路: 异常首先应该考虑去恢复
# retry为1表示已经轮训完成后还没有解决问题这个时候需要检测异常
if params[:retry].to_i == 1
begin
# 如果模板没有问题,则通过中间层检测实训仓库是否异常
@ -542,7 +535,7 @@ class GamesController < ApplicationController
end
end
end
# 有异常,版本库获取不到代码,前端轮训30S后调用retry == 1
# 有异常,版本库获取不到代码,前端轮训15S后调用retry == 1
tip_exception(0, e.message)
end
end

@ -2,7 +2,8 @@ class LibrariesController < ApplicationController
include PaginateHelper
before_action :require_login, :check_auth, except: %i[index show]
before_action :check_account, except: %i[index show]
after_action :increment_visit_count, only: [:show, :edit, :update]
helper_method :current_library, :library_manageable?
def index
@ -80,6 +81,10 @@ class LibrariesController < ApplicationController
current_user&.id == library.user_id || admin_or_business?
end
def increment_visit_count
current_library.increment_visited_count! if current_library && current_library.id
end
def save_params
params.permit(:title, :content, :author_name, :author_school_name,
:cover_id, :publish, attachment_ids: [], tag_ids: [])

@ -5,7 +5,6 @@ class ShixunsController < ApplicationController
before_action :require_login, :check_auth, except: [:download_file, :index, :menus]
before_action :check_account, only: [:new, :create, :shixun_exec]
before_action :check_auth, except: [:download_file, :index, :menus]
before_action :find_shixun, :shixun_access_allowed, except: [:index, :new, :create, :menus, :get_recommend_shixuns,
:propaedeutics, :departments, :apply_shixun_mirror,
@ -514,6 +513,103 @@ class ShixunsController < ApplicationController
# 以前在开启挑战的时候检测实训是否更新更新则重置觉得应该放在TPI更好
# 中间需要一个过渡动画
# TODO: 第一次开启实训都会去判断是否是纯选择题类型,感觉做成在创建关卡的时候就判断该实训是否是纯选择题更加合适
def shixun_exec
if is_shixun_opening?
tip_show_exception(-3, "#{@shixun.opening_time.strftime('%Y-%m-%d %H:%M:%S')}")
end
current_myshixun = @shixun.current_myshixun(current_user.id)
min_challenges = @shixun.challenges.pluck(:id , :st)
Rails.logger.info("11111111112#{current_myshixun.try(:id)}")
Rails.logger.info("111111111102#{params[:reset] != 1}")
# 因为读写分离有延迟所以如果是重置来的请求可以先跳过重置过来的params[:reset]为1
if current_myshixun && params[:reset] != "1"
games = current_myshixun.games
# 如果TPM和TPI的管卡数不相等或者关卡顺序错了说明实训被极大的改动需要重置,实训发布前打过的实训都需要重置
if is_shixun_reset?(games, min_challenges, current_myshixun)
# 这里页面弹框要收到 当前用户myshixun的identifier.
tip_show_exception("/myshixuns/#{current_myshixun.try(:identifier)}/reset_my_game")
end
# 如果存在实训,则直接进入实训
# 如果实训允许跳关传参params[:challenge_id]跳入具体的关卡
@current_task =
if params[:challenge_id]
game = games.where(challenge_id: params[:challenge_id]).take
if @shixun.task_pass || game.status != 3
game
else
current_myshixun.current_task(games)
end
else
current_myshixun.current_task(games)
end
else
# 如果未创建关卡一定不能开启实训否则TPI没法找到当前的关卡
if @shixun.challenges_count == 0
tip_exception("开启实战前请先创建实训关卡")
end
# 判断实训是否全为选择题
is_choice_type = (min_challenges.size == min_challenges.select{|challenge| challenge.last == 1}.count)
if !is_choice_type
commit = GitService.commits(repo_path: @repo_path).try(:first)
uid_logger("First comit########{commit}")
tip_exception("开启实战前请先在版本库中提交代码") if commit.blank?
commit_id = commit["id"]
end
ActiveRecord::Base.transaction do
begin
cloud_bridge = edu_setting('cloud_bridge')
myshixun_identifier = generate_identifier Myshixun, 10
myshixun = @shixun.myshixuns.create!(user_id: current_user.id, identifier: myshixun_identifier,
modify_time: @shixun.modify_time, reset_time: @shixun.reset_time,
onclick_time: Time.now, commit_id: commit_id, repo_name: (is_choice_type ? "-1" : nil))
uid_logger("myshixun_id is #{myshixun.id}")
# 其它创建关卡等操作
challenges = @shixun.challenges
# 之所以增加user_id是为了方便统计查询性能
game_attrs = %i[challenge_id myshixun_id status user_id open_time identifier modify_time created_at updated_at]
Game.bulk_insert(*game_attrs) do |worker|
base_attr = { myshixun_id: myshixun.id, user_id: myshixun.user_id }
challenges.each_with_index do |challenge, index|
status = (index == 0 ? 0 : 3)
game_identifier = generate_identifier(Game, 12)
worker.add(base_attr.merge(challenge_id: challenge.id, status: status, open_time: Time.now,
identifier: game_identifier, modify_time: challenge.modify_time))
end
end
# 如果实训是纯选择题则不需要去fork仓库以及中间层的相关操作了
unless is_choice_type
# fork仓库
project_fork(myshixun, @repo_path, current_user.login)
rep_url = Base64.urlsafe_encode64(repo_ip_url @repo_path )
uid_logger("start openGameInstance")
uri = "#{cloud_bridge}/bridge/game/openGameInstance"
logger.info("end openGameInstance")
params = {tpiID: "#{myshixun.id}", tpmGitURL:rep_url, tpiRepoName: myshixun.repo_name.split("/").last}
uid_logger("openGameInstance params is #{params}")
interface_post uri, params, 83, "实训云平台繁忙繁忙等级83"
end
@current_task = myshixun.current_task(myshixun.games)
uid_logger("## shixun exec: myshixun id is #{myshixun.id}")
rescue Exception => e
uid_logger_error(e.message)
tip_exception("实训云平台繁忙繁忙等级81")
raise ActiveRecord::Rollback
end
end
end
end
# def shixun_exec
# if is_shixun_opening?
# tip_show_exception(-3, "#{@shixun.opening_time.strftime('%Y-%m-%d %H:%M:%S')}")
@ -521,10 +617,6 @@ class ShixunsController < ApplicationController
# current_myshixun = @shixun.current_myshixun(current_user.id)
#
# min_challenges = @shixun.challenges.pluck(:id , :st)
#
# Rails.logger.info("11111111112#{current_myshixun.try(:id)}")
# Rails.logger.info("111111111102#{params[:reset] != 1}")
#
# # 因为读写分离有延迟所以如果是重置来的请求可以先跳过重置过来的params[:reset]为1
# if current_myshixun && params[:reset] != "1"
# games = current_myshixun.games
@ -541,7 +633,6 @@ class ShixunsController < ApplicationController
# current_myshixun.update_column(:repo_name, repo_name)
# end
#
#
# # 如果存在实训,则直接进入实训
# # 如果实训允许跳关传参params[:challenge_id]跳入具体的关卡
# @current_task =
@ -570,159 +661,59 @@ class ShixunsController < ApplicationController
# commit_id = commit["id"]
# end
#
# ActiveRecord::Base.transaction do
# begin
# cloud_bridge = edu_setting('cloud_bridge')
# myshixun_identifier = generate_identifier Myshixun, 10
# myshixun = @shixun.myshixuns.create!(user_id: current_user.id, identifier: myshixun_identifier,
# modify_time: @shixun.modify_time, reset_time: @shixun.reset_time,
# onclick_time: Time.now, commit_id: commit_id)
# uid_logger("myshixun_id is #{myshixun.id}")
#
#
# # 其它创建关卡等操作
# challenges = @shixun.challenges
# # 之所以增加user_id是为了方便统计查询性能
# game_attrs = %i[challenge_id myshixun_id status user_id open_time identifier modify_time created_at updated_at]
# Game.bulk_insert(*game_attrs) do |worker|
# base_attr = { myshixun_id: myshixun.id, user_id: myshixun.user_id }
# challenges.each_with_index do |challenge, index|
# status = (index == 0 ? 0 : 3)
# game_identifier = generate_identifier(Game, 12)
# worker.add(base_attr.merge(challenge_id: challenge.id, status: status, open_time: Time.now,
# identifier: game_identifier, modify_time: challenge.modify_time))
# begin
# ActiveRecord::Base.transaction do
# begin
# myshixun_identifier = generate_identifier Myshixun, 10
# myshixun_params = {user_id: current_user.id, identifier: myshixun_identifier,
# modify_time: @shixun.modify_time, reset_time: @shixun.reset_time,
# onclick_time: Time.now, commit_id: commit_id}
# @myshixun = @shixun.myshixuns.create!(myshixun_params)
# # 其它创建关卡等操作
# challenges = @shixun.challenges
# # 之所以增加user_id是为了方便统计查询性能
# game_attrs = %i[challenge_id myshixun_id status user_id open_time identifier modify_time created_at updated_at]
# Game.bulk_insert(*game_attrs) do |worker|
# base_attr = {myshixun_id: @myshixun.id, user_id: @myshixun.user_id}
# challenges.each_with_index do |challenge, index|
# status = (index == 0 ? 0 : 3)
# game_identifier = generate_identifier(Game, 12)
# worker.add(base_attr.merge(challenge_id: challenge.id, status: status, open_time: Time.now,
# identifier: game_identifier, modify_time: challenge.modify_time))
# end
# end
# @current_task = @myshixun.current_task(@myshixun.games)
# rescue Exception => e
# logger.error("------ActiveRecord::RecordInvalid: #{e.message}")
# raise("ActiveRecord::RecordInvalid")
# end
#
# # 如果实训是纯选择题则不需要去fork仓库以及中间层的相关操作了
# end
# # 如果实训是纯选择题则不需要去fork仓库以及中间层的相关操作了
# ActiveRecord::Base.transaction do
# unless is_choice_type
# # fork仓库
# project_fork(myshixun, @repo_path, current_user.login)
#
# rep_url = Base64.urlsafe_encode64(repo_ip_url @repo_path )
# cloud_bridge = edu_setting('cloud_bridge')
# project_fork(@myshixun, @repo_path, current_user.login)
# rep_url = Base64.urlsafe_encode64(repo_ip_url @repo_path)
# uid_logger("start openGameInstance")
# uri = "#{cloud_bridge}/bridge/game/openGameInstance"
# logger.info("end openGameInstance")
# params = {tpiID: "#{myshixun.id}", tpmGitURL:rep_url, tpiRepoName: myshixun.repo_name.split("/").last}
# params = {tpiID: "#{@myshixun.id}", tpmGitURL: rep_url, tpiRepoName: @myshixun.repo_name.split("/").last}
# uid_logger("openGameInstance params is #{params}")
# interface_post uri, params, 83, "实训云平台繁忙繁忙等级83"
# end
#
# @current_task = myshixun.current_task(myshixun.games)
# uid_logger("## shixun exec: myshixun id is #{myshixun.id}")
# rescue Exception => e
# uid_logger_error(e.message)
# tip_exception("实训云平台繁忙繁忙等级81")
# raise ActiveRecord::Rollback
# end
# rescue Exception => e
# logger.info("shixun_exec error: #{e.message}")
# if e.message != "ActiveRecord::RecordInvalid"
# logger.error("##########project_fork error #{e.message}")
# @myshixun.destroy!
# end
# raise "实训云平台繁忙繁忙等级81"
# end
# end
# end
def shixun_exec
if is_shixun_opening?
tip_show_exception(-3, "#{@shixun.opening_time.strftime('%Y-%m-%d %H:%M:%S')}")
end
current_myshixun = @shixun.current_myshixun(current_user.id)
min_challenges = @shixun.challenges.pluck(:id , :st)
# 因为读写分离有延迟所以如果是重置来的请求可以先跳过重置过来的params[:reset]为1
if current_myshixun && params[:reset] != "1"
games = current_myshixun.games
# 如果TPM和TPI的管卡数不相等或者关卡顺序错了说明实训被极大的改动需要重置,实训发布前打过的实训都需要重置
if is_shixun_reset?(games, min_challenges, current_myshixun)
# 这里页面弹框要收到 当前用户myshixun的identifier.
tip_show_exception("/myshixuns/#{current_myshixun.try(:identifier)}/reset_my_game")
end
if current_myshixun.repo_name.nil?
g = Gitlab.client
repo_name = g.project(current_myshixun.gpid).try(:path_with_namespace)
current_myshixun.update_column(:repo_name, repo_name)
end
# 如果存在实训,则直接进入实训
# 如果实训允许跳关传参params[:challenge_id]跳入具体的关卡
@current_task =
if params[:challenge_id]
game = games.where(challenge_id: params[:challenge_id]).take
if @shixun.task_pass || game.status != 3
game
else
current_myshixun.current_task(games)
end
else
current_myshixun.current_task(games)
end
else
# 如果未创建关卡一定不能开启实训否则TPI没法找到当前的关卡
if @shixun.challenges_count == 0
tip_exception("开启实战前请先创建实训关卡")
end
# 判断实训是否全为选择题
is_choice_type = (min_challenges.size == min_challenges.select{|challenge| challenge.last == 1}.count)
if !is_choice_type
commit = GitService.commits(repo_path: @repo_path).try(:first)
uid_logger("First comit########{commit}")
tip_exception("开启实战前请先在版本库中提交代码") if commit.blank?
commit_id = commit["id"]
end
begin
ActiveRecord::Base.transaction do
begin
myshixun_identifier = generate_identifier Myshixun, 10
myshixun_params = {user_id: current_user.id, identifier: myshixun_identifier,
modify_time: @shixun.modify_time, reset_time: @shixun.reset_time,
onclick_time: Time.now, commit_id: commit_id}
@myshixun = @shixun.myshixuns.create!(myshixun_params)
# 其它创建关卡等操作
challenges = @shixun.challenges
# 之所以增加user_id是为了方便统计查询性能
game_attrs = %i[challenge_id myshixun_id status user_id open_time identifier modify_time created_at updated_at]
Game.bulk_insert(*game_attrs) do |worker|
base_attr = {myshixun_id: @myshixun.id, user_id: @myshixun.user_id}
challenges.each_with_index do |challenge, index|
status = (index == 0 ? 0 : 3)
game_identifier = generate_identifier(Game, 12)
worker.add(base_attr.merge(challenge_id: challenge.id, status: status, open_time: Time.now,
identifier: game_identifier, modify_time: challenge.modify_time))
end
end
@current_task = @myshixun.current_task(@myshixun.games)
rescue Exception => e
logger.error("------ActiveRecord::RecordInvalid: #{e.message}")
raise("ActiveRecord::RecordInvalid")
end
end
# 如果实训是纯选择题则不需要去fork仓库以及中间层的相关操作了
ActiveRecord::Base.transaction do
unless is_choice_type
# fork仓库
cloud_bridge = edu_setting('cloud_bridge')
project_fork(@myshixun, @repo_path, current_user.login)
rep_url = Base64.urlsafe_encode64(repo_ip_url @repo_path)
uid_logger("start openGameInstance")
uri = "#{cloud_bridge}/bridge/game/openGameInstance"
logger.info("end openGameInstance")
params = {tpiID: "#{@myshixun.id}", tpmGitURL: rep_url, tpiRepoName: @myshixun.repo_name.split("/").last}
uid_logger("openGameInstance params is #{params}")
interface_post uri, params, 83, "实训云平台繁忙繁忙等级83"
end
end
rescue Exception => e
logger.info("shixun_exec error: #{e.message}")
if e.message != "ActiveRecord::RecordInvalid"
logger.error("##########project_fork error #{e.message}")
@myshixun.destroy!
end
raise "实训云平台繁忙繁忙等级81"
end
end
end
# gameID 及实训ID
# status: 0 , 1 申请过, 2实训关卡路径未填 3 实训标签未填, 4 实训未创建关卡
def publish

@ -1,4 +1,5 @@
class Users::PrivateMessageDetailsController < Users::BaseController
before_action :require_login
before_action :private_user_resources!
after_action :update_message_status, only: [:show]

@ -1,4 +1,5 @@
class Users::PrivateMessagesController < Users::BaseController
before_action :require_login
before_action :private_user_resources!
after_action :update_onclick_time!, only: [:index]

@ -34,6 +34,10 @@ class Library < ApplicationRecord
end
end
def increment_visited_count!(num = 1)
increment_column!(:visited_count, num)
end
def generate_uuid
uuid = Util::UUID.time_uuid
while Library.exists?(uuid: uuid)
@ -42,4 +46,10 @@ class Library < ApplicationRecord
self.uuid = uuid
end
private
def increment_column!(column, num = 1)
self.class.connection.execute("update #{self.class.table_name} set #{column} = COALESCE(#{column}, 0) + #{num} where id = #{id}")
end
end

@ -3,9 +3,10 @@ class ModifyKeContentsForMarkdown < ActiveRecord::Migration[5.2]
def change
# def ke_transform_to_md content
# return content if content.blank?
# s_contents = sanitize(content, tags: %w(img a span table td tr tbody pre), attributes: %w(src href target style))
# s_contents.gsub(/^(\n)/, "").gsub(/(\n)+/, "<br>").gsub(/$(\n)/, "")
# .gsub(/(\n)+/, "<br>").gsub("\t", " ")
# s_contents = sanitize(content, tags: %w(img a table td tr tbody pre), attributes: %w(src href target style))
# s_contents.gsub(">\n<", "><").gsub(/^\n/, "").gsub(" ", "").gsub(/(\n)+/, "<br />")
# .gsub("\t", "").gsub("\n", "").gsub(" ", "&nbsp;&nbsp;&nbsp;&nbsp;").gsub(/(<br\s?\/?>)+/, "<br />")
# s_contents.gsub("\n", "<br />").gsub(/(<br\s?\/?>)+/, "<br />")
# end
#
# # 课程讨论区
@ -13,20 +14,22 @@ class ModifyKeContentsForMarkdown < ActiveRecord::Migration[5.2]
# content = ke_transform_to_md m.content
# m.update_column(:content, content)
# end
#
# # 试卷的标题
# ExerciseQuestion.find_each do |eq|
#试卷的标题
# ExerciseQuestion.where(:exercise_id => 1892).find_each do |eq|
# puts("#eq.question_title: #{eq.question_title}")
# question_title = ke_transform_to_md eq.question_title
# puts("#question_title: #{question_title}")
# eq.update_column(:question_title, question_title)
# end
#
# # 试卷的答案
# ExerciseStandardAnswer.find_each do |esa|
# answer_text = ke_transform_to_md esa.answer_text
# esa.update_column(:answer_text, answer_text)
# end
#
# # 试卷题库的问题标题
# 试卷题库的问题标题
# ExerciseBankQuestion.find_each do |ebq|
# question_title = ke_transform_to_md ebq.question_title
# ebq.update_column(:question_title, question_title)
@ -37,7 +40,7 @@ class ModifyKeContentsForMarkdown < ActiveRecord::Migration[5.2]
# answer_text = ke_transform_to_md ebsa.answer_text
# ebsa.update_column(:answer_text, answer_text)
# end
#
# # 问卷的标题
# PollQuestion.find_each do |pq|
# question_title = ke_transform_to_md pq.question_title

@ -4,8 +4,9 @@ class SecondMofidyKeContentsForMd < ActiveRecord::Migration[5.2]
# def ke_transform_to_md content
# return content if content.blank?
# s_contents = sanitize(content, tags: %w(img a span table td tr tbody pre), attributes: %w(src href target style))
# s_contents.gsub(/^(\n)/, "").gsub(/(\n)+/, "<br>").gsub(/$(\n)/, "")
# .gsub(/(\n)+/, "<br>").gsub("\t", " ")
# s_contents.gsub(">\n<", "><").gsub(/^\n/, "").gsub(" ", "&nbsp;").gsub(/(\n)+/, "<br />")
# .gsub("\t", "&nbsp;&nbsp;&nbsp;&nbsp;").gsub("\n", "").gsub(" ", "&nbsp;").gsub(/(<br\s?\/?>)+/, "<br />")
#
# end
#
# # 作业

@ -4,8 +4,8 @@ class ThirdModifyKeForStudentWork < ActiveRecord::Migration[5.2]
# def ke_transform_to_md content
# return content if content.blank?
# s_contents = sanitize(content, tags: %w(img a span table td tr tbody pre), attributes: %w(src href target style))
# s_contents.gsub(/^(\n)/, "").gsub(/(\n)+/, "<br>").gsub(/$(\n)/, "")
# .gsub(/(\n)+/, "<br>").gsub("\t", " ")
# s_contents.gsub(">\n<", "><").gsub(/^\n/, "").gsub(" ", "&nbsp;").gsub(/(\n)+/, "<br />")
# .gsub("\t", "&nbsp;&nbsp;&nbsp;&nbsp;").gsub("\n", "").gsub(" ", "&nbsp;").gsub(/(<br\s?\/?>)+/, "<br />")
# end
#
# # 学生的作品 过滤掉一些描述的ke图片的作品

@ -11,5 +11,17 @@ namespace :myshixun do
end
end
end
task choose_repo: :environment do
myshixuns = Myshixun.where("repo_name is null and created_at > '2019-07-20 00:00:00'")
myshixuns.each do |myshixun|
shixun = myshixun.shixun
challenges = shixun.challenges
# 如果是选择题则把myshixuns表的repo_name改成-1
if challenges.select{|challenge| challenge.st.to_i == 1}.size == challenges.count
myshixun.update_attributes(:repo_name => "-1")
end
end
end
end

File diff suppressed because one or more lines are too long

@ -17,6 +17,7 @@ function locationurl(list){
window.location.replace(list)
}
}
let hashTimeout
// TODO 开发期多个身份切换
let debugType =""
@ -137,6 +138,18 @@ export function initAxiosInterceptors(props) {
}
throw new axios.Cancel('Operation canceled by the user.');
} else {
// hash跳转
var hash = window.location.hash;
if (hash) {
hashTimeout && window.clearTimeout(hashTimeout)
hashTimeout = setTimeout(() => {
var element = document.querySelector(hash);
if (element) {
element.scrollIntoView();
}
}, 400)
}
}
// if(response.data.status === 401){
// console.log("401401401")

@ -12,6 +12,7 @@ export function markdownToHTML(oldContent, selector) {
window.$('#' + selector).html(oldContent)
} else {
try {
$("#"+selector).html('')
// selector ||
var markdwonParser = window.editormd.markdownToHTML(selector || "md_div", {
markdown: oldContent, // .replace(/▁/g,"▁▁▁"),

@ -8,7 +8,15 @@ class LinkAfterLogin extends Component {
}
checkAuth = () => {
if (this.props.checkIfLogin()) {
this.props.history.push(this.props.to)
if(this.props.checkProfileComplete){
if(this.props.checkIfProfileCompleted()){
this.props.history.push(this.props.to)
}else{
this.props.showProfileCompleteDialog();
}
}else{
this.props.history.push(this.props.to)
}
} else {
this.props.showLoginDialog()
}

@ -498,7 +498,7 @@ pop_box_new(htmlvalue, 480, 182);
return resData
}
fetchAll(stageId) {
fetchAll(stageId, noTimeout) {
if (window.__fetchAllFlag == true ) {
console.log('TPIContextProvider call fetchAll repeatly!')
@ -544,6 +544,13 @@ pop_box_new(htmlvalue, 480, 182);
return;
}
if (response.data.status == 404) {
// 如果第一次发生404则隔1s后再调用一次本接口因为ucloud主从同步可能有延迟
if (!noTimeout) {
setTimeout(() => {
this.fetchAll(stageId, true)
}, 1000)
return;
}
window.location.href = '/myshixuns/not_found'
return;
}

@ -13,7 +13,8 @@ export function CNotificationHOC(options = {}) {
});
this.state = {
dialogOpen: false
dialogOpen: false,
defineOpen:false
}
}
@ -126,8 +127,21 @@ export function CNotificationHOC(options = {}) {
this.onCancel = null
this.onOk = null
}
// 附件太大提示框
define = (object) =>{
const { title, content } = object;
this.setState({ title, content, defineOpen: true })
}
onDialogdefineOkBtnClick = () =>{
this.onCancel && this.onCancel();
this.setState({ defineOpen: false })
this.onCancel = null
this.onOk = null
}
render() {
const { snackbarOpen, snackbarText, snackbarHorizontal, snackbarVertical, dialogOpen, content } = this.state;
const { snackbarOpen, snackbarText, snackbarHorizontal, snackbarVertical, dialogOpen, content ,defineOpen } = this.state;
return (
@ -136,19 +150,31 @@ export function CNotificationHOC(options = {}) {
modalsType={dialogOpen}
modalsTopval={
content
}
}
modalsBottomval={""}
modalCancel={this.handleDialogClose}
modalSave={this.onDialogOkBtnClick}
okText={this.okText}
>
</Modals>
<Modals
modalsType={defineOpen}
modalsTopval={
content
}
loadtype={true}
modalsBottomval={""}
modalCancel={undefined}
modalSave={this.onDialogdefineOkBtnClick}
>
</Modals>
<WrappedComponent {...this.props}
showNotification= { this.showNotification }
bytesToSize={this.bytesToSize}
getNowFormatDates={(value,type)=>this.getNowFormatDates(value,type)}
configNotification={ this.configNotification }
confirm={ this.confirm }
define={ this.define }
>
</WrappedComponent>
</React.Fragment>

@ -353,8 +353,8 @@ class CoursesBanner extends Component {
{AccountProfiletype===true?<AccountProfile
hideAccountProfile={()=>this.hideAccountProfile()}
{...this.state}
{...this.props}
{...this.state}
/>:""}

@ -248,7 +248,7 @@ class Addcourses extends Component{
}else{
notification.open({
response.data.message && notification.open({
message:"提示",
description:response.data.message
});

@ -189,7 +189,7 @@ class MemoDetail extends Component {
let filesize = item.filesize
attachments.push(
<div className="color-grey df" key={index}>
<div className="color-grey df" key={index} style={{ lineHeight: '17px'}}>
<a className="color-grey ">
<i className="font-14 color-green iconfont icon-fujian mr8" aria-hidden="true"></i>
</a>

@ -116,7 +116,7 @@ class ShixunsHome extends Component {
}
return (
<div className="newMain clearfix backFAFAFA">
<div className="newMain clearfix backFAFAFA shixunsHome">
{this.state.updata===undefined?"":<UpgradeModals
{...this.state}
/>}
@ -128,6 +128,9 @@ class ShixunsHome extends Component {
<style>
{
`
.shixunsHome .educontent {
width: 1223px;
}
.banners{
overflow: hidden;
}

@ -85,12 +85,18 @@ class CaseDetail extends Component{
{
CaseDetail && CaseDetail.status == "pending" && <span class="edu-filter-btn fl cdefault edu-activity-green ml10">草稿</span>
}
{
CaseDetail && CaseDetail.status == "processing" && <span class="edu-filter-btn fl cdefault edu-activity-green ml10">审核中</span>
}
{
CaseDetail && CaseDetail.status == "refused" && <span class="edu-filter-btn fl cdefault edu-activity-orange ml10">未通过</span>
}
</span>
<a href="/moop_cases" className="fr color-grey-9 mt5">返回</a>
</p>
<div className="edu-back-white">
<div className="padding30">
<div className="df">
<div className="df mb5">
<a href="/users/moop"><img alt="82274?1563067098" className="radius mr15 mt3" height="50" src={getImageUrl(`images/${creator && creator.image_url}`)} width="50" /></a>
<div className="flex1">
<li className="clearfix mb5">
@ -103,11 +109,16 @@ class CaseDetail extends Component{
operation && operation.can_editable ? <ActionBtn style="colorBlue" to={`/moop_cases/${this.props.match.params.caseID}/edit`} className="fr mr20">编辑</ActionBtn>:""
}
</li>
<li className="clearfix">
<li className="clearfix lineh-20">
<span className="fl color-grey-9 mr20">{creator && creator.school_name}</span>
<span className="fr">
<span className="fl color-grey-9 mr30">编码<span className="color-grey-6">{CaseDetail && CaseDetail.uuid}</span></span>
{
CaseDetail && CaseDetail.status=="published" ?
<span className="fl color-grey-9">发布时间<span className="color-grey-6">{CaseDetail && CaseDetail.published_at}</span></span>
:
<span className="fl color-grey-9">上传时间<span className="color-grey-6">{CaseDetail && CaseDetail.created_at}</span></span>
}
</span>
</li>
</div>
@ -123,7 +134,7 @@ class CaseDetail extends Component{
`}
</style>
<div class="mt20 setMDStyle">
{ CaseDetail && CaseDetail.content && <MarkdownToHtml content={CaseDetail.content} selector="casesDetail" style={{width:"100%!important"}}></MarkdownToHtml>}
{ CaseDetail && CaseDetail.content && <MarkdownToHtml content={CaseDetail.content} id="casesDetail" selector="casesDetail" style={{width:"100%!important"}}></MarkdownToHtml>}
</div>
{ attachments &&
<div className="mt10">

@ -30,9 +30,18 @@ class CaseItem extends Component{
<span className="color-grey-3 mr10">{item.author_name}</span>
<span className="color-grey-3 mr20">{item.author_school_name}</span>
<span className="color-grey-c fr">
<span className="color-grey-c mr20"><span className=" item-group-icon mr5"><i className="fa fa-eye"></i></span>{item.visited_count} </span>
<span className="color-grey-c mr20"><span className=" item-group-icon mr5"><i className="fa fa-thumbs-o-up"></i></span>{item.praise_count} </span>
<span className="color-grey-c"><span className=" item-group-icon mr5"><i className="fa fa-download"></i></span>{item.download_count} </span>
{
item.visited_count && item.visited_count != 0 ?
<span className="color-grey-c ml20"><span className=" item-group-icon mr5"><i className="fa fa-eye"></i></span>{item.visited_count} </span>:""
}
{
item.praise_count && item.praise_count != 0 ?
<span className="color-grey-c ml20"><span className=" item-group-icon mr5"><i className="fa fa-thumbs-o-up"></i></span>{item.praise_count} </span>:""
}
{
item.download_count && item.download_count != 0 ?
<span className="color-grey-c ml20" style={{"marginRight":'1px'}}><span className=" item-group-icon mr5"><i className="fa fa-download"></i></span>{item.download_count} </span>:""
}
</span>
</p>
</div>

@ -3,7 +3,7 @@ import { Input , Spin , Pagination } from "antd";
import './css/moopCases.css'
import '../courses/css/Courses.css'
import { ActionBtn } from 'educoder';
import { ActionBtn , LinkAfterLogin } from 'educoder';
import axios from 'axios'
@ -13,6 +13,8 @@ import mainImg from '../../images/moop_cases/teach_ex.jpg'
import CaseItem from './CaseItem'
const Search = Input.Search;
class CaseList extends Component{
constructor(props){
@ -23,7 +25,8 @@ class CaseList extends Component{
page:1,
pageSize:20,
libraries:undefined,
totalCount:undefined
totalCount:undefined,
isSpin:false
}
}
@ -44,7 +47,8 @@ class CaseList extends Component{
if(result){
this.setState({
libraries:result.data.libraries,
totalCount:result.data.count
totalCount:result.data.count,
isSpin:false
})
}
}).catch((error)=>{
@ -71,6 +75,9 @@ class CaseList extends Component{
// 搜索
searchInfo = () =>{
this.setState({
isSpin:true
})
let { type , search , pageSize } = this.state;
this.InitList( type , search , 1 , pageSize );
}
@ -86,6 +93,8 @@ class CaseList extends Component{
render(){
let { type , search ,libraries , totalCount ,pageSize ,page } = this.state;
let { checkIfLogin } = this.props;
return(
<React.Fragment>
<img src={mainImg} width="100%" />
@ -93,16 +102,22 @@ class CaseList extends Component{
<div className="edu-back-white mb30 mt30">
<p className="padding20-30 clearfix bor-bottom-greyE">
<span className="font-18 fl color-grey-3">教学案例</span>
<ActionBtn style="colorBlue" className="fr" to="/moop_cases/new">发布案例</ActionBtn>
<LinkAfterLogin {...this.props} to={'/moop_cases/new'} className="fr edu-default-btn edu-blueline-btn" checkProfileComplete = {true}
>发布案例</LinkAfterLogin>
{/* <ActionBtn style="colorBlue" className="fr" to="/moop_cases/new">发布案例</ActionBtn> */}
</p>
<div className="clearfix pl30 pr30">
<ul className="fl library_nav mt25">
<li className={type == 0 ? "active" :""} onClick={()=>this.changeType(0)}>
<a href="javascript:void(0)">全部</a>
</li>
<li className={type == 1 ? "active" :""} onClick={()=>this.changeType(1)}>
<a href="javascript:void(0)">我的</a>
</li>
{
checkIfLogin() &&
<li className={type == 1 ? "active" :""} onClick={()=>this.changeType(1)}>
<a href="javascript:void(0)">我的</a>
</li>
}
</ul>
<div className="fr mt16 mb16 searchView"style={{width:"300px"}}>
<Search
@ -114,18 +129,20 @@ class CaseList extends Component{
</div>
</div>
</div>
{
libraries && libraries.length > 0 && <CaseItem {...this.props} {...this.state} libraries={libraries}></CaseItem>
}
{
libraries && libraries.length == 0 && <div className="mb50"><NoneData></NoneData></div>
}
{
totalCount && totalCount > pageSize &&
<div className="mb50 edu-txt-center clearfix">
<Pagination defaultCurrent={page} current={page} pageSize={pageSize} showQuickJumper onChange={this.onChangePage} total={totalCount}></Pagination>
</div>
}
<Spin size="large" spinning={this.state.isSpin}>
{
libraries && libraries.length > 0 && <CaseItem {...this.props} {...this.state} libraries={libraries}></CaseItem>
}
{
libraries && libraries.length == 0 && <div className="mb50"><NoneData></NoneData></div>
}
{
totalCount && totalCount > pageSize &&
<div className="mb50 edu-txt-center clearfix">
<Pagination defaultCurrent={page} current={page} pageSize={pageSize} showQuickJumper onChange={this.onChangePage} total={totalCount}></Pagination>
</div>
}
</Spin>
</div>
</React.Fragment>
)

@ -3,7 +3,7 @@ import './css/moopCases.css'
import '../courses/css/Courses.css'
import { Form , Input , Upload , Button , Icon , message , Tooltip } from "antd";
import { getImageUrl , MarkdownToHtml , ActionBtn , appendFileSizeToUploadFile , appendFileSizeToUploadFileAll , getUrl , getUploadActionUrl } from 'educoder';
import { getImageUrl , setImagesUrl , MarkdownToHtml , ActionBtn , appendFileSizeToUploadFile , appendFileSizeToUploadFileAll , getUrl , getUploadActionUrl } from 'educoder';
import Tags from './CaseTags'
@ -48,7 +48,7 @@ class CaseNew extends Component{
// 上传附件-删除确认框
onAttachmentRemove = (file, stateName) => {
this.props.confirm({
content: '是否确认删除?',
content: '是否确认删除?',
onOk: () => {
this.deleteAttachment(file, stateName)
},
@ -74,8 +74,11 @@ class CaseNew extends Component{
const index = state[stateName].indexOf(file);
const newFileList = state[stateName].slice();
newFileList.splice(index, 1);
console.log("newFileList");
console.log(newFileList.map(item =>{ return( item.id )}));
return {
[stateName]: newFileList,
filesID:newFileList.map(item =>{ return( item.id )})
};
});
}
@ -87,20 +90,18 @@ class CaseNew extends Component{
}
// 上传附件-change
handleContentUploadChange = (info) => {
let contentFileList = info.fileList;
this.setState({ contentFileList: appendFileSizeToUploadFileAll(contentFileList)});
let list = appendFileSizeToUploadFileAll(contentFileList);
let arr = list.map(item=>{
return ( item.response && item.response.id )
})
this.setState({
filesID:arr,
checkFile:arr.length > 0 ? false : true
})
console.log("fujian");
console.log(list.map(item=>{
return ( item.response && item.response.id )
}));
if (info.file.status === 'done' || info.file.status === 'uploading') {
let contentFileList = info.fileList;
this.setState({ contentFileList: appendFileSizeToUploadFileAll(contentFileList)});
let list = appendFileSizeToUploadFileAll(contentFileList);
let arr = list.map(item=>{
return ( item.response && item.response.id )
})
this.setState({
filesID:arr,
checkFile:arr.length > 0 ? false : true
})
}
}
// 上传封面图-change
@ -139,7 +140,7 @@ class CaseNew extends Component{
userUnit:this.props.CaseDetail.author_school_name,
})
this.setState({
contentFileList:this.props.CaseDetail.attachments.map(item => {
contentFileList:this.props.attachments.map(item => {
return {
id: item.id,
uid: item.id,
@ -149,12 +150,18 @@ class CaseNew extends Component{
status: 'done'
}
}),
filesID:this.props.attachments.map(item => {
return ( item.id )
}),
coverID:this.props.cover && this.props.cover.id,
imageUrl:this.props.CaseDetail.cover && getImageUrl(this.props.CaseDetail.cover.url),
imageUrl:this.props.CaseDetail.cover && setImagesUrl(this.props.CaseDetail.cover.url),
casesTags:this.props.tags.map(item=>{
return (item.id);
})
})
console.log(this.props.attachments.map(item => {
return ( item.id )
}))
}
}
@ -183,7 +190,11 @@ class CaseNew extends Component{
})
return;
}
//const mdContnet = this.DescMdRef.current.getValue().trim();
const mdContnet = this.DescMdRef.current.getValue().trim();
console.log(mdContnet)
values.description = mdContnet;
console.log(values);
let url = caseID ? `/libraries/${caseID}.json`: `/libraries.json`;
if(caseID){
@ -260,9 +271,13 @@ class CaseNew extends Component{
beforeUpload: (file) => {
const isLt150M = file.size / 1024 / 1024 < 150;
if (!isLt150M) {
message.error('文件大小必须小于150MB!');
//message.error('文件大小必须小于150MB!');
this.props.define({
title:'提示',
content:"该文件无法上传。超过文件大小限制(150MB),建议上传到百度云等其它共享工具里然后再txt文档里给出链接以及共享密码并上传"
})
return isLt150M;
}
return isLt150M;
}
};
// 上传封面图-html
@ -279,6 +294,8 @@ class CaseNew extends Component{
action:`${getUploadActionUrl()}`,
onChange:this.handleChange,
}
console.log('111');
console.log(!caseID || (CaseDetail && CaseDetail.status == "pending"));
return(
<div className="educontent mt10 mb50">
<style>
@ -379,7 +396,7 @@ class CaseNew extends Component{
<p className="ant-upload-text color-grey-c">从我的电脑选择要上传的文档按住CTRL可以上传多份文档单个文件最大限制150MB</p>
</Dragger>
{
checkFile && <div style={{left:"0px",bottom:"-21px"}} class="ant-form-explain">请先上传附件</div>
checkFile == true && <div style={{left:"0px",bottom:"-21px"}} class="ant-form-explain">请先上传附件</div>
}
</div>
<p className="lineh-25 mt20 mb10 clearfix">
@ -419,7 +436,7 @@ class CaseNew extends Component{
<Form.Item>
<div className="clearfix mt30 mb30">
{
!caseID && <Button type="primary" onClick={()=>this.handleSubmit("submit")} className="defalutSubmitbtn fl mr20">申请发布</Button>
(!caseID || (CaseDetail && CaseDetail.status == "pending" || CaseDetail.status == "refused")) ? <Button type="primary" onClick={()=>this.handleSubmit("submit")} className="defalutSubmitbtn fl mr20">申请发布</Button> : ""
}
<a className="defalutCancelbtn fl" onClick={()=>this.handleSubmit("save")}>保存</ a>
</div>

@ -48,6 +48,13 @@
border: 1px solid green;
line-height: 17px;
}
.edu-activity-orange {
background-color: #ff6800;
color: #fff!important;
cursor: pointer;
border: 1px solid #ff6800;
line-height: 17px;
}
.edu-activity-blue {
background-color: #4CACFF;
color: #fff!important;
@ -132,6 +139,9 @@
display: block;
cursor: pointer;
}
.librariesField .ant-upload.ant-upload-drag{
border:none!important;
}
.uploadImage .ant-upload.ant-upload-select-picture-card{
width:120px;
height: 90px;

@ -46,6 +46,9 @@ class Index extends Component{
user_praised:true,
}
}
componentDidMount(){
window.document.title = '教学案例'
}
// 获取案例详情
getDetail = (caseID) =>{
let url=`/libraries/${caseID}.json`

@ -330,8 +330,8 @@ class DetailCards extends Component{
<div>
{AccountProfiletype===true?<AccountProfile
hideAccountProfile={()=>this.hideAccountProfile()}
{...this.state}
{...this.props}
{...this.state}
/>:""}
<Modals

@ -372,7 +372,7 @@ class PathDetailIndex extends Component{
<span className="font-16">课程须知</span>
{detailInfoList===undefined?"":detailInfoList.allow_statistics===true?
<Tooltip placement="bottom" title={"编辑"}>
<Link to={"/paths/"+this.props.match.params.pathId+"/edit"} className="fr mtf5">
<Link to={"/paths/"+this.props.match.params.pathId+"/edit#learning_notes"} className="fr mtf5">
<i className="iconfont icon-bianjidaibeijing font-20 color-green"></i>
</Link>
</Tooltip>

@ -213,13 +213,15 @@ class PathNew extends Component{
}
render(){
let pathId = this.props.match.params.pathId;
let {pathName,description,point,flag_name}=this.state;
return(
<div className="newContainer">
<div className="newMain clearfix">
<div className="educontent mt10 mb50">
<div className="mb10 edu-back-white">
<p className="padding20 bor-bottom-greyE font-18 color-grey-3">创建实践课程</p>
<p className="padding20 bor-bottom-greyE font-18 color-grey-3">{pathId ? '编辑' : '创建'}实践课程</p>
<div className="padding30-20" id="part_Name">
<p className="color-grey-6 font-16 mb15">实践课程名称</p>
<div className="df">
@ -251,7 +253,7 @@ class PathNew extends Component{
</div>
<div className="mb10 edu-back-white padding30-20" id="part_point">
<p className="color-grey-6 font-16 mb15">学习须知</p>
<p className="color-grey-6 font-16 mb15" id="learning_notes">学习须知</p>
<div className="df">
<span className="mr30 color-orange pt10">*</span>
<div className="flex1 mr20">

@ -204,14 +204,14 @@ class PackageConcent extends Component {
<div className="educontent clearfix mtf10" style={{flex: "1 0 auto"}}>
{isRender===true?<LoginDialog
Modifyloginvalue={()=>this.Modifyloginvalue()}
{...this.state}
{...this.props}
{...this.state}
/>:""}
{AccountProfiletype===true?<AccountProfile
hideAccountProfile={()=>this.hideAccountProfile()}
{...this.state}
{...this.props}
{...this.state}
/>:""}
<div className="stud-class-set">
<div className="news">

@ -628,6 +628,7 @@ submittojoinclass=(value)=>{
let activePaths = false;
let coursestype=false;
let activePackages=false;
let activeMoopCases=false;
if (match.path === '/forums') {
@ -640,7 +641,9 @@ submittojoinclass=(value)=>{
coursestype = true;
}else if (match.path.startsWith('/crowdsourcing')) {
activePackages = true;
}else {
}else if(match.path.startsWith('/moop_cases')){
activeMoopCases = true;
}else {
activeIndex = true;
}
@ -650,14 +653,14 @@ submittojoinclass=(value)=>{
{isRender===true?<LoginDialog
Modifyloginvalue={()=>this.Modifyloginvalue()}
{...this.state}
{...this.props}
{...this.state}
/>:""}
{AccountProfiletype===true?<AccountProfile
hideAccountProfile={()=>this.hideAccountProfile()}
{...this.state}
{...this.props}
{...this.state}
/>:""}
<a href="/" className={"fl mr60 ml25"}>
<img alt="高校智能化教学与实训平台" className="logoimg" src={getImageUrl("images/educoder/headNavLogo.png?1526520218")}></img>
@ -725,7 +728,7 @@ submittojoinclass=(value)=>{
src={require('./roundedRectangle.png')}
/>
</li>
<li className=""><a href={this.props.Headertop===undefined?"":this.props.Headertop.moop_cases_url}>教学案例</a></li>
<li className={`${activeMoopCases === true ? 'pr active' : 'pr'}`}><a href={this.props.Headertop===undefined?"":this.props.Headertop.moop_cases_url}>教学案例</a></li>
<li className={`${activePackages === true ? 'pr active' : 'pr'}`}>
<a href={'/crowdsourcing'}>众包创新</a>
</li>

@ -571,8 +571,8 @@ class TPMBanner extends Component {
{AccountProfiletype===true?<AccountProfile
hideAccountProfile={()=>this.hideAccountProfile()}
{...this.state}
{...this.props}
{...this.state}
/>:""}

@ -10,6 +10,8 @@ import axios from 'axios';
import './TPMIndex.css'
import LoginDialog from '../login/LoginDialog';
import AccountProfile from '../user/AccountProfile';
import Trialapplication from "../login/Trialapplication";
// import "antd/dist/antd.css";
// import '../../css/educoder/edu-common.css'
@ -76,7 +78,8 @@ export function TPMIndexHOC(WrappedComponent) {
Footerdown:undefined,
coursedata: {},
isRender: false
isRender: false,
AccountProfiletype: false
}
}
@ -300,11 +303,26 @@ export function TPMIndexHOC(WrappedComponent) {
isRender: true
})
}
checkIfLogin = () => {
return this.state.current_user && this.state.current_user.login != ''
}
hideAccountProfile = () => {
this.setState({
AccountProfiletype: false
})
}
showProfileCompleteDialog = () => {
this.setState({
AccountProfiletype: true
})
}
checkIfProfileCompleted = () => {
return this.state.current_user && this.state.current_user.profile_completed
}
render() {
let{Headertop,Footerdown, isRender}=this.state;
let{Headertop,Footerdown, isRender, AccountProfiletype}=this.state;
const common = {
isSuperAdmin:this.isSuperAdmin,
isAdminOrCreator:this.isAdminOrCreator,
@ -319,14 +337,21 @@ export function TPMIndexHOC(WrappedComponent) {
showLoginDialog: this.showLoginDialog,
checkIfLogin: this.checkIfLogin,
showProfileCompleteDialog: this.showProfileCompleteDialog,
checkIfProfileCompleted: this.checkIfProfileCompleted,
}
return (
<div>
{isRender===true ? <LoginDialog
Modifyloginvalue={()=>this.hideLoginDialog()}
{...this.state}
{...this.props}
{...this.state}
/> : ""}
{AccountProfiletype===true ? <AccountProfile
hideAccountProfile={()=>this.hideAccountProfile()}
{...this.props}
{...this.state}
/>:""}
<SiderBar Headertop={Headertop}/>
{/* 注释掉了1440 影响到了手机屏幕的展示 */}
<style>{

@ -101,7 +101,7 @@ function md_elocalStorage(editor,mdu,id){
function create_editorMD(id, width, high, placeholder, imageUrl, callback, initValue,
onchange, watch, { noStorage, showNullButton }, that) {
onchange, watch, { noStorage, showNullButton, emoji }, that) {
// 还是出现了setting只有一份被共用的问题
var editorName = window.editormd(id, {
@ -115,7 +115,7 @@ function create_editorMD(id, width, high, placeholder, imageUrl, callback, initV
syncScrolling: "single",
tex: true,
tocm: true,
emoji: true,
emoji: !!emoji ,
taskList: true,
codeFold: true,
searchReplace: true,
@ -266,7 +266,8 @@ export default class TPMMDEditor extends Component {
window[__editorName.id+'_'] = __editorName;
}, initValue, this.onEditorChange,this.props.watch, {
noStorage: this.props.noStorage,
showNullButton: this.props.showNullButton
showNullButton: this.props.showNullButton,
emoji: this.props.emoji
}, this);
}

@ -194,7 +194,8 @@ export default class TPMquestion extends Component {
})
// this.contentMdRef.current.setValue(response.data.task_pass || '')
if(response.data.chooses.length===0){
this.questionadd()
// 新建选择题时,没法切回 ‘本关任务’ tab
// this.questionadd()
}
this.shixunsautoHeight()

@ -299,8 +299,8 @@ class Challenges extends Component {
<React.Fragment>
{AccountProfiletype===true?<AccountProfile
hideAccountProfile={()=>this.hideAccountProfile()}
{...this.state}
{...this.props}
{...this.state}
/>:""}
{loadingContent ?

Loading…
Cancel
Save