Merge branch 'dev_aliyun' of https://bdgit.educoder.net/Hjqreturn/educoder into dev_aliyun

chromesetting
daiao 5 years ago
commit 5782207a05

@ -0,0 +1,57 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-courses-index-page').length > 0) {
let searchContainer = $(".course-list-form");
let searchForm = $("form.search-form",searchContainer);
searchContainer.on('change', '.course-homepage-show', function(){
searchForm.find('input[type="submit"]').trigger('click');
});
//导出
searchContainer.on('click', "#course-export", function () {
window.location.href = "/admins/courses.xlsx?" + searchForm.serialize();
});
$(".course-list-container").on("change", '.course-setting-form', function () {
var s_id = $(this).attr("data-id");
var s_value = $(this).val();
var s_name = $(this).attr("name");
var json = {};
json[s_name] = s_value;
$.ajax({
url: "/admins/courses/" + s_id,
type: "PUT",
dataType:'script',
data: json
});
});
// ************** 学校选择 *************
searchForm.find('.school-select').select2({
theme: 'bootstrap4',
placeholder: '请选择单位',
minimumInputLength: 1,
ajax: {
delay: 500,
url: '/api/schools/search.json',
dataType: 'json',
data: function (params) {
return {keyword: params.term};
},
processResults: function (data) {
return {results: data.schools}
}
},
templateResult: function (item) {
if (!item.id || item.id === '') return item.text;
return item.name;
},
templateSelection: function (item) {
if (item.id) {
}
return item.name || item.text;
}
});
}
});

@ -0,0 +1,60 @@
$(document).on('turbolinks:load', function() {
var $modal = $('.modal.admin-merge-course-list-modal');
if ($modal.length > 0) {
var $form = $modal.find('form.admin-merge-course-list-form');
var $originCourseListIdInput = $form.find('input[name="origin_course_list_id"]');
$form.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
course_list_name: {
required: true
}
},
messages: {
course_list_name: {
required: '请输入课程名称'
}
}
});
// modal ready fire
$modal.on('show.bs.modal', function (event) {
var $link = $(event.relatedTarget);
var couresListId = $link.data('courseListId');
var url = $link.data('url');
$originCourseListIdInput.val(couresListId);
$form.data('url', url);
});
$modal.on('click', '.submit-btn', function(){
$form.find('.error').html('');
if ($form.valid()) {
var url = $form.data('url');
$.ajax({
method: 'POST',
dataType: 'json',
url: url,
data: $form.serialize(),
success: function(){
$.notify({ message: '操作成功' });
$modal.modal('hide');
setTimeout(function(){
window.location.reload();
}, 500);
},
error: function(res){
var data = res.responseJSON;
$form.find('.error').html(data.message);
}
});
}
});
}
});

@ -0,0 +1,35 @@
class Admins::CourseListsController < Admins::BaseController
def index
course_lists = Admins::CourseListQuery.call(params)
@course_lists = paginate course_lists.preload(:courses, :user)
@params_page = params[:page] || 1
respond_to do |format|
format.js
format.html
end
end
def destroy
CourseList.find(params[:id]).destroy!
render_delete_success
end
def merge
origin_course_list = CourseList.find_by!(id: params[:origin_course_list_id])
o_courselist = CourseList.find_by(name: params[:course_list_name])
if o_courselist
origin_course_list.courses.each do |course|
course.update!(name: course.name.sub(origin_course_list.name, params[:course_list_name]), course_list_id: o_courselist.id)
end
origin_course_list.destroy
else
origin_course_list.courses.each do |course|
course.update!(name: course.name.sub(origin_course_list.name, params[:course_list_name]))
end
origin_course_list.update!(name: params[:course_list_name])
end
render_ok
end
end

@ -0,0 +1,49 @@
class Admins::CoursesController < Admins::BaseController
before_action :find_course, except: [:index]
def index
default_sort('created_at', 'desc')
courses = Admins::CourseQuery.call(params)
@ended_courses = courses.where(is_end: 1).size
@processed_courses = courses.where(is_end: 0).size
@courses = paginate courses.includes(:school, :students, :attachments, :homework_commons, teacher: :user_extension)
respond_to do |format|
format.js
format.html
format.xlsx do
@courses = courses.includes(:school, :students, :attachments, :homework_commons, :course_acts, teacher: :user_extension)
filename = "课堂列表_#{Time.current.strftime('%Y%m%d%H%M%S')}.xlsx"
render xlsx: 'index', filename: filename
end
end
end
def destroy
if @course.is_delete == 0
@course.delete!
Tiding.create!(user_id: current_user.id, trigger_user_id: current_user.id, container_id: @course.id,
container_type: 'DeleteCourse', tiding_type: 'System', belong_container: @course, extra: @course.name)
end
end
def update
if @course.update_attributes(setting_params)
render_ok
else
redirect_to admins_courses_path
flash[:danger] = "更新失败"
end
end
private
def find_course
@course = Course.find_by!(id: params[:id])
end
def setting_params
params.permit(:homepage_show, :email_notify)
end
end

@ -0,0 +1,25 @@
class Admins::ProjectsController < Admins::BaseController
def index
default_sort('created_at', 'desc')
search = params[:search].to_s.strip
projects = Project.where("name like ?", "%#{search}%")
@projects = paginate projects.includes(:owner, :members, :issues, :versions, :attachments, :project_score)
end
def destroy
project = Project.find_by!(id: params[:id])
ActiveRecord::Base.transaction do
g = Gitlab.client
g.delete_project(project.gpid)
# 删除Trustie版本库记录
repoisitory = Repository.where(project_id: project.id, type: "Repository::Gitlab").first
repoisitory.destroy!
Tiding.where(container_id: project.id, container_type: ["JoinProject", "DealProject", "ReporterJoinProject", "ManagerJoinProject"]).destroy_all
project.destroy!
render_delete_success
end
end
end

