commit
6bd122723a
@ -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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
@ -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');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -0,0 +1,5 @@
|
|||||||
|
class SettingsController < ApplicationController
|
||||||
|
def show
|
||||||
|
@laboratory = current_laboratory
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,5 @@
|
|||||||
|
class TemplatesController < ApplicationController
|
||||||
|
def show
|
||||||
|
@template = EcTemplate.find_by_name!(params[:name])
|
||||||
|
end
|
||||||
|
end
|
@ -1,6 +1,6 @@
|
|||||||
module MemosHelper
|
module MemosHelper
|
||||||
|
|
||||||
def forum_list
|
def forum_list
|
||||||
[{id: 5, name: "技术分享"}, {id: 3, name: "操作指南"}]
|
[{id: 5, name: "技术分享"}, {id: 3, name: "操作指南"}, {id: 16, name: "通知公告"}]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
class CreateDiffRecordJob < ApplicationJob
|
||||||
|
queue_as :default
|
||||||
|
|
||||||
|
def perform(user_id, obj_id, obj_klass, column_name, before, after)
|
||||||
|
user = User.find_by(id: user_id)
|
||||||
|
obj = obj_klass.constantize.find_by(id: obj_id)
|
||||||
|
|
||||||
|
return if user.blank? || obj.blank?
|
||||||
|
|
||||||
|
CreateDiffRecordService.call(user, obj, column_name, before, after)
|
||||||
|
end
|
||||||
|
end
|
@ -1,75 +0,0 @@
|
|||||||
module Util::Wechat
|
|
||||||
BASE_SITE = 'https://api.weixin.qq.com'.freeze
|
|
||||||
|
|
||||||
Error = Class.new(StandardError)
|
|
||||||
|
|
||||||
class << self
|
|
||||||
attr_accessor :appid, :secret
|
|
||||||
|
|
||||||
def js_sdk_signature(url, noncestr, timestamp)
|
|
||||||
data = { jsapi_ticket: jsapi_ticket, noncestr: noncestr, timestamp: timestamp, url: url }
|
|
||||||
str = data.map { |k, v| "#{k}=#{v}" }.join('&')
|
|
||||||
Digest::SHA1.hexdigest(str)
|
|
||||||
end
|
|
||||||
|
|
||||||
def access_token
|
|
||||||
# 7200s 有效时间
|
|
||||||
Rails.cache.fetch(access_token_cache_key, expires_in: 100.minutes) do
|
|
||||||
result = request(:get, '/cgi-bin/token', appid: appid, secret: secret, grant_type: 'client_credential')
|
|
||||||
result['access_token']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def refresh_access_token
|
|
||||||
Rails.cache.delete(access_token_cache_key)
|
|
||||||
access_token
|
|
||||||
end
|
|
||||||
|
|
||||||
def jsapi_ticket
|
|
||||||
# 7200s 有效时间
|
|
||||||
Rails.cache.fetch(jsapi_ticket_cache_key, expires_in: 100.minutes) do
|
|
||||||
result = request(:get, '/cgi-bin/ticket/getticket', access_token: access_token, type: 'jsapi')
|
|
||||||
result['ticket']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def refresh_jsapi_ticket
|
|
||||||
Rails.cache.delete(jsapi_ticket_cache_key)
|
|
||||||
jsapi_ticket
|
|
||||||
end
|
|
||||||
|
|
||||||
def access_token_cache_key
|
|
||||||
"#{base_cache_key}/access_token"
|
|
||||||
end
|
|
||||||
|
|
||||||
def jsapi_ticket_cache_key
|
|
||||||
"#{base_cache_key}/jsapi_ticket"
|
|
||||||
end
|
|
||||||
|
|
||||||
def base_cache_key
|
|
||||||
"wechat/#{appid}"
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def request(method, url, **params)
|
|
||||||
Rails.logger.error("[wechat] request: #{method} #{url} #{params.inspect}")
|
|
||||||
|
|
||||||
client = Faraday.new(url: BASE_SITE)
|
|
||||||
response = client.public_send(method, url, params)
|
|
||||||
result = JSON.parse(response.body)
|
|
||||||
|
|
||||||
Rails.logger.error("[wechat] response:#{response.status} #{result.inspect}")
|
|
||||||
|
|
||||||
if response.status != 200
|
|
||||||
raise Error, result.inspect
|
|
||||||
end
|
|
||||||
|
|
||||||
if result['errcode'].present? && result['errcode'].to_i.nonzero?
|
|
||||||
raise Error, result.inspect
|
|
||||||
end
|
|
||||||
|
|
||||||
result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -0,0 +1,2 @@
|
|||||||
|
module Wechat
|
||||||
|
end
|
@ -0,0 +1,11 @@
|
|||||||
|
class Wechat::App
|
||||||
|
class << self
|
||||||
|
attr_accessor :appid, :secret
|
||||||
|
|
||||||
|
delegate :access_token, :jscode2session, to: :client
|
||||||
|
|
||||||
|
def client
|
||||||
|
@_client ||= Wechat::Client.new(appid, secret)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,74 @@
|
|||||||
|
class Wechat::Client
|
||||||
|
BASE_SITE = 'https://api.weixin.qq.com'.freeze
|
||||||
|
|
||||||
|
attr_reader :appid, :secret
|
||||||
|
|
||||||
|
def initialize(appid, secret)
|
||||||
|
@appid = appid
|
||||||
|
@secret = secret
|
||||||
|
end
|
||||||
|
|
||||||
|
def access_token
|
||||||
|
# 7200s 有效时间
|
||||||
|
Rails.cache.fetch(access_token_cache_key, expires_in: 100.minutes) do
|
||||||
|
result = request(:get, '/cgi-bin/token', appid: appid, secret: secret, grant_type: 'client_credential')
|
||||||
|
result['access_token']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def refresh_access_token
|
||||||
|
Rails.cache.delete(access_token_cache_key)
|
||||||
|
access_token
|
||||||
|
end
|
||||||
|
|
||||||
|
def jsapi_ticket
|
||||||
|
# 7200s 有效时间
|
||||||
|
Rails.cache.fetch(jsapi_ticket_cache_key, expires_in: 100.minutes) do
|
||||||
|
result = request(:get, '/cgi-bin/ticket/getticket', access_token: access_token, type: 'jsapi')
|
||||||
|
result['ticket']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def refresh_jsapi_ticket
|
||||||
|
Rails.cache.delete(jsapi_ticket_cache_key)
|
||||||
|
jsapi_ticket
|
||||||
|
end
|
||||||
|
|
||||||
|
def jscode2session(code)
|
||||||
|
request(:get, '/sns/jscode2session', appid: appid, secret: secret, js_code: code, grant_type: 'authorization_code')
|
||||||
|
end
|
||||||
|
|
||||||
|
def access_token_cache_key
|
||||||
|
"#{base_cache_key}/access_token"
|
||||||
|
end
|
||||||
|
|
||||||
|
def jsapi_ticket_cache_key
|
||||||
|
"#{base_cache_key}/jsapi_ticket"
|
||||||
|
end
|
||||||
|
|
||||||
|
def base_cache_key
|
||||||
|
"wechat/#{appid}"
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def request(method, url, **params)
|
||||||
|
Rails.logger.error("[wechat] request: #{method} #{url} #{params.except(:secret).inspect}")
|
||||||
|
|
||||||
|
client = Faraday.new(url: BASE_SITE)
|
||||||
|
response = client.public_send(method, url, params)
|
||||||
|
result = JSON.parse(response.body)
|
||||||
|
|
||||||
|
Rails.logger.error("[wechat] response:#{response.status} #{result.inspect}")
|
||||||
|
|
||||||
|
if response.status != 200
|
||||||
|
raise Wechat::Error.parse(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
if result['errcode'].present? && result['errcode'].to_i.nonzero?
|
||||||
|
raise Wechat::Error.parse(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
result
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,14 @@
|
|||||||
|
class Wechat::Error < StandardError
|
||||||
|
attr_reader :code
|
||||||
|
|
||||||
|
def initialize(code, message)
|
||||||
|
super message
|
||||||
|
@code = code
|
||||||
|
end
|
||||||
|
|
||||||
|
class << self
|
||||||
|
def parse(result)
|
||||||
|
new(result['errcode'], result['errmsg'])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,17 @@
|
|||||||
|
class Wechat::OfficialAccount
|
||||||
|
class << self
|
||||||
|
attr_accessor :appid, :secret
|
||||||
|
|
||||||
|
delegate :access_token, :jsapi_ticket, to: :client
|
||||||
|
|
||||||
|
def js_sdk_signature(url, noncestr, timestamp)
|
||||||
|
data = { jsapi_ticket: jsapi_ticket, noncestr: noncestr, timestamp: timestamp, url: url }
|
||||||
|
str = data.map { |k, v| "#{k}=#{v}" }.join('&')
|
||||||
|
Digest::SHA1.hexdigest(str)
|
||||||
|
end
|
||||||
|
|
||||||
|
def client
|
||||||
|
@_client ||= Wechat::Client.new(appid, secret)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,8 @@
|
|||||||
|
class DiffRecord < ApplicationRecord
|
||||||
|
belongs_to :user
|
||||||
|
belongs_to :container, polymorphic: true
|
||||||
|
|
||||||
|
has_one :diff_record_content, dependent: :destroy
|
||||||
|
|
||||||
|
delegate :content, to: :diff_record_content
|
||||||
|
end
|
@ -0,0 +1,3 @@
|
|||||||
|
class DiffRecordContent < ApplicationRecord
|
||||||
|
belongs_to :diff_record
|
||||||
|
end
|
@ -1,2 +1,3 @@
|
|||||||
class Forum < ApplicationRecord
|
class Forum < ApplicationRecord
|
||||||
|
has_many :memos, dependent: :destroy
|
||||||
end
|
end
|
||||||
|
@ -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
|
@ -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
|
@ -0,0 +1,4 @@
|
|||||||
|
class LaboratoryUser < ApplicationRecord
|
||||||
|
belongs_to :laboratory
|
||||||
|
belongs_to :user
|
||||||
|
end
|
@ -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.joins(:school).where(like_sql, keyword: "%#{keyword}%")
|
||||||
|
end
|
||||||
|
|
||||||
|
custom_sort laboratories, params[:sort_by], params[:sort_direction]
|
||||||
|
end
|
||||||
|
end
|
@ -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
|
@ -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
|
@ -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
|
@ -1,3 +1,9 @@
|
|||||||
class ApplicationService
|
class ApplicationService
|
||||||
include Callable
|
include Callable
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def strip(str)
|
||||||
|
str.to_s.strip.presence
|
||||||
|
end
|
||||||
end
|
end
|
@ -0,0 +1,46 @@
|
|||||||
|
class CreateDiffRecordService < ApplicationService
|
||||||
|
attr_reader :user, :obj, :column_name, :after, :before
|
||||||
|
|
||||||
|
def initialize(user, obj, column_name, before, after)
|
||||||
|
@user = user
|
||||||
|
@obj = obj
|
||||||
|
@before = before
|
||||||
|
@after = after
|
||||||
|
@column_name = column_name
|
||||||
|
end
|
||||||
|
|
||||||
|
def call
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
diff_record = DiffRecord.create!(user: user, container: obj, column_name: column_name)
|
||||||
|
diff_record.create_diff_record_content!(content: diff_content)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def diff_content
|
||||||
|
content = ''
|
||||||
|
|
||||||
|
arr = []
|
||||||
|
index = 0
|
||||||
|
fragment_size = 1
|
||||||
|
Diffy::Diff.new(before, after).each do |line|
|
||||||
|
unless line =~ /^[\+-]/
|
||||||
|
if arr.empty? && index < fragment_size
|
||||||
|
content += line
|
||||||
|
index += 1
|
||||||
|
else
|
||||||
|
index = 0
|
||||||
|
arr << line
|
||||||
|
arr.shift if arr.size > fragment_size
|
||||||
|
end
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
content += arr.join('') if arr.present?
|
||||||
|
content += line
|
||||||
|
arr.clear
|
||||||
|
end
|
||||||
|
content
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,19 @@
|
|||||||
|
<% define_admin_breadcrumbs do %>
|
||||||
|
<% add_admin_breadcrumb('云上实验室') %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div class="box search-form-container laboratory-list-form">
|
||||||
|
<%= 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' } %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="box laboratory-list-container">
|
||||||
|
<%= render(partial: 'admins/laboratories/shared/list', locals: { laboratories: @laboratories }) %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= render 'admins/laboratories/shared/create_laboratory_modal' %>
|
||||||
|
<%= render 'admins/laboratories/shared/add_laboratory_user_modal' %>
|
@ -0,0 +1 @@
|
|||||||
|
$('.laboratory-list-container').html("<%= j(render partial: 'admins/laboratories/shared/list', locals: { laboratories: @laboratories }) %>");
|
@ -0,0 +1,30 @@
|
|||||||
|
<div class="modal fade admin-add-laboratory-user-modal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">添加管理员</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form class="admin-add-laboratory-user-form">
|
||||||
|
<%= hidden_field_tag(:laboratory_id, nil) %>
|
||||||
|
|
||||||
|
<div class="form-group d-flex">
|
||||||
|
<label class="col-form-label">管理员:</label>
|
||||||
|
<div class="d-flex flex-column-reverse w-75">
|
||||||
|
<select id="user_ids" name="user_ids" class="form-control laboratory-user-select"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="error text-danger"></div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
|
||||||
|
<button type="button" class="btn btn-primary submit-btn">确认</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,28 @@
|
|||||||
|
<div class="modal fade admin-create-laboratory-modal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">新建云上实验室</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form class="admin-create-laboratory-form" data-url="<%= admins_laboratories_path %>">
|
||||||
|
<div class="form-group d-flex">
|
||||||
|
<label for="school_id" class="col-form-label">选择单位:</label>
|
||||||
|
<div class="d-flex flex-column-reverse w-75">
|
||||||
|
<select id="school_id" name="school_id" class="form-control school-select"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="error text-danger"></div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
|
||||||
|
<button type="button" class="btn btn-primary submit-btn">确认</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,40 @@
|
|||||||
|
<% school = laboratory.school %>
|
||||||
|
<td class="text-left"><%= school&.name || 'EduCoder主站' %></td>
|
||||||
|
<td class="text-left">
|
||||||
|
<% if laboratory.identifier %>
|
||||||
|
<%= link_to laboratory.site, "https://#{laboratory.site}", target: '_blank' %>
|
||||||
|
<% else %>
|
||||||
|
--
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<% if school && school.identifier.present? %>
|
||||||
|
<%= link_to school.identifier.to_s, statistics_college_path(school.identifier), target: '_blank' %>
|
||||||
|
<% else %>
|
||||||
|
--
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
|
<td class="member-container">
|
||||||
|
<div class="laboratory-user">
|
||||||
|
<% laboratory.users.each do |user| %>
|
||||||
|
<span class="laboratory-user-item laboratory-user-item-<%= user.id %>">
|
||||||
|
<%= 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 %>
|
||||||
|
<i class="fa fa-close"></i>
|
||||||
|
<% end %>
|
||||||
|
</span>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td><%= laboratory.created_at.strftime('%Y-%m-%d %H:%M') %></td>
|
||||||
|
<td class="action-container">
|
||||||
|
<%= 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 %>
|
||||||
|
</td>
|
@ -0,0 +1,25 @@
|
|||||||
|
<table class="table table-hover text-center laboratory-list-table">
|
||||||
|
<thead class="thead-light">
|
||||||
|
<tr>
|
||||||
|
<th width="20%" class="text-left">单位名称</th>
|
||||||
|
<th width="16%" class="text-left">域名</th>
|
||||||
|
<th width="10%">统计链接</th>
|
||||||
|
<th width="22%">管理员</th>
|
||||||
|
<th width="14%"><%= sort_tag('创建时间', name: 'id', path: admins_laboratories_path) %></th>
|
||||||
|
<th width="20%">操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% if laboratories.present? %>
|
||||||
|
<% laboratories.each do |laboratory| %>
|
||||||
|
<tr class="laboratory-item laboratory-item-<%= laboratory.id %>">
|
||||||
|
<%= render 'admins/laboratories/shared/laboratory_item', laboratory: laboratory %>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<%= render 'admins/shared/no_data_for_table' %>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<%= render partial: 'admins/shared/paginate', locals: { objects: laboratories } %>
|
@ -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 }) %>")
|
@ -0,0 +1,2 @@
|
|||||||
|
$.notify({ message: '操作成功' });
|
||||||
|
$('.laboratory-list-container .laboratory-item-<%= current_laboratory.id %> .laboratory-user-item-<%= @laboratory_user.user_id %>').remove();
|
@ -0,0 +1,11 @@
|
|||||||
|
json.course_groups @course_groups.each do |group|
|
||||||
|
json.(group, :id, :course_members_count, :name)
|
||||||
|
json.invite_code group.invite_code if @user_course_identity < Course::STUDENT
|
||||||
|
json.member_manager member_manager(group, @teachers) if @user_course_identity < Course::NORMAL
|
||||||
|
end
|
||||||
|
|
||||||
|
if @user_course_identity == Course::STUDENT
|
||||||
|
json.current_group_id @current_group_id
|
||||||
|
end
|
||||||
|
json.none_group_member_count @course.none_group_count
|
||||||
|
json.group_count @all_group_count
|
@ -1 +1,7 @@
|
|||||||
json.course_evaluations @course_evaluations, partial: 'ecs/course_evaluations/shared/ec_course_evaluation_slim', as: :ec_course_evaluation
|
json.course_evaluations do
|
||||||
|
json.array! @course_evaluations do |course_evaluation|
|
||||||
|
json.partial! 'ecs/course_evaluations/shared/ec_course_evaluation_slim', ec_course_evaluation: course_evaluation
|
||||||
|
|
||||||
|
json.evaluation_relates course_evaluation.evaluation_relates
|
||||||
|
end
|
||||||
|
end
|
@ -1 +0,0 @@
|
|||||||
json.partial! 'ecs/shared/user', user: @user
|
|
@ -0,0 +1,6 @@
|
|||||||
|
json.course_groups @course_groups do |group|
|
||||||
|
json.id group.id
|
||||||
|
json.name group.name
|
||||||
|
json.end_time @group_settings.select{|group_setting| group_setting.course_group_id == group.id}.first&.end_time
|
||||||
|
end
|
||||||
|
json.end_time @exercise.end_time
|
@ -1,4 +1,6 @@
|
|||||||
json.course_groups @course_groups do |group|
|
json.course_groups @course_groups do |group|
|
||||||
json.id group.id
|
json.id group.id
|
||||||
json.name group.name
|
json.name group.name
|
||||||
end
|
json.end_time @group_settings.select{|group_setting| group_setting.course_group_id == group.id}.first&.end_time
|
||||||
|
end
|
||||||
|
json.end_time @homework.end_time
|
@ -0,0 +1,6 @@
|
|||||||
|
json.course_groups @course_groups do |group|
|
||||||
|
json.id group.id
|
||||||
|
json.name group.name
|
||||||
|
json.end_time @group_settings.select{|group_setting| group_setting.course_group_id == group.id}.first&.end_time
|
||||||
|
end
|
||||||
|
json.end_time @poll.end_time
|
@ -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
|
@ -1,3 +1,4 @@
|
|||||||
json.status 1
|
json.status 1
|
||||||
json.message "发送成功"
|
json.message "发送成功"
|
||||||
json.course_id @course.id
|
json.course_id @course.id
|
||||||
|
json.first_category_url module_url(@course.none_hidden_course_modules.first, @course)
|
@ -0,0 +1,3 @@
|
|||||||
|
json.template do
|
||||||
|
json.partial! 'attachments/attachment_simple', attachment: @template.attachments.last
|
||||||
|
end
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue