diff --git a/Gemfile b/Gemfile
index ac2778c59..7a37b7c44 100644
--- a/Gemfile
+++ b/Gemfile
@@ -98,3 +98,7 @@ gem 'aasm'
gem 'enumerize'
gem 'diffy'
+
+# oauth2
+gem 'omniauth', '~> 1.9.0'
+gem 'omniauth-oauth2', '~> 1.6.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index aabf3ffba..4b7902353 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -121,7 +121,7 @@ GEM
grape-entity (0.7.1)
activesupport (>= 4.0)
multi_json (>= 1.3.2)
- hashie (3.6.0)
+ hashie (3.5.7)
htmlentities (4.3.4)
httparty (0.16.2)
multi_xml (>= 0.5.2)
@@ -179,6 +179,12 @@ GEM
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
+ omniauth (1.9.0)
+ hashie (>= 3.4.6, < 3.7.0)
+ rack (>= 1.6.2, < 3)
+ omniauth-oauth2 (1.6.0)
+ oauth2 (~> 1.1)
+ omniauth (~> 1.9)
pdfkit (0.8.4.1)
popper_js (1.14.5)
public_suffix (4.0.1)
@@ -381,6 +387,8 @@ DEPENDENCIES
listen (>= 3.0.5, < 3.2)
mysql2 (>= 0.4.4, < 0.6.0)
oauth2
+ omniauth (~> 1.9.0)
+ omniauth-oauth2 (~> 1.6.0)
pdfkit
puma (~> 3.11)
rack-cors
diff --git a/app/assets/javascripts/admins/laboratories/edit.js b/app/assets/javascripts/admins/laboratories/edit.js
new file mode 100644
index 000000000..63b26bbe0
--- /dev/null
+++ b/app/assets/javascripts/admins/laboratories/edit.js
@@ -0,0 +1,86 @@
+$(document).on('turbolinks:load', function() {
+ if ($('body.admins-laboratory-settings-show-page, body.admins-laboratory-settings-update-page').length > 0) {
+ var $container = $('.edit-laboratory-setting-container');
+ var $form = $container.find('.edit_laboratory');
+
+ $('.logo-item-left').on("change", 'input[type="file"]', function () {
+ var $fileInput = $(this);
+ var file = this.files[0];
+ var imageType = /image.*/;
+ if (file && file.type.match(imageType)) {
+ var reader = new FileReader();
+ reader.onload = function () {
+ var $box = $fileInput.parent();
+ $box.find('img').attr('src', reader.result).css('display', 'block');
+ $box.addClass('has-img');
+ };
+ reader.readAsDataURL(file);
+ } else {
+ }
+ });
+
+ createMDEditor('laboratory-footer-editor', { height: 200, placeholder: '请输入备案信息' });
+
+ $form.validate({
+ errorElement: 'span',
+ errorClass: 'danger text-danger',
+ errorPlacement:function(error,element){
+ if(element.parent().hasClass("input-group")){
+ element.parent().after(error);
+ }else{
+ element.after(error)
+ }
+ },
+ rules: {
+ identifier: {
+ required: true,
+ checkSite: true
+ },
+ name: {
+ required: true
+ }
+ }
+ });
+ $.validator.addMethod("checkSite",function(value,element,params){
+ var checkSite = /^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/;
+ return this.optional(element)||(checkSite.test(value + '.educoder.com'));
+ },"域名不合法!");
+
+ $form.on('click', '.submit-btn', function(){
+ $form.find('.submit-btn').attr('disabled', 'disabled');
+ $form.find('.error').html('');
+ var valid = $form.valid();
+
+ $('input[name="navbar[][name]"]').each(function(_, e){
+ var $ele = $(e);
+ if($ele.val() === undefined || $ele.val().length === 0){
+ $ele.addClass('danger text-danger');
+ valid = false;
+ } else {
+ $ele.removeClass('danger text-danger');
+ }
+ });
+
+ if(!valid) return;
+ $.ajax({
+ method: 'PATCH',
+ dataType: 'json',
+ url: $form.attr('action'),
+ data: new FormData($form[0]),
+ processData: false,
+ contentType: false,
+ success: function(data){
+ $.notify({ message: '保存成功' });
+ window.location.reload();
+ },
+ error: function(res){
+ var data = res.responseJSON;
+ $form.find('.error').html(data.message);
+ },
+ complete: function(){
+ $form.find('.submit-btn').attr('disabled', false);
+ }
+ });
+ })
+ }
+});
\ No newline at end of file
diff --git a/app/assets/javascripts/admins/laboratories/index.js b/app/assets/javascripts/admins/laboratories/index.js
new file mode 100644
index 000000000..abb7cb72d
--- /dev/null
+++ b/app/assets/javascripts/admins/laboratories/index.js
@@ -0,0 +1,164 @@
+$(document).on('turbolinks:load', function() {
+ if ($('body.admins-laboratories-index-page').length > 0) {
+ var $searchContainer = $('.laboratory-list-form');
+ var $searchForm = $searchContainer.find('form.search-form');
+ var $list = $('.laboratory-list-container');
+
+ // ============== 新建 ===============
+ var $modal = $('.modal.admin-create-laboratory-modal');
+ var $form = $modal.find('form.admin-create-laboratory-form');
+ var $schoolSelect = $modal.find('.school-select');
+
+ $form.validate({
+ errorElement: 'span',
+ errorClass: 'danger text-danger',
+ rules: {
+ school_id: {
+ required: true
+ }
+ },
+ messages: {
+ school_id: {
+ required: '请选择所属单位'
+ }
+ }
+ });
+
+ // modal ready fire
+ $modal.on('show.bs.modal', function () {
+ $schoolSelect.select2('val', ' ');
+ });
+
+ // ************** 学校选择 *************
+ var matcherFunc = function(params, data){
+ if ($.trim(params.term) === '') {
+ return data;
+ }
+ if (typeof data.text === 'undefined') {
+ return null;
+ }
+
+ if (data.name && data.name.indexOf(params.term) > -1) {
+ var modifiedData = $.extend({}, data, true);
+ return modifiedData;
+ }
+
+ // Return `null` if the term should not be displayed
+ return null;
+ };
+
+ var defineSchoolSelect = function(schools) {
+ $schoolSelect.select2({
+ theme: 'bootstrap4',
+ placeholder: '请选择单位',
+ minimumInputLength: 1,
+ data: schools,
+ templateResult: function (item) {
+ if(!item.id || item.id === '') return item.text;
+ return item.name;
+ },
+ templateSelection: function(item){
+ if (item.id) {
+ $('#school_id').val(item.id);
+ }
+ return item.name || item.text;
+ },
+ matcher: matcherFunc
+ });
+ }
+
+ $.ajax({
+ url: '/api/schools/for_option.json',
+ dataType: 'json',
+ type: 'GET',
+ success: function(data) {
+ defineSchoolSelect(data.schools);
+ }
+ });
+
+ $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);
+ }
+ });
+ }
+ });
+
+ // ============= 添加管理员 ==============
+ var $addMemberModal = $('.admin-add-laboratory-user-modal');
+ var $addMemberForm = $addMemberModal.find('.admin-add-laboratory-user-form');
+ var $memberSelect = $addMemberModal.find('.laboratory-user-select');
+ var $laboratoryIdInput = $addMemberForm.find('input[name="laboratory_id"]')
+
+ $addMemberModal.on('show.bs.modal', function(event){
+ var $link = $(event.relatedTarget);
+ var laboratoryId = $link.data('laboratory-id');
+ $laboratoryIdInput.val(laboratoryId);
+
+ $memberSelect.select2('val', ' ');
+ });
+
+ $memberSelect.select2({
+ theme: 'bootstrap4',
+ placeholder: '请输入要添加的管理员姓名',
+ multiple: true,
+ minimumInputLength: 1,
+ ajax: {
+ delay: 500,
+ url: '/admins/users',
+ dataType: 'json',
+ data: function(params){
+ return { name: params.term };
+ },
+ processResults: function(data){
+ return { results: data.users }
+ }
+ },
+ templateResult: function (item) {
+ if(!item.id || item.id === '') return item.text;
+ return item.real_name;
+ },
+ templateSelection: function(item){
+ if (item.id) {
+ }
+ return item.real_name || item.text;
+ }
+ });
+
+ $addMemberModal.on('click', '.submit-btn', function(){
+ $addMemberForm.find('.error').html('');
+
+ var laboratoryId = $laboratoryIdInput.val();
+ var memberIds = $memberSelect.val();
+ if (laboratoryId && memberIds && memberIds.length > 0) {
+ $.ajax({
+ method: 'POST',
+ dataType: 'script',
+ url: '/admins/laboratories/' + laboratoryId + '/laboratory_user',
+ data: { user_ids: memberIds }
+ });
+ } else {
+ $addMemberModal.modal('hide');
+ }
+ });
+ }
+});
\ No newline at end of file
diff --git a/app/assets/stylesheets/admins/laboratories.scss b/app/assets/stylesheets/admins/laboratories.scss
new file mode 100644
index 000000000..ad5c8c5a8
--- /dev/null
+++ b/app/assets/stylesheets/admins/laboratories.scss
@@ -0,0 +1,99 @@
+.admins-laboratories-index-page {
+ .laboratory-list-table {
+ .member-container {
+ .laboratory-user {
+ display: flex;
+ justify-content: center;
+ flex-wrap: wrap;
+
+ .laboratory-user-item {
+ display: flex;
+ align-items: center;
+ height: 22px;
+ line-height: 22px;
+ padding: 2px 5px;
+ margin: 2px 2px;
+ border: 1px solid #91D5FF;
+ background-color: #E6F7FF;
+ color: #91D5FF;
+ border-radius: 4px;
+ }
+ }
+ }
+ }
+}
+.admins-laboratory-settings-show-page, .admins-laboratory-settings-update-page {
+ .edit-laboratory-setting-container {
+ .logo-item {
+ display: flex;
+
+ &-img {
+ display: block;
+ width: 80px;
+ height: 80px;
+ }
+
+ &-upload {
+ cursor: pointer;
+ position: absolute;
+ top: 0;
+ width: 80px;
+ height: 80px;
+ background: #F5F5F5;
+ border: 1px solid #E5E5E5;
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: 27px;
+ left: 39px;
+ width: 2px;
+ height: 26px;
+ background: #E5E5E5;
+ }
+
+ &::after {
+ content: '';
+ position: absolute;
+ top: 39px;
+ left: 27px;
+ width: 26px;
+ height: 2px;
+ background: #E5E5E5;
+ }
+ }
+
+ &-left {
+ position: relative;
+ width: 80px;
+ height: 80px;
+
+ &.has-img {
+ .logo-item-upload {
+ display: none;
+ }
+
+ &:hover {
+ .logo-item-upload {
+ display: block;
+ background: rgba(145, 145, 145, 0.8);
+ }
+ }
+ }
+ }
+
+ &-right {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ color: #777777;
+ font-size: 12px;
+ }
+
+ &-title {
+ color: #23272B;
+ font-size: 14px;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb
index 1d6f89ec0..fed6ec280 100644
--- a/app/controllers/accounts_controller.rb
+++ b/app/controllers/accounts_controller.rb
@@ -183,20 +183,6 @@ class AccountsController < ApplicationController
end
private
- def autologin_cookie_name
- edu_setting('autologin_cookie_name') || 'autologin'
- end
-
- def logout_user
- if User.current.logged?
- if autologin = cookies.delete(autologin_cookie_name)
- User.current.delete_autologin_token(autologin)
- end
- User.current.delete_session_token(session[:tk])
- self.logged_user = nil
- end
- session[:user_id] = nil
- end
# type 事件类型 1:用户注册 2:忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验证手机号是否有效 # 如果有新的继续后面加
# login_type 1:手机类型 2:邮箱类型
diff --git a/app/controllers/admins/laboratories_controller.rb b/app/controllers/admins/laboratories_controller.rb
new file mode 100644
index 000000000..e393c6677
--- /dev/null
+++ b/app/controllers/admins/laboratories_controller.rb
@@ -0,0 +1,32 @@
+class Admins::LaboratoriesController < Admins::BaseController
+ def index
+ params[:sort_by] = params[:sort_by].presence || 'id'
+ params[:sort_direction] = params[:sort_direction].presence || 'desc'
+
+ laboratories = Admins::LaboratoryQuery.call(params)
+ @laboratories = paginate laboratories.preload(:school, :laboratory_users)
+ end
+
+ def create
+ Admins::CreateLaboratoryService.call(create_params)
+ render_ok
+ rescue Admins::CreateLaboratoryService::Error => ex
+ render_error(ex.message)
+ end
+
+ def destroy
+ current_laboratory.destroy!
+
+ render_delete_success
+ end
+
+ private
+
+ def current_laboratory
+ @_current_laboratory ||= Laboratory.find(params[:id])
+ end
+
+ def create_params
+ params.permit(:school_id)
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/admins/laboratory_settings_controller.rb b/app/controllers/admins/laboratory_settings_controller.rb
new file mode 100644
index 000000000..f9676bfd3
--- /dev/null
+++ b/app/controllers/admins/laboratory_settings_controller.rb
@@ -0,0 +1,20 @@
+class Admins::LaboratorySettingsController < Admins::BaseController
+ def show
+ @laboratory = current_laboratory
+ end
+
+ def update
+ Admins::SaveLaboratorySettingService.call(current_laboratory, form_params)
+ render_ok
+ end
+
+ private
+
+ def current_laboratory
+ @_current_laboratory ||= Laboratory.find(params[:laboratory_id])
+ end
+
+ def form_params
+ params.permit(:identifier, :name, :nav_logo, :login_logo, :tab_logo, :footer, navbar: %i[name link hidden])
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/admins/laboratory_users_controller.rb b/app/controllers/admins/laboratory_users_controller.rb
new file mode 100644
index 000000000..36e389a3e
--- /dev/null
+++ b/app/controllers/admins/laboratory_users_controller.rb
@@ -0,0 +1,19 @@
+class Admins::LaboratoryUsersController < Admins::BaseController
+ helper_method :current_laboratory
+
+ def create
+ Admins::AddLaboratoryUserService.call(current_laboratory, params.permit(user_ids: []))
+ current_laboratory.reload
+ end
+
+ def destroy
+ @laboratory_user = current_laboratory.laboratory_users.find_by(user_id: params[:user_id])
+ @laboratory_user.destroy! if @laboratory_user.present?
+ end
+
+ private
+
+ def current_laboratory
+ @_current_laboratory ||= Laboratory.find(params[:laboratory_id])
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index b539a0c68..5c93b08b9 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -7,6 +7,8 @@ class ApplicationController < ActionController::Base
include ControllerRescueHandler
include GitHelper
include LoggerHelper
+ include LaboratoryHelper
+ include LoginHelper
protect_from_forgery prepend: true, unless: -> { request.format.json? }
@@ -234,12 +236,6 @@ class ApplicationController < ActionController::Base
# end
end
- def start_user_session(user)
- session[:user_id] = user.id
- session[:ctime] = Time.now.utc.to_i
- session[:atime] = Time.now.utc.to_i
- end
-
def user_setup
# reacct静态资源加载不需要走这一步
return if params[:controller] == "main"
@@ -280,17 +276,6 @@ class ApplicationController < ActionController::Base
# User.current = User.find 81403
end
- # Sets the logged in user
- def logged_user=(user)
- reset_session
- if user && user.is_a?(User)
- User.current = user
- start_user_session(user)
- else
- User.current = User.anonymous
- end
- end
-
# Returns the current user or nil if no user is logged in
# and starts a session if needed
def find_current_user
@@ -306,10 +291,6 @@ class ApplicationController < ActionController::Base
end
end
- def autologin_cookie_name
- edu_setting('autologin_cookie_name').presence || 'autologin'
- end
-
def try_to_autologin
if cookies[autologin_cookie_name]
# auto-login feature starts a new session
@@ -620,22 +601,6 @@ class ApplicationController < ActionController::Base
cookies[:fileDownload] = true
end
- def set_autologin_cookie(user)
- token = Token.get_or_create_permanent_login_token(user, "autologin")
- cookie_options = {
- :value => token.value,
- :expires => 1.month.from_now,
- :path => '/',
- :secure => false,
- :httponly => true
- }
- if edu_setting('cookie_domain').present?
- cookie_options = cookie_options.merge(domain: edu_setting('cookie_domain'))
- end
- cookies[autologin_cookie_name] = cookie_options
- logger.info("cookies is #{cookies}")
- end
-
# 149课程的评审用户数据创建(包含创建课堂学生)
def open_class_user
user = User.find_by(login: "OpenClassUser")
diff --git a/app/controllers/bind_users_controller.rb b/app/controllers/bind_users_controller.rb
new file mode 100644
index 000000000..354b2993b
--- /dev/null
+++ b/app/controllers/bind_users_controller.rb
@@ -0,0 +1,22 @@
+class BindUsersController < ApplicationController
+ before_action :require_login
+
+ def create
+ user = CreateBindUserService.call(current_user, create_params)
+ successful_authentication(user) if user.id != current_user.id
+
+ render_ok
+ rescue ApplicationService::Error => ex
+ render_error(ex.message)
+ end
+
+ def new_user
+ current_user
+ end
+
+ private
+
+ def create_params
+ params.permit(:username, :password, :type, :not_bind)
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/concerns/laboratory_helper.rb b/app/controllers/concerns/laboratory_helper.rb
new file mode 100644
index 000000000..fbb18b36d
--- /dev/null
+++ b/app/controllers/concerns/laboratory_helper.rb
@@ -0,0 +1,15 @@
+module LaboratoryHelper
+ extend ActiveSupport::Concern
+
+ included do
+ helper_method :default_setting
+ end
+
+ def current_laboratory
+ @_current_laboratory ||= (Laboratory.find_by_subdomain(request.subdomain) || Laboratory.find(1))
+ end
+
+ def default_setting
+ @_default_setting ||= LaboratorySetting.find_by(laboratory_id: 1)
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/concerns/login_helper.rb b/app/controllers/concerns/login_helper.rb
new file mode 100644
index 000000000..e94cf8a21
--- /dev/null
+++ b/app/controllers/concerns/login_helper.rb
@@ -0,0 +1,69 @@
+module LoginHelper
+ extend ActiveSupport::Concern
+
+ def edu_setting(name)
+ EduSetting.get(name)
+ end
+
+ def autologin_cookie_name
+ edu_setting('autologin_cookie_name').presence || 'autologin'
+ end
+
+ def set_autologin_cookie(user)
+ token = Token.get_or_create_permanent_login_token(user, "autologin")
+ cookie_options = {
+ :value => token.value,
+ :expires => 1.month.from_now,
+ :path => '/',
+ :secure => false,
+ :httponly => true
+ }
+ if edu_setting('cookie_domain').present?
+ cookie_options = cookie_options.merge(domain: edu_setting('cookie_domain'))
+ end
+ cookies[autologin_cookie_name] = cookie_options
+ Rails.logger.info("cookies is #{cookies}")
+ end
+
+ def successful_authentication(user)
+ Rails.logger.info("id: #{user&.id} Successful authentication start: '#{user.login}' from #{request.remote_ip} at #{Time.now.utc}")
+ # Valid user
+ self.logged_user = user
+
+ # generate a key and set cookie if autologin
+ set_autologin_cookie(user)
+
+ UserAction.create(action_id: user&.id, action_type: 'Login', user_id: user&.id, ip: request.remote_ip)
+ user.update_column(:last_login_on, Time.now)
+ # 注册完成后有一天的试用申请(先去掉)
+ # UserDayCertification.create(user_id: user.id, status: 1)
+ end
+
+ def logout_user
+ if User.current.logged?
+ if autologin = cookies.delete(autologin_cookie_name)
+ User.current.delete_autologin_token(autologin)
+ end
+ User.current.delete_session_token(session[:tk])
+ self.logged_user = nil
+ end
+ session[:user_id] = nil
+ end
+
+ # Sets the logged in user
+ def logged_user=(user)
+ reset_session
+ if user && user.is_a?(User)
+ User.current = user
+ start_user_session(user)
+ else
+ User.current = User.anonymous
+ end
+ end
+
+ def start_user_session(user)
+ session[:user_id] = user.id
+ session[:ctime] = Time.now.utc.to_i
+ session[:atime] = Time.now.utc.to_i
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/exercise_questions_controller.rb b/app/controllers/exercise_questions_controller.rb
index 9eeba6adc..aacef6bc7 100644
--- a/app/controllers/exercise_questions_controller.rb
+++ b/app/controllers/exercise_questions_controller.rb
@@ -619,7 +619,7 @@ class ExerciseQuestionsController < ApplicationController
:status => 0
}
ExerciseShixunAnswer.create(ex_shixun_option)
- new_obj_score = @c_score
+ new_obj_score = ex_obj_score + @c_score
end
total_scores = new_obj_score + ex_subj_score
if total_scores < 0.0
diff --git a/app/controllers/exercises_controller.rb b/app/controllers/exercises_controller.rb
index f9d9531f1..9979ae48f 100644
--- a/app/controllers/exercises_controller.rb
+++ b/app/controllers/exercises_controller.rb
@@ -694,6 +694,7 @@ class ExercisesController < ApplicationController
#首页批量或单独 立即发布,应是跳出弹窗,设置开始时间和截止时间。
def publish
+ group_ids = params[:group_ids]&.reject(&:blank?)
if params[:detail].blank?
tip_exception("缺少截止时间参数") if params[:end_time].blank?
tip_exception("截止时间不能早于当前时间") if params[:end_time] <= strf_time(Time.now)
@@ -701,7 +702,6 @@ class ExercisesController < ApplicationController
@course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day)
else
group_end_times = params[:group_end_times].reject(&:blank?).map{|time| time.to_time}
- group_ids = params[:group_ids].reject(&:blank?)
tip_exception("缺少截止时间参数") if group_end_times.blank?
tip_exception("截止时间和分班参数的个数不一致") if group_end_times.length != group_ids.length
group_end_times.each do |time|
@@ -1103,6 +1103,7 @@ class ExercisesController < ApplicationController
ActiveRecord::Base.transaction do
begin
can_commit_exercise = false
+ user_left_time = nil
if @user_course_identity > Course::ASSISTANT_PROFESSOR #为学生时
if params[:commit_method].to_i == 2 #自动提交时
user_left_time = get_exercise_left_time(@exercise,current_user)
@@ -1131,7 +1132,7 @@ class ExercisesController < ApplicationController
CommitExercsieNotifyJobJob.perform_later(@exercise.id, current_user.id)
normal_status(0,"试卷提交成功!")
else
- normal_status(-1,"提交失败,未到截止时间!")
+ normal_status(-2,"#{user_left_time.to_i}")
end
else
normal_status(-1,"提交失败,当前用户不为课堂学生!")
@@ -1151,7 +1152,7 @@ class ExercisesController < ApplicationController
# 1 老师权限,0 学生权限
@is_teacher_or = (@user_course_identity < Course::STUDENT) ? 1 : 0
@student_status = 2
- @exercise_questions = @exercise.exercise_questions.includes(:exercise_shixun_challenges,:exercise_standard_answers,:exercise_answers,:exercise_shixun_answers).order("question_number ASC")
+ @exercise_questions = @exercise.exercise_questions.includes(:exercise_shixun_challenges,:exercise_standard_answers,:exercise_answers,:exercise_shixun_answers,:exercise_answer_comments).order("question_number ASC")
@question_status = []
get_exercise_status = @exercise.get_exercise_status(current_user) #当前用户的试卷状态
@ex_answer_status = @exercise.get_exercise_status(@ex_user&.user) #当前试卷用户的试卷状态
@@ -1322,7 +1323,7 @@ class ExercisesController < ApplicationController
end
rescue Exception => e
uid_logger_error(e.message)
- tip_exception("页面调用失败!")
+ tip_exception(e.message)
raise ActiveRecord::Rollback
end
end
@@ -1708,9 +1709,9 @@ class ExercisesController < ApplicationController
ques_number = q.question_number
end
if q.question_type != Exercise::PRACTICAL
- ques_vote = q.exercise_answers.search_exercise_answer("user_id",user_id)
+ ques_vote = q.exercise_answers.select{|answer| answer.user_id == user_id}
else
- ques_vote = q.exercise_shixun_answers.search_shixun_answers("user_id",user_id)
+ ques_vote = q.exercise_shixun_answers.select{|answer| answer.user_id == user_id}
end
ques_status = 0
if ques_vote.present?
diff --git a/app/controllers/homework_commons_controller.rb b/app/controllers/homework_commons_controller.rb
index 9941e1d42..83b80bec4 100644
--- a/app/controllers/homework_commons_controller.rb
+++ b/app/controllers/homework_commons_controller.rb
@@ -159,8 +159,13 @@ class HomeworkCommonsController < ApplicationController
end
# 作品状态 0: 未提交, 1 按时提交, 2 延迟提交
- unless params[:work_status].blank?
- @student_works = @student_works.where(work_status: params[:work_status])
+ if params[:work_status].present?
+ work_status = params[:work_status].map{|status| status.to_i}
+ all_student_works = @student_works.left_joins(:myshixun)
+ @student_works = all_student_works.where(work_status: work_status)
+
+ @student_works = @student_works.or(all_student_works.where(work_status: 0)).or(all_student_works.where(myshixuns: {status: 0})) if work_status.include?(3)
+ @student_works = @student_works.or(all_student_works.where(myshixuns: {status: 1})) if work_status.include?(4)
end
# 分班情况
@@ -1044,6 +1049,7 @@ class HomeworkCommonsController < ApplicationController
def publish_homework
tip_exception("请至少选择一个分班") if params[:group_ids].blank? && @course.course_groups.size != 0
+ group_ids = params[:group_ids]&.reject(&:blank?)
if params[:detail].blank?
tip_exception("缺少截止时间参数") if params[:end_time].blank?
tip_exception("截止时间不能早于当前时间") if params[:end_time] <= strf_time(Time.now)
@@ -1051,7 +1057,6 @@ class HomeworkCommonsController < ApplicationController
@course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day)
else
group_end_times = params[:group_end_times].reject(&:blank?).map{|time| time.to_time}
- group_ids = params[:group_ids].reject(&:blank?)
tip_exception("缺少截止时间参数") if group_end_times.blank?
tip_exception("截止时间和分班参数的个数不一致") if group_end_times.length != group_ids.length
group_end_times.each do |time|
diff --git a/app/controllers/oauth/base_controller.rb b/app/controllers/oauth/base_controller.rb
new file mode 100644
index 000000000..e2eb26a2a
--- /dev/null
+++ b/app/controllers/oauth/base_controller.rb
@@ -0,0 +1,20 @@
+class Oauth::BaseController < ActionController::Base
+ include RenderHelper
+ include LoginHelper
+
+ skip_before_action :verify_authenticity_token
+
+ private
+
+ def session_user_id
+ session[:user_id]
+ end
+
+ def current_user
+ @_current_user ||= User.find_by(id: session_user_id)
+ end
+
+ def auth_hash
+ request.env['omniauth.auth']
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/oauth/qq_controller.rb b/app/controllers/oauth/qq_controller.rb
new file mode 100644
index 000000000..4b9a46443
--- /dev/null
+++ b/app/controllers/oauth/qq_controller.rb
@@ -0,0 +1,9 @@
+class Oauth::QQController < Oauth::BaseController
+ def create
+ user, new_user = Oauth::CreateOrFindQqAccountService.call(current_user, auth_hash)
+
+ successful_authentication(user)
+
+ render_ok(new_user: new_user)
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/oauth/wechat_controller.rb b/app/controllers/oauth/wechat_controller.rb
new file mode 100644
index 000000000..6c0c53eb6
--- /dev/null
+++ b/app/controllers/oauth/wechat_controller.rb
@@ -0,0 +1,11 @@
+class Oauth::WechatController < Oauth::BaseController
+ def create
+ user, new_user = Oauth::CreateOrFindWechatAccountService.call(current_user ,params)
+
+ successful_authentication(user)
+
+ render_ok(new_user: new_user)
+ rescue Oauth::CreateOrFindWechatAccountService::Error => ex
+ render_error(ex.message)
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/polls_controller.rb b/app/controllers/polls_controller.rb
index 2259907e4..da5917e1b 100644
--- a/app/controllers/polls_controller.rb
+++ b/app/controllers/polls_controller.rb
@@ -254,6 +254,7 @@ class PollsController < ApplicationController
#首页批量或单独 立即发布,应是跳出弹窗,设置开始时间和截止时间。
def publish
+ group_ids = params[:group_ids]&.reject(&:blank?)
if params[:detail].blank?
tip_exception("缺少截止时间参数") if params[:end_time].blank?
tip_exception("截止时间不能早于当前时间") if params[:end_time] <= strf_time(Time.now)
@@ -261,7 +262,6 @@ class PollsController < ApplicationController
@course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day)
else
group_end_times = params[:group_end_times].reject(&:blank?).map{|time| time.to_time}
- group_ids = params[:group_ids].reject(&:blank?)
tip_exception("缺少截止时间参数") if group_end_times.blank?
tip_exception("截止时间和分班参数的个数不一致") if group_end_times.length != group_ids.length
group_end_times.each do |time|
diff --git a/app/controllers/question_banks_controller.rb b/app/controllers/question_banks_controller.rb
index 7781b5792..ddb0f3ce1 100644
--- a/app/controllers/question_banks_controller.rb
+++ b/app/controllers/question_banks_controller.rb
@@ -91,12 +91,16 @@ class QuestionBanksController < ApplicationController
banks = @object_type.classify.constantize.where(id: params[:object_id])
course = current_user.manage_courses.find_by!(id: params[:course_id])
task_ids = []
+ homework_type = ""
+ container_type = ""
banks.each do |bank|
case @object_type
when 'HomeworkBank' # 作业
task = quote_homework_bank bank, course
+ homework_type = task.homework_type
when 'ExerciseBank'
- if bank.container_type == 'Exercise' # 试卷
+ container_type = bank.container_type
+ if container_type == 'Exercise' # 试卷
task = quote_exercise_bank bank, course
else # 问卷
task = quote_poll_bank bank, course
@@ -108,7 +112,23 @@ class QuestionBanksController < ApplicationController
end
task_ids << task.id if task
end
- render :json => {task_ids: task_ids, status: 0, message: "发送成功"}
+
+ case @object_type
+ when 'HomeworkBank' # 作业
+ category_id = course.course_modules.find_by(module_type: homework_type == "normal" ? "common_homework" : "group_homework")&.id
+ when 'ExerciseBank'
+ if container_type == 'Exercise' # 试卷
+ category_id = course.course_modules.find_by(module_type: "exercise")&.id
+ else # 问卷
+ category_id = course.course_modules.find_by(module_type: "poll")&.id
+ end
+ when 'GtaskBank'
+ category_id = course.course_modules.find_by(module_type: "graduation")&.id
+ when 'GtopicBank'
+ category_id = course.course_modules.find_by(module_type: "graduation")&.id
+ end
+
+ render :json => {task_ids: task_ids, category_id: category_id, status: 0, message: "发送成功"}
end
def destroy
diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb
new file mode 100644
index 000000000..ce5481147
--- /dev/null
+++ b/app/controllers/settings_controller.rb
@@ -0,0 +1,5 @@
+class SettingsController < ApplicationController
+ def show
+ @laboratory = current_laboratory
+ end
+end
\ No newline at end of file
diff --git a/app/forms/users/update_password_form.rb b/app/forms/users/update_password_form.rb
index 023caa40f..4da341839 100644
--- a/app/forms/users/update_password_form.rb
+++ b/app/forms/users/update_password_form.rb
@@ -4,5 +4,4 @@ class Users::UpdatePasswordForm
attr_accessor :password, :old_password
validates :password, presence: true
- validates :old_password, presence: true
end
\ No newline at end of file
diff --git a/app/helpers/exercises_helper.rb b/app/helpers/exercises_helper.rb
index ef9261990..e13f754be 100644
--- a/app/helpers/exercises_helper.rb
+++ b/app/helpers/exercises_helper.rb
@@ -10,9 +10,9 @@ module ExercisesHelper
exercise_obj_status.each do |q|
q_type = q.question_type
if q_type == Exercise::PRACTICAL
- answers_content = q.exercise_shixun_answers.search_shixun_answers("user_id",user_id)
+ answers_content = q.exercise_shixun_answers.select{|answer| answer.user_id == user_id}
else
- answers_content = q.exercise_answers.search_answer_users("user_id",user_id)
+ answers_content = q.exercise_answers.select{|answer| answer.user_id == user_id}
end
if q_type <= Exercise::JUDGMENT
@@ -40,7 +40,7 @@ module ExercisesHelper
ques_score = 0.0
end
else
- ques_score = answers_content.score_reviewed.select(:score).pluck(:score).sum
+ ques_score = answers_content.select{|answer| answer.score >= 0.0}.pluck(:score).sum
end
if ques_score >= q.question_score #满分作答为正确
@@ -64,7 +64,7 @@ module ExercisesHelper
exercise_sub_status = exercise_questions.find_by_custom("question_type",Exercise::SUBJECTIVE) #主观题
@ex_sub_array = [] #主观题的已答/未答
exercise_sub_status.each do |s|
- sub_answer = s.exercise_answers.search_answer_users("user_id",user_id) #主观题只有一个回答
+ sub_answer = s.exercise_answers.select{|answer| answer.user_id == user_id} #主观题只有一个回答
if sub_answer.present? && sub_answer.first.score >= 0.0
if s.question_score <= sub_answer.first.score
stand_status = 1
@@ -772,12 +772,12 @@ module ExercisesHelper
question_comment = []
# user_score_pre = nil
if ques_type == 5
- exercise_answers = q.exercise_shixun_answers.search_shixun_answers("user_id",ex_answerer_id)
+ exercise_answers = q.exercise_shixun_answers.select{|answer| answer.user_id == ex_answerer_id}
else
- exercise_answers = q.exercise_answers.search_exercise_answer("user_id",ex_answerer_id) #试卷用户的回答
+ exercise_answers = q.exercise_answers.select{|answer| answer.user_id == ex_answerer_id} #试卷用户的回答
end
if student_status == 2 #当前为老师,或为学生且已提交
- user_score_pre = exercise_answers.score_reviewed
+ user_score_pre = exercise_answers.select{|answer| answer.score >= 0.0}
if ques_type == 4 #主观题时,且没有大于0的分数时,为空
user_score = user_score_pre.present? ? user_score_pre.pluck(:score).sum : nil
elsif ques_type == 5 || ques_type == 3
@@ -829,7 +829,7 @@ module ExercisesHelper
if ex_type == 4 #填空题/主观题/实训题有评论的
q_answer_id = exercise_answers.present? ? exercise_answers.first.id : nil
- question_comment = q.exercise_answer_comments.search_answer_comments("exercise_answer_id",q_answer_id)
+ question_comment = q.exercise_answer_comments.select{|comment| comment.exercise_answer_id == q_answer_id}
end
{
"user_score": (user_score.present? ? user_score.round(1).to_s : nil),
diff --git a/app/helpers/export_helper.rb b/app/helpers/export_helper.rb
index 05b1b2f8b..28bef0a89 100644
--- a/app/helpers/export_helper.rb
+++ b/app/helpers/export_helper.rb
@@ -286,10 +286,18 @@ module ExportHelper
@user_columns = []
ques_type_boolean = question_types.include?(4)
if ques_type_boolean #仅存在主观题或客观题的时候
- @table_columns = @table_columns + %w(客观题成绩 主观题成绩 最终成绩 开始答题时间 提交时间)
+ @table_columns = @table_columns + %w(客观题成绩 主观题成绩 最终成绩)
else
- @table_columns = @table_columns + %w(最终成绩 开始答题时间 提交时间)
+ @table_columns = @table_columns + %w(最终成绩)
end
+ for i in 1 .. exercise.exercise_questions.size
+ @table_columns = @table_columns + ["第#{i}题"]
+ end
+
+ @table_columns = @table_columns + %w(开始答题时间 提交时间)
+
+ questions = exercise.exercise_questions.includes(:exercise_answers,:exercise_shixun_answers).order("question_number ASC")
+
export_ex_users.includes(user: :user_extension).each_with_index do |e_user,index|
user_info = e_user.user
member = course.students.find_by_user_id(e_user.user_id)
@@ -312,11 +320,36 @@ module ExportHelper
user_option = [index+1,user_login,user_real_name, user_mail,
user_student_id,user_course,user_commit_stu]
if ques_type_boolean
- other_user_option = [user_obj_score,user_suj_score,user_score,user_start_time,user_end_time]
+ other_user_option = [user_obj_score,user_suj_score,user_score]
else
- other_user_option = [user_score,user_start_time,user_end_time]
+ other_user_option = [user_score]
end
- user_option = user_option + other_user_option
+
+ time_option = [user_start_time,user_end_time]
+
+ score_option = []
+ questions.each do |q|
+ q_type = q.question_type
+ if q_type == Exercise::PRACTICAL
+ answers_content = q.exercise_shixun_answers.select{|answer| answer.user_id == e_user.user_id}
+ else
+ answers_content = q.exercise_answers.select{|answer| answer.user_id == e_user.user_id}
+ end
+
+ if q_type <= Exercise::JUDGMENT || q_type == Exercise::SUBJECTIVE
+ if answers_content.present? #学生有回答时,分数已经全部存到exercise_answer 表,所以可以直接取第一个值
+ ques_score = answers_content.first.score
+ ques_score = ques_score.nil? || ques_score < 0 ? 0.0 : ques_score
+ else
+ ques_score = 0.0
+ end
+ else
+ ques_score = answers_content.select{|answer| answer.score >= 0.0}.pluck(:score).sum
+ end
+ score_option << ques_score
+ end
+
+ user_option = user_option + other_user_option + score_option + time_option
@user_columns.push(user_option)
end
end
diff --git a/app/helpers/homework_commons_helper.rb b/app/helpers/homework_commons_helper.rb
index cc23d05d6..efc14dc5e 100644
--- a/app/helpers/homework_commons_helper.rb
+++ b/app/helpers/homework_commons_helper.rb
@@ -222,9 +222,17 @@ module HomeworkCommonsHelper
[{ id: 0 ,name: "未评", count: homework.uncomment_count(user_id)}, {id: 1, name: "已评", count: homework.comment_count(user_id)}]
end
+ # 作品状态
+ def practice_homework_status homework, member
+ [{id: 3, name: "未通关", count: homework.un_complete_count(member)},
+ {id: 4, name: "已通关", count: homework.complete_count(member)},
+ {id: 1, name: "按时完成", count: homework.finished_count(member)},
+ {id: 2, name: "延时完成", count: homework.delay_finished_count(member)}]
+ end
+
# 作品状态
def homework_status homework, member
- [{id: 0, name: "未提交", count: homework.unfinished_count(member)},
+ [{id: 0, name: "未提交", count: homework.unfinished_count(member)},
{id: 1, name: "按时提交", count: homework.finished_count(member)},
{id: 2, name: "延时提交", count: homework.delay_finished_count(member)}]
end
diff --git a/app/libs/omniauth/strategies/qq.rb b/app/libs/omniauth/strategies/qq.rb
new file mode 100644
index 000000000..513257e3c
--- /dev/null
+++ b/app/libs/omniauth/strategies/qq.rb
@@ -0,0 +1,50 @@
+module OmniAuth
+ module Strategies
+ class QQ < OmniAuth::Strategies::OAuth2
+ option :client_options, {
+ site: 'https://graph.qq.com',
+ authorize_url: '/oauth2.0/authorize',
+ token_url: '/oauth2.0/token'
+ }
+
+ def request_phase
+ super
+ end
+
+ def authorize_params
+ super.tap do |params|
+ %w[scope client_options].each do |v|
+ if request.params[v]
+ params[v.to_sym] = request.params[v]
+ end
+ end
+ end
+ end
+
+ uid { raw_info['openid'].to_s }
+
+ info do
+ {
+ name: user_info['nickname'],
+ nickname: user_info['nickname'],
+ image: user_info['figureurl_qq_1']
+ }
+ end
+
+ extra do
+ { raw_info: user_info }
+ end
+
+ def raw_info
+ access_token.options[:mode] = :query
+ @raw_info ||= access_token.get('/oauth2.0/me').parsed
+ end
+
+ def user_info
+ access_token.options[:mode] = :query
+ params = { oauth_consumer_key: options.client_id, openid: raw_info['openid'], format: 'json' }
+ @user_info ||= access_token.get('/user/get_user_info', params: params)
+ end
+ end
+ end
+end
diff --git a/app/libs/util.rb b/app/libs/util.rb
index 72e728ab9..84f14a6c0 100644
--- a/app/libs/util.rb
+++ b/app/libs/util.rb
@@ -1,3 +1,5 @@
+require 'open-uri'
+
module Util
module_function
@@ -29,6 +31,16 @@ module Util
end
end
+ def download_file(url, save_path)
+ data = open(url, &:read)
+ file = File.new(save_path, 'w+')
+ file.binmode
+ file << data
+ file.flush
+ file.close
+ file
+ end
+
def logger_error(exception)
Rails.logger.error(exception.message)
exception.backtrace.each { |message| Rails.logger.error(message) }
diff --git a/app/libs/util/file_manage.rb b/app/libs/util/file_manage.rb
index 822bfca4f..2f87a3e86 100644
--- a/app/libs/util/file_manage.rb
+++ b/app/libs/util/file_manage.rb
@@ -10,31 +10,35 @@ module Util::FileManage
File.join(Rails.root, "public", "images", relative_path)
end
- def disk_filename(source_type, source_id,image_file=nil)
- File.join(storage_path, "#{source_type}", "#{source_id}")
+ def disk_filename(source_type, source_id, suffix=nil)
+ File.join(storage_path, "#{source_type}", "#{source_id}#{suffix}")
end
- def exist?(source_type, source_id)
- File.exist?(disk_filename(source_type, source_id))
+ def source_disk_filename(source, suffix=nil)
+ disk_filename(source.class.name, source.id, suffix)
end
- def exists?(source)
- File.exist?(disk_filename(source.class, source.id))
+ def exist?(source_type, source_id, suffix=nil)
+ File.exist?(disk_filename(source_type, source_id, suffix))
+ end
+
+ def exists?(source, suffix=nil)
+ File.exist?(disk_filename(source.class, source.id, suffix))
end
def disk_file_url(source_type, source_id, suffix = nil)
- t = ctime(source_type, source_id)
+ t = ctime(source_type, source_id, suffix)
File.join('/images', relative_path, "#{source_type}", "#{source_id}#{suffix}") + "?t=#{t}"
end
- def source_disk_file_url(source)
- disk_file_url(source.class, source.id)
+ def source_disk_file_url(source, suffix=nil)
+ disk_file_url(source.class, source.id, suffix)
end
- def ctime(source_type, source_id)
- return nil unless exist?(source_type, source_id)
+ def ctime(source_type, source_id, suffix)
+ return nil unless exist?(source_type, source_id, suffix)
- File.ctime(disk_filename(source_type, source_id)).to_i
+ File.ctime(disk_filename(source_type, source_id, suffix)).to_i
end
def disk_auth_filename(source_type, source_id, type)
diff --git a/app/libs/wechat_oauth.rb b/app/libs/wechat_oauth.rb
new file mode 100644
index 000000000..ba4baee30
--- /dev/null
+++ b/app/libs/wechat_oauth.rb
@@ -0,0 +1,13 @@
+module WechatOauth
+ class << self
+ attr_accessor :appid, :secret, :scope, :base_url
+
+ def logger
+ @_logger ||= STDOUT
+ end
+
+ def logger=(l)
+ @_logger = l
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/libs/wechat_oauth/error.rb b/app/libs/wechat_oauth/error.rb
new file mode 100644
index 000000000..ac7f5fddc
--- /dev/null
+++ b/app/libs/wechat_oauth/error.rb
@@ -0,0 +1,14 @@
+class WechatOauth::Error < StandardError
+ attr_reader :code
+
+ def initialize(code, msg)
+ super(msg)
+ @code = code
+ end
+
+ def message
+ I18n.t("oauth.wechat.#{code}")
+ rescue I18n::MissingTranslationData
+ super
+ end
+end
\ No newline at end of file
diff --git a/app/libs/wechat_oauth/service.rb b/app/libs/wechat_oauth/service.rb
new file mode 100644
index 000000000..35ef8f455
--- /dev/null
+++ b/app/libs/wechat_oauth/service.rb
@@ -0,0 +1,61 @@
+module WechatOauth::Service
+ module_function
+
+ def request(method, url, params)
+ WechatOauth.logger.info("[WechatOauth] [#{method.to_s.upcase}] #{url} || #{params}")
+
+ client = Faraday.new(url: WechatOauth.base_url)
+ response = client.public_send(method, url, params)
+ result = JSON.parse(response.body)
+
+ WechatOauth.logger.info("[WechatOauth] [#{response.status}] #{result}")
+
+ if result['errcode'].present? && result['errcode'].to_s != '0'
+ raise WechatOauth::Error.new(result['errcode'], result['errmsg'])
+ end
+
+ result
+ end
+
+ # https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
+ # response:
+ # {
+ # "access_token":"ACCESS_TOKEN",
+ # "expires_in":7200,
+ # "refresh_token":"REFRESH_TOKEN",
+ # "openid":"OPENID",
+ # "scope":"SCOPE",
+ # "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
+ # }
+ def access_token(code)
+ params = {
+ appid: WechatOauth.appid,
+ secret: WechatOauth.secret,
+ code: code,
+ grant_type: 'authorization_code'
+ }
+
+ request(:get, '/sns/oauth2/access_token', params)
+ end
+
+ # https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Authorized_Interface_Calling_UnionID.html
+ # response:
+ # {
+ # "openid":"OPENID",
+ # "nickname":"NICKNAME",
+ # "sex":1,
+ # "province":"PROVINCE",
+ # "city":"CITY",
+ # "country":"COUNTRY",
+ # "headimgurl": "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0",
+ # "privilege":[
+ # "PRIVILEGE1",
+ # "PRIVILEGE2"
+ # ],
+ # "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
+ #
+ # }
+ def user_info(access_token, openid)
+ request(:get, '/sns/userinfo', access_token: access_token, openid: openid)
+ end
+end
\ No newline at end of file
diff --git a/app/models/homework_common.rb b/app/models/homework_common.rb
index abe254b28..fc2dd3ea4 100644
--- a/app/models/homework_common.rb
+++ b/app/models/homework_common.rb
@@ -240,6 +240,16 @@ class HomeworkCommon < ApplicationRecord
self.teacher_works(member).delay_finished.count
end
+ # 未通关数
+ def un_complete_count member
+ teacher_works(member).count - complete_count(member)
+ end
+
+ # 通关数
+ def complete_count member
+ Myshixun.where(id: self.teacher_works(member).pluck(:myshixun_id), status: 1).count
+ end
+
# 分组作业的最大分组id
def max_group_id
self.student_works.has_committed.maximum(:group_id).to_i + 1
diff --git a/app/models/laboratory.rb b/app/models/laboratory.rb
new file mode 100644
index 000000000..53e66ece0
--- /dev/null
+++ b/app/models/laboratory.rb
@@ -0,0 +1,26 @@
+class Laboratory < ApplicationRecord
+ belongs_to :school, optional: true
+
+ has_many :laboratory_users, dependent: :destroy
+ has_many :users, through: :laboratory_users, source: :user
+
+ has_one :laboratory_setting, dependent: :destroy
+
+ validates :identifier, uniqueness: { case_sensitive: false }, allow_nil: true
+
+ def site
+ rails_env = EduSetting.get('rails_env')
+ suffix = rails_env && rails_env != 'production' ? ".#{rails_env}.educoder.net" : '.educoder.net'
+
+ identifier ? "#{identifier}#{suffix}" : ''
+ end
+
+ def self.find_by_subdomain(subdomain)
+ return if subdomain.blank?
+
+ rails_env = EduSetting.get('rails_env')
+ subdomain = subdomain.slice(0, subdomain.size - rails_env.size - 1) if subdomain.end_with?(rails_env) # winse.dev => winse
+
+ find_by_identifier(subdomain)
+ end
+end
\ No newline at end of file
diff --git a/app/models/laboratory_setting.rb b/app/models/laboratory_setting.rb
new file mode 100644
index 000000000..32848dca2
--- /dev/null
+++ b/app/models/laboratory_setting.rb
@@ -0,0 +1,54 @@
+class LaboratorySetting < ApplicationRecord
+ belongs_to :laboratory
+
+ serialize :config, JSON
+
+ %i[name navbar footer].each do |method_name|
+ define_method method_name do
+ config&.[](method_name.to_s)
+ end
+
+ define_method "#{method_name}=" do |value|
+ self.config ||= {}
+ config.[]=(method_name.to_s, value)
+ end
+ end
+
+ def login_logo_url
+ logo_url('login')
+ end
+
+ def nav_logo_url
+ logo_url('nav')
+ end
+
+ def tab_logo_url
+ logo_url('tab')
+ end
+
+ def default_navbar
+ self.class.default_config[:navbar]
+ end
+
+ private
+
+ def logo_url(type)
+ return nil unless Util::FileManage.exists?(self, type)
+ Util::FileManage.source_disk_file_url(self, type)
+ end
+
+ def self.default_config
+ {
+ name: nil,
+ navbar: [
+ { 'name' => '实践课程', 'link' => '/paths', 'hidden' => false },
+ { 'name' => '翻转课堂', 'link' => '/courses', 'hidden' => false },
+ { 'name' => '实现项目', 'link' => '/shixuns', 'hidden' => false },
+ { 'name' => '在线竞赛', 'link' => '/competitions', 'hidden' => false },
+ { 'name' => '教学案例', 'link' => '/moop_cases', 'hidden' => false },
+ { 'name' => '交流问答', 'link' => '/forums', 'hidden' => false },
+ ],
+ footer: nil
+ }
+ end
+end
\ No newline at end of file
diff --git a/app/models/laboratory_user.rb b/app/models/laboratory_user.rb
new file mode 100644
index 000000000..be6c0c4dd
--- /dev/null
+++ b/app/models/laboratory_user.rb
@@ -0,0 +1,4 @@
+class LaboratoryUser < ApplicationRecord
+ belongs_to :laboratory
+ belongs_to :user
+end
\ No newline at end of file
diff --git a/app/models/open_user.rb b/app/models/open_user.rb
new file mode 100644
index 000000000..91228b976
--- /dev/null
+++ b/app/models/open_user.rb
@@ -0,0 +1,9 @@
+class OpenUser < ApplicationRecord
+ belongs_to :user
+
+ validates :uid, presence: true, uniqueness: { scope: :type }
+
+ def can_bind_cache_key
+ "open_user:#{type}:#{uid}:can_bind"
+ end
+end
\ No newline at end of file
diff --git a/app/models/open_users/qq.rb b/app/models/open_users/qq.rb
new file mode 100644
index 000000000..242693ce5
--- /dev/null
+++ b/app/models/open_users/qq.rb
@@ -0,0 +1,3 @@
+class OpenUsers::QQ < OpenUser
+
+end
\ No newline at end of file
diff --git a/app/models/open_users/wechat.rb b/app/models/open_users/wechat.rb
new file mode 100644
index 000000000..046b3e086
--- /dev/null
+++ b/app/models/open_users/wechat.rb
@@ -0,0 +1,3 @@
+class OpenUsers::Wechat < OpenUser
+
+end
\ No newline at end of file
diff --git a/app/models/user.rb b/app/models/user.rb
index 7bfe9c36f..0bebe8584 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -28,7 +28,12 @@ class User < ApplicationRecord
MIX_PASSWORD_LIMIT = 8
+ LOGIN_CHARS = %W(2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z).freeze
+
has_one :user_extension, dependent: :destroy
+ has_many :open_users, dependent: :destroy
+ has_one :wechat_open_user, class_name: 'OpenUsers::Wechat'
+ has_one :qq_open_user, class_name: 'OpenUsers::QQ'
accepts_nested_attributes_for :user_extension, update_only: true
has_many :memos, foreign_key: 'author_id'
@@ -38,7 +43,7 @@ class User < ApplicationRecord
has_many :myshixuns, :dependent => :destroy
has_many :study_shixuns, through: :myshixuns, source: :shixun # 已学习的实训
has_many :course_messages
- has_many :courses, dependent: :destroy
+ has_many :courses, foreign_key: 'tea_id', dependent: :destroy
#试卷
has_many :exercise_banks, :dependent => :destroy
@@ -628,6 +633,15 @@ class User < ApplicationRecord
admin? || business?
end
+ def self.generate_login(prefix)
+ login = prefix + LOGIN_CHARS.sample(8).join('')
+ while User.exists?(login: login)
+ login = prefix + LOGIN_CHARS.sample(8).join('')
+ end
+
+ login
+ end
+
protected
def validate_password_length
# 管理员的初始密码是5位
diff --git a/app/queries/admins/laboratory_query.rb b/app/queries/admins/laboratory_query.rb
new file mode 100644
index 000000000..8667bb8ed
--- /dev/null
+++ b/app/queries/admins/laboratory_query.rb
@@ -0,0 +1,23 @@
+class Admins::LaboratoryQuery < ApplicationQuery
+ include CustomSortable
+
+ attr_reader :params
+
+ sort_columns :id, default_by: :id, default_direction: :desc
+
+ def initialize(params)
+ @params = params
+ end
+
+ def call
+ laboratories = Laboratory.all
+
+ keyword = strip_param(:keyword)
+ if keyword.present?
+ like_sql = 'schools.name LIKE :keyword OR laboratories.identifier LIKE :keyword'
+ laboratories = laboratories.left_joins(:school).where(like_sql, keyword: "%#{keyword}%")
+ end
+
+ custom_sort laboratories, params[:sort_by], params[:sort_direction]
+ end
+end
\ No newline at end of file
diff --git a/app/services/admins/add_laboratory_user_service.rb b/app/services/admins/add_laboratory_user_service.rb
new file mode 100644
index 000000000..16df30880
--- /dev/null
+++ b/app/services/admins/add_laboratory_user_service.rb
@@ -0,0 +1,19 @@
+class Admins::AddLaboratoryUserService < ApplicationService
+ attr_reader :laboratory, :params
+
+ def initialize(laboratory, params)
+ @laboratory = laboratory
+ @params = params
+ end
+
+ def call
+ columns = %i[]
+ LaboratoryUser.bulk_insert(*columns) do |worker|
+ Array.wrap(params[:user_ids]).compact.each do |user_id|
+ next if laboratory.laboratory_users.exists?(user_id: user_id)
+
+ worker.add(laboratory_id: laboratory.id, user_id: user_id)
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/services/admins/create_laboratory_service.rb b/app/services/admins/create_laboratory_service.rb
new file mode 100644
index 000000000..98300d5af
--- /dev/null
+++ b/app/services/admins/create_laboratory_service.rb
@@ -0,0 +1,20 @@
+class Admins::CreateLaboratoryService < ApplicationService
+ Error = Class.new(StandardError)
+
+ attr_reader :params
+
+ def initialize(params)
+ @params = params
+ end
+
+ def call
+ raise Error, '单位不能为空' if params[:school_id].blank?
+ raise Error, '该单位已存在' if Laboratory.exists?(school_id: params[:school_id])
+
+ ActiveRecord::Base.transaction do
+ laboratory = Laboratory.create!(school_id: params[:school_id])
+
+ laboratory.create_laboratory_setting!
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/services/admins/save_laboratory_setting_service.rb b/app/services/admins/save_laboratory_setting_service.rb
new file mode 100644
index 000000000..00e202cd9
--- /dev/null
+++ b/app/services/admins/save_laboratory_setting_service.rb
@@ -0,0 +1,51 @@
+class Admins::SaveLaboratorySettingService < ApplicationService
+ attr_reader :laboratory, :laboratory_setting, :params
+
+ def initialize(laboratory, params)
+ @params = params
+ @laboratory = laboratory
+ @laboratory_setting = laboratory.laboratory_setting
+ end
+
+ def call
+ ActiveRecord::Base.transaction do
+ laboratory.identifier = strip params[:identifier]
+ laboratory_setting.name = strip params[:name]
+ laboratory_setting.navbar = navbar_config
+ laboratory_setting.footer = strip params[:footer]
+
+ laboratory.save!
+ laboratory_setting.save!
+
+ deal_logo_file
+ end
+
+ laboratory
+ end
+
+ private
+
+ def navbar_config
+ params[:navbar].map do |nav|
+ hash = {}
+ hash[:name] = strip nav[:name]
+ hash[:link] = strip nav[:link]
+ hash[:hidden] = nav[:hidden].to_s == 0
+ hash
+ end
+ end
+
+ def deal_logo_file
+ save_logo_file(params[:nav_logo], 'nav')
+ save_logo_file(params[:login_logo], 'login')
+ save_logo_file(params[:tab_logo], 'tab')
+ end
+
+ def save_logo_file(file, type)
+ return unless file.present? && file.is_a?(ActionDispatch::Http::UploadedFile)
+
+ file_path = Util::FileManage.source_disk_filename(laboratory_setting, type)
+ File.delete(file_path) if File.exist?(file_path) # 删除之前的文件
+ Util.write_file(file, file_path)
+ end
+end
\ No newline at end of file
diff --git a/app/services/application_service.rb b/app/services/application_service.rb
index c6f66c098..1be6896eb 100644
--- a/app/services/application_service.rb
+++ b/app/services/application_service.rb
@@ -1,3 +1,11 @@
class ApplicationService
include Callable
+
+ Error = Class.new(StandardError)
+
+ private
+
+ def strip(str)
+ str.to_s.strip.presence
+ end
end
\ No newline at end of file
diff --git a/app/services/create_bind_user_service.rb b/app/services/create_bind_user_service.rb
new file mode 100644
index 000000000..5455b7561
--- /dev/null
+++ b/app/services/create_bind_user_service.rb
@@ -0,0 +1,52 @@
+class CreateBindUserService < ApplicationService
+ attr_reader :user, :params
+
+ def initialize(user, params)
+ @user = user
+ @params = params
+ end
+
+ def call
+ raise Error, '系统错误' if open_user.blank?
+ raise Error, '系统错误' unless can_bind_user?
+
+ if params[:not_bind].to_s == 'true'
+ clear_can_bind_user_flag
+ return user
+ end
+
+ bind_user = User.try_to_login(params[:username], params[:password])
+ raise Error, '用户名或者密码错误' if bind_user.blank?
+
+ ActiveRecord::Base.transaction do
+ open_user.user_id = bind_user.id
+ open_user.save!
+
+ user.user_extension.delete
+ user.delete
+ end
+
+ clear_can_bind_user_flag
+
+ bind_user
+ end
+
+ private
+
+ def open_user
+ @_open_user ||= begin
+ case params[:type].to_s
+ when 'wechat' then user.wechat_open_user
+ when 'qq' then user.qq_open_user
+ end
+ end
+ end
+
+ def can_bind_user?
+ Rails.cache.read(open_user.can_bind_cache_key).present?
+ end
+
+ def clear_can_bind_user_flag
+ Rails.cache.delete(open_user.can_bind_cache_key)
+ end
+end
\ No newline at end of file
diff --git a/app/services/oauth/create_or_find_qq_account_service.rb b/app/services/oauth/create_or_find_qq_account_service.rb
new file mode 100644
index 000000000..691764ea2
--- /dev/null
+++ b/app/services/oauth/create_or_find_qq_account_service.rb
@@ -0,0 +1,38 @@
+class Oauth::CreateOrFindQqAccountService < ApplicationService
+
+ attr_reader :user, :params
+
+ def initialize(user, params)
+ @user = user
+ @params = params
+ end
+
+ def call
+ new_user = false
+ # 存在该用户
+ open_user = OpenUsers::QQ.find_by(uid: params['uid'])
+ return [open_user.user, new_user] if open_user.present?
+
+ if user.blank? || !user.logged?
+ new_user = true
+ # 新用户
+ login = User.generate_login('q')
+ @user = User.new(login: login, nickname: params.dig('info', 'nickname'), type: 'User', status: User::STATUS_ACTIVE)
+ end
+
+ ActiveRecord::Base.transaction do
+ if user.new_record?
+ user.save!
+
+ gender = params.dig('extra', 'raw_info', 'gender') == '女' ? 1 : 0
+ user.create_user_extension!(gender: gender)
+ end
+
+ new_open_user = OpenUsers::QQ.create!(user: user, uid: params['uid'])
+
+ Rails.cache.write(new_open_user.can_bind_cache_key, 1, expires_in: 1.hours) if new_user # 方便后面进行账号绑定
+ end
+
+ [user, new_user]
+ end
+end
\ No newline at end of file
diff --git a/app/services/oauth/create_or_find_wechat_account_service.rb b/app/services/oauth/create_or_find_wechat_account_service.rb
new file mode 100644
index 000000000..0313054b7
--- /dev/null
+++ b/app/services/oauth/create_or_find_wechat_account_service.rb
@@ -0,0 +1,57 @@
+class Oauth::CreateOrFindWechatAccountService < ApplicationService
+ Error = Class.new(StandardError)
+
+ attr_reader :user, :params
+
+ def initialize(user, params)
+ @user = user
+ @params = params
+ end
+
+ def call
+ code = params['code'].to_s.strip
+ raise Error, 'Code不能为空' if code.blank?
+ new_user = false
+
+ result = WechatOauth::Service.access_token(code)
+ result = WechatOauth::Service.user_info(result['access_token'], result['openid'])
+
+ # 存在该用户
+ open_user = OpenUsers::Wechat.find_by(uid: result['unionid'])
+ return [open_user.user, new_user] if open_user.present?
+
+ if user.blank? || !user.logged?
+ new_user = true
+ # 新用户
+ login = User.generate_login('w')
+ @user = User.new(login: login, nickname: result['nickname'], type: 'User', status: User::STATUS_ACTIVE)
+ end
+
+ ActiveRecord::Base.transaction do
+ if new_user
+ user.save!
+
+ gender = result['sex'].to_i == 1 ? 0 : 1
+ user.create_user_extension!(gender: gender)
+
+ # 下载头像
+ avatar_path = Util::FileManage.source_disk_filename(user)
+ Util.download_file(result['headimgurl'], avatar_path)
+ end
+
+ new_open_user= OpenUsers::Wechat.create!(user: user, uid: result['unionid'])
+
+ Rails.cache.write(new_open_user.can_bind_cache_key, 1, expires_in: 1.hours) if new_user # 方便后面进行账号绑定
+ end
+
+ [user, new_user]
+ rescue WechatOauth::Error => ex
+ raise Error, ex.message
+ end
+
+ private
+
+ def code
+ params[:code].to_s.strip
+ end
+end
\ No newline at end of file
diff --git a/app/services/users/update_password_service.rb b/app/services/users/update_password_service.rb
index 0df32eb76..53c6f74c8 100644
--- a/app/services/users/update_password_service.rb
+++ b/app/services/users/update_password_service.rb
@@ -11,7 +11,7 @@ class Users::UpdatePasswordService < ApplicationService
def call
Users::UpdatePasswordForm.new(params).validate!
- raise Error, '旧密码不匹配' unless user.check_password?(params[:old_password])
+ raise Error, '旧密码不匹配' unless user.check_password?(params[:old_password]) || user.hashed_password.blank?
ActiveRecord::Base.transaction do
user.update!(password: params[:password])
diff --git a/app/views/admins/laboratories/index.html.erb b/app/views/admins/laboratories/index.html.erb
new file mode 100644
index 000000000..012eed792
--- /dev/null
+++ b/app/views/admins/laboratories/index.html.erb
@@ -0,0 +1,19 @@
+<% define_admin_breadcrumbs do %>
+ <% add_admin_breadcrumb('云上实验室') %>
+<% end %>
+
+
+ <%= form_tag(admins_laboratories_path(unsafe_params), method: :get, class: 'form-inline search-form flex-1', remote: true) do %>
+ <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-6 col-md-4 ml-3', placeholder: '学校名称/二级域名前缀检索') %>
+ <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
+ <% end %>
+
+ <%= javascript_void_link '新建', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-laboratory-modal' } %>
+
+
+
+ <%= render(partial: 'admins/laboratories/shared/list', locals: { laboratories: @laboratories }) %>
+
+
+<%= render 'admins/laboratories/shared/create_laboratory_modal' %>
+<%= render 'admins/laboratories/shared/add_laboratory_user_modal' %>
\ No newline at end of file
diff --git a/app/views/admins/laboratories/index.js.erb b/app/views/admins/laboratories/index.js.erb
new file mode 100644
index 000000000..dc17c6a6d
--- /dev/null
+++ b/app/views/admins/laboratories/index.js.erb
@@ -0,0 +1 @@
+$('.laboratory-list-container').html("<%= j(render partial: 'admins/laboratories/shared/list', locals: { laboratories: @laboratories }) %>");
\ No newline at end of file
diff --git a/app/views/admins/laboratories/shared/_add_laboratory_user_modal.html.erb b/app/views/admins/laboratories/shared/_add_laboratory_user_modal.html.erb
new file mode 100644
index 000000000..a13565cd6
--- /dev/null
+++ b/app/views/admins/laboratories/shared/_add_laboratory_user_modal.html.erb
@@ -0,0 +1,30 @@
+
\ No newline at end of file
diff --git a/app/views/admins/laboratories/shared/_create_laboratory_modal.html.erb b/app/views/admins/laboratories/shared/_create_laboratory_modal.html.erb
new file mode 100644
index 000000000..0a77477d3
--- /dev/null
+++ b/app/views/admins/laboratories/shared/_create_laboratory_modal.html.erb
@@ -0,0 +1,28 @@
+
\ No newline at end of file
diff --git a/app/views/admins/laboratories/shared/_laboratory_item.html.erb b/app/views/admins/laboratories/shared/_laboratory_item.html.erb
new file mode 100644
index 000000000..5dd97b549
--- /dev/null
+++ b/app/views/admins/laboratories/shared/_laboratory_item.html.erb
@@ -0,0 +1,40 @@
+<% school = laboratory.school %>
+<%= school&.name || 'EduCoder主站' %>
+
+ <% if laboratory.identifier %>
+ <%= link_to laboratory.site, "https://#{laboratory.site}", target: '_blank' %>
+ <% else %>
+ --
+ <% end %>
+
+
+ <% if school && school.identifier.present? %>
+ <%= link_to school.identifier.to_s, statistics_college_path(school.identifier), target: '_blank' %>
+ <% else %>
+ --
+ <% end %>
+
+
+
+ <% laboratory.users.each do |user| %>
+
+ <%= link_to user.real_name, "/users/#{user.login}", target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } %>
+ <%= link_to(admins_laboratory_laboratory_user_path(laboratory, user_id: user.id),
+ method: :delete, remote: true, class: 'ml-1 delete-laboratory-user-action',
+ data: { confirm: '确认删除吗?' }) do %>
+
+ <% end %>
+
+ <% end %>
+
+
+<%= laboratory.created_at.strftime('%Y-%m-%d %H:%M') %>
+
+ <%= link_to '定制', admins_laboratory_laboratory_setting_path(laboratory) %>
+
+ <% if school.present? && laboratory.id != 1 %>
+ <%= javascript_void_link '添加管理员', class: 'action', data: { laboratory_id: laboratory.id, toggle: 'modal', target: '.admin-add-laboratory-user-modal' } %>
+
+ <%= delete_link '删除', admins_laboratory_path(laboratory, element: ".laboratory-item-#{laboratory.id}"), class: 'delete-laboratory-action' %>
+ <% end %>
+
\ No newline at end of file
diff --git a/app/views/admins/laboratories/shared/_list.html.erb b/app/views/admins/laboratories/shared/_list.html.erb
new file mode 100644
index 000000000..33a47eed7
--- /dev/null
+++ b/app/views/admins/laboratories/shared/_list.html.erb
@@ -0,0 +1,25 @@
+
+
+
+ 单位名称
+ 域名
+ 统计链接
+ 管理员
+ <%= sort_tag('创建时间', name: 'id', path: admins_laboratories_path) %>
+ 操作
+
+
+
+ <% if laboratories.present? %>
+ <% laboratories.each do |laboratory| %>
+
+ <%= render 'admins/laboratories/shared/laboratory_item', laboratory: laboratory %>
+
+ <% end %>
+ <% else %>
+ <%= render 'admins/shared/no_data_for_table' %>
+ <% end %>
+
+
+
+<%= render partial: 'admins/shared/paginate', locals: { objects: laboratories } %>
\ No newline at end of file
diff --git a/app/views/admins/laboratory_settings/show.html.erb b/app/views/admins/laboratory_settings/show.html.erb
new file mode 100644
index 000000000..120bba6cb
--- /dev/null
+++ b/app/views/admins/laboratory_settings/show.html.erb
@@ -0,0 +1,131 @@
+<% define_admin_breadcrumbs do %>
+ <% add_admin_breadcrumb('云上实验室', admins_laboratories_path) %>
+ <% add_admin_breadcrumb('单位定制') %>
+<% end %>
+
+
+ <%= simple_form_for(@laboratory, url: admins_laboratory_laboratory_setting_path(@laboratory), method: 'patch', html: { enctype: 'multipart/form-data' }) do |f| %>
+ <% setting = @laboratory.laboratory_setting %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <%= javascript_void_link '保存', class: 'btn btn-primary mr-3 px-4 submit-btn' %>
+ <%= link_to '取消', admins_laboratories_path, class: 'btn btn-secondary px-4' %>
+
+ <% end %>
+
\ No newline at end of file
diff --git a/app/views/admins/laboratory_users/create.js.erb b/app/views/admins/laboratory_users/create.js.erb
new file mode 100644
index 000000000..f43fd7887
--- /dev/null
+++ b/app/views/admins/laboratory_users/create.js.erb
@@ -0,0 +1,4 @@
+$('.modal.admin-add-laboratory-user-modal').modal('hide');
+$.notify({ message: '操作成功' });
+
+$('.laboratory-list-table .laboratory-item-<%= current_laboratory.id %>').html("<%= j(render partial: 'admins/laboratories/shared/laboratory_item', locals: { laboratory: current_laboratory }) %>")
\ No newline at end of file
diff --git a/app/views/admins/laboratory_users/destroy.js.erb b/app/views/admins/laboratory_users/destroy.js.erb
new file mode 100644
index 000000000..16ef62910
--- /dev/null
+++ b/app/views/admins/laboratory_users/destroy.js.erb
@@ -0,0 +1,2 @@
+$.notify({ message: '操作成功' });
+$('.laboratory-list-container .laboratory-item-<%= current_laboratory.id %> .laboratory-user-item-<%= @laboratory_user.user_id %>').remove();
\ No newline at end of file
diff --git a/app/views/admins/shared/_sidebar.html.erb b/app/views/admins/shared/_sidebar.html.erb
index 553820e69..145910928 100644
--- a/app/views/admins/shared/_sidebar.html.erb
+++ b/app/views/admins/shared/_sidebar.html.erb
@@ -33,23 +33,13 @@
<%= sidebar_item_group('#schools-submenu', '单位管理', icon: 'building') do %>
<%= sidebar_item(admins_schools_path, '单位列表', icon: 'university', controller: 'admins-schools') %>
<%= sidebar_item(admins_departments_path, '部门列表', icon: 'sitemap', controller: 'admins-departments') %>
+ <%= sidebar_item(admins_laboratories_path, '云上实验室', icon: 'cloud', controller: 'admins-laboratories') %>
<% end %>
-
- <%#= sidebar_item_group('#course-submenu', '课堂+', icon: 'mortar-board') do %>
-
-
-
-
- <%# end %>
-
-
<%= sidebar_item_group('#user-submenu', '用户', icon: 'user') do %>
<%= sidebar_item(admins_users_path, '用户列表', icon: 'user', controller: 'admins-users') %>
-
-
<% end %>
diff --git a/app/views/courses/students.json.jbuilder b/app/views/courses/students.json.jbuilder
index 0b5d7fe71..5788b44f2 100644
--- a/app/views/courses/students.json.jbuilder
+++ b/app/views/courses/students.json.jbuilder
@@ -1,7 +1,7 @@
json.students do
json.array! @students do |student|
json.user_id student.user_id
- # json.login student.user.try(:login)
+ json.login student.user.try(:login)
json.name student.user.try(:real_name)
json.name_link user_path(student.user)
json.student_id student.user.try(:student_id)
diff --git a/app/views/exercises/_user_exercise_info.json.jbuilder b/app/views/exercises/_user_exercise_info.json.jbuilder
index bdac3a985..c351a9b26 100644
--- a/app/views/exercises/_user_exercise_info.json.jbuilder
+++ b/app/views/exercises/_user_exercise_info.json.jbuilder
@@ -65,7 +65,7 @@ json.exercise_questions do
shixun_type: user_ques_answers[:shixun_type],
ques_position: nil,
edit_type:nil
- if user_ques_comments.count > 0
+ if user_ques_comments.size > 0
json.question_comments do
json.partial! "exercises/exercise_comments", question_comment:user_ques_answers[:question_comment].first
end
diff --git a/app/views/homework_commons/works_list.json.jbuilder b/app/views/homework_commons/works_list.json.jbuilder
index e567ea79a..839b40bfd 100644
--- a/app/views/homework_commons/works_list.json.jbuilder
+++ b/app/views/homework_commons/works_list.json.jbuilder
@@ -23,7 +23,7 @@ if @user_course_identity < Course::STUDENT
if @homework.homework_type != "practice"
json.teacher_comment teacher_comment @homework, @current_user.id
end
- json.task_status homework_status @homework, @member
+ json.task_status @homework.homework_type != "practice" ? homework_status(@homework, @member) : practice_homework_status(@homework, @member)
json.course_group_info course_group_info @course, @current_user.id
elsif @user_course_identity == Course::STUDENT
diff --git a/app/views/settings/show.json.jbuilder b/app/views/settings/show.json.jbuilder
new file mode 100644
index 000000000..0765e303b
--- /dev/null
+++ b/app/views/settings/show.json.jbuilder
@@ -0,0 +1,12 @@
+json.setting do
+ setting = @laboratory.laboratory_setting
+
+ json.name setting.name || default_setting.name
+ json.nav_logo_url setting.nav_logo_url || default_setting.nav_logo_url
+ json.login_logo_url setting.login_logo_url || default_setting.login_logo_url
+ json.tab_logo_url setting.tab_logo_url || default_setting.tab_logo_url
+
+ json.navbar setting.navbar || default_setting.navbar
+
+ json.footer setting.footer || default_setting.footer
+end
\ No newline at end of file
diff --git a/app/views/users/accounts/show.json.jbuilder b/app/views/users/accounts/show.json.jbuilder
index ec81cc6bf..48d69924c 100644
--- a/app/views/users/accounts/show.json.jbuilder
+++ b/app/views/users/accounts/show.json.jbuilder
@@ -25,3 +25,5 @@ json.department_name extension&.department&.name
json.base_info_completed user.profile_completed?
json.all_certified user.all_certified?
+
+json.has_password user.hashed_password.present?
diff --git a/config/admins/sidebar.yml b/config/admins/sidebar.yml
index 30af794b7..9da34a014 100644
--- a/config/admins/sidebar.yml
+++ b/config/admins/sidebar.yml
@@ -1 +1,2 @@
-admins-mirror_scripts: 'admins-mirror_repositories'
\ No newline at end of file
+admins-mirror_scripts: 'admins-mirror_repositories'
+admins-laboratory_settings: 'admins-laboratories'
\ No newline at end of file
diff --git a/config/configuration.yml.example b/config/configuration.yml.example
index 6feee28d9..612011a7f 100644
--- a/config/configuration.yml.example
+++ b/config/configuration.yml.example
@@ -1,4 +1,13 @@
defaults: &defaults
+ oauth:
+ qq:
+ appid: 'test'
+ secret: 'test123456'
+ wechat:
+ appid: 'test'
+ secret: 'test'
+ scope: 'snsapi_login'
+ base_url: 'https://api.weixin.qq.com'
aliyun_vod:
access_key_id: 'test'
access_key_secret: 'test'
diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb
index d173fb9fa..a501cb14f 100644
--- a/config/initializers/inflections.rb
+++ b/config/initializers/inflections.rb
@@ -14,3 +14,8 @@
# ActiveSupport::Inflector.inflections(:en) do |inflect|
# inflect.acronym 'RESTful'
# end
+
+ActiveSupport::Inflector.inflections do |inflect|
+ inflect.acronym 'QQ'
+ inflect.acronym 'OmniAuth'
+end
\ No newline at end of file
diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb
new file mode 100644
index 000000000..27ade9ed4
--- /dev/null
+++ b/config/initializers/omniauth.rb
@@ -0,0 +1,17 @@
+OmniAuth.config.add_camelization 'qq', 'QQ'
+
+oauth_config = {}
+begin
+ config = Rails.application.config_for(:configuration)
+ oauth_config = config.dig('oauth', 'qq')
+ raise 'oauth qq config missing' if oauth_config.blank?
+rescue => ex
+ raise ex if Rails.env.production?
+
+ puts %Q{\033[33m [warning] qq oauth config or configuration.yml missing,
+ please add it or execute 'cp config/configuration.yml.example config/configuration.yml' \033[0m}
+end
+
+Rails.application.config.middleware.use OmniAuth::Builder do
+ provider :qq, oauth_config['appid'], oauth_config['secret']
+end
diff --git a/config/initializers/wechat_oauth_init.rb b/config/initializers/wechat_oauth_init.rb
new file mode 100644
index 000000000..6c7f849ec
--- /dev/null
+++ b/config/initializers/wechat_oauth_init.rb
@@ -0,0 +1,17 @@
+oauth_config = {}
+begin
+ config = Rails.application.config_for(:configuration)
+ oauth_config = config.dig('oauth', 'wechat')
+ raise 'oauth wechat config missing' if oauth_config.blank?
+rescue => ex
+ raise ex if Rails.env.production?
+
+ puts %Q{\033[33m [warning] wechat oauth config or configuration.yml missing,
+ please add it or execute 'cp config/configuration.yml.example config/configuration.yml' \033[0m}
+end
+
+WechatOauth.appid = oauth_config['appid']
+WechatOauth.secret = oauth_config['secret']
+WechatOauth.scope = oauth_config['scope']
+WechatOauth.base_url = oauth_config['base_url']
+WechatOauth.logger = Rails.logger
diff --git a/config/locales/laboratories/zh-CN.yml b/config/locales/laboratories/zh-CN.yml
new file mode 100644
index 000000000..42127f0a1
--- /dev/null
+++ b/config/locales/laboratories/zh-CN.yml
@@ -0,0 +1,7 @@
+zh-CN:
+ activerecord:
+ models:
+ laboratory: ''
+ attributes:
+ laboratory:
+ identifier: '二级域名'
\ No newline at end of file
diff --git a/config/locales/oauth/wechat.zh-CN.yml b/config/locales/oauth/wechat.zh-CN.yml
new file mode 100644
index 000000000..12b58c513
--- /dev/null
+++ b/config/locales/oauth/wechat.zh-CN.yml
@@ -0,0 +1,4 @@
+'zh-CN':
+ oauth:
+ wechat:
+ '40029': '授权已失效,请重新授权'
\ No newline at end of file
diff --git a/config/routes.rb b/config/routes.rb
index 50b61c463..32725e8e9 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -824,6 +824,11 @@ Rails.application.routes.draw do
end
end
resource :template, only: [:show]
+ resource :setting, only: [:show]
+
+ get '/auth/qq/callback', to: 'oauth/qq#create'
+ get '/auth/wechat/callback', to: 'oauth/wechat#create'
+ resource :bind_user, only: [:create]
end
namespace :admins do
@@ -953,7 +958,7 @@ Rails.application.routes.draw do
resources :choose_mirror_repositories, only: [:new, :create]
resources :schools, only: [:index, :destroy]
resources :departments, only: [:index, :create, :edit, :update, :destroy] do
- resource :department_member, only: [:create, :update, :destroy]
+ resource :department_member, only: [:create, :destroy]
post :merge, on: :collection
end
resources :myshixuns, only: [:index]
@@ -971,6 +976,10 @@ Rails.application.routes.draw do
resources :carousels, only: [:index, :create, :update, :destroy] do
post :drag, on: :collection
end
+ resources :laboratories, only: [:index, :create, :destroy] do
+ resource :laboratory_setting, only: [:show, :update]
+ resource :laboratory_user, only: [:create, :destroy]
+ end
end
resources :colleges, only: [] do
diff --git a/db/migrate/20190821054352_create_open_users.rb b/db/migrate/20190821054352_create_open_users.rb
new file mode 100644
index 000000000..f8e0aba4b
--- /dev/null
+++ b/db/migrate/20190821054352_create_open_users.rb
@@ -0,0 +1,14 @@
+class CreateOpenUsers < ActiveRecord::Migration[5.2]
+ def change
+ create_table :open_users do |t|
+ t.references :user
+
+ t.string :type
+ t.string :uid
+
+ t.timestamps
+
+ t.index [:type, :uid], unique: true
+ end
+ end
+end
diff --git a/db/migrate/20191010011844_create_laboratories.rb b/db/migrate/20191010011844_create_laboratories.rb
new file mode 100644
index 000000000..3dfb442f0
--- /dev/null
+++ b/db/migrate/20191010011844_create_laboratories.rb
@@ -0,0 +1,12 @@
+class CreateLaboratories < ActiveRecord::Migration[5.2]
+ def change
+ create_table :laboratories do |t|
+ t.references :school
+ t.string :identifier
+
+ t.timestamps
+
+ t.index :identifier, unique: true
+ end
+ end
+end
diff --git a/db/migrate/20191010012226_create_laboratory_users.rb b/db/migrate/20191010012226_create_laboratory_users.rb
new file mode 100644
index 000000000..1b7ae762d
--- /dev/null
+++ b/db/migrate/20191010012226_create_laboratory_users.rb
@@ -0,0 +1,8 @@
+class CreateLaboratoryUsers < ActiveRecord::Migration[5.2]
+ def change
+ create_table :laboratory_users do |t|
+ t.references :laboratory
+ t.references :user
+ end
+ end
+end
diff --git a/db/migrate/20191010063403_create_laboratory_settings.rb b/db/migrate/20191010063403_create_laboratory_settings.rb
new file mode 100644
index 000000000..7f0a5f015
--- /dev/null
+++ b/db/migrate/20191010063403_create_laboratory_settings.rb
@@ -0,0 +1,9 @@
+class CreateLaboratorySettings < ActiveRecord::Migration[5.2]
+ def change
+ create_table :laboratory_settings do |t|
+ t.references :laboratory
+
+ t.text :config
+ end
+ end
+end
diff --git a/db/migrate/20191011025619_init_edu_coder_laboratory.rb b/db/migrate/20191011025619_init_edu_coder_laboratory.rb
new file mode 100644
index 000000000..831ca3985
--- /dev/null
+++ b/db/migrate/20191011025619_init_edu_coder_laboratory.rb
@@ -0,0 +1,22 @@
+class InitEduCoderLaboratory < ActiveRecord::Migration[5.2]
+ def change
+ ActiveRecord::Base.transaction do
+ laboratory = Laboratory.create!(id: 1, identifier: 'www')
+ setting = laboratory.build_laboratory_setting
+ footer = %Q{
+
+ }
+ config = setting.class.default_config.merge(name: 'EduCoder', footer: footer)
+ setting.config = config
+ setting.save!
+ end
+ end
+end
diff --git a/public/assets/.sprockets-manifest-4627fa5586ef7fed55ca286af7c028e9.json b/public/assets/.sprockets-manifest-4627fa5586ef7fed55ca286af7c028e9.json
index 52d2a4e9b..b8b49cc9d 100644
--- a/public/assets/.sprockets-manifest-4627fa5586ef7fed55ca286af7c028e9.json
+++ b/public/assets/.sprockets-manifest-4627fa5586ef7fed55ca286af7c028e9.json
@@ -1 +1 @@
-{"files":{"admin-cd9ca8bacc973ce2dbace30c97f6c40bc08e2c2ee44972f668e738e1902c0121.js":{"logical_path":"admin.js","mtime":"2019-09-11T16:20:07+08:00","size":4350881,"digest":"cd9ca8bacc973ce2dbace30c97f6c40bc08e2c2ee44972f668e738e1902c0121","integrity":"sha256-zZyousyXPOLbrOMMl/bEC8COLC7kSXL2aOc44ZAsASE="},"admin-a1b3356efe50ff4717cf22475639b5333c5354ba03fd107c9b7a8d4ae76f47aa.css":{"logical_path":"admin.css","mtime":"2019-09-11T16:20:07+08:00","size":773445,"digest":"a1b3356efe50ff4717cf22475639b5333c5354ba03fd107c9b7a8d4ae76f47aa","integrity":"sha256-obM1bv5Q/0cXzyJHVjm1MzxTVLoD/RB8m3qNSudvR6o="},"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot":{"logical_path":"font-awesome/fontawesome-webfont.eot","mtime":"2019-08-14T17:22:43+08:00","size":165742,"digest":"7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979","integrity":"sha256-e/yrbbmdXPvxcFygU23ceFhUMsxfpBu9etDwCQM7KXk="},"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2":{"logical_path":"font-awesome/fontawesome-webfont.woff2","mtime":"2019-08-14T17:22:43+08:00","size":77160,"digest":"2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe","integrity":"sha256-Kt78vAQefRj88tQXh53FoJmXqmTWdbejxLbOM9oT8/4="},"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff":{"logical_path":"font-awesome/fontawesome-webfont.woff","mtime":"2019-08-14T17:22:43+08:00","size":98024,"digest":"ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07","integrity":"sha256-ugxZ3rVFD1y0Gz+TYJ7i0NmVQVh33foiPoqKdTNHTwc="},"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf":{"logical_path":"font-awesome/fontawesome-webfont.ttf","mtime":"2019-08-14T17:22:43+08:00","size":165548,"digest":"aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8","integrity":"sha256-qljzPyOaD7AvXHpsRcBD16msmgkzNYBmlOzW1O3A1qg="},"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg":{"logical_path":"font-awesome/fontawesome-webfont.svg","mtime":"2019-08-14T17:22:43+08:00","size":444379,"digest":"ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4","integrity":"sha256-rWFXkmwWIrpOHQPUePFUE2hSS/xG9R5C/g2UX37zI+Q="},"college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js":{"logical_path":"college.js","mtime":"2019-09-26T14:40:40+08:00","size":3352744,"digest":"18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287","integrity":"sha256-GPXoQAMxY06JijWswhh4FcCWwl4Kt0q6NBrpFhZs0oc="},"college-944d4273f62c7538368b9017fdd3387b5e3bea31a87873770eb231324546d4d9.css":{"logical_path":"college.css","mtime":"2019-09-11T16:20:07+08:00","size":546841,"digest":"944d4273f62c7538368b9017fdd3387b5e3bea31a87873770eb231324546d4d9","integrity":"sha256-lE1Cc/YsdTg2i5AX/dM4e1476jGoeHN3DrIxMkVG1Nk="},"logo-7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423.png":{"logical_path":"logo.png","mtime":"2019-09-03T08:55:53+08:00","size":2816,"digest":"7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423","integrity":"sha256-f/ESVocJv5f5iY/ockm3qPIA/x9I1TfYWvhyFfGHBCM="},"application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js":{"logical_path":"application.js","mtime":"2019-09-26T14:40:40+08:00","size":600706,"digest":"9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb","integrity":"sha256-nPvD15JZmh0N5ce4QgnhwrLmAzbw8B4Z8FgWY5GHCPs="},"application-5eb87c6e13676d0183317debce17fade27e68c4acee28c419438da15d53c94f2.css":{"logical_path":"application.css","mtime":"2019-09-11T16:20:07+08:00","size":1844002,"digest":"5eb87c6e13676d0183317debce17fade27e68c4acee28c419438da15d53c94f2","integrity":"sha256-Xrh8bhNnbQGDMX3rzhf63ifmjErO4oxBlDjaFdU8lPI="},"admin-c9e5ebe6191548550e27514196ea125cfbb402820ec125a0c9acf99d2d378fe4.js":{"logical_path":"admin.js","mtime":"2019-09-21T15:28:08+08:00","size":4382031,"digest":"c9e5ebe6191548550e27514196ea125cfbb402820ec125a0c9acf99d2d378fe4","integrity":"sha256-yeXr5hkVSFUOJ1FBluoSXPu0AoIOwSWgyaz5nS03j+Q="},"admin-59c59f8cae8bef4a8359286c985458110c9d03ea121516595c988943f4717c38.css":{"logical_path":"admin.css","mtime":"2019-09-21T14:49:04+08:00","size":840093,"digest":"59c59f8cae8bef4a8359286c985458110c9d03ea121516595c988943f4717c38","integrity":"sha256-WcWfjK6L70qDWShsmFRYEQydA+oSFRZZXJiJQ/RxfDg="},"college-38f953d6ba5b85d3fab63cb3c2bbf0d057ccc6454d07cfaafac3b06da37b8437.css":{"logical_path":"college.css","mtime":"2019-09-16T13:56:09+08:00","size":579109,"digest":"38f953d6ba5b85d3fab63cb3c2bbf0d057ccc6454d07cfaafac3b06da37b8437","integrity":"sha256-OPlT1rpbhdP6tjyzwrvw0FfMxkVNB8+q+sOwbaN7hDc="},"application-646b1158a4e8c1f13e684d6fe9025abc75f8d3ba5256e440802c0398223374f3.css":{"logical_path":"application.css","mtime":"2019-09-21T14:49:04+08:00","size":1988767,"digest":"646b1158a4e8c1f13e684d6fe9025abc75f8d3ba5256e440802c0398223374f3","integrity":"sha256-ZGsRWKTowfE+aE1v6QJavHX407pSVuRAgCwDmCIzdPM="},"admin-a47e37c0ec7cf5f22380249776d1e82d65b6b6aa272ed7389185aa200fa40751.js":{"logical_path":"admin.js","mtime":"2019-09-25T15:33:05+08:00","size":4383107,"digest":"a47e37c0ec7cf5f22380249776d1e82d65b6b6aa272ed7389185aa200fa40751","integrity":"sha256-pH43wOx89fIjgCSXdtHoLWW2tqonLtc4kYWqIA+kB1E="},"admin-432c4eac09b036c57ff1e88d902b8aa7df81164e4b419bac557cf1366c1d3ad9.js":{"logical_path":"admin.js","mtime":"2019-09-25T15:35:20+08:00","size":4383103,"digest":"432c4eac09b036c57ff1e88d902b8aa7df81164e4b419bac557cf1366c1d3ad9","integrity":"sha256-QyxOrAmwNsV/8eiNkCuKp9+BFk5LQZusVXzxNmwdOtk="},"admin-978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a.js":{"logical_path":"admin.js","mtime":"2019-09-30T14:43:41+08:00","size":4387200,"digest":"978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a","integrity":"sha256-l45c5gf3fCaBShdPSA2nmsJGwiAYaO+EZUqgO7Zye1o="},"admin-896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7.css":{"logical_path":"admin.css","mtime":"2019-09-30T14:43:41+08:00","size":842269,"digest":"896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7","integrity":"sha256-iWKB9HMXIrDAhNuxryHQ80pbwULViv9Xs5GGSrcd3Kc="},"application-97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd.css":{"logical_path":"application.css","mtime":"2019-09-30T14:43:41+08:00","size":1993118,"digest":"97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd","integrity":"sha256-l/MT6bt9JUdmSffXIVlZz0IUgP0KN4XRlWlTv5Sh6L0="}},"assets":{"admin.js":"admin-978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a.js","admin.css":"admin-896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7.css","font-awesome/fontawesome-webfont.eot":"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot","font-awesome/fontawesome-webfont.woff2":"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2","font-awesome/fontawesome-webfont.woff":"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff","font-awesome/fontawesome-webfont.ttf":"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf","font-awesome/fontawesome-webfont.svg":"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg","college.js":"college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js","college.css":"college-38f953d6ba5b85d3fab63cb3c2bbf0d057ccc6454d07cfaafac3b06da37b8437.css","logo.png":"logo-7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423.png","application.js":"application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js","application.css":"application-97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd.css"}}
\ No newline at end of file
+{"files":{"admin-cd9ca8bacc973ce2dbace30c97f6c40bc08e2c2ee44972f668e738e1902c0121.js":{"logical_path":"admin.js","mtime":"2019-09-11T16:20:07+08:00","size":4350881,"digest":"cd9ca8bacc973ce2dbace30c97f6c40bc08e2c2ee44972f668e738e1902c0121","integrity":"sha256-zZyousyXPOLbrOMMl/bEC8COLC7kSXL2aOc44ZAsASE="},"admin-a1b3356efe50ff4717cf22475639b5333c5354ba03fd107c9b7a8d4ae76f47aa.css":{"logical_path":"admin.css","mtime":"2019-09-11T16:20:07+08:00","size":773445,"digest":"a1b3356efe50ff4717cf22475639b5333c5354ba03fd107c9b7a8d4ae76f47aa","integrity":"sha256-obM1bv5Q/0cXzyJHVjm1MzxTVLoD/RB8m3qNSudvR6o="},"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot":{"logical_path":"font-awesome/fontawesome-webfont.eot","mtime":"2019-08-14T17:22:43+08:00","size":165742,"digest":"7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979","integrity":"sha256-e/yrbbmdXPvxcFygU23ceFhUMsxfpBu9etDwCQM7KXk="},"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2":{"logical_path":"font-awesome/fontawesome-webfont.woff2","mtime":"2019-08-14T17:22:43+08:00","size":77160,"digest":"2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe","integrity":"sha256-Kt78vAQefRj88tQXh53FoJmXqmTWdbejxLbOM9oT8/4="},"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff":{"logical_path":"font-awesome/fontawesome-webfont.woff","mtime":"2019-08-14T17:22:43+08:00","size":98024,"digest":"ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07","integrity":"sha256-ugxZ3rVFD1y0Gz+TYJ7i0NmVQVh33foiPoqKdTNHTwc="},"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf":{"logical_path":"font-awesome/fontawesome-webfont.ttf","mtime":"2019-08-14T17:22:43+08:00","size":165548,"digest":"aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8","integrity":"sha256-qljzPyOaD7AvXHpsRcBD16msmgkzNYBmlOzW1O3A1qg="},"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg":{"logical_path":"font-awesome/fontawesome-webfont.svg","mtime":"2019-08-14T17:22:43+08:00","size":444379,"digest":"ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4","integrity":"sha256-rWFXkmwWIrpOHQPUePFUE2hSS/xG9R5C/g2UX37zI+Q="},"college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js":{"logical_path":"college.js","mtime":"2019-09-26T14:40:40+08:00","size":3352744,"digest":"18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287","integrity":"sha256-GPXoQAMxY06JijWswhh4FcCWwl4Kt0q6NBrpFhZs0oc="},"college-944d4273f62c7538368b9017fdd3387b5e3bea31a87873770eb231324546d4d9.css":{"logical_path":"college.css","mtime":"2019-09-11T16:20:07+08:00","size":546841,"digest":"944d4273f62c7538368b9017fdd3387b5e3bea31a87873770eb231324546d4d9","integrity":"sha256-lE1Cc/YsdTg2i5AX/dM4e1476jGoeHN3DrIxMkVG1Nk="},"logo-7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423.png":{"logical_path":"logo.png","mtime":"2019-09-03T08:55:53+08:00","size":2816,"digest":"7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423","integrity":"sha256-f/ESVocJv5f5iY/ockm3qPIA/x9I1TfYWvhyFfGHBCM="},"application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js":{"logical_path":"application.js","mtime":"2019-09-26T14:40:40+08:00","size":600706,"digest":"9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb","integrity":"sha256-nPvD15JZmh0N5ce4QgnhwrLmAzbw8B4Z8FgWY5GHCPs="},"application-5eb87c6e13676d0183317debce17fade27e68c4acee28c419438da15d53c94f2.css":{"logical_path":"application.css","mtime":"2019-09-11T16:20:07+08:00","size":1844002,"digest":"5eb87c6e13676d0183317debce17fade27e68c4acee28c419438da15d53c94f2","integrity":"sha256-Xrh8bhNnbQGDMX3rzhf63ifmjErO4oxBlDjaFdU8lPI="},"admin-c9e5ebe6191548550e27514196ea125cfbb402820ec125a0c9acf99d2d378fe4.js":{"logical_path":"admin.js","mtime":"2019-09-21T15:28:08+08:00","size":4382031,"digest":"c9e5ebe6191548550e27514196ea125cfbb402820ec125a0c9acf99d2d378fe4","integrity":"sha256-yeXr5hkVSFUOJ1FBluoSXPu0AoIOwSWgyaz5nS03j+Q="},"admin-59c59f8cae8bef4a8359286c985458110c9d03ea121516595c988943f4717c38.css":{"logical_path":"admin.css","mtime":"2019-09-21T14:49:04+08:00","size":840093,"digest":"59c59f8cae8bef4a8359286c985458110c9d03ea121516595c988943f4717c38","integrity":"sha256-WcWfjK6L70qDWShsmFRYEQydA+oSFRZZXJiJQ/RxfDg="},"college-38f953d6ba5b85d3fab63cb3c2bbf0d057ccc6454d07cfaafac3b06da37b8437.css":{"logical_path":"college.css","mtime":"2019-09-16T13:56:09+08:00","size":579109,"digest":"38f953d6ba5b85d3fab63cb3c2bbf0d057ccc6454d07cfaafac3b06da37b8437","integrity":"sha256-OPlT1rpbhdP6tjyzwrvw0FfMxkVNB8+q+sOwbaN7hDc="},"application-646b1158a4e8c1f13e684d6fe9025abc75f8d3ba5256e440802c0398223374f3.css":{"logical_path":"application.css","mtime":"2019-09-21T14:49:04+08:00","size":1988767,"digest":"646b1158a4e8c1f13e684d6fe9025abc75f8d3ba5256e440802c0398223374f3","integrity":"sha256-ZGsRWKTowfE+aE1v6QJavHX407pSVuRAgCwDmCIzdPM="},"admin-a47e37c0ec7cf5f22380249776d1e82d65b6b6aa272ed7389185aa200fa40751.js":{"logical_path":"admin.js","mtime":"2019-09-25T15:33:05+08:00","size":4383107,"digest":"a47e37c0ec7cf5f22380249776d1e82d65b6b6aa272ed7389185aa200fa40751","integrity":"sha256-pH43wOx89fIjgCSXdtHoLWW2tqonLtc4kYWqIA+kB1E="},"admin-432c4eac09b036c57ff1e88d902b8aa7df81164e4b419bac557cf1366c1d3ad9.js":{"logical_path":"admin.js","mtime":"2019-09-25T15:35:20+08:00","size":4383103,"digest":"432c4eac09b036c57ff1e88d902b8aa7df81164e4b419bac557cf1366c1d3ad9","integrity":"sha256-QyxOrAmwNsV/8eiNkCuKp9+BFk5LQZusVXzxNmwdOtk="},"admin-978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a.js":{"logical_path":"admin.js","mtime":"2019-09-30T14:43:41+08:00","size":4387200,"digest":"978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a","integrity":"sha256-l45c5gf3fCaBShdPSA2nmsJGwiAYaO+EZUqgO7Zye1o="},"admin-896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7.css":{"logical_path":"admin.css","mtime":"2019-09-30T14:43:41+08:00","size":842269,"digest":"896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7","integrity":"sha256-iWKB9HMXIrDAhNuxryHQ80pbwULViv9Xs5GGSrcd3Kc="},"application-97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd.css":{"logical_path":"application.css","mtime":"2019-09-30T14:43:41+08:00","size":1993118,"digest":"97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd","integrity":"sha256-l/MT6bt9JUdmSffXIVlZz0IUgP0KN4XRlWlTv5Sh6L0="},"admin-2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888.js":{"logical_path":"admin.js","mtime":"2019-10-11T14:38:33+08:00","size":4394616,"digest":"2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888","integrity":"sha256-LNsjRC+nNQJThbiPKQDfBP7zi2FTAEGm2+N17w8K6Ig="},"admin-2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc.css":{"logical_path":"admin.css","mtime":"2019-10-10T17:12:05+08:00","size":846514,"digest":"2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc","integrity":"sha256-LChUuaAhWN7VqAmq9xRKhjCxA1SrTlb+zE3/zHE3lsw="},"application-50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af.css":{"logical_path":"application.css","mtime":"2019-10-10T17:12:05+08:00","size":2001607,"digest":"50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af","integrity":"sha256-UAWa6SmGYEO0cBUShwL8+6U9MqLfFI5k4dlhwQZRxq8="}},"assets":{"admin.js":"admin-2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888.js","admin.css":"admin-2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc.css","font-awesome/fontawesome-webfont.eot":"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot","font-awesome/fontawesome-webfont.woff2":"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2","font-awesome/fontawesome-webfont.woff":"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff","font-awesome/fontawesome-webfont.ttf":"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf","font-awesome/fontawesome-webfont.svg":"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg","college.js":"college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js","college.css":"college-38f953d6ba5b85d3fab63cb3c2bbf0d057ccc6454d07cfaafac3b06da37b8437.css","logo.png":"logo-7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423.png","application.js":"application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js","application.css":"application-50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af.css"}}
\ No newline at end of file
diff --git a/public/assets/admin-896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7.css b/public/assets/admin-2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc.css
similarity index 99%
rename from public/assets/admin-896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7.css
rename to public/assets/admin-2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc.css
index fe1e75888..5b6bef77a 100644
--- a/public/assets/admin-896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7.css
+++ b/public/assets/admin-2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc.css
@@ -25274,6 +25274,114 @@ input.form-control {
color: #6c757d;
}
+/* line 4, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratories-index-page .laboratory-list-table .member-container .laboratory-user {
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-pack: center;
+ justify-content: center;
+ flex-wrap: wrap;
+}
+
+/* line 9, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratories-index-page .laboratory-list-table .member-container .laboratory-user .laboratory-user-item {
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-align: center;
+ align-items: center;
+ height: 22px;
+ line-height: 22px;
+ padding: 2px 5px;
+ margin: 2px 2px;
+ border: 1px solid #91D5FF;
+ background-color: #E6F7FF;
+ color: #91D5FF;
+ border-radius: 4px;
+}
+
+/* line 27, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item {
+ display: -webkit-box;
+ display: flex;
+}
+
+/* line 30, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-img, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-img {
+ display: block;
+ width: 80px;
+ height: 80px;
+}
+
+/* line 36, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-upload, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-upload {
+ cursor: pointer;
+ position: absolute;
+ top: 0;
+ width: 80px;
+ height: 80px;
+ background: #F5F5F5;
+ border: 1px solid #E5E5E5;
+}
+
+/* line 45, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-upload::before, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-upload::before {
+ content: '';
+ position: absolute;
+ top: 27px;
+ left: 39px;
+ width: 2px;
+ height: 26px;
+ background: #E5E5E5;
+}
+
+/* line 55, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-upload::after, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-upload::after {
+ content: '';
+ position: absolute;
+ top: 39px;
+ left: 27px;
+ width: 26px;
+ height: 2px;
+ background: #E5E5E5;
+}
+
+/* line 66, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-left, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-left {
+ position: relative;
+ width: 80px;
+ height: 80px;
+}
+
+/* line 72, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-left.has-img .logo-item-upload, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-left.has-img .logo-item-upload {
+ display: none;
+}
+
+/* line 77, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-left.has-img:hover .logo-item-upload, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-left.has-img:hover .logo-item-upload {
+ display: block;
+ background: rgba(145, 145, 145, 0.8);
+}
+
+/* line 85, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-right, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-right {
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-orient: vertical;
+ -webkit-box-direction: normal;
+ flex-direction: column;
+ -webkit-box-pack: justify;
+ justify-content: space-between;
+ color: #777777;
+ font-size: 12px;
+}
+
+/* line 93, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-title, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-title {
+ color: #23272B;
+ font-size: 14px;
+}
+
/* line 4, app/assets/stylesheets/admins/library_applies.scss */
.admins-library-applies-index-page .library-applies-list-container span.apply-status-agreed {
color: #28a745;
diff --git a/public/assets/admin-896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7.css.gz b/public/assets/admin-2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc.css.gz
similarity index 78%
rename from public/assets/admin-896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7.css.gz
rename to public/assets/admin-2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc.css.gz
index 581305db5..bfac93da9 100644
Binary files a/public/assets/admin-896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7.css.gz and b/public/assets/admin-2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc.css.gz differ
diff --git a/public/assets/admin-978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a.js b/public/assets/admin-2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888.js
similarity index 99%
rename from public/assets/admin-978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a.js
rename to public/assets/admin-2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888.js
index 49e1025c7..6203a3ce8 100644
--- a/public/assets/admin-978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a.js
+++ b/public/assets/admin-2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888.js
@@ -134528,6 +134528,256 @@ $(document).on('turbolinks:load', function() {
}
})
;
+$(document).on('turbolinks:load', function() {
+ if ($('body.admins-laboratory-settings-show-page, body.admins-laboratory-settings-update-page').length > 0) {
+ var $container = $('.edit-laboratory-setting-container');
+ var $form = $container.find('.edit_laboratory');
+
+ $('.logo-item-left').on("change", 'input[type="file"]', function () {
+ var $fileInput = $(this);
+ var file = this.files[0];
+ var imageType = /image.*/;
+ if (file && file.type.match(imageType)) {
+ var reader = new FileReader();
+ reader.onload = function () {
+ var $box = $fileInput.parent();
+ $box.find('img').attr('src', reader.result).css('display', 'block');
+ $box.addClass('has-img');
+ };
+ reader.readAsDataURL(file);
+ } else {
+ }
+ });
+
+ createMDEditor('laboratory-footer-editor', { height: 200, placeholder: '请输入备案信息' });
+
+ $form.validate({
+ errorElement: 'span',
+ errorClass: 'danger text-danger',
+ errorPlacement:function(error,element){
+ if(element.parent().hasClass("input-group")){
+ element.parent().after(error);
+ }else{
+ element.after(error)
+ }
+ },
+ rules: {
+ identifier: {
+ required: true,
+ checkSite: true
+ },
+ name: {
+ required: true
+ }
+ }
+ });
+ $.validator.addMethod("checkSite",function(value,element,params){
+ var checkSite = /^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/;
+ return this.optional(element)||(checkSite.test(value + '.educoder.com'));
+ },"域名不合法!");
+
+ $form.on('click', '.submit-btn', function(){
+ $form.find('.submit-btn').attr('disabled', 'disabled');
+ $form.find('.error').html('');
+ var valid = $form.valid();
+
+ $('input[name="navbar[][name]"]').each(function(_, e){
+ var $ele = $(e);
+ if($ele.val() === undefined || $ele.val().length === 0){
+ $ele.addClass('danger text-danger');
+ valid = false;
+ } else {
+ $ele.removeClass('danger text-danger');
+ }
+ });
+
+ if(!valid) return;
+ $.ajax({
+ method: 'PATCH',
+ dataType: 'json',
+ url: $form.attr('action'),
+ data: new FormData($form[0]),
+ processData: false,
+ contentType: false,
+ success: function(data){
+ $.notify({ message: '保存成功' });
+ window.location.reload();
+ },
+ error: function(res){
+ var data = res.responseJSON;
+ $form.find('.error').html(data.message);
+ },
+ complete: function(){
+ $form.find('.submit-btn').attr('disabled', false);
+ }
+ });
+ })
+ }
+});
+$(document).on('turbolinks:load', function() {
+ if ($('body.admins-laboratories-index-page').length > 0) {
+ var $searchContainer = $('.laboratory-list-form');
+ var $searchForm = $searchContainer.find('form.search-form');
+ var $list = $('.laboratory-list-container');
+
+ // ============== 新建 ===============
+ var $modal = $('.modal.admin-create-laboratory-modal');
+ var $form = $modal.find('form.admin-create-laboratory-form');
+ var $schoolSelect = $modal.find('.school-select');
+
+ $form.validate({
+ errorElement: 'span',
+ errorClass: 'danger text-danger',
+ rules: {
+ school_id: {
+ required: true
+ }
+ },
+ messages: {
+ school_id: {
+ required: '请选择所属单位'
+ }
+ }
+ });
+
+ // modal ready fire
+ $modal.on('show.bs.modal', function () {
+ $schoolSelect.select2('val', ' ');
+ });
+
+ // ************** 学校选择 *************
+ var matcherFunc = function(params, data){
+ if ($.trim(params.term) === '') {
+ return data;
+ }
+ if (typeof data.text === 'undefined') {
+ return null;
+ }
+
+ if (data.name && data.name.indexOf(params.term) > -1) {
+ var modifiedData = $.extend({}, data, true);
+ return modifiedData;
+ }
+
+ // Return `null` if the term should not be displayed
+ return null;
+ };
+
+ var defineSchoolSelect = function(schools) {
+ $schoolSelect.select2({
+ theme: 'bootstrap4',
+ placeholder: '请选择单位',
+ minimumInputLength: 1,
+ data: schools,
+ templateResult: function (item) {
+ if(!item.id || item.id === '') return item.text;
+ return item.name;
+ },
+ templateSelection: function(item){
+ if (item.id) {
+ $('#school_id').val(item.id);
+ }
+ return item.name || item.text;
+ },
+ matcher: matcherFunc
+ });
+ }
+
+ $.ajax({
+ url: '/api/schools/for_option.json',
+ dataType: 'json',
+ type: 'GET',
+ success: function(data) {
+ defineSchoolSelect(data.schools);
+ }
+ });
+
+ $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);
+ }
+ });
+ }
+ });
+
+ // ============= 添加管理员 ==============
+ var $addMemberModal = $('.admin-add-laboratory-user-modal');
+ var $addMemberForm = $addMemberModal.find('.admin-add-laboratory-user-form');
+ var $memberSelect = $addMemberModal.find('.laboratory-user-select');
+ var $laboratoryIdInput = $addMemberForm.find('input[name="laboratory_id"]')
+
+ $addMemberModal.on('show.bs.modal', function(event){
+ var $link = $(event.relatedTarget);
+ var laboratoryId = $link.data('laboratory-id');
+ $laboratoryIdInput.val(laboratoryId);
+
+ $memberSelect.select2('val', ' ');
+ });
+
+ $memberSelect.select2({
+ theme: 'bootstrap4',
+ placeholder: '请输入要添加的管理员姓名',
+ multiple: true,
+ minimumInputLength: 1,
+ ajax: {
+ delay: 500,
+ url: '/admins/users',
+ dataType: 'json',
+ data: function(params){
+ return { name: params.term };
+ },
+ processResults: function(data){
+ return { results: data.users }
+ }
+ },
+ templateResult: function (item) {
+ if(!item.id || item.id === '') return item.text;
+ return item.real_name;
+ },
+ templateSelection: function(item){
+ if (item.id) {
+ }
+ return item.real_name || item.text;
+ }
+ });
+
+ $addMemberModal.on('click', '.submit-btn', function(){
+ $addMemberForm.find('.error').html('');
+
+ var laboratoryId = $laboratoryIdInput.val();
+ var memberIds = $memberSelect.val();
+ if (laboratoryId && memberIds && memberIds.length > 0) {
+ $.ajax({
+ method: 'POST',
+ dataType: 'script',
+ url: '/admins/laboratories/' + laboratoryId + '/laboratory_user',
+ data: { user_ids: memberIds }
+ });
+ } else {
+ $addMemberModal.modal('hide');
+ }
+ });
+ }
+});
$(document).on('turbolinks:load', function() {
if ($('body.admins-library-applies-index-page').length > 0) {
var $searchFrom = $('.library-applies-list-form');
diff --git a/public/assets/admin-978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a.js.gz b/public/assets/admin-2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888.js.gz
similarity index 98%
rename from public/assets/admin-978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a.js.gz
rename to public/assets/admin-2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888.js.gz
index 13e36b4f6..492769a78 100644
Binary files a/public/assets/admin-978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a.js.gz and b/public/assets/admin-2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888.js.gz differ
diff --git a/public/assets/application-97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd.css b/public/assets/application-50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af.css
similarity index 99%
rename from public/assets/application-97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd.css
rename to public/assets/application-50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af.css
index c1d8ae955..f62f2f56d 100644
--- a/public/assets/application-97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd.css
+++ b/public/assets/application-50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af.css
@@ -25274,6 +25274,114 @@ input.form-control {
color: #6c757d;
}
+/* line 4, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratories-index-page .laboratory-list-table .member-container .laboratory-user {
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-pack: center;
+ justify-content: center;
+ flex-wrap: wrap;
+}
+
+/* line 9, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratories-index-page .laboratory-list-table .member-container .laboratory-user .laboratory-user-item {
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-align: center;
+ align-items: center;
+ height: 22px;
+ line-height: 22px;
+ padding: 2px 5px;
+ margin: 2px 2px;
+ border: 1px solid #91D5FF;
+ background-color: #E6F7FF;
+ color: #91D5FF;
+ border-radius: 4px;
+}
+
+/* line 27, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item {
+ display: -webkit-box;
+ display: flex;
+}
+
+/* line 30, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-img, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-img {
+ display: block;
+ width: 80px;
+ height: 80px;
+}
+
+/* line 36, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-upload, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-upload {
+ cursor: pointer;
+ position: absolute;
+ top: 0;
+ width: 80px;
+ height: 80px;
+ background: #F5F5F5;
+ border: 1px solid #E5E5E5;
+}
+
+/* line 45, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-upload::before, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-upload::before {
+ content: '';
+ position: absolute;
+ top: 27px;
+ left: 39px;
+ width: 2px;
+ height: 26px;
+ background: #E5E5E5;
+}
+
+/* line 55, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-upload::after, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-upload::after {
+ content: '';
+ position: absolute;
+ top: 39px;
+ left: 27px;
+ width: 26px;
+ height: 2px;
+ background: #E5E5E5;
+}
+
+/* line 66, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-left, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-left {
+ position: relative;
+ width: 80px;
+ height: 80px;
+}
+
+/* line 72, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-left.has-img .logo-item-upload, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-left.has-img .logo-item-upload {
+ display: none;
+}
+
+/* line 77, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-left.has-img:hover .logo-item-upload, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-left.has-img:hover .logo-item-upload {
+ display: block;
+ background: rgba(145, 145, 145, 0.8);
+}
+
+/* line 85, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-right, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-right {
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-orient: vertical;
+ -webkit-box-direction: normal;
+ flex-direction: column;
+ -webkit-box-pack: justify;
+ justify-content: space-between;
+ color: #777777;
+ font-size: 12px;
+}
+
+/* line 93, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-title, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-title {
+ color: #23272B;
+ font-size: 14px;
+}
+
/* line 4, app/assets/stylesheets/admins/library_applies.scss */
.admins-library-applies-index-page .library-applies-list-container span.apply-status-agreed {
color: #28a745;
@@ -26246,6 +26354,113 @@ input.form-control {
.admins-identity-authentications-index-page .identity-authentication-list-container span.apply-status-3 {
color: #6c757d;
}
+/* line 4, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratories-index-page .laboratory-list-table .member-container .laboratory-user {
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-pack: center;
+ justify-content: center;
+ flex-wrap: wrap;
+}
+
+/* line 9, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratories-index-page .laboratory-list-table .member-container .laboratory-user .laboratory-user-item {
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-align: center;
+ align-items: center;
+ height: 22px;
+ line-height: 22px;
+ padding: 2px 5px;
+ margin: 2px 2px;
+ border: 1px solid #91D5FF;
+ background-color: #E6F7FF;
+ color: #91D5FF;
+ border-radius: 4px;
+}
+
+/* line 27, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item {
+ display: -webkit-box;
+ display: flex;
+}
+
+/* line 30, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-img, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-img {
+ display: block;
+ width: 80px;
+ height: 80px;
+}
+
+/* line 36, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-upload, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-upload {
+ cursor: pointer;
+ position: absolute;
+ top: 0;
+ width: 80px;
+ height: 80px;
+ background: #F5F5F5;
+ border: 1px solid #E5E5E5;
+}
+
+/* line 45, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-upload::before, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-upload::before {
+ content: '';
+ position: absolute;
+ top: 27px;
+ left: 39px;
+ width: 2px;
+ height: 26px;
+ background: #E5E5E5;
+}
+
+/* line 55, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-upload::after, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-upload::after {
+ content: '';
+ position: absolute;
+ top: 39px;
+ left: 27px;
+ width: 26px;
+ height: 2px;
+ background: #E5E5E5;
+}
+
+/* line 66, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-left, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-left {
+ position: relative;
+ width: 80px;
+ height: 80px;
+}
+
+/* line 72, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-left.has-img .logo-item-upload, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-left.has-img .logo-item-upload {
+ display: none;
+}
+
+/* line 77, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-left.has-img:hover .logo-item-upload, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-left.has-img:hover .logo-item-upload {
+ display: block;
+ background: rgba(145, 145, 145, 0.8);
+}
+
+/* line 85, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-right, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-right {
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-orient: vertical;
+ -webkit-box-direction: normal;
+ flex-direction: column;
+ -webkit-box-pack: justify;
+ justify-content: space-between;
+ color: #777777;
+ font-size: 12px;
+}
+
+/* line 93, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-title, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-title {
+ color: #23272B;
+ font-size: 14px;
+}
/* line 4, app/assets/stylesheets/admins/library_applies.scss */
.admins-library-applies-index-page .library-applies-list-container span.apply-status-agreed {
color: #28a745;
diff --git a/public/assets/application-50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af.css.gz b/public/assets/application-50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af.css.gz
new file mode 100644
index 000000000..f5162f95a
Binary files /dev/null and b/public/assets/application-50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af.css.gz differ
diff --git a/public/assets/application-97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd.css.gz b/public/assets/application-97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd.css.gz
deleted file mode 100644
index 60ba5db0b..000000000
Binary files a/public/assets/application-97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd.css.gz and /dev/null differ
diff --git a/public/assets/application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js.gz b/public/assets/application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js.gz
index af63ccbfc..d17b8f444 100644
Binary files a/public/assets/application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js.gz and b/public/assets/application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js.gz differ
diff --git a/public/assets/college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js.gz b/public/assets/college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js.gz
index 3a4c01edc..d4b8b22dc 100644
Binary files a/public/assets/college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js.gz and b/public/assets/college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js.gz differ
diff --git a/public/react/src/App.js b/public/react/src/App.js
index 9b85acfa8..34b5d1a4f 100644
--- a/public/react/src/App.js
+++ b/public/react/src/App.js
@@ -64,6 +64,18 @@ const EducoderLogin = Loadable({
loader: () => import('./modules/login/EducoderLogin'),
loading: Loading,
})
+
+//微信登录
+const Otherlogin=Loadable({
+ loader: () => import('./modules/login/Otherlogin'),
+ loading: Loading,
+})
+
+const Otherloginstart=Loadable({
+ loader: () => import('./modules/login/Otherloginstart'),
+ loading: Loading,
+})
+
const TestIndex = Loadable({
loader: () => import('./modules/test'),
loading: Loading,
@@ -406,6 +418,12 @@ class App extends Component {
+
+
{
diff --git a/public/react/src/modules/courses/busyWork/CommonWorkSetting.js b/public/react/src/modules/courses/busyWork/CommonWorkSetting.js
index 5d9a7dc16..ad25f553f 100644
--- a/public/react/src/modules/courses/busyWork/CommonWorkSetting.js
+++ b/public/react/src/modules/courses/busyWork/CommonWorkSetting.js
@@ -167,7 +167,12 @@ class CommonWorkSetting extends Component{
}
on('commonwork_fetch_all', this.fetchAllListener)
- }
+
+
+ if(this.props.isAdmin()===true){
+ this.setState({startEditFlag: true})
+ }
+ }
componentWillUnmount() {
off('commonwork_fetch_all', this.fetchAllListener)
}
diff --git a/public/react/src/modules/courses/coursesPublic/HomeworkModal.js b/public/react/src/modules/courses/coursesPublic/HomeworkModal.js
index 89e747004..ee19745c9 100644
--- a/public/react/src/modules/courses/coursesPublic/HomeworkModal.js
+++ b/public/react/src/modules/courses/coursesPublic/HomeworkModal.js
@@ -46,10 +46,16 @@ class HomeworkModal extends Component{
}
if(this.props.starttimes!=undefined&&this.props.starttimes!=""){
+ if(this.props.starttimesend!=undefined&&this.props.starttimesend!=""){
+ this.setState({
+ endtime:this.props.starttimesend,
+ })
+ }else {
+ this.setState({
+ endtime:moment(moment(handleDateString(this.props.starttimes)).add(1, 'week')).format("YYYY-MM-DD HH:mm")
+ })
+ }
- this.setState({
- endtime:moment(moment(handleDateString(this.props.starttimes)).add(1, 'months')).format("YYYY-MM-DD HH:mm")
- })
}
}
componentDidUpdate=(prevProps)=>{
@@ -69,9 +75,16 @@ class HomeworkModal extends Component{
if(prevProps.starttimes!=this.props.starttimes){
if(this.props.starttimes!=undefined&&this.props.starttimes!=""){
- this.setState({
- endtime:moment(moment(handleDateString(this.props.starttimes)).add(1, 'months')).format("YYYY-MM-DD HH:mm")
- })
+ if(this.props.starttimesend!=undefined&&this.props.starttimesend!=""){
+ this.setState({
+ endtime:this.props.starttimesend,
+ })
+ }else{
+ this.setState({
+ endtime:moment(moment(handleDateString(this.props.starttimes)).add(1, 'week')).format("YYYY-MM-DD HH:mm")
+ })
+ }
+
}
}
}
diff --git a/public/react/src/modules/courses/coursesPublic/OneSelfOrderModal.js b/public/react/src/modules/courses/coursesPublic/OneSelfOrderModal.js
new file mode 100644
index 000000000..d706bf2ee
--- /dev/null
+++ b/public/react/src/modules/courses/coursesPublic/OneSelfOrderModal.js
@@ -0,0 +1,310 @@
+import React,{ Component } from "react";
+import { Modal,Checkbox,DatePicker} from "antd";
+import { handleDateString } from 'educoder';
+import locale from 'antd/lib/date-picker/locale/zh_CN';
+import moment from 'moment';
+const CheckboxGroup = Checkbox.Group;
+const dateFormat = 'YYYY-MM-DD HH:mm';
+
+function range(start, end) {
+ const result = [];
+ for (let i = start; i < end; i++) {
+ result.push(i);
+ }
+ return result;
+}
+function disabledDateTime() {
+ return {
+ // disabledHours: () => range(0, 24).splice(4, 20),
+ disabledMinutes: () => range(1, 30).concat(range(31, 60)),
+ // disabledSeconds: () => [55, 56],
+ };
+}
+
+
+function disabledDate(current) {
+ return current && current < moment().endOf('day').subtract(1, 'days');
+}
+
+
+
+class OneSelfOrderModal extends Component{
+ constructor(props){
+ super(props);
+ this.state={
+ group_ids:[],
+ endtime:""
+ }
+ }
+ componentDidMount() {
+
+
+ if(this.props.course_groups!=undefined&&this.props.course_groups.length!=0){
+
+ let arr=this.props.course_groups.map(item => item.id);
+ this.shixunhomeworkedit(arr);
+ }
+
+ if(this.props.starttimes===undefined||this.props.starttimes===""||this.props.starttimes===null){
+ this.setState({
+ endtime:moment(moment(handleDateString(this.props.staytime)).add(1, 'months')).format("YYYY-MM-DD HH:mm")
+ })
+ }else{
+ this.setState({
+ endtime:moment(handleDateString(this.props.starttimes)).format("YYYY-MM-DD HH:mm")
+ })
+ }
+ }
+ componentDidUpdate=(prevProps)=>{
+ // if(prevProps.visible!=this.props.visible){
+ //
+ // if(this.props.course_groups!=undefined){
+ // let arr=this.props.course_groups.map(item => item.id);
+ // this.shixunhomeworkedit(arr);
+ // }
+ // }
+ if(prevProps.course_groups!=this.props.course_groups){
+ if(this.props.course_groups!=undefined){
+ let arr=this.props.course_groups.map(item => item.id);
+ this.shixunhomeworkedit(arr);
+ }
+ }
+ if(prevProps.starttimes!=this.props.starttimes){
+
+ if(this.props.starttimes===undefined||this.props.starttimes===""||this.props.starttimes===null){
+ this.setState({
+ endtime:moment(moment(handleDateString(this.props.staytime)).add(1, 'months')).format("YYYY-MM-DD HH:mm")
+ })
+ }else{
+ this.setState({
+ endtime:moment(handleDateString(this.props.starttimes)).format("YYYY-MM-DD HH:mm")
+ })
+ }
+ }
+ }
+
+ //勾选实训
+ shixunhomeworkedit=(list)=>{
+
+ this.setState({
+ group_ids:list
+ })
+ this.props.getcourse_groupslist && this.props.getcourse_groupslist(list)
+ }
+
+ onChangeTimeend= (date, dateString) => {
+ // console.log('startValue',dateString);
+
+ this.setState({
+ endtime: date===null?"":handleDateString(dateString),
+ })
+
+ }
+
+ propsSaves=(ds,endtime)=>{
+
+ if(ds.length ===0&&endtime === ""){
+ this.props.Saves()
+ }else{
+ if(this.props.typs!="end"){
+ if(endtime === ""||endtime===undefined||endtime===null){
+
+ this.setState({
+ endtimetype:true,
+ endtimetypevalue:"截止时间不能为空"
+ })
+ return
+ }
+
+ if(moment(endtime,"YYYY-MM-DD HH:mm") <= moment(this.props.starttimes,"YYYY-MM-DD HH:mm")){
+ this.setState({
+ endtimetype:true,
+ endtimetypevalue:"必须晚于发布时间"
+ })
+ return
+ }
+ }
+ this.props.Saves(ds,moment(handleDateString(endtime),"YYYY-MM-DD HH:mm").format("YYYY-MM-DD HH:mm"))
+ }
+
+
+ }
+
+ render(){
+ let {group_ids,endtime}=this.state;
+ let {course_groups}=this.props;
+
+ // console.log(this.props.starttimes)
+ // console.log(endtime)
+ // console.log(this.props.starttimes)
+ // console.log(this.state.endtime)
+
+ // console.log(this.props.starttime,this.props.endtime)
+ // TODO course_groups为空时的处理
+
+ // let endtimelist=this.props.starttimes===undefined||this.props.starttimes===""?"":moment(handleDateString(endtime)).add(1,'months')
+ return(
+
+ {
+ this.props.OneSelftype===true?:""
+ }
+ {
+ this.props.OneSelftype===true?
+
+
+
+ { this.props.usingCheckBeforePost ?
+
+
+ 发布设置均可修改,
+
+ 点击修改
+
+
+
+ 此设置将对所有分班生效
+
+ :
+
+
+ {this.props.Topval}
+ {this.props.Topvalright}
+
+
+ {this.props.Botvalleft===undefined?"":"{this.props.Botvalleft}" }
+ {this.props.Botval}
+
+ }
+
+
+ {this.props.starttime===undefined||
+ this.props.starttime===""?""
+ :
+
+ 发布时间:
+ {this.props.starttime}
+ {this.props.modaltype===undefined||this.props.modaltype===2?
+ {/*{this.props.endtime}*/}
+ 截止时间:
+
+ {this.state.endtimetype===true?{this.state.endtimetypevalue}
:""}
+ :""}
+
}
+ {/* usingCheckBeforePost 为true的时候 全选所有分班 */}
+
+
+ {this.props.modaltype===undefined||this.props.modaltype===2
+ || this.props.usingCheckBeforePost ?"":
+
+ 分班名称
+
+ 截止时间
+
+ }
+ {this.props.modaltype===undefined||this.props.modaltype===2
+ || this.props.usingCheckBeforePost ?"":
+ }
+
+
+
+
+ :""}
+
+ )
+ }
+}
+export default OneSelfOrderModal;
\ No newline at end of file
diff --git a/public/react/src/modules/courses/css/Courses.css b/public/react/src/modules/courses/css/Courses.css
index 12c4e8d3e..160104920 100644
--- a/public/react/src/modules/courses/css/Courses.css
+++ b/public/react/src/modules/courses/css/Courses.css
@@ -1701,4 +1701,47 @@ input.ant-input-number-input:focus {
/*}*/
.yslinputcourput .ant-form-explain{
padding-left: 0px !important;
+}
+.wechatloginfont{
+ font-size: 14px;
+ font-family: PingFangSC-Regular,PingFangSC;
+ font-weight: 400;
+ color: #555555;
+ line-height: 20px;
+ margin-top: 10px;
+}
+
+.wechatdiv{
+ margin-top: 40px !important;
+ width: 800px !important;
+}
+
+.wechatContent{
+ padding: 0px 50px;
+}
+
+.wechatpass{
+ width: 300px;
+ height: 46px;
+}
+
+.wechatnewchat{
+ width: 62px;
+ height: 62px;
+ background: rgba(255,170,170,1);
+ border-radius: 50%;
+ text-align: center;
+ line-height: 62px;
+ color: #fff;
+ margin: 0 auto;
+}
+.wechatweoldchat{
+ width: 62px;
+ height: 62px;
+ background:rgba(164,211,255,1);
+ border-radius: 50%;
+ text-align: center;
+ line-height: 62px;
+ color: #fff;
+ margin: 0 auto;
}
\ No newline at end of file
diff --git a/public/react/src/modules/courses/exercise/ExerciseReviewAndAnswer.js b/public/react/src/modules/courses/exercise/ExerciseReviewAndAnswer.js
index a9143176c..75da4f2fe 100644
--- a/public/react/src/modules/courses/exercise/ExerciseReviewAndAnswer.js
+++ b/public/react/src/modules/courses/exercise/ExerciseReviewAndAnswer.js
@@ -96,8 +96,8 @@ class ExerciseReviewAndAnswer extends Component{
//window.addEventListener('scroll', this.handleScroll);
}
- remainTime=()=>{
- let { time } = this.state;
+ remainTime=(time)=>{
+ // let { time } = this.state;
let h=moment(parseInt(time)*1000).hour()-8;
let m=moment(parseInt(time)*1000).minutes();
let s=moment(parseInt(time)*1000).seconds();
@@ -133,15 +133,21 @@ class ExerciseReviewAndAnswer extends Component{
commit_method:2
}).then((result)=>{
if(result){
- this.setState({
- Modalstype:true,
- Modalstopval:'答题结束了,系统已自动提交试卷',
- modalsBottomval:"不能再修改答题",
- ModalCancel:undefined,
- ModalSave:this.sureCommit,
- Loadtype:true
- })
- this.props.showNotification(`${result.data.message}`);
+ if(result.data.status===0){
+ this.setState({
+ Modalstype:true,
+ Modalstopval:'答题结束了,系统已自动提交试卷',
+ modalsBottomval:"不能再修改答题",
+ ModalCancel:undefined,
+ ModalSave:this.sureCommit,
+ Loadtype:true
+ })
+ this.props.showNotification(`${result.data.message}`);
+ }
+
+ if(result.data.status===-2){
+ this.remainTime(parseInt(result.data.message))
+ }
}
}).catch((error)=>{
console.log(error);
@@ -238,7 +244,7 @@ class ExerciseReviewAndAnswer extends Component{
isSpin:false
})
if(result.data.exercise.left_time != null){
- this.remainTime();
+ this.remainTime(result.data.exercise.left_time);
}
}
}).catch((error)=>{
diff --git a/public/react/src/modules/courses/graduation/tasks/GraduationTaskDetail.js b/public/react/src/modules/courses/graduation/tasks/GraduationTaskDetail.js
index b9830d1fc..66e4ccb8d 100644
--- a/public/react/src/modules/courses/graduation/tasks/GraduationTaskDetail.js
+++ b/public/react/src/modules/courses/graduation/tasks/GraduationTaskDetail.js
@@ -20,89 +20,89 @@ import Modals from '../../../modals/Modals';
//毕设描述
const GraduationTasksquestions= Loadable({
- loader: () => import('./GraduationTaskssettingquestions'),
- loading: Loading,
+ loader: () => import('./GraduationTaskssettingquestions'),
+ loading: Loading,
})
//毕设任务设置
const GraduationTaskssetting=Loadable({
- loader: () => import('./GraduationTaskssetting'),
- loading: Loading,
+ loader: () => import('./GraduationTaskssetting'),
+ loading: Loading,
})
//毕设任务列表
const GraduationTaskslist=Loadable({
- loader: () => import('./GraduationTaskssettinglist'),
- loading: Loading,
+ loader: () => import('./GraduationTaskssettinglist'),
+ loading: Loading,
})
class GraduationTaskDetail extends Component{
- constructor(props){
- super(props);
- this.state={
- modalname:undefined,
- visible:false,
+ constructor(props){
+ super(props);
+ this.state={
+ modalname:undefined,
+ visible:false,
Topval:undefined,
starttime:undefined,
starttimes:undefined,
typs:undefined,
- endtime:undefined,
- Cancelname:undefined,
- Savesname:undefined,
- Cancel:undefined,
- Saves:undefined,
- Topvalright:undefined,
- Botvalleft:undefined,
- course_groupslist:undefined,
- course_groups:undefined,
- questionslist:undefined,
- tab:"list",
- visibles:undefined,
- Modalstype:undefined,
- Modalstopval:undefined,
- ModalCancel:undefined,
- ModalSave:undefined,
- acrossVisible:undefined
- }
- }
- componentDidMount(){
- this.getdatas()
- }
- getdatas=()=>{
- const task_Id = this.props.match.params.task_Id;
- let url="/graduation_tasks/"+task_Id+".json";
-
- axios.get(url).then((result)=>{
- if(result.status===200){
- this.setState({
- questionslist:result.data
- })
-
- }
- }).catch((error)=>{
- console.log(error)
- })
- }
-
-
- // 交叉评阅设置弹框
- openAcross=()=>{
- this.setState({
- acrossVisible:true
- })
- }
- closeAcross=()=>{
- this.setState({
- acrossVisible:false
- })
+ endtime:undefined,
+ Cancelname:undefined,
+ Savesname:undefined,
+ Cancel:undefined,
+ Saves:undefined,
+ Topvalright:undefined,
+ Botvalleft:undefined,
+ course_groupslist:undefined,
+ course_groups:undefined,
+ questionslist:undefined,
+ tab:"list",
+ visibles:undefined,
+ Modalstype:undefined,
+ Modalstopval:undefined,
+ ModalCancel:undefined,
+ ModalSave:undefined,
+ acrossVisible:undefined
+ }
+ }
+ componentDidMount(){
this.getdatas()
- }
+ }
+ getdatas=()=>{
+ const task_Id = this.props.match.params.task_Id;
+ let url="/graduation_tasks/"+task_Id+".json";
+ axios.get(url).then((result)=>{
+ if(result.status===200){
+ this.setState({
+ questionslist:result.data
+ })
+
+ }
+ }).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;
+ // 交叉评阅设置弹框
+ openAcross=()=>{
+ this.setState({
+ acrossVisible:true
+ })
+ }
+ closeAcross=()=>{
+ this.setState({
+ acrossVisible:false
+ })
+ this.getdatas()
+ }
+
+
+
+ //返回
+ 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;
// let courseId = this.props.match.params.coursesId;
// if(courseId===undefined){
// this.props.history.push("/courses");
@@ -112,66 +112,66 @@ class GraduationTaskDetail extends Component{
// this.props.history.goBack()
this.props.history.replace(`/courses/${this.state.questionslist.course_id}/graduation_tasks/${this.state.questionslist.graduation_id}`);
- }
- //立即发布
- publish=()=>{
- let starttime= this.props.getNowFormatDates(1,1);
- let endtime=this.props.getNowFormatDates(2,1);
- // this.homeworkstart()
- this.setState({
- modalname:"立即发布",
- visible:true,
+ }
+ //立即发布
+ publish=()=>{
+ let starttime= this.props.getNowFormatDates(1,1);
+ let endtime=this.props.getNowFormatDates(2,1);
+ // this.homeworkstart()
+ this.setState({
+ modalname:"立即发布",
+ visible:true,
Topval:"学生将立即收到毕设任务",
- // Botvalleft:"点击修改",
+ // Botvalleft:"点击修改",
// Botval:`本操作只对"未发布"的分班有效`,
starttime:moment(moment(new Date())).format("YYYY-MM-DD HH:mm") ,
starttimes:this.props.getNowFormatDates(1),
typs:"start",
- endtime:endtime,
- Cancelname:"暂不发布",
- Savesname:"立即发布",
- Cancel:this.cancelmodel,
- Saves:this.homepublish,
- })
- }
- // 确定立即发布
- homepublish=(ids,endtime)=>{
- this.cancelmodel();
- let task_Id=this.props.match.params.task_Id;
- const cid = this.props.match.params.coursesId;
- // let url = `/courses/${cid}/graduation_tasks/publish_task.json`;
-
- let url="/courses/"+cid+"/graduation_tasks/publish_task.json"
- axios.post(url,{
- task_ids:[task_Id],
- group_ids: this.state.course_groupslist,
+ endtime:endtime,
+ Cancelname:"暂不发布",
+ Savesname:"立即发布",
+ Cancel:this.cancelmodel,
+ Saves:this.homepublish,
+ })
+ }
+ // 确定立即发布
+ homepublish=(ids,endtime)=>{
+ this.cancelmodel();
+ let task_Id=this.props.match.params.task_Id;
+ const cid = this.props.match.params.coursesId;
+ // let url = `/courses/${cid}/graduation_tasks/publish_task.json`;
+
+ let url="/courses/"+cid+"/graduation_tasks/publish_task.json"
+ axios.post(url,{
+ task_ids:[task_Id],
+ group_ids: this.state.course_groupslist,
end_time:endtime,
- }).then((response)=>{
- if (response.data.status == 0) {
+ }).then((response)=>{
+ if (response.data.status == 0) {
this.getdatas()
- this.props.showNotification(response.data.message);
-
- this.setState({
- // Modalstopval:response.data.message,
- // ModalSave:this.cancelmodel,
- // Loadtype:true,
- course_groupslist:[],
- checkAllValue:false
- })
- }
- }).catch((error)=>{
-
- })
- }
-
- // 刷新
- resetList=()=>{
- this.getdatas();
- this.child && this.child.searchValue();
- }
-
- // 立即截止
- end=()=>{
+ this.props.showNotification(response.data.message);
+
+ this.setState({
+ // Modalstopval:response.data.message,
+ // ModalSave:this.cancelmodel,
+ // Loadtype:true,
+ course_groupslist:[],
+ checkAllValue:false
+ })
+ }
+ }).catch((error)=>{
+
+ })
+ }
+
+ // 刷新
+ resetList=()=>{
+ this.getdatas();
+ this.child && this.child.searchValue();
+ }
+
+ // 立即截止
+ end=()=>{
// this.homeworkstart()
this.setState({
modalname:"立即截止",
@@ -185,74 +185,74 @@ class GraduationTaskDetail extends Component{
Saves:this.coursetaskend,
typs:"end",
})
- }
- coursetaskend=()=>{
- const coursesId = this.props.match.params.coursesId;
- const task_Id = this.props.match.params.task_Id;
-
- let url = `/courses/${coursesId}/graduation_tasks/end_task.json`;
- axios.post(url,{
- task_ids:[task_Id],
- group_ids: this.state.course_groupslist,
- }).then((response)=>{
- if (response.data.status == 0) {
- this.props.showNotification(response.data.message);
- this.cancelmodel();
- this.getdatas();
- this.child && this.child.reInit();
- }
-
- }).catch((error)=>{
-
- })
- }
- // 取消
- cancelmodel=()=>{
- this.setState({
- Modalstype:false,
- Loadtype:false,
- visible:false,
- Modulationtype:false,
- Allocationtype:false,
- Modalstopval:"",
- ModalCancel:"",
- ModalSave:"",
- })
- }
-
- getcourse_groupslist=(id)=>{
- this.setState({
- course_groupslist:id
- })
- }
-
- setTab = (tab) =>{
- this.setState({
- tab
- })
- }
-
- // 关联项目
- AssociationItems=()=>{
+ }
+ coursetaskend=()=>{
+ const coursesId = this.props.match.params.coursesId;
+ const task_Id = this.props.match.params.task_Id;
+
+ let url = `/courses/${coursesId}/graduation_tasks/end_task.json`;
+ axios.post(url,{
+ task_ids:[task_Id],
+ group_ids: this.state.course_groupslist,
+ }).then((response)=>{
+ if (response.data.status == 0) {
+ this.props.showNotification(response.data.message);
+ this.cancelmodel();
+ this.getdatas();
+ this.child && this.child.reInit();
+ }
+
+ }).catch((error)=>{
+
+ })
+ }
+ // 取消
+ cancelmodel=()=>{
+ this.setState({
+ Modalstype:false,
+ Loadtype:false,
+ visible:false,
+ Modulationtype:false,
+ Allocationtype:false,
+ Modalstopval:"",
+ ModalCancel:"",
+ ModalSave:"",
+ })
+ }
+
+ getcourse_groupslist=(id)=>{
+ this.setState({
+ course_groupslist:id
+ })
+ }
+
+ setTab = (tab) =>{
+ this.setState({
+ tab
+ })
+ }
+
+ // 关联项目
+ AssociationItems=()=>{
this.setState({
visibles:true
})
- }
- Cancel=()=>{
+ }
+ Cancel=()=>{
this.setState({
visibles:false
})
- }
- // 取消关联
- cannelAssociation=()=>{
+ }
+ // 取消关联
+ cannelAssociation=()=>{
this.setState({
Modalstype:true,
Modalstopval:"确定要取消该项目关联?",
ModalCancel:this.cannerassocition,
ModalSave:this.savetassociton
})
- }
- savetassociton=()=>{
+ }
+ savetassociton=()=>{
this.cannerassocition();
let {questionslist}=this.state;
let url = "/graduation_tasks/"+questionslist.task_id+"/graduation_works/cancel_relate_project.json";
@@ -266,7 +266,7 @@ class GraduationTaskDetail extends Component{
})
}
- cannerassocition=()=>{
+ cannerassocition=()=>{
this.setState({
Modalstype:false,
Modalstopval:"",
@@ -275,10 +275,10 @@ class GraduationTaskDetail extends Component{
loadtype:false,
visibles:false
})
- }
- // 补交附件
- handaccessory=()=>{
- // let {taskslistdata}=this.state;
+ }
+ // 补交附件
+ handaccessory=()=>{
+ // let {taskslistdata}=this.state;
// let courseId=this.props.match.params.coursesId;
//
// let url="/courses/"+courseId+"/graduation_tasks/"+taskslistdata.work_id+"/appraise"
@@ -292,129 +292,129 @@ class GraduationTaskDetail extends Component{
this.setState({
avisible:false
})
- }
-
-
- bindRef = ref => { this.child = ref } ;
- render(){
-
- let courseId=this.props.match.params.coursesId;
- let category_id=this.props.match.params.category_id;
- let task_Id=this.props.match.params.task_Id;
- let {
- questionslist ,
- tab ,
- visibles ,
- Modalstype,
- Modalstopval,
- ModalCancel,
- ModalSave,
- acrossVisible
- } = this.state
-
- const commom = {
- setTab:this.setTab,
- getdatas:this.getdatas
- }
- return(
-
- {
- questionslist &&
-
-
this.getcourse_groupslist(id)}
- />
- {/*关联项目*/}
- {visibles===true?
- this.Cancel()}
- taskid={ questionslist && questionslist.task_id }
- funlist={this.resetList}
- />
- :""}
-
- {this.state.avisible===true? :""}
- {/*提示*/}
-
-
- {
- acrossVisible &&
-
- }
-
-
- {questionslist.course_name}
- >
- {questionslist.graduation_name}
- >
- 任务详情
-
-
-
- {questionslist.task_name}
-
-
-
返回
-
-
-
-
-
任务列表
-
毕设描述
-
设置
-
- {/*
导出成绩 */}
- {/*{this.props.isAdmin()?
导出成绩 :""}*/}
- {/*{this.props.isAdmin()?
导出作品附件 :""}*/}
-
-
- {this.props.isAdmin()?
- 导出
-
- :""}
- {questionslist.work_status===undefined||questionslist.work_status===null||questionslist.work_status.length===0?"":questionslist.work_status.map((item,key)=>{
- return(
-
+
+ {this.props.isAdmin()?
+ 导出
+
+ :""}
+ {questionslist.work_status===undefined||questionslist.work_status===null||questionslist.work_status.length===0?"":questionslist.work_status.map((item,key)=>{
+ return(
+
{item==="提交作品"?提交作品 :""}
- {item==="补交作品"?补交作品 :""}
- {item==="修改作品"?修改作品 :""}
- {item==="查看作品"?查看作品 :""}
- {item==="创建项目"?创建项目 :""}
- {item==="关联项目"?关联项目 :""}
- {item==="取消关联"?取消关联 :""}
- {item==="补交附件"?补交附件 :""}
+ {item==="补交作品"?补交作品 :""}
+ {item==="修改作品"?修改作品 :""}
+ {item==="查看作品"?查看作品 :""}
+ {item==="创建项目"?创建项目 :""}
+ {item==="关联项目"?关联项目 :""}
+ {item==="取消关联"?取消关联 :""}
+ {item==="补交附件"?补交附件 :""}
- )
- })}
-
- {/*项目在线质量检测 */}
- { this.props.isAdmin() ? questionslist.status===1 ? { this.end()} }>立即截止 : "" : "" }
- { this.props.isAdmin() ? questionslist.status===0 ? { this.publish()} }>立即发布 : "" : "" }
- { this.props.isAdmin() && questionslist.cross_comment ? 交叉评阅设置 : "" }
- { this.props.isAdmin() ? 编辑任务 : "" }
-
-
-
-
- ()
- }
- >
-
- ()
- }
- >
-
- ()
- }>
-
-
-
- }
-
-
- )
- }
+ )
+ })}
+
+ {/*项目在线质量检测 */}
+ { this.props.isAdmin() ? questionslist.status===1 ? { this.end()} }>立即截止 : "" : "" }
+ { this.props.isAdmin() ? questionslist.status===0 ? { this.publish()} }>立即发布 : "" : "" }
+ { this.props.isAdmin() && questionslist.cross_comment ? 交叉评阅设置 : "" }
+ { this.props.isAdmin() ? 编辑任务 : "" }
+
+
+
+
+ ()
+ }
+ >
+
+ ()
+ }
+ >
+
+ ()
+ }>
+
+
+
+ }
+
+
+ )
+ }
}
// CNotificationHOC() ( SnackbarHOC() ( TPMIndexHOC))
export default (GraduationTaskDetail) ;
\ No newline at end of file
diff --git a/public/react/src/modules/courses/graduation/tasks/index.js b/public/react/src/modules/courses/graduation/tasks/index.js
index 9dac05a36..0660d9c2f 100644
--- a/public/react/src/modules/courses/graduation/tasks/index.js
+++ b/public/react/src/modules/courses/graduation/tasks/index.js
@@ -473,7 +473,7 @@ class GraduationTasks extends Component{
checkAllValue:false
})
this.publishcanner();
- debugger
+
let {search,page,order} = this.state
this.fetchAll(search,page,order);
}
diff --git a/public/react/src/modules/courses/members/ChangeRolePop.js b/public/react/src/modules/courses/members/ChangeRolePop.js
index 896360a40..13130394a 100644
--- a/public/react/src/modules/courses/members/ChangeRolePop.js
+++ b/public/react/src/modules/courses/members/ChangeRolePop.js
@@ -6,7 +6,7 @@ import axios from 'axios'
/**
角色数组, CREATOR: 创建者, PROFESSOR: 教师, ASSISTANT_PROFESSOR: 助教, STUDENT: 学生
*/
-function ChangeRolePop({ member_roles = [], record, courseId, onChangeRoleSuccess, showNotification }) {
+function ChangeRolePop({ member_roles = [], record, courseId, onChangeRoleSuccess, showNotification, getUserId, fetchUser }) {
const [checkBoxRoles, setCheckBoxRoles] = useState(member_roles)
useEffect(() => {
setCheckBoxRoles(member_roles)
@@ -32,7 +32,8 @@ function ChangeRolePop({ member_roles = [], record, courseId, onChangeRoleSucces
}
const onConfirm = async () => {
if (checkBoxRoles && checkBoxRoles.length == 0) {
- showNotification('请至少选择一个角色')
+ showNotification('请至少选择一个角色');
+ setCheckBoxRoles(member_roles);
return;
}
const url = `/courses/${courseId}/change_member_role.json`
@@ -43,7 +44,12 @@ function ChangeRolePop({ member_roles = [], record, courseId, onChangeRoleSucces
if (response.data.status == 0) {
showNotification('保存成功')
onChangeRoleSuccess()
+
trigger('updatabanner')
+ if (fetchUser && record.user_id == getUserId) {
+ fetchUser()
+ }
+
}
console.log(response)
}
diff --git a/public/react/src/modules/courses/members/studentsList.js b/public/react/src/modules/courses/members/studentsList.js
index e3859345a..3a5a55b71 100644
--- a/public/react/src/modules/courses/members/studentsList.js
+++ b/public/react/src/modules/courses/members/studentsList.js
@@ -26,11 +26,11 @@ const buildColumns = (that,isParent) => {
let showSorter = isParent==true
const courseId = that.props.match.params.coursesId
const columns=[{
- title: '序号',
+ title: '序号1',
dataIndex: 'id',
key: 'id',
align:'center',
- width:"10%",
+ width:"8%",
className:"color-grey-6",
render: (id, student, index) => {
return (that.state.page - 1) * 20 + index + 1
@@ -143,7 +143,7 @@ const buildColumns = (that,isParent) => {
columns.push({
title: '操作',
key: 'action',
- width: '20%',
+ width: '22%',
align:'center',
render: (text, record) => {
return (
@@ -155,6 +155,8 @@ const buildColumns = (that,isParent) => {
member_roles={record.member_roles}
onChangeRoleSuccess={that.onChangeRoleSuccess}
showNotification={that.props.showNotification}
+ getUserId={that.props.isUserid}
+ fetchUser={that.props.fetchUser}
>
)
@@ -545,6 +547,20 @@ class studentsList extends Component{
addDir = () => {
trigger('groupAdd', this.props.coursesids)
}
+ addToDir = async () => {
+ const courseId = this.props.match.params.coursesId
+ const url = `/courses/${courseId}/join_course_group.json`
+ const course_group_id = this.props.match.params.course_group_id
+
+ const response = await axios.post(url, {
+ course_group_id
+ })
+ if (response && response.data.status == 0) {
+ this.props.showNotification('加入成功')
+ this.props.updataleftNavfun()
+ this.fetchAll()
+ }
+ }
renameDir = () => {
const course_group_id = this.props.match.params.course_group_id
trigger('groupRename', { id: parseInt(course_group_id), name: this.state.course_group_name})
@@ -591,6 +607,7 @@ class studentsList extends Component{
render(){
const isAdmin = this.props.isAdmin()
+ const isStudent = this.props.isStudent()
const isSuperAdmin = this.props.isSuperAdmin()
const isCourseEnd = this.props.isCourseEnd()
let {
@@ -702,6 +719,8 @@ class studentsList extends Component{
{
// pageType !== TYPE_STUDENTS &&
!isCourseEnd && isAdmin && isParent && this.addDir()}>添加分班 }
+ {
+ isStudent && !isParent && course_group_id != 0 && this.addToDir()}>加入分班 }
{
isAdmin && !isParent && course_group_id != 0 && this.deleteDir()}>删除分班 }
{
diff --git a/public/react/src/modules/courses/members/teacherList.js b/public/react/src/modules/courses/members/teacherList.js
index 72b667300..fe46e9bf4 100644
--- a/public/react/src/modules/courses/members/teacherList.js
+++ b/public/react/src/modules/courses/members/teacherList.js
@@ -165,6 +165,9 @@ function buildColumns(that) {
member_roles={record.member_roles}
onChangeRoleSuccess={that.onChangeRoleSuccess}
showNotification={that.props.showNotification}
+ getUserId={that.props.isUserid}
+ fetchUser={that.props.fetchUser}
+
>
{/* this.addTeacher()}>添加教师 }
{ isAdmin && this.addStudent()}>添加学生 } */}
- { isAdmin && this.showChangeAdminModal()}>更换管理员 }
+ { isAdminOrCreator && this.showChangeAdminModal()}>更换管理员 }
}
diff --git a/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js b/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js
index 5eed392d5..926e1b023 100644
--- a/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js
+++ b/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js
@@ -80,6 +80,7 @@ class Listofworksstudentone extends Component {
teacherdata: undefined,
task_status: [],
visibles: false,
+ starttimesend:undefined,
course_group_info: [],
styletable: {
"display": "none"
@@ -2789,6 +2790,7 @@ class Listofworksstudentone extends Component {
axios.get(url).then((response) => {
if (response.status === 200) {
+ const dataformat = 'YYYY-MM-DD HH:mm';
let starttime = this.props.getNowFormatDates(1);
let endtime = this.props.getNowFormatDates(2);
this.setState({
@@ -2801,6 +2803,7 @@ class Listofworksstudentone extends Component {
starttime: "发布时间:" + moment(moment(new Date())).format("YYYY-MM-DD HH:mm"),
endtime: "截止时间:" + endtime,
starttimes:starttime,
+ starttimesend:response.data.end_time===undefined||response.data.end_time===null||response.data.end_time===""?undefined:response.data.end_time,
typs:"start",
Cancelname: "暂不发布",
Savesname: "立即发布",
@@ -2949,6 +2952,7 @@ class Listofworksstudentone extends Component {
course_groupyslstwo: undefined,
typs:undefined,
starttimes:undefined,
+ starttimesend:undefined,
})
}
@@ -3170,6 +3174,7 @@ class Listofworksstudentone extends Component {
course_groups={this.state.course_groups}
getcourse_groupslist={(id) => this.getcourse_groupslist(id)}
starttimes={this.state.starttimes}
+ starttimesend={this.state.starttimesend}
typs={this.state.typs}
/>
{
diff --git a/public/react/src/modules/courses/shixunHomework/ShixunHomeworkPage.js b/public/react/src/modules/courses/shixunHomework/ShixunHomeworkPage.js
index 9b0353b9e..32dd3a4ee 100644
--- a/public/react/src/modules/courses/shixunHomework/ShixunHomeworkPage.js
+++ b/public/react/src/modules/courses/shixunHomework/ShixunHomeworkPage.js
@@ -61,9 +61,9 @@ class ShixunHomeworkPage extends Component {
}
Getdataback = (jobsettingsdata, teacherdata) => {
- // console.log("56");
- // console.log(jobsettingsdata);
- // console.log(teacherdata);
+ console.log("ShixunHomeworkPage56");
+ console.log(jobsettingsdata);
+ console.log(teacherdata);
this.setState({
jobsettingsdatapage: jobsettingsdata.data.message===undefined?jobsettingsdata:undefined,
diff --git a/public/react/src/modules/courses/shixunHomework/ShixunStudentWork.js b/public/react/src/modules/courses/shixunHomework/ShixunStudentWork.js
index 3caa63ac3..95de82445 100644
--- a/public/react/src/modules/courses/shixunHomework/ShixunStudentWork.js
+++ b/public/react/src/modules/courses/shixunHomework/ShixunStudentWork.js
@@ -74,6 +74,7 @@ class ShixunStudentWork extends Component {
DownloadType:false,
DownloadMessageval:undefined,
lunxun:true,
+ starttimesend:undefined,
}
}
@@ -415,6 +416,7 @@ class ShixunStudentWork extends Component {
axios.get(url).then((response) => {
if(response.status===200){
+ const dataformat = 'YYYY-MM-DD HH:mm';
let starttime= this.props.getNowFormatDates(1);
let endtime=this.props.getNowFormatDates(2);
this.setState({
@@ -432,6 +434,7 @@ class ShixunStudentWork extends Component {
Saves:this.homeworkstartend,
course_groups:response.data.course_groups,
starttimes:starttime,
+ starttimesend:response.data.end_time===undefined||response.data.end_time===null||response.data.end_time===""?undefined:response.data.end_time,
typs:"start",
})
}
@@ -754,6 +757,7 @@ class ShixunStudentWork extends Component {
course_groups={this.state.course_groups}
getcourse_groupslist={(id)=>this.getcourse_groupslist(id)}
starttimes={this.state.starttimes}
+ starttimesend={this.state.starttimesend}
typs={this.state.typs}
/>
diff --git a/public/react/src/modules/courses/shixunHomework/ShixunhomeWorkItem.js b/public/react/src/modules/courses/shixunHomework/ShixunhomeWorkItem.js
index 7aa1d44de..88d143011 100644
--- a/public/react/src/modules/courses/shixunHomework/ShixunhomeWorkItem.js
+++ b/public/react/src/modules/courses/shixunHomework/ShixunhomeWorkItem.js
@@ -354,8 +354,8 @@ class ShixunhomeWorkItem extends Component{
{/* {discussMessage.author.name} */}
{ discussMessage.author && {discussMessage.author} }
- {discussMessage.commit_count===undefined?"":已开始做题 {discussMessage.commit_count} }
- {discussMessage.uncommit_count===undefined?"":未开始做题 {discussMessage.uncommit_count} }
+ {discussMessage.commit_count===undefined?"":已开始做题 {discussMessage.commit_count}人 }
+ {discussMessage.uncommit_count===undefined?"":未开始做题 {discussMessage.uncommit_count}人 }
{/*{discussMessage.replies_count} 3 未评 */}
{
@@ -381,7 +381,7 @@ class ShixunhomeWorkItem extends Component{
{
discussMessage && discussMessage.upper_category_name &&
22 }>
- { {discussMessage.upper_category_name} }
+ { {discussMessage.upper_category_name} }
}
diff --git a/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js b/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js
index 142d1e074..5d2462ec0 100644
--- a/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js
+++ b/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js
@@ -120,7 +120,7 @@ class Trainingjobsetting extends Component {
showmodel:false,
code_review:false,
testscripttiptype:false,
-
+ starttimesend:undefined,
end_timebool:false,
late_timesbool:false,
work_efficiencys:false,
@@ -1013,18 +1013,17 @@ class Trainingjobsetting extends Component {
//完成效率评分占比
onChangeeffectiveness = (e) => {
if( e.target.checked === true){
- this.state.latedeductiontwo=20;
this.setState({
completionefficiencyscore: e.target.checked,
work_efficiencys:e.target.checked,
- latedeductiontwo: 20,
+ latedeductiontwo: 0,
})
//均分比例
if(this.state.proportion==="均分比例"){
- this.Equalproportion(20);
+ this.Equalproportion(0);
}else if(this.state.proportion==="经验值比例"){
- this.Empiricalvalueratio(20);
+ this.Empiricalvalueratio(0);
}
@@ -1526,6 +1525,7 @@ class Trainingjobsetting extends Component {
axios.get(url).then((response) => {
if (response.status === 200) {
+ const dataformat = 'YYYY-MM-DD HH:mm';
let starttime = this.props.getNowFormatDates(1);
let endtime = this.props.getNowFormatDates(2);
this.setState({
@@ -1537,7 +1537,8 @@ class Trainingjobsetting extends Component {
Botval:`本操作只对"未发布"的分班有效`,
starttime: "发布时间:" + moment(moment(new Date())).format("YYYY-MM-DD HH:mm"),
starttimes:starttime,
- typs:"start",
+ starttimesend:response.data.end_time===undefined||response.data.end_time===null||response.data.end_time===""?undefined:response.data.end_time,
+ typs:"start",
endtime: "截止时间:" + endtime,
Cancelname: "暂不发布",
Savesname: "立即发布",
@@ -1669,6 +1670,7 @@ class Trainingjobsetting extends Component {
addnametab: undefined,
typs:undefined,
starttimes:undefined,
+ starttimesend:undefined,
})
}
cancelBox=()=>{
@@ -1725,15 +1727,15 @@ class Trainingjobsetting extends Component {
completionefficiencyscore:true,
work_efficiencys:this.state.work_efficiencys,
unifiedsetting:this.state.unifiedsetting,
- latedeductiontwo:20,
+ latedeductiontwo:this.state.latedeductiontwo,
});
//均分比例
try {
if(this.state.proportion==="均分比例"){
- this.Equalproportion(20);
+ this.Equalproportion(this.state.latedeductiontwo);
}else if(this.state.proportion==="经验值比例"){
- this.Empiricalvalueratio(20);
+ this.Empiricalvalueratio(this.state.latedeductiontwo);
}
}catch (e) {
@@ -1838,19 +1840,19 @@ class Trainingjobsetting extends Component {
flagPageEditstwo:releasetime,
flagPageEditsthrees:deadline,
flagPageEditsfor:endtime,
- completionefficiencyscore:true,
+ completionefficiencyscore:false,
work_efficiencys:datas.data.work_efficiency,
unifiedsetting:datas.data.unified_setting,
- latedeductiontwo:20,
+ latedeductiontwo:datas.data.eff_score,
});
//均分比例
// result.data.shixun_evaluation === 0 ? "均分比例" : result.data.shixun_evaluation === 1 ? "经验值比例" : result.data.shixun_evaluation === 2 ?
try {
if(datas.data.shixun_evaluation === 0){
- this.Equalproportion(20);
+ this.Equalproportion(datas.data.eff_score);
}else if(datas.data.shixun_evaluation === 1){
- this.Empiricalvalueratio(20);
+ this.Empiricalvalueratio(datas.data.eff_score);
}
}catch (e) {
@@ -2121,7 +2123,8 @@ class Trainingjobsetting extends Component {
course_groups={this.state.course_groups}
getcourse_groupslist={(id) => this.getcourse_groupslist(id)}
starttimes={this.state.starttimes}
- typs={this.state.typs}
+ starttimesend={this.state.starttimesend}
+ typs={this.state.typs}
/>
{
if (response.status === 200) {
+ const dataformat = 'YYYY-MM-DD HH:mm';
let starttime = this.props.getNowFormatDates(1);
let endtime = this.props.getNowFormatDates(2);
this.setState({
@@ -173,6 +175,7 @@ class Workquestionandanswer extends Component {
Saves: this.homeworkstartend,
course_groups: response.data.course_groups,
starttimes: starttime,
+ starttimesend:response.data.end_time===undefined||response.data.end_time===null||response.data.end_time===""?undefined:response.data.end_time,
typs: "start",
})
}
@@ -441,6 +444,7 @@ class Workquestionandanswer extends Component {
course_groups={this.state.course_groups}
getcourse_groupslist={(id) => this.getcourse_groupslist(id)}
starttimes={this.state.starttimes}
+ starttimesend={this.state.starttimesend}
typs={this.state.typs}
/>
diff --git a/public/react/src/modules/courses/shixunHomework/shixunHomework.js b/public/react/src/modules/courses/shixunHomework/shixunHomework.js
index 8e68a7398..cbfdaf72b 100644
--- a/public/react/src/modules/courses/shixunHomework/shixunHomework.js
+++ b/public/react/src/modules/courses/shixunHomework/shixunHomework.js
@@ -4,6 +4,7 @@ import { WordsBtn,on, off, trigger } from 'educoder';
import {BrowserRouter as Router,Route,Switch,Link} from 'react-router-dom';
import axios from'axios';
import HomeworkModal from "../coursesPublic/HomeworkModal";
+import OneSelfOrderModal from "../coursesPublic/OneSelfOrderModal";
import ShixunModal from "../coursesPublic/ShixunModal";
import PathModal from "../coursesPublic/PathModal";
import NewShixunModel from '../coursesPublic/NewShixunModel';
@@ -227,10 +228,11 @@ class ShixunHomework extends Component{
addnametab:undefined,
typs:undefined,
starttimes:undefined,
+ OneSelftype:false
})
+ this.cancelmodel()
+ this.homeworkupdatalist(Coursename,page,order);
this.props.updataleftNavfun()
- this.homeworkupdatalist(Coursename,page,order);
- this.cancelmodel()
}
@@ -272,6 +274,48 @@ class ShixunHomework extends Component{
});
}
+
+
+ //立即发布
+ homeworkOneSelfstart=()=>{
+ let selectnum= this.testonSelect();
+ if(selectnum===true){
+ this.noSelect();
+ return
+ }
+ let coursesId=this.props.match.params.coursesId;
+ let url="/courses/"+coursesId+"/all_course_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,
+ OneSelftype: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,
+ course_groups:response.data.course_groups,
+ })
+ }
+ }).catch((error) => {
+ console.log(error)
+ });
+
+ }
+
+
cancelmodels=()=>{
this.setState({
Modalstype:false,
@@ -316,10 +360,12 @@ class ShixunHomework extends Component{
})
}
- this.props.showNotification(result.data.message)
+
+ this.homeworkupdatalist(Coursename,page,order);
+ this.cancelmodel()
+ this.props.showNotification(result.data.message)
this.props.updataleftNavfun()
- this.homeworkupdatalist(Coursename,page,order);
- this.cancelmodel()
+
}
}).catch((error)=>{
console.log(error);
@@ -414,11 +460,11 @@ class ShixunHomework extends Component{
Loadtype:false,
course_groups:[]
})
-
+ this.homeworkupdatalist(Coursename,page,order);
+ this.cancelmodel()
this.props.showNotification(response.data.message)
this.props.updataleftNavfun()
- this.homeworkupdatalist(Coursename,page,order);
- this.cancelmodel()
+
}
})
.catch(function (error) {
@@ -722,8 +768,8 @@ class ShixunHomework extends Component{
checkedtype:false,
antIcon:false
})
+ this.homeworkupdatalist(Coursename,page,order);
this.props.showNotification(response.data.message)
- this.homeworkupdatalist(Coursename,page,order);
this.props.updataleftNavfun()
}else{
this.setState({
@@ -782,6 +828,7 @@ class ShixunHomework extends Component{
this.setState({
Modalstype:false,
+ OneSelftype:false,
Loadtype:false,
visible:false,
Modalstopval:"",
@@ -834,9 +881,10 @@ class ShixunHomework extends Component{
Loadtype:false,
checkBoxValues:[]
})
+ this.homeworkupdatalist(Coursename,page,order);
this.props.showNotification(response.data.message)
- this.homeworkupdatalist(Coursename,page,order);
+
}
})
.catch(function (error) {
@@ -886,9 +934,10 @@ class ShixunHomework extends Component{
checkBoxValues:[],
checkAllValue:false
})
+ this.homeworkupdatalist(Coursename,page,order);
this.props.showNotification('已完成')
this.props.updataleftNavfun()
- this.homeworkupdatalist(Coursename,page,order);
+
}
})
}
@@ -1022,7 +1071,7 @@ class ShixunHomework extends Component{
loadtype={this.state.Loadtype}
antIcon={this.state.antIcon}
/>:""}
- {/*立即发布*/}
+ {/*批量立即发布*/}
{visible===true?
this.getcourse_groupslist(id)}
/>:""}
-
+ {/*{单个立即发布}*/}
+ {/*this.getcourse_groupslist(id)}*/}
+ {/*/>*/}
{shixunmodal===true||shixunpath===true?:""}
{isRender===true?
{this.handleDialogClose()}}>
@@ -519,14 +573,14 @@ class LoginDialog extends Component {
-
+ {weixinlogin===true?"":
{this.enter(0)}}>登录
{/*{this.register(0)}}>快捷登录 */}
-
+ }