@ -24,26 +24,53 @@ class FilesController < ApplicationController
get_category(@course, @course_second_category_id)
@total_count = @attachments.size
@publish_count = @attachments.published.size
@unpublish_count = @total_count - @publish_count
@attachments = @attachments.by_keywords(params[:search])
@attachments =
case @user.course_identity(@course)
when 5
@attachments.published
when 6, 7
@attachments.publiced.published
if @user.course_identity(@course) == 5
member = @course.course_members.find_by(user_id: current_user.id, is_active: 1)
if member.try(:course_group_id).to_i == 0
@attachments = @attachments.published.unified_setting
else
@attachments
not_atta_ids = @course.attachment_group_settings.none_published.where("course_group_id = #{member.try(:course_group_id)}").pluck(:attachment_id)
@attachments = @attachments.where.not(id: not_atta_ids).published
end
elsif @user.course_identity(@course) > 5
@attachments = @attachments.publiced.published
end
@publish_count = @attachments.published.size
@unpublish_count = @total_count - @publish_count
@attachments = @attachments.by_keywords(params[:search])
@attachments = @attachments.page(@page).per(@page_size)
end
def bulk_publish
return normal_status(403, "您没有权限进行操作") if current_user.course_identity(@course) >= 5
@course.attachments.by_ids(@attachment_ids).unpublish.update_all(is_publish: 1, publish_time: Time.now)
tip_exception("请至少选择一个分班") if params[:group_ids].blank? && @course.course_groups.size != 0
attachments = @course.attachments.by_ids(@attachment_ids)
ActiveRecord::Base.transaction do
# 有分班设置时
if @course.course_group_module? && @course.course_groups_count != 0 && params[:group_ids]
group_ids = params[:group_ids]&.reject(&:blank?)
charge_group_ids = @course.charge_group_ids(current_user)
publish_groups = charge_group_ids & group_ids if group_ids
attachments.each do |atta|
if atta.published? && !atta.unified_setting || !atta.published?
create_atta_group_settings atta
atta.update_all(unified_setting: 0) if atta.unified_setting
none_publish_settings = atta.attachment_group_settings.where(course_group_id: publish_groups).none_published
none_publish_settings.update_all(publish_time: Time.now)
end
end
end
# 未发布的资源更新状态
attachments.where(is_publish: 0).update_all(is_publish: 1, publish_time: Time.now)
end
render_ok
end
@ -153,6 +180,10 @@ class FilesController < ApplicationController
attachment.is_publish = @atta_is_publish
attachment.delay_publish = @atta_delay_publish
attachment.publish_time = @atta_publish_time
attachment.unified_setting = @unified_setting
if @unified_setting == 0
attachment_group_setting attachment, params[:group_settings]
end
# attachment.set_publish_time(publish_time) if is_unified_setting
# attachment.set_course_group_publish_time(@course, course_group_publish_times) if @course.course_groups.size > 0 && !is_unified_setting && publish_time.blank?
attachment.save!
@ -195,6 +226,10 @@ class FilesController < ApplicationController
attach_copied_obj.is_publish = @atta_is_publish
attach_copied_obj.delay_publish = @atta_delay_publish
attach_copied_obj.publish_time = @atta_publish_time
attach_copied_obj.unified_setting = @unified_setting
if @unified_setting == 0
attachment_group_setting attach_copied_obj, params[:group_settings]
end
attach_copied_obj.course_second_category_id = course_second_category_id
attach_copied_obj.copy_from = ori.copy_from.nil? ? ori.id : ori.copy_from
if attach_copied_obj.attachtype == nil
@ -234,6 +269,12 @@ class FilesController < ApplicationController
@old_attachment.is_publish = @atta_is_publish
@old_attachment.delay_publish = @atta_delay_publish
@old_attachment.publish_time = @atta_publish_time
@old_attachment.unified_setting = @unified_setting
if @unified_setting == 0
attachment_group_setting @old_attachment, params[:group_settings]
else
@old_attachment.attachment_group_settings.destroy_all
end
if params[:description] && !params[:description].strip.blank? && params[:description] != @old_attachment.description
@old_attachment.description = params[:description]
@ -319,9 +360,40 @@ class FilesController < ApplicationController
def publish_params
tip_exception("缺少发布参数") if params[:delay_publish].blank?
tip_exception("缺少延期发布的时间参数") if params[:delay_publish].to_i == 1 && params[:publish_time].blank?
@atta_is_publish = params[:delay_publish].to_i == 1 && params[:publish_time].to_time > Time.now ? 0 : 1
@unified_setting = 1
if params[:delay_publish].to_i == 1 && @course.course_group_module? && @course.course_groups_count != 0
tip_exception("分班发布设置不能为空") if params[:group_settings].blank?
min_publish_time = params[:group_settings].pluck(:publish_time).reject(&:blank?).min
max_publish_time = params[:group_settings].pluck(:publish_time).reject(&:blank?).max
tip_exception("分班发布设置不能为空") if min_publish_time.blank?
# 分班设置中的时间一样且包含所有分班 则按统一设置处理,否则是非统一设置
@unified_setting = 0 unless min_publish_time == max_publish_time && params[:group_settings].pluck(:group_id).flatten.sort == @course.course_groups.pluck(:id).sort
elsif params[:delay_publish].to_i == 1
tip_exception("缺少延期发布的时间参数") if params[:publish_time].blank?
min_publish_time = params[:publish_time]
end
@atta_is_publish = params[:delay_publish].to_i == 1 && min_publish_time.to_time > Time.now ? 0 : 1
@atta_delay_publish = params[:delay_publish].to_i
@atta_publish_time = params[:delay_publish].to_i == 1 && params[:publish_time] ? params[:publish_time] : Time.now
@atta_publish_time = params[:delay_publish].to_i == 1 ? min_publish_time : Time.now
end
def create_atta_group_settings atta
if atta.attachment_group_settings.size != @course.course_groups.size
@course.course_groups.where.not(id: atta.attachment_group_settings.pluck(:course_group_id)).each do |group|
atta.attachment_group_settings << AttachmentGroupSetting.new(course_group_id: group.id, course_id: @course.id,
publish_time: atta.publish_time)
end
end
end
def attachment_group_setting attachment, group_setting
create_atta_group_settings attachment
group_setting.each do |setting|
tip_exception("分班id不能为空") if setting[:group_id].length == 0
tip_exception("发布时间不能为空") if setting[:publish_time].blank?
AttachmentGroupSetting.where(attachment_id: attachment.id, course_group_id: setting[:group_id]).
update_all(publish_time: setting[:publish_time])
end
end
end

@ -13,7 +13,8 @@ class HomeworkCommonsController < ApplicationController
:reference_answer, :publish_groups, :end_groups, :alter_name, :update_explanation,
:update_score, :update_student_score]
before_action :user_course_identity
before_action :homework_publish, only: [:show, :works_list, :code_review_results, :show_comment, :settings, :reference_answer, :update_student_score]
before_action :homework_publish, only: [:show, :works_list, :code_review_results, :show_comment, :settings, :reference_answer,
:update_student_score]
before_action :teacher_allowed, only: [:new, :edit, :create, :update, :shixuns, :subjects, :create_shixun_homework,
:publish_homework, :end_homework, :set_public, :choose_category, :move_to_category,
:choose_category, :create_subject_homework, :multi_destroy, :group_list, :homework_code_repeat,

@ -23,6 +23,7 @@ class Attachment < ApplicationRecord
scope :mine, -> (author_id) { where(author_id: author_id) }
scope :simple_columns, -> { select(:id, :filename, :filesize, :created_on, :cloud_url, :author_id, :content_type) }
scope :search_by_container, -> (ids) {where(container_id: ids)}
scope :unified_setting, -> {where("unified_setting = ? ", 1)}
validates_length_of :description, maximum: 100

@ -3,4 +3,6 @@ class AttachmentGroupSetting < ActiveRecord::Base
belongs_to :course_group
belongs_to :course
scope :none_published, -> {where("attachment_group_settings.publish_time IS NULL OR attachment_group_settings.publish_time > ?", Time.now)}
end

@ -31,6 +31,7 @@ class Course < ApplicationRecord
has_many :graduation_groups, dependent: :destroy
has_many :course_members, dependent: :destroy
has_many :students, -> { course_students }, class_name: 'CourseMember'
has_many :teacher_course_members, -> { teachers_and_admin }, class_name: 'CourseMember'
has_many :teacher_users, through: :teacher_course_members, source: :user
has_many :course_messages, dependent: :destroy
@ -114,6 +115,10 @@ class Course < ApplicationRecord
course_members.where(user_id: user_id, role: role).exists?
end
def course_group_module?
course_modules.exists?(module_type: "course_group", hidden: 0)
end
# 作业对应的子目录/父目录名称
def category_info type
course_module = course_modules.find_by(module_type: type)
@ -234,11 +239,6 @@ class Course < ApplicationRecord
course_members.where(role: %i[CREATOR PROFESSOR])
end
# 课堂学生
def students
course_members.where(role: %i[STUDENT])
end
# 更新课程的访问人数
def update_visits(new_visits)
update_attributes(visits: new_visits)
@ -367,6 +367,23 @@ class Course < ApplicationRecord
count = course_challeng_count == 0 ? 0 : ((my_challenge_count.to_f / course_challeng_count).round(2) * 100).to_i
end
# 课堂实训作业的评测次数
def evaluate_count
course_user_ids = students.pluck(:user_id)
shixun_ids = homework_commons.joins(:homework_commons_shixun).where(homework_type: 4).pluck(:shixun_id)
return 0 if shixun_ids.blank?
Game.joins(:challenge).where(challenges: {shixun_id: shixun_ids}, games: {user_id: course_user_ids}).sum(:evaluate_count)
end
def max_activity_time
course_acts.pluck(:updated_at).max
end
# 课堂作业数
def course_homework_count type
homework_commons.select{|homework| homework.homework_type == type}.size
end
private
#创建课程后,给该用户发送消息

@ -5,4 +5,5 @@ class CourseList < ApplicationRecord
has_many :exercise_banks
has_many :gtask_banks
has_many :gtopic_banks
belongs_to :user
end

@ -7,6 +7,9 @@ class Project < ApplicationRecord
has_many :issues
has_many :user_grades, dependent: :destroy
has_many :attachments, as: :container, dependent: :destroy
has_one :project_score, dependent: :destroy
has_many :versions, -> { order("versions.effective_date DESC, versions.name DESC") }, dependent: :destroy
after_create do
SyncTrustieJob.perform_later("project", 1) if allow_sync_to_trustie?

@ -0,0 +1,3 @@
class Version < ApplicationRecord
belongs_to :project
end

@ -0,0 +1,30 @@
class Admins::CourseListQuery < ApplicationQuery
include CustomSortable
attr_reader :params
sort_columns :created_at, default_by: :created_at, default_direction: :desc
def initialize(params)
@params = params
end
def call
course_lists = CourseList.all
# 关键字模糊查询
keyword = params[:keyword].to_s.strip
if keyword.present?
search_type = params[:search_type] || "0"
case search_type
when "0"
course_lists = course_lists.joins(:user)
.where('CONCAT(lastname, firstname) like :keyword', keyword: "%#{keyword}%")
when "1"
course_lists = course_lists.where('name like :keyword', keyword: "%#{keyword}%")
end
end
custom_sort(course_lists, params[:sort_by], params[:sort_direction])
end
end

@ -0,0 +1,44 @@
class Admins::CourseQuery < ApplicationQuery
include CustomSortable
attr_reader :params
sort_columns :created_at, default_by: :created_at, default_direction: :desc, default_table: 'courses'
def initialize(params)
@params = params
end
def call
courses = Course.all
courses = courses.where(id: params[:id]) if params[:id].present?
# 状态过滤
status =
case params[:status].to_s.strip
when 'processing' then 0
when 'ended' then 1
end
courses = courses.where(is_end: status) if status
# 单位
if params[:school_id].present?
courses = courses.where(school_id: params[:school_id])
end
# 首页展示
if params[:homepage_show].present? && params[:homepage_show].to_s == 'true'
courses = courses.where(homepage_show: true)
end
# 关键字
keyword = params[:keyword].to_s.strip
if keyword
sql = 'CONCAT(lastname, firstname) LIKE :keyword OR courses.name LIKE :keyword OR course_lists.name LIKE :keyword'
courses = courses.joins(:teacher, :course_list).where(sql, keyword: "%#{keyword}%")
end
custom_sort(courses, params[:sort_by], params[:sort_direction])
end
end

@ -0,0 +1,22 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('课程列表') %>
<% end %>
<div class="box search-form-container course-list-list-form">
<%= form_tag(admins_course_lists_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %>
<div class="form-group">
<label>搜索类型:</label>
<% auto_trial_options = [['创建者姓名', 0], ['课程名称', 1]] %>
<%= select_tag(:search_type, options_for_select(auto_trial_options), class: 'form-control') %>
</div>
<%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '输入关键字搜索') %>
<%= submit_tag('搜索', class: 'btn btn-primary ml-3','data-disable-with': '搜索中...') %>
<%= link_to "清除",admins_course_lists_path,class: "btn btn-default",id:"course-lists-clear-search",'data-disable-with': '清除中...' %>
<% end %>
</div>
<div class="box admin-list-container course-list-list-container">
<%= render partial: 'admins/course_lists/shared/list', locals: { courses: @course_lists } %>
</div>
<%= render 'admins/course_lists/shared/merge_course_list_modal' %>

@ -0,0 +1 @@
$(".course-list-list-container").html("<%= j render partial: 'admins/course_lists/shared/list', locals: { courses: @course_lists }%>");

@ -0,0 +1,37 @@
<table class="table table-hover text-center shixuns-list-table">
<thead class="thead-light">
<th width="4%">序号</th>
<th width="8%">ID</th>
<th width="38%" class="text-left">课程名称</th>
<th width="10%">课堂数</th>
<th width="10%">创建者</th>
<th width="12%"><%= sort_tag('创建时间', name: 'created_at', path: admins_course_lists_path) %></th>
<th width="18%">操作</th>
</thead>
<tbody>
<% if courses.present? %>
<% courses.each_with_index do |course_list,index| %>
<tr id="course-list-item-<%= course_list.id %>">
<td><%= list_index_no(@params_page.to_i, index) %></td>
<td><%= course_list.id %></td>
<td class="text-left"><%= course_list.name %></td>
<% course_count = course_list.courses.size %>
<td><%= course_count %></td>
<td><%= link_to course_list.user.try(:real_name),"/users/#{course_list.user.try(:login)}",target:'_blank' %></td>
<td><%= format_time course_list.created_at %></td>
<td class="operate">
<% if course_count == 0 %>
<%= delete_link '删除', admins_course_list_path(course_list, element: ".course-list-item-#{course_list.id}"), class: 'delete-department-action' %>
<% end %>
<%= javascript_void_link '修改', class: 'action', data: { course_list_id: course_list.id,
toggle: 'modal', target: '.admin-merge-course-list-modal', url: merge_admins_course_lists_path } %>
</td>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: courses } %>

@ -0,0 +1,29 @@
<div class="modal fade admin-merge-course-list-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">修改课程</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form class="admin-merge-course-list-form" data-url="<%= merge_admins_course_lists_path %>">
<%= hidden_field_tag(:origin_course_list_id, nil) %>
<div class="form-group d-flex">
<label for="course_list_id" class="col-form-label">更改为:</label>
<div class="d-flex flex-column-reverse w-75">
<input id="course_list_name" name="course_list_name" placeholder="请输入课程名称" class="form-control">
</div>
</div>
<div class="error text-danger"></div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary submit-btn">确认</button>
</div>
</div>
</div>
</div>

@ -0,0 +1,2 @@
alert("删除成功");
$(".course-item-<%= @course.id %>").find(".delete-course-action").remove();

@ -0,0 +1,34 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('课堂列表') %>
<% end %>
<div class="box search-form-container course-list-form">
<%= form_tag(admins_courses_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %>
<div class="form-group mr-1">
<label for="status">状态:</label>
<% status_options = [['全部', ''], ["正在进行(#{@processed_courses})", 'processing'], ["已结束#{@ended_courses}", 'ended']] %>
<%= select_tag(:status, options_for_select(status_options), class: 'form-control') %>
</div>
<div class="form-group col-12 col-md-3">
<label for="school_name">单位:</label>
<%= select_tag :school_id, options_for_select([''], params[:school_id]), class: 'form-control school-select flex-1' %>
</div>
<%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-12 col-md-2 mr-3', placeholder: '创建者/课堂名称/课程名称检索') %>
<div class="form-check mr-2">
<%= hidden_field_tag(:homepage_show, false, id:'') %>
<%= check_box_tag(:homepage_show, true, params[:homepage_show].to_s == 'true', class: 'form-check-input course-homepage-show') %>
<label class="form-check-label" for="homepage_show">只看首页展示</label>
</div>
<%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
<input type="reset" class="btn btn-secondary clear-btn" value="清空"/>
<% end %>
<a href="javascript:void(0)" class="btn btn-primary" id="course-export" data-disable-with = '导出中...'>导出</a>
</div>
<div class="box admin-list-container course-list-container">
<%= render partial: 'admins/courses/shared/list', locals: { courses: @courses } %>
</div>

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

@ -0,0 +1,29 @@
wb = xlsx_package.workbook
wb.styles do |s|
blue_cell = s.add_style :bg_color => "FAEBDC", :sz => 10,:height => 25,:b => true, :border => { :style => :thin, :color =>"000000" },:alignment => {wrap_text: true,:horizontal => :center,:vertical => :center}
wb.add_worksheet(name: "课堂列表") do |sheet|
sheet.add_row %w(ID 课堂名称 成员 资源 普通作业 分组作业 实训作业 试卷 评测次数 私有 状态 单位 创建者 创建时间 动态时间), :height => 25,:style => blue_cell
@courses.each do |course|
data = [
course.id,
course.name,
course.course_members_count,
get_attachment_count(course, 0),
course.course_homework_count(1),
course.course_homework_count(3),
course.course_homework_count(4),
course.exercises_count,
course.evaluate_count,
course.is_public == 1 ? "--" : "√",
course.is_end ? "已结束" : "正在进行",
course.school&.name,
course.teacher&.real_name,
course.created_at&.strftime('%Y-%m-%d %H:%M'),
course.max_activity_time ? course.max_activity_time&.strftime('%Y-%m-%d %H:%M') : "--"
]
sheet.add_row(data)
end
end
end

@ -0,0 +1,62 @@
<table class="table table-hover text-center subject-list-table">
<thead class="thead-light">
<tr>
<th width="4%">ID</th>
<th width="10%" class="text-left">课堂名称</th>
<th width="6%">成员</th>
<th width="4%">资源</th>
<th width="4%">普通作业</th>
<th width="4%">分组作业</th>
<th width="4%">实训作业</th>
<th width="4%">试卷</th>
<th width="7%">评测次数</th>
<th width="4%">私有</th>
<th width="6%">状态</th>
<th width="10%">单位</th>
<th width="7%">创建者</th>
<th width="10%"><%= sort_tag('创建时间', name: 'created_at', path: admins_courses_path) %></th>
<th width="4%">首页</th>
<th width="6%">邮件通知</th>
<th width="6%">操作</th>
</tr>
</thead>
<tbody>
<% if courses.present? %>
<% courses.each do |course| %>
<tr class="course-item-<%= course.id %>">
<td><%= course.id %></td>
<td class="text-left">
<%= link_to(course.name, "/courses/#{course.id}", target: '_blank') %>
</td>
<td><%= course.course_members_count %></td>
<td><%= get_attachment_count(course, 0) %></td>
<td><%= course.course_homework_count(1) %></td>
<td><%= course.course_homework_count(3) %></td>
<td><%= course.course_homework_count(4) %></td>
<td><%= course.exercises_count %></td>
<td><%= course.evaluate_count %></td>
<td><%= course.is_public == 1 ? "--" : "√" %></td>
<td><%= course.is_end ? "已结束" : "正在进行" %></td>
<td><%= course.school&.name %></td>
<td><%= course.teacher&.real_name %></td>
<td><%= course.created_at&.strftime('%Y-%m-%d %H:%M') %></td>
<td>
<%= check_box_tag :homepage_show,!course.homepage_show,course.homepage_show,remote:true,data:{id:course.id},class:"course-setting-form" %>
</td>
<td>
<%= check_box_tag :email_notify,!course.email_notify,course.email_notify,remote:true,data:{id:course.id},class:"course-setting-form" %>
</td>
<td class="action-container">
<% if course.is_delete == 0 %>
<%= delete_link '删除', admins_course_path(course, element: ".course-item-#{course.id}"), class: 'delete-course-action' %>
<% end %>
</td>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: courses } %>

@ -21,7 +21,7 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary submit-btn">确认</button>
<a href="javascript:void(0)" class="btn btn-primary submit-btn" data-disable-with = '导入中...'>确认</a>
</div>
</div>
</div>

@ -0,0 +1,2 @@
alert("删除成功");
$(".course-item-<%= @course.id %>").find(".delete-course-action").remove();

@ -0,0 +1,15 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('项目列表') %>
<% end %>
<div class="box search-form-container project-list-form">
<%= form_tag(admins_projects_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %>
<%= text_field_tag(:search, params[:search], class: 'form-control col-12 col-md-2 mr-3', placeholder: '项目名称检索') %>
<%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
<input type="reset" class="btn btn-secondary clear-btn" value="清空"/>
<% end %>
</div>
<div class="box admin-list-container project-list-container">
<%= render partial: 'admins/projects/shared/list', locals: { projects: @projects } %>
</div>

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

@ -0,0 +1,48 @@
<table class="table table-hover text-center subject-list-table">
<thead class="thead-light">
<tr>
<th width="4%">ID</th>
<th width="15%" class="text-left">项目名称</th>
<th width="6%">公开</th>
<th width="5%">issue</th>
<th width="5%">资源</th>
<th width="6%">版本库</th>
<th width="8%">PullRequest</th>
<th width="6%">里程碑</th>
<th width="10%">成员</th>
<th width="10%">管理员</th>
<th width="15%"><%= sort_tag('创建时间', name: 'created_at', path: admins_projects_path) %></th>
<th width="10%">操作</th>
</tr>
</thead>
<tbody>
<% if projects.present? %>
<% projects.each do |project| %>
<tr class="project-item-<%= project.id %>">
<td><%= project.id %></td>
<td class="text-left">
<%= link_to(project.name, "/projects/#{project.id}", target: '_blank') %>
</td>
<td><%= project.is_public ? '√' : '' %></td>
<td><%= project.issues.size %></td>
<td><%= project.attachments.size %></td>
<td><%= project.project_score.try(:changeset_num).to_i %></td>
<td><%= project.project_score.try(:pull_request_num).to_i %></td>
<td><%= project.versions.size %></td>
<td><%= project.members.size %></td>
<td>
<%= project.owner ? link_to(project.owner&.real_name, "/users/#{project.owner&.login}", target: '_blank') : "" %>
</td>
<td><%= project.created_on&.strftime('%Y-%m-%d %H:%M') %></td>
<td class="action-container">
<%= delete_link '删除', admins_project_path(project, element: ".project-item-#{project.id}"), class: 'delete-project-action' %>
</td>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: projects } %>

@ -32,8 +32,17 @@
<li>
<%= sidebar_item_group('#subject-submenu', '实践课程', icon: 'th-list') do %>
<li><%= sidebar_item(admins_subjects_path, '课程列表', icon: 'cog', controller: 'admins-subjects') %></li>
<% end %>
<li><%= sidebar_item(admins_subjects_path, '课程列表', icon: 'cog', controller: 'admins-subjects') %></li>
<% end %>
</li>
<li>
<%= sidebar_item_group('#course-submenu', '课堂管理', icon: 'book') do %>
<li><%= sidebar_item(admins_course_lists_path, '课程列表', icon: 'list', controller: 'admins-course_lists') %></li>
<li><%= sidebar_item(admins_courses_path, '课堂列表', icon: 'clone', controller: 'admins-courses') %></li>
<!-- <li><%#= sidebar_item(admins_mirror_repositories_path, '镜像管理', icon: 'cubes', controller: 'admins-mirror_repositories') %></li>-->
<li><%= sidebar_item(admins_projects_path, '项目列表', icon: 'database', controller: 'admins-projects') %></li>
<% end %>
</li>
<li>

@ -1,3 +1,3 @@
json.partial! 'attachments/attachment', attachment: @file
# json.partial! "files/course_groups", attachment_group_settings: @file.attachment_group_settings
json.partial! "files/course_groups", attachment_group_settings: @file.attachment_group_settings
json.partial! "attachment_histories/list", attachment_histories: @attachment_histories

@ -1173,6 +1173,14 @@ Rails.application.routes.draw do
resources :partners, only: [:index, :create, :destroy] do
resources :customers, only: [:index, :create, :destroy]
end
resources :course_lists, only: [:index, :destroy] do
post :merge, on: :collection
end
resources :courses, only: [:index, :destroy, :update]
resources :projects, only: [:index, :destroy]
end
namespace :cooperative do

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

@ -43,7 +43,23 @@ class Fileslists extends Component{
course_second_categories:[]
}
}
getcourse_groupslists=()=>{
let coursesId=this.props.match.params.coursesId;
let url="/courses/"+coursesId+"/all_course_groups.json";
axios.get(url).then((response) => {
if(response.status===200){
this.setState({
course_groups:response.data.course_groups
})
}
}).catch((error) => {
console.log(error)
});
}
componentDidMount=()=>{
this.getcourse_groupslists()
this.setState({
isSpin:true,
checkBoxValues:[],
@ -81,6 +97,7 @@ class Fileslists extends Component{
}
componentDidUpdate = (prevProps) => {
if(prevProps.match.params.main_id != this.props.match.params.main_id){
this.getcourse_groupslists()
this.setState({
isSpin:true,
checkBoxValues:[],
@ -95,6 +112,7 @@ class Fileslists extends Component{
}
}
if(prevProps.match.params.Id != this.props.match.params.Id){
this.getcourse_groupslists()
this.setState({
isSpin:true,
checkBoxValues:[],
@ -610,10 +628,12 @@ class Fileslists extends Component{
}
let starttime= this.props.getNowFormatDates(1);
let endtime=this.props.getNowFormatDates(2);
this.setState({
modalname:"立即发布",
modaltype:this.state.course_groups===null||this.state.course_groups.length===0?2:1,
visible:true,
typs:"start",
typs:"end",
Topval:"学生将能立即收到资源",
// Botvalleft:"暂不发布",
// Botval:`本操作只对"未发布"的分班有效`,
@ -624,7 +644,9 @@ class Fileslists extends Component{
Savesname:"立即发布",
Cancel:this.homeworkhide,
Saves:this.homeworkstartend,
course_groups:this.state.course_groups,
})
}
// 立即发布
homeworkstartend=(ds,endtime)=>{
@ -633,6 +655,7 @@ class Fileslists extends Component{
let url ="/files/bulk_publish.json";
axios.put(url, {
course_id:coursesId,
group_ids:ds,
ids :checkBoxValues,
}).then((result)=>{
if(result.status===200){
@ -674,6 +697,12 @@ class Fileslists extends Component{
starttimes:undefined,
})
}
getcourse_groupslist=(id)=>{
this.setState({
course_groupslist:id
})
}
render(){
let { searchValue,
checkBoxValues,
@ -705,7 +734,7 @@ class Fileslists extends Component{
let category_id= this.props.match.params.category_id;
// console.log(this.state.course_groups)
return(
<React.Fragment >

@ -91,7 +91,6 @@ class HomeworkModal extends Component{
//勾选实训
shixunhomeworkedit=(list)=>{
this.setState({
group_ids:list
})
@ -108,7 +107,6 @@ class HomeworkModal extends Component{
}
propsSaves=(ds,endtime)=>{
if(ds.length ===0&&endtime === ""){
this.props.Saves()
}else{

@ -42,7 +42,12 @@ class Selectresource extends Component{
getallfiles:false,
searchtype:'getallfiles',
Radiovalue:0,
datatime:undefined
datatime:undefined,
course_group_publish_times:[
{
group_id : [],
publish_time :undefined,
}],
}
}
componentDidMount() {
@ -212,7 +217,7 @@ class Selectresource extends Component{
savecouseShixunModal=()=>{
let {patheditarry,datatime,Radiovalue}=this.state;
let {patheditarry,datatime,Radiovalue,course_group_publish_times}=this.state;
let {coursesId,attachmentId}=this.props;
let url="/files/import.json";
@ -227,7 +232,7 @@ class Selectresource extends Component{
})
}
if(this.state.Radiovalue===1){
if(this.state.Radiovalue===1&&this.props.course_groups.length===0){
if(datatime===undefined||datatime===null||datatime=== ""){
this.setState({
Radiovaluetype:true
@ -245,7 +250,8 @@ class Selectresource extends Component{
attachment_ids:patheditarry,
course_second_category_id:this.props.coursesidtype===undefined||this.props.coursesidtype==="node"?0:attachmentId,
delay_publish:Radiovalue,
publish_time:Radiovalue===1?datatime:undefined
publish_time:this.props.course_groups.length===0?Radiovalue===1?datatime===undefined? undefined:datatime:undefined:undefined,
group_settings:this.props.course_groups.length===0?undefined:course_group_publish_times
}
).then((response) => {
if(response.data.status===0){
@ -271,15 +277,58 @@ class Selectresource extends Component{
});
}
onChangeTimepublish= (date, dateString) => {
onChangeTimepublish= (date, dateString,key,type) => {
if(type===1){
this.setState({
datatime:handleDateString(dateString),
})
}else if(type===2){
let {course_group_publish_times}=this.state;
let newgroup_publish=course_group_publish_times;
for(var i=0; i<newgroup_publish.length; i++){
if(i===parseInt(key)){
newgroup_publish[i].publish_time=handleDateString(dateString);
}
}
this.setState({
course_group_publish_times:newgroup_publish,
})
}
}
addgrouppublish=()=>{
let newlist=this.state.course_group_publish_times;
newlist.push( {
group_id : undefined,
publish_time :undefined,
})
this.setState({
datatime:handleDateString(dateString),
course_group_publish_times:newlist
})
}
deletegrouppublish=(key)=>{
let newlist=this.state.course_group_publish_times;
newlist.splice(key,1);
this.setState({
course_group_publish_times:newlist
})
}
selectassigngroups=(e,index,key)=>{
debugger
let {course_group_publish_times}=this.state;
let newgroup_publish=course_group_publish_times;
for(var i=0; i<newgroup_publish.length; i++){
if(i===parseInt(key)){
newgroup_publish[i].group_id=e;
}
}
this.setState({
course_group_publish_times:newgroup_publish,
})
}
render(){
let {Searchvalue,type,Resourcelist,hometypepvisible,patheditarry,datatime}=this.state;
let {Searchvalue,type,Resourcelist,hometypepvisible,patheditarry,datatime,course_group_publish_times}=this.state;
let {visible}=this.props;
const antIcon = <Icon type="loading" style={{ fontSize: 24 }} spin />;
const radioStyle = {
@ -287,6 +336,8 @@ class Selectresource extends Component{
height: '30px',
lineHeight: '30px',
};
console.log(course_group_publish_times)
return(
<div>
{/*提示*/}
@ -325,12 +376,12 @@ class Selectresource extends Component{
}
#shixun_tab_div{
padding: 0 30px;
padding-top:30px;
padding-top:15px;
}
.search-news{
width: 237px!important;
height: 30px;
margin-bottom: 30px;
margin-bottom: 15px;
}
`}</style>
@ -373,7 +424,7 @@ class Selectresource extends Component{
height: 37px;
}
.scrollbox{
height:250px !important;
height:145px !important;
}
.selectfilsbox{
height: 50px;
@ -424,7 +475,7 @@ class Selectresource extends Component{
<Tooltip placement="bottom" title={this.props.isStudent()===true?"不支持学生延迟发布":""} >
<Radio style={radioStyle} value={1} className={"fl"} disabled={this.props.isStudent()}>
<span className={"mr5"}>延期发布</span>
<DatePicker
{this.props.course_groups.length>0?"":<DatePicker
dropdownClassName="hideDisable"
showTime={{ format: 'HH:mm' }}
locale={locale}
@ -438,18 +489,100 @@ class Selectresource extends Component{
disabledTime={disabledDateTime}
disabledDate={disabledDate}
disabled={this.state.Radiovalue===1?false:true}
/>
/>}
</Radio>
</Tooltip>
<span className={"fl mt5 color-grey-c"}>(按照设置的时间定时发布)</span>
</Radio.Group>
<style>{`
.Selectleft20{
margin-left: 20px !important;
width: 176px;
height: 40px;
}
#startimes .ant-calendar-picker-icon{
margin-top:-11px;
}
.resourcebox{
max-height:106px;
overflow: auto;
}
.ml70{
margin-left:70px;
}
`}</style>
{this.props.course_groups.length>0?this.props.isStudent()===true?"":<div className={"resourcebox"}>
{this.state.Radiovalue===1?<style>
{
`
.ant-input, .ant-input .ant-input-suffix {
background-color: #fff!important;
}
`
}
</style>:""}
{
course_group_publish_times.map((item,key)=>{
return(
<div className={"mt10"} key={key}>
<Select
mode="multiple"
className={"ml70"}
disabled={this.state.Radiovalue===1?false:true}
placeholder="请选择分班名称"
value={item.group_id}
style={{ width: 200 }}
onChange={(e,index)=>this.selectassigngroups(e,index,key)}
>
{this.props.course_groups.map((items,keys)=>{
let type=0
{course_group_publish_times.map((itsem,kesy)=>{
if(itsem.group_id===items.id){
type=1
return
}
})}
if(type===0){
return(
<Option value={items.name} key={keys} id={items.id}>{items.name}</Option>
)
}
})}
</Select>
<DatePicker
disabled={this.state.Radiovalue===1?false:true}
dropdownClassName="hideDisable"
showTime={{ format: 'HH:mm' }}
locale={locale}
showToday={false}
format={dateFormat}
placeholder="请选择发布时间"
id={"startimes"}
className={"Selectleft20 "}
width={"200px"}
value={item.publish_time===undefined||item.publish_time===""?undefined:moment(item.publish_time, dateFormat)}
onChange={(e,index)=>this.onChangeTimepublish(e,index,key,2)}
// onChange={ this.onChangeTimepublish }
disabledTime={disabledDateTime}
disabledDate={disabledDate}
/>
{key!=0?<i className="iconfont icon-shanchu color-grey-c font-14 font-n ml20" onClick={()=>this.deletegrouppublish(key)}></i>:""}
{key+1===this.props.course_groups.length?"":<i className="iconfont icon-tianjiafangda color-green ml15" onClick={this.addgrouppublish}></i>}
</div>
)
})
}
</div>:""}
</div>
{this.state.patheditarrytype===true?<p className={"color-red ml20"}>请选择资源</p>:""}
{this.state.Radiovaluetype===true?<p className={"color-red ml20"}>发布时间不能为空</p>:""}
<div className="mt20 marginauto clearfix edu-txt-center">
<a className="pop_close task-btn mr30 margin-tp26" onClick={this.hidecouseShixunModal}>取消</a>
<a className="task-btn task-btn-orange margin-tp26" id="submit_send_shixun" onClick={this.savecouseShixunModal}>确定</a>
<a className="pop_close task-btn mr30 margin-tp26" onClick={this.hidecouseShixunModal}>取消</a>
<a className="task-btn task-btn-orange margin-tp26" id="submit_send_shixun" onClick={this.savecouseShixunModal}>确定</a>
</div>
</div>
</Spin>

@ -14,12 +14,12 @@ function range(start, end) {
}
return result;
}
function disabledDateTime() {
return {
// disabledHours: () => range(0, 24).splice(4, 20),
disabledMinutes: () => range(1, 30).concat(range(31, 60)),
// disabledSeconds: () => [0, 60],
};
// disabledSeconds: () => range(0,60)
}
}
function disabledDate(current) {
@ -45,8 +45,8 @@ class Sendresource extends Component{
// moment(new Date()).format('YYYY-MM-DD HH:mm:ss'),
course_group_publish_times:[
{
course_group_id : undefined,
publish_time :""
group_id : undefined,
publish_time :undefined,
}],
course_groups:undefined,
course_groups_count:undefined,
@ -127,7 +127,7 @@ class Sendresource extends Component{
}
Saves=()=>{
let {fileList,description,is_public,datatime,Radiovalue} =this.state;
let {fileList,description,is_public,datatime,Radiovalue,course_group_publish_times} =this.state;
let newfileList=[];
for(var list of fileList){
@ -141,7 +141,7 @@ class Sendresource extends Component{
return
}
if(this.state.Radiovalue===1){
if(this.state.Radiovalue===1&&this.props.course_groups.length===0){
if(datatime===undefined||datatime===null||datatime=== ""){
this.setState({
Radiovaluetype:true
@ -156,6 +156,7 @@ class Sendresource extends Component{
if(description===undefined){
}else if(description.length>100){
@ -176,9 +177,10 @@ class Sendresource extends Component{
course_second_category_id:this.props.coursesidtype===undefined||this.props.coursesidtype==="node"?0:attachmentId,
attachment_ids:newfileList,
is_public:is_public,
publish_time:Radiovalue===1?datatime===undefined? undefined:datatime:undefined,
publish_time:this.props.course_groups.length===0?Radiovalue===1?datatime===undefined? undefined:datatime:undefined:"",
description:description,
delay_publish:Radiovalue,
group_settings:this.props.course_groups.length===0?"":course_group_publish_times
}).then((result)=>{
if(result.data.status===0){
@ -237,10 +239,42 @@ class Sendresource extends Component{
Radiovalue: e.target.value,
});
}
addgrouppublish=()=>{
let newlist=this.state.course_group_publish_times;
newlist.push( {
group_id : undefined,
publish_time :undefined,
})
this.setState({
course_group_publish_times:newlist
})
}
deletegrouppublish=(key)=>{
let newlist=this.state.course_group_publish_times;
newlist.splice(key,1);
this.setState({
course_group_publish_times:newlist
})
}
selectassigngroups=(e,index,key)=>{
let {course_group_publish_times}=this.state;
let newgroup_publish=course_group_publish_times;
for(var i=0; i<newgroup_publish.length; i++){
if(i===parseInt(key)){
newgroup_publish[i].group_id=e;
}
}
this.setState({
course_group_publish_times:newgroup_publish,
})
}
render(){
let { newfileListtype,descriptiontype,
is_public,
datatime,
course_group_publish_times
}=this.state;
const uploadProps = {
@ -265,6 +299,7 @@ class Sendresource extends Component{
};
//console.log(this.state.course_group_publish_times)
return(
<div>
{/*提示*/}
@ -401,8 +436,8 @@ class Sendresource extends Component{
<Tooltip placement="bottom" title={this.props.isStudent()===true?"不支持学生延迟发布":""}>
<Radio style={radioStyle} value={1} className={"fl"} disabled={this.props.isStudent()}>
<span className={"mr5"}>延期发布</span>
{this.props.course_groups.length===0?
<DatePicker
dropdownClassName="hideDisable"
showTime={{ format: 'HH:mm' }}
locale={locale}
@ -416,11 +451,93 @@ class Sendresource extends Component{
disabledTime={disabledDateTime}
disabledDate={disabledDate}
disabled={this.state.Radiovalue===1?false:true}
/>
/>:""}
</Radio>
</Tooltip>
<span className={"fl mt5 color-grey-c"}>(按照设置的时间定时发布)</span>
</Radio.Group>
<style>{`
.Selectleft20{
margin-left: 20px !important;
width: 176px;
height: 40px;
}
#startimes .ant-calendar-picker-icon{
margin-top:-11px;
}
.resourcebox{
max-height:150px;
overflow: auto;
}
.ml70{
margin-left:70px;
}
`}</style>
{this.props.course_groups.length>0?this.props.isStudent()===true?"":<div className={"resourcebox"}>
{this.state.Radiovalue===1?<style>
{
`
.ant-input, .ant-input .ant-input-suffix {
background-color: #fff!important;
}
`
}
</style>:""}
{
course_group_publish_times.map((item,key)=>{
return(
<div className={"mt10"} key={key}>
<Select
mode="multiple"
disabled={this.state.Radiovalue===1?false:true}
placeholder="请选择分班名称"
value={item.group_id}
style={{ width: 200 }}
onChange={(e,index)=>this.selectassigngroups(e,index,key)}
>
{this.props.course_groups.map((items,keys)=>{
let type=0
{course_group_publish_times.map((itsem,kesy)=>{
if(itsem.group_id===items.id){
type=1
return
}
})}
if(type===0){
return(
<Option value={items.name} key={keys} id={items.id}>{items.name}</Option>
)
}
})}
</Select>
<DatePicker
disabled={this.state.Radiovalue===1?false:true}
dropdownClassName="hideDisable"
showTime={{ format: 'HH:mm' }}
locale={locale}
showToday={false}
format={dateFormat}
placeholder="请选择发布时间"
id={"startimes"}
className={"Selectleft20 "}
width={"200px"}
value={item.publish_time===undefined||item.publish_time===""?undefined:moment(item.publish_time, dateFormat)}
onChange={(e,index)=>this.onChangeTimepublish(e,index,key,2)}
// onChange={ this.onChangeTimepublish }
disabledTime={disabledDateTime}
disabledDate={disabledDate}
/>
{key!=0?<i className="iconfont icon-shanchu color-grey-c font-14 font-n ml20" onClick={()=>this.deletegrouppublish(key)}></i>:""}
{key+1===this.props.course_groups.length?"":<i className="iconfont icon-tianjiafangda color-green ml15" onClick={this.addgrouppublish}></i>}
</div>
)
})
}
</div>:""}
</div>

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe Version, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end
Loading…
Cancel
Save