Merge branch 'dev_aliyun' into dev_hjm_groupwork_leader_@

dev_unstable^2
hjm 6 years ago
commit 0ce29ed776

@ -11,6 +11,7 @@
//= require select2
//= require jquery.cxselect
//= require bootstrap-datepicker
//= require bootstrap.viewer
//= require_tree ./i18n
//= require_tree ./admins
@ -25,6 +26,9 @@ $(document).on('turbolinks:load', function(){
$('[data-toggle="tooltip"]').tooltip();
$('[data-toggle="popover"]').popover();
// 图片查看大图
$('img.preview-image').bootstrapViewer();
// flash alert提示框自动关闭
if($('.admin-alert-container .alert').length > 0){
setTimeout(function(){
@ -33,10 +37,25 @@ $(document).on('turbolinks:load', function(){
}
});
$(document).on("turbolinks:before-cache", function () {
$('[data-toggle="tooltip"]').tooltip('hide');
$('[data-toggle="popover"]').popover('hide');
});
// var progressBar = new Turbolinks.ProgressBar();
// $(document).on('ajax:send', function(event){
// console.log('ajax send', event);
// progressBar.setValue(0)
// progressBar.show()
// });
//
// $(document).on('ajax:complete', function(event){
// console.log('ajax complete', event);
// progressBar.setValue(1)
// progressBar.hide() // 分页时不触发,奇怪
// });
// $(document).on('ajax:success', function(event){
// console.log('ajax success', event);
// });
// $(document).on('ajax:error', function(event){
// console.log('ajax error', event);
// });
$(function () {
});

@ -0,0 +1,54 @@
$(document).on('turbolinks:load', function() {
var $refuseModal = $('.admin-common-refuse-modal');
if ($refuseModal.length > 0) {
var $form = $refuseModal.find('form.admin-common-refuse-form');
var $applyIdInput = $refuseModal.find('.modal-body input[name="apply_id"]');
$form.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
reason: {
required: true,
maxlength: 200
},
}
});
// modal ready fire
$refuseModal.on('show.bs.modal', function (event) {
var $link = $(event.relatedTarget);
var applyId = $link.data('id');
var url = $link.data('url');
$applyIdInput.val(applyId);
$form.data('url', url);
});
// modal visited fire
$refuseModal.on('shown.bs.modal', function(){
$refuseModal.find('.modal-body input[name="reason"]').focus();
});
$refuseModal.on('hide.bs.modal', function () {
$applyIdInput.val('');
$form.data('url', '');
})
$refuseModal.on('click', '.submit-btn', function(){
$form.find('.error').html('');
if ($form.valid()) {
var url = $form.data('url');
$.ajax({
method: 'POST',
dataType: 'script',
url: url,
data: $form.serialize(),
}).done(function(){
$refuseModal.modal('hide');
});
}
});
}
});

@ -0,0 +1,20 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-identity-authentications-index-page').length > 0) {
var $searchFrom = $('.identity-authentication-list-form');
$searchFrom.find('select[name="status"]').val('pending');
$searchFrom.on('click', '.search-form-tab', function(){
var $link = $(this);
$searchFrom.find('input[name="keyword"]').val('');
$searchFrom.find('select[name="status"]').val('processed');
if($link.data('value') === 'processed'){
$searchFrom.find('.status-filter').show();
} else {
$searchFrom.find('.status-filter').hide();
$searchFrom.find('select[name="status"]').val('pending');
}
});
}
})

@ -0,0 +1,20 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-professional-authentications-index-page').length > 0) {
var $searchFrom = $('.professional-authentication-list-form');
$searchFrom.find('select[name="status"]').val('pending');
$searchFrom.on('click', '.search-form-tab', function(){
var $link = $(this);
$searchFrom.find('input[name="keyword"]').val('');
$searchFrom.find('select[name="status"]').val('processed');
if($link.data('value') === 'processed'){
$searchFrom.find('.status-filter').show();
} else {
$searchFrom.find('.status-filter').hide();
$searchFrom.find('select[name="status"]').val('pending');
}
});
}
})

@ -0,0 +1,10 @@
$(document).on('turbolinks:load', function() {
var $tabs = $('.search-form-container .search-form-tabs');
if ($tabs.length > 0) {
$tabs.on('click', '.search-form-tab', function(){
var $activeTab = $(this);
$tabs.find('.search-form-tab').removeClass('active');
$activeTab.addClass('active');
});
}
});

@ -0,0 +1,20 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-shixun-authorizations-index-page').length > 0) {
var $searchFrom = $('.shixun-authorization-list-form');
$searchFrom.find('select[name="status"]').val('pending');
$searchFrom.on('click', '.search-form-tab', function(){
var $link = $(this);
$searchFrom.find('input[name="keyword"]').val('');
$searchFrom.find('select[name="status"]').val('processed');
if($link.data('value') === 'processed'){
$searchFrom.find('.status-filter').show();
} else {
$searchFrom.find('.status-filter').hide();
$searchFrom.find('select[name="status"]').val('pending');
}
});
}
})

@ -0,0 +1,20 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-subject-authorizations-index-page').length > 0) {
var $searchFrom = $('.subject-authorization-list-form');
$searchFrom.find('select[name="status"]').val('pending');
$searchFrom.on('click', '.search-form-tab', function(){
var $link = $(this);
$searchFrom.find('input[name="keyword"]').val('');
$searchFrom.find('select[name="status"]').val('processed');
if($link.data('value') === 'processed'){
$searchFrom.find('.status-filter').show();
} else {
$searchFrom.find('.status-filter').hide();
$searchFrom.find('select[name="status"]').val('pending');
}
});
}
})

@ -0,0 +1,50 @@
/*
* Copyright 2018-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* WebSite: http://bootstrap-viewer.leftso.com
*/
$.fn.bootstrapViewer = function (options) {
$(this).on('click', function () {
var opts = $.extend({}, $.fn.bootstrapViewer.defaults, options);
var viewer = $('<div class="modal fade bs-example-modal-lg text-center" id="bootstrapViewer" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" >\n' +
' <div class="modal-dialog modal-lg" style="display: inline-block; width: auto;">\n' +
' <div class="modal-content">\n' +
' <img' +
'\t\t\t class="carousel-inner img-responsive img-rounded img-viewer" \n' +
'\t\t\t onclick="$(\'#bootstrapViewer\').modal(\'hide\');setTimeout(function(){$(\'#bootstrapViewer\').remove();},200);"\n' +
'\t\t\t onmouseover="this.style.cursor=\'zoom-out\';" \n' +
'\t\t\t onmouseout="this.style.cursor=\'default\'" \n' +
'\t\t\t />\n' +
' </div>\n' +
' </div>\n' +
' </div>');
$('body').append(viewer);
if ($(this).attr(opts.src)) {
$("#bootstrapViewer").find(".img-viewer").attr("src", $(this).attr(opts.src));
$("#bootstrapViewer").modal();
} else {
throw "图片不存在"
}
})
$(this).on('mouseover', function () {
$(this).css('cursor', 'zoom-in');
})
}
$.fn.bootstrapViewer.defaults = {
src: 'src'
}

@ -1,403 +1,403 @@
/*!
* jQuery cxSelect
* @name jquery.cxselect.js
* @version 1.4.1
* @date 2016-11-02
* @author ciaoca
* @email ciaoca@gmail.com
* @site https://github.com/ciaoca/cxSelect
* @license Released under the MIT license
*/
(function(factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else {
factory(window.jQuery || window.Zepto || window.$);
};
}(function($) {
var cxSelect = function() {
var self = this;
var dom, settings, callback;
// 分配参数
for (var i = 0, l = arguments.length; i < l; i++) {
if (cxSelect.isJquery(arguments[i]) || cxSelect.isZepto(arguments[i])) {
dom = arguments[i];
} else if (cxSelect.isElement(arguments[i])) {
dom = $(arguments[i]);
} else if (typeof arguments[i] === 'function') {
callback = arguments[i];
} else if (typeof arguments[i] === 'object') {
settings = arguments[i];
};
};
var api = new cxSelect.init(dom, settings);
if (typeof callback === 'function') {
callback(api);
};
return api;
};
cxSelect.isElement = function(o){
if (o && (typeof HTMLElement === 'function' || typeof HTMLElement === 'object') && o instanceof HTMLElement) {
return true;
} else {
return (o && o.nodeType && o.nodeType === 1) ? true : false;
};
};
cxSelect.isJquery = function(o){
return (o && o.length && (typeof jQuery === 'function' || typeof jQuery === 'object') && o instanceof jQuery) ? true : false;
};
cxSelect.isZepto = function(o){
return (o && o.length && (typeof Zepto === 'function' || typeof Zepto === 'object') && Zepto.zepto.isZ(o)) ? true : false;
};
cxSelect.getIndex = function(n, required) {
return required ? n : n - 1;
};
cxSelect.getData = function(data, space) {
if (typeof space === 'string' && space.length) {
space = space.split('.');
for (var i = 0, l = space.length; i < l; i++) {
data = data[space[i]];
};
};
return data;
};
cxSelect.init = function(dom, settings) {
var self = this;
if (!cxSelect.isJquery(dom) && !cxSelect.isZepto(dom)) {return};
var theSelect = {
dom: {
box: dom
}
};
self.attach = cxSelect.attach.bind(theSelect);
self.detach = cxSelect.detach.bind(theSelect);
self.setOptions = cxSelect.setOptions.bind(theSelect);
self.clear = cxSelect.clear.bind(theSelect);
theSelect.changeEvent = function() {
cxSelect.selectChange.call(theSelect, this.className);
};
theSelect.settings = $.extend({}, $.cxSelect.defaults, settings, {
url: theSelect.dom.box.data('url'),
emptyStyle: theSelect.dom.box.data('emptyStyle'),
required: theSelect.dom.box.data('required'),
firstTitle: theSelect.dom.box.data('firstTitle'),
firstValue: theSelect.dom.box.data('firstValue'),
jsonSpace: theSelect.dom.box.data('jsonSpace'),
jsonName: theSelect.dom.box.data('jsonName'),
jsonValue: theSelect.dom.box.data('jsonValue'),
jsonSub: theSelect.dom.box.data('jsonSub')
});
var _dataSelects = theSelect.dom.box.data('selects');
if (typeof _dataSelects === 'string' && _dataSelects.length) {
theSelect.settings.selects = _dataSelects.split(',');
};
self.setOptions();
self.attach();
// 使用独立接口获取数据
if (!theSelect.settings.url && !theSelect.settings.data) {
cxSelect.start.apply(theSelect);
// 设置自定义数据
} else if ($.isArray(theSelect.settings.data)) {
cxSelect.start.call(theSelect, theSelect.settings.data);
// 设置 URL通过 Ajax 获取数据
} else if (typeof theSelect.settings.url === 'string' && theSelect.settings.url.length) {
$.getJSON(theSelect.settings.url, function(json) {
cxSelect.start.call(theSelect, json);
});
};
};
// 设置参数
cxSelect.setOptions = function(opts) {
var self = this;
if (opts) {
$.extend(self.settings, opts);
};
// 初次或重设选择器组
if (!$.isArray(self.selectArray) || !self.selectArray.length || (opts && opts.selects)) {
self.selectArray = [];
if ($.isArray(self.settings.selects) && self.settings.selects.length) {
var _tempSelect;
for (var i = 0, l = self.settings.selects.length; i < l; i++) {
_tempSelect = self.dom.box.find('select.' + self.settings.selects[i]);
if (!_tempSelect || !_tempSelect.length) {break};
self.selectArray.push(_tempSelect);
};
};
};
if (opts) {
if (!$.isArray(opts.data) && typeof opts.url === 'string' && opts.url.length) {
$.getJSON(self.settings.url, function(json) {
cxSelect.start.call(self, json);
});
} else {
cxSelect.start.call(self, opts.data);
};
};
};
// 绑定
cxSelect.attach = function() {
var self = this;
if (!self.attachStatus) {
self.dom.box.on('change', 'select', self.changeEvent);
};
if (typeof self.attachStatus === 'boolean') {
cxSelect.start.call(self);
};
self.attachStatus = true;
};
// 移除绑定
cxSelect.detach = function() {
var self = this;
self.dom.box.off('change', 'select', self.changeEvent);
self.attachStatus = false;
};
// 清空选项
cxSelect.clear = function(index) {
var self = this;
var _style = {
display: '',
visibility: ''
};
index = isNaN(index) ? 0 : index;
// 清空后面的 select
for (var i = index, l = self.selectArray.length; i < l; i++) {
self.selectArray[i].empty().prop('disabled', true);
if (self.settings.emptyStyle === 'none') {
_style.display = 'none';
} else if (self.settings.emptyStyle === 'hidden') {
_style.visibility = 'hidden';
};
self.selectArray[i].css(_style);
};
};
cxSelect.start = function(data) {
var self = this;
if ($.isArray(data)) {
self.settings.data = cxSelect.getData(data, self.settings.jsonSpace);
};
if (!self.selectArray.length) {return};
// 保存默认值
for (var i = 0, l = self.selectArray.length; i < l; i++) {
if (typeof self.selectArray[i].attr('data-value') !== 'string' && self.selectArray[i][0].options.length) {
self.selectArray[i].attr('data-value', self.selectArray[i].val());
};
};
if (self.settings.data || (typeof self.selectArray[0].data('url') === 'string' && self.selectArray[0].data('url').length)) {
cxSelect.getOptionData.call(self, 0);
} else {
self.selectArray[0].prop('disabled', false).css({
'display': '',
'visibility': ''
});
};
};
// 获取选项数据
cxSelect.getOptionData = function(index) {
var self = this;
if (typeof index !== 'number' || isNaN(index) || index < 0 || index >= self.selectArray.length) {return};
var _indexPrev = index - 1;
var _select = self.selectArray[index];
var _selectData;
var _valueIndex;
var _dataUrl = _select.data('url');
var _jsonSpace = typeof _select.data('jsonSpace') === 'undefined' ? self.settings.jsonSpace : _select.data('jsonSpace');
var _query = {};
var _queryName;
var _selectName;
var _selectValue;
cxSelect.clear.call(self, index);
// 使用独立接口
if (typeof _dataUrl === 'string' && _dataUrl.length) {
if (index > 0) {
for (var i = 0, j = 1; i < index; i++, j++) {
_queryName = self.selectArray[j].data('queryName');
_selectName = self.selectArray[i].attr('name');
_selectValue = self.selectArray[i].val();
if (typeof _queryName === 'string' && _queryName.length) {
_query[_queryName] = _selectValue;
} else if (typeof _selectName === 'string' && _selectName.length) {
_query[_selectName] = _selectValue;
};
};
};
$.getJSON(_dataUrl, _query, function(json) {
_selectData = cxSelect.getData(json, _jsonSpace);
cxSelect.buildOption.call(self, index, _selectData);
});
// 使用整合数据
} else if (self.settings.data && typeof self.settings.data === 'object') {
_selectData = self.settings.data;
for (var i = 0; i < index; i++) {
_valueIndex = cxSelect.getIndex(self.selectArray[i][0].selectedIndex, typeof self.selectArray[i].data('required') === 'boolean' ? self.selectArray[i].data('required') : self.settings.required);
if (typeof _selectData[_valueIndex] === 'object' && $.isArray(_selectData[_valueIndex][self.settings.jsonSub]) && _selectData[_valueIndex][self.settings.jsonSub].length) {
_selectData = _selectData[_valueIndex][self.settings.jsonSub];
} else {
_selectData = null;
break;
};
};
cxSelect.buildOption.call(self, index, _selectData);
};
};
// 构建选项列表
cxSelect.buildOption = function(index, data) {
var self = this;
var _select = self.selectArray[index];
var _required = typeof _select.data('required') === 'boolean' ? _select.data('required') : self.settings.required;
var _firstTitle = typeof _select.data('firstTitle') === 'undefined' ? self.settings.firstTitle : _select.data('firstTitle');
var _firstValue = typeof _select.data('firstValue') === 'undefined' ? self.settings.firstValue : _select.data('firstValue');
var _jsonName = typeof _select.data('jsonName') === 'undefined' ? self.settings.jsonName : _select.data('jsonName');
var _jsonValue = typeof _select.data('jsonValue') === 'undefined' ? self.settings.jsonValue : _select.data('jsonValue');
if (!$.isArray(data)) {return};
var _html = !_required ? '<option value="' + String(_firstValue) + '">' + String(_firstTitle) + '</option>' : '';
// 区分标题、值的数据
if (typeof _jsonName === 'string' && _jsonName.length) {
// 无值字段时使用标题作为值
if (typeof _jsonValue !== 'string' || !_jsonValue.length) {
_jsonValue = _jsonName;
};
for (var i = 0, l = data.length; i < l; i++) {
_html += '<option value="' + String(data[i][_jsonValue]) + '">' + String(data[i][_jsonName]) + '</option>';
};
// 数组即为值的数据
} else {
for (var i = 0, l = data.length; i < l; i++) {
_html += '<option value="' + String(data[i]) + '">' + String(data[i]) + '</option>';
};
};
_select.html(_html).prop('disabled', false).css({
'display': '',
'visibility': ''
});
// 初次加载设置默认值
if (typeof _select.attr('data-value') === 'string') {
_select.val(String(_select.attr('data-value'))).removeAttr('data-value');
if (_select[0].selectedIndex < 0) {
_select[0].options[0].selected = true;
};
};
if (_required || _select[0].selectedIndex > 0) {
_select.trigger('change');
};
};
// 改变选择时的处理
cxSelect.selectChange = function(name) {
var self = this;
if (typeof name !== 'string' || !name.length) {return};
var index;
name = name.replace(/\s+/g, ',');
name = ',' + name + ',';
// 获取当前 select 位置
for (var i = 0, l = self.selectArray.length; i < l; i++) {
if (name.indexOf(',' + self.settings.selects[i] + ',') > -1) {
index = i;
break;
};
};
if (typeof index === 'number' && index > -1) {
index += 1;
cxSelect.getOptionData.call(self, index);
};
};
$.cxSelect = function() {
return cxSelect.apply(this, arguments);
};
// 默认值
$.cxSelect.defaults = {
selects: [], // 下拉选框组
url: null, // 列表数据文件路径URL或数组数据
data: null, // 自定义数据
emptyStyle: null, // 无数据状态显示方式
required: false, // 是否为必选
firstTitle: '请选择', // 第一个选项的标题
firstValue: '', // 第一个选项的值
jsonSpace: '', // 数据命名空间
jsonName: 'n', // 数据标题字段名称
jsonValue: '', // 数据值字段名称
jsonSub: 's' // 子集数据字段名称
};
$.fn.cxSelect = function(settings, callback) {
this.each(function(i) {
$.cxSelect(this, settings, callback);
});
return this;
};
}));
/*!
* jQuery cxSelect
* @name jquery.cxselect.js
* @version 1.4.1
* @date 2016-11-02
* @author ciaoca
* @email ciaoca@gmail.com
* @site https://github.com/ciaoca/cxSelect
* @license Released under the MIT license
*/
(function(factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else {
factory(window.jQuery || window.Zepto || window.$);
};
}(function($) {
var cxSelect = function() {
var self = this;
var dom, settings, callback;
// 分配参数
for (var i = 0, l = arguments.length; i < l; i++) {
if (cxSelect.isJquery(arguments[i]) || cxSelect.isZepto(arguments[i])) {
dom = arguments[i];
} else if (cxSelect.isElement(arguments[i])) {
dom = $(arguments[i]);
} else if (typeof arguments[i] === 'function') {
callback = arguments[i];
} else if (typeof arguments[i] === 'object') {
settings = arguments[i];
};
};
var api = new cxSelect.init(dom, settings);
if (typeof callback === 'function') {
callback(api);
};
return api;
};
cxSelect.isElement = function(o){
if (o && (typeof HTMLElement === 'function' || typeof HTMLElement === 'object') && o instanceof HTMLElement) {
return true;
} else {
return (o && o.nodeType && o.nodeType === 1) ? true : false;
};
};
cxSelect.isJquery = function(o){
return (o && o.length && (typeof jQuery === 'function' || typeof jQuery === 'object') && o instanceof jQuery) ? true : false;
};
cxSelect.isZepto = function(o){
return (o && o.length && (typeof Zepto === 'function' || typeof Zepto === 'object') && Zepto.zepto.isZ(o)) ? true : false;
};
cxSelect.getIndex = function(n, required) {
return required ? n : n - 1;
};
cxSelect.getData = function(data, space) {
if (typeof space === 'string' && space.length) {
space = space.split('.');
for (var i = 0, l = space.length; i < l; i++) {
data = data[space[i]];
};
};
return data;
};
cxSelect.init = function(dom, settings) {
var self = this;
if (!cxSelect.isJquery(dom) && !cxSelect.isZepto(dom)) {return};
var theSelect = {
dom: {
box: dom
}
};
self.attach = cxSelect.attach.bind(theSelect);
self.detach = cxSelect.detach.bind(theSelect);
self.setOptions = cxSelect.setOptions.bind(theSelect);
self.clear = cxSelect.clear.bind(theSelect);
theSelect.changeEvent = function() {
cxSelect.selectChange.call(theSelect, this.className);
};
theSelect.settings = $.extend({}, $.cxSelect.defaults, settings, {
url: theSelect.dom.box.data('url'),
emptyStyle: theSelect.dom.box.data('emptyStyle'),
required: theSelect.dom.box.data('required'),
firstTitle: theSelect.dom.box.data('firstTitle'),
firstValue: theSelect.dom.box.data('firstValue'),
jsonSpace: theSelect.dom.box.data('jsonSpace'),
jsonName: theSelect.dom.box.data('jsonName'),
jsonValue: theSelect.dom.box.data('jsonValue'),
jsonSub: theSelect.dom.box.data('jsonSub')
});
var _dataSelects = theSelect.dom.box.data('selects');
if (typeof _dataSelects === 'string' && _dataSelects.length) {
theSelect.settings.selects = _dataSelects.split(',');
};
self.setOptions();
self.attach();
// 使用独立接口获取数据
if (!theSelect.settings.url && !theSelect.settings.data) {
cxSelect.start.apply(theSelect);
// 设置自定义数据
} else if ($.isArray(theSelect.settings.data)) {
cxSelect.start.call(theSelect, theSelect.settings.data);
// 设置 URL通过 Ajax 获取数据
} else if (typeof theSelect.settings.url === 'string' && theSelect.settings.url.length) {
$.getJSON(theSelect.settings.url, function(json) {
cxSelect.start.call(theSelect, json);
});
};
};
// 设置参数
cxSelect.setOptions = function(opts) {
var self = this;
if (opts) {
$.extend(self.settings, opts);
};
// 初次或重设选择器组
if (!$.isArray(self.selectArray) || !self.selectArray.length || (opts && opts.selects)) {
self.selectArray = [];
if ($.isArray(self.settings.selects) && self.settings.selects.length) {
var _tempSelect;
for (var i = 0, l = self.settings.selects.length; i < l; i++) {
_tempSelect = self.dom.box.find('select.' + self.settings.selects[i]);
if (!_tempSelect || !_tempSelect.length) {break};
self.selectArray.push(_tempSelect);
};
};
};
if (opts) {
if (!$.isArray(opts.data) && typeof opts.url === 'string' && opts.url.length) {
$.getJSON(self.settings.url, function(json) {
cxSelect.start.call(self, json);
});
} else {
cxSelect.start.call(self, opts.data);
};
};
};
// 绑定
cxSelect.attach = function() {
var self = this;
if (!self.attachStatus) {
self.dom.box.on('change', 'select', self.changeEvent);
};
if (typeof self.attachStatus === 'boolean') {
cxSelect.start.call(self);
};
self.attachStatus = true;
};
// 移除绑定
cxSelect.detach = function() {
var self = this;
self.dom.box.off('change', 'select', self.changeEvent);
self.attachStatus = false;
};
// 清空选项
cxSelect.clear = function(index) {
var self = this;
var _style = {
display: '',
visibility: ''
};
index = isNaN(index) ? 0 : index;
// 清空后面的 select
for (var i = index, l = self.selectArray.length; i < l; i++) {
self.selectArray[i].empty().prop('disabled', true);
if (self.settings.emptyStyle === 'none') {
_style.display = 'none';
} else if (self.settings.emptyStyle === 'hidden') {
_style.visibility = 'hidden';
};
self.selectArray[i].css(_style);
};
};
cxSelect.start = function(data) {
var self = this;
if ($.isArray(data)) {
self.settings.data = cxSelect.getData(data, self.settings.jsonSpace);
};
if (!self.selectArray.length) {return};
// 保存默认值
for (var i = 0, l = self.selectArray.length; i < l; i++) {
if (typeof self.selectArray[i].attr('data-value') !== 'string' && self.selectArray[i][0].options.length) {
self.selectArray[i].attr('data-value', self.selectArray[i].val());
};
};
if (self.settings.data || (typeof self.selectArray[0].data('url') === 'string' && self.selectArray[0].data('url').length)) {
cxSelect.getOptionData.call(self, 0);
} else {
self.selectArray[0].prop('disabled', false).css({
'display': '',
'visibility': ''
});
};
};
// 获取选项数据
cxSelect.getOptionData = function(index) {
var self = this;
if (typeof index !== 'number' || isNaN(index) || index < 0 || index >= self.selectArray.length) {return};
var _indexPrev = index - 1;
var _select = self.selectArray[index];
var _selectData;
var _valueIndex;
var _dataUrl = _select.data('url');
var _jsonSpace = typeof _select.data('jsonSpace') === 'undefined' ? self.settings.jsonSpace : _select.data('jsonSpace');
var _query = {};
var _queryName;
var _selectName;
var _selectValue;
cxSelect.clear.call(self, index);
// 使用独立接口
if (typeof _dataUrl === 'string' && _dataUrl.length) {
if (index > 0) {
for (var i = 0, j = 1; i < index; i++, j++) {
_queryName = self.selectArray[j].data('queryName');
_selectName = self.selectArray[i].attr('name');
_selectValue = self.selectArray[i].val();
if (typeof _queryName === 'string' && _queryName.length) {
_query[_queryName] = _selectValue;
} else if (typeof _selectName === 'string' && _selectName.length) {
_query[_selectName] = _selectValue;
};
};
};
$.getJSON(_dataUrl, _query, function(json) {
_selectData = cxSelect.getData(json, _jsonSpace);
cxSelect.buildOption.call(self, index, _selectData);
});
// 使用整合数据
} else if (self.settings.data && typeof self.settings.data === 'object') {
_selectData = self.settings.data;
for (var i = 0; i < index; i++) {
_valueIndex = cxSelect.getIndex(self.selectArray[i][0].selectedIndex, typeof self.selectArray[i].data('required') === 'boolean' ? self.selectArray[i].data('required') : self.settings.required);
if (typeof _selectData[_valueIndex] === 'object' && $.isArray(_selectData[_valueIndex][self.settings.jsonSub]) && _selectData[_valueIndex][self.settings.jsonSub].length) {
_selectData = _selectData[_valueIndex][self.settings.jsonSub];
} else {
_selectData = null;
break;
};
};
cxSelect.buildOption.call(self, index, _selectData);
};
};
// 构建选项列表
cxSelect.buildOption = function(index, data) {
var self = this;
var _select = self.selectArray[index];
var _required = typeof _select.data('required') === 'boolean' ? _select.data('required') : self.settings.required;
var _firstTitle = typeof _select.data('firstTitle') === 'undefined' ? self.settings.firstTitle : _select.data('firstTitle');
var _firstValue = typeof _select.data('firstValue') === 'undefined' ? self.settings.firstValue : _select.data('firstValue');
var _jsonName = typeof _select.data('jsonName') === 'undefined' ? self.settings.jsonName : _select.data('jsonName');
var _jsonValue = typeof _select.data('jsonValue') === 'undefined' ? self.settings.jsonValue : _select.data('jsonValue');
if (!$.isArray(data)) {return};
var _html = !_required ? '<option value="' + String(_firstValue) + '">' + String(_firstTitle) + '</option>' : '';
// 区分标题、值的数据
if (typeof _jsonName === 'string' && _jsonName.length) {
// 无值字段时使用标题作为值
if (typeof _jsonValue !== 'string' || !_jsonValue.length) {
_jsonValue = _jsonName;
};
for (var i = 0, l = data.length; i < l; i++) {
_html += '<option value="' + String(data[i][_jsonValue]) + '">' + String(data[i][_jsonName]) + '</option>';
};
// 数组即为值的数据
} else {
for (var i = 0, l = data.length; i < l; i++) {
_html += '<option value="' + String(data[i]) + '">' + String(data[i]) + '</option>';
};
};
_select.html(_html).prop('disabled', false).css({
'display': '',
'visibility': ''
});
// 初次加载设置默认值
if (typeof _select.attr('data-value') === 'string') {
_select.val(String(_select.attr('data-value'))).removeAttr('data-value');
if (_select[0].selectedIndex < 0) {
_select[0].options[0].selected = true;
};
};
if (_required || _select[0].selectedIndex > 0) {
_select.trigger('change');
};
};
// 改变选择时的处理
cxSelect.selectChange = function(name) {
var self = this;
if (typeof name !== 'string' || !name.length) {return};
var index;
name = name.replace(/\s+/g, ',');
name = ',' + name + ',';
// 获取当前 select 位置
for (var i = 0, l = self.selectArray.length; i < l; i++) {
if (name.indexOf(',' + self.settings.selects[i] + ',') > -1) {
index = i;
break;
};
};
if (typeof index === 'number' && index > -1) {
index += 1;
cxSelect.getOptionData.call(self, index);
};
};
$.cxSelect = function() {
return cxSelect.apply(this, arguments);
};
// 默认值
$.cxSelect.defaults = {
selects: [], // 下拉选框组
url: null, // 列表数据文件路径URL或数组数据
data: null, // 自定义数据
emptyStyle: null, // 无数据状态显示方式
required: false, // 是否为必选
firstTitle: '请选择', // 第一个选项的标题
firstValue: '', // 第一个选项的值
jsonSpace: '', // 数据命名空间
jsonName: 'n', // 数据标题字段名称
jsonValue: '', // 数据值字段名称
jsonSub: 's' // 子集数据字段名称
};
$.fn.cxSelect = function(settings, callback) {
this.each(function(i) {
$.cxSelect(this, settings, callback);
});
return this;
};
}));

File diff suppressed because one or more lines are too long

@ -1,21 +1,21 @@
/*!
* jQuery Validation Plugin v1.19.1
*
* https://jqueryvalidation.org/
*
* Copyright (c) 2019 Jörn Zaefferer
* Released under the MIT license
*/
(function( factory ) {
if ( typeof define === "function" && define.amd ) {
define( ["jquery"], factory );
} else if (typeof module === "object" && module.exports) {
module.exports = factory( require( "jquery" ) );
} else {
factory( jQuery );
}
}(function( $ ) {
/*!
* jQuery Validation Plugin v1.19.1
*
* https://jqueryvalidation.org/
*
* Copyright (c) 2019 Jörn Zaefferer
* Released under the MIT license
*/
(function( factory ) {
if ( typeof define === "function" && define.amd ) {
define( ["jquery"], factory );
} else if (typeof module === "object" && module.exports) {
module.exports = factory( require( "jquery" ) );
} else {
factory( jQuery );
}
}(function( $ ) {
$.extend( $.fn, {
// https://jqueryvalidation.org/validate/
@ -1610,7 +1610,7 @@ $.extend( $.validator, {
}
} );
// Ajax mode: abort
// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()
@ -1646,5 +1646,5 @@ if ( $.ajaxPrefilter ) {
return ajax.apply( this, arguments );
};
}
return $;
return $;
}));

@ -25,7 +25,7 @@ a {
}
}
input.danger {
textarea.danger, input.danger {
border-color: #dc3545!important;
}
@ -43,4 +43,8 @@ label.error {
height: calc(1.5em + 0.75rem + 2px)
}
}
}
.flex-1 {
flex: 1;
}

@ -100,5 +100,11 @@
font-size: 24px;
}
}
.nav-tabs {
.nav-link {
padding: 0.5rem 2rem;
}
}
}

@ -0,0 +1,9 @@
.admins-identity-authentications-index-page {
.identity-authentication-list-container {
span {
&.apply-status-1 { color: #28a745; }
&.apply-status-2 { color: #dc3545; }
&.apply-status-3 { color: #6c757d; }
}
}
}

@ -0,0 +1,9 @@
.admins-professional-authentications-index-page {
.professional-authentication-list-container {
span {
&.apply-status-1 { color: #28a745; }
&.apply-status-2 { color: #dc3545; }
&.apply-status-3 { color: #6c757d; }
}
}
}

@ -0,0 +1,9 @@
.admins-shixun-authorizations-index-page {
.shixun-authorization-list-container {
span {
&.apply-status-1 { color: #28a745; }
&.apply-status-2 { color: #dc3545; }
&.apply-status-3 { color: #6c757d; }
}
}
}

@ -0,0 +1,9 @@
.admins-subject-authorizations-index-page {
.subject-authorization-list-container {
span {
&.apply-status-1 { color: #28a745; }
&.apply-status-2 { color: #dc3545; }
&.apply-status-3 { color: #6c757d; }
}
}
}

@ -0,0 +1,26 @@
class Admins::IdentityAuthenticationsController < Admins::BaseController
def index
params[:status] ||= 'pending'
applies = Admins::ApplyUserAuthenticationQuery.call(params.merge(type: 1))
@applies = paginate applies.preload(user: { user_extension: [:school, :department] })
end
def agree
Admins::IdentityAuths::AgreeApplyService.call(current_apply)
render_success_js
end
def refuse
Admins::IdentityAuths::RefuseApplyService.call(current_apply, params)
render_success_js
end
private
def current_apply
@_current_apply ||= ApplyUserAuthentication.real_name_auth.find(params[:id])
end
end

@ -0,0 +1,26 @@
class Admins::ProfessionalAuthenticationsController < Admins::BaseController
def index
params[:status] ||= 'pending'
applies = Admins::ApplyUserAuthenticationQuery.call(params.merge(type: 2))
@applies = paginate applies.preload(user: { user_extension: [:school, :department] })
end
def agree
Admins::ProfessionalAuths::AgreeApplyService.call(current_apply)
render_success_js
end
def refuse
Admins::ProfessionalAuths::RefuseApplyService.call(current_apply, params)
render_success_js
end
private
def current_apply
@_current_apply ||= ApplyUserAuthentication.professional_auth.find(params[:id])
end
end

@ -0,0 +1,48 @@
class Admins::ShixunAuthorizationsController < Admins::BaseController
def index
params[:status] ||= 'pending'
applies = ApplyAction.where(container_type: 'ApplyShixun')
status =
case params[:status]
when 'pending' then 0
when 'processed' then [1, 2]
when 'agreed' then 1
when 'refused' then 2
else 0
end
applies = applies.where(status: status) if status.present?
# 关键字模糊查询
keyword = params[:keyword].to_s.strip
if keyword.present?
applies = applies.joins('JOIN shixuns ON shixuns.id = apply_actions.container_id')
.where('shixuns.name LIKE :keyword', keyword: "%#{keyword}%")
end
applies = applies.order(updated_at: :desc)
@applies = paginate applies.includes(user: :user_extension)
shixun_ids = @applies.map(&:container_id)
@shixun_map = Shixun.where(id: shixun_ids).each_with_object({}) { |s, h| h[s.id] = s }
end
def agree
Admins::ShixunAuths::AgreeApplyService.call(current_apply, current_user)
render_success_js
end
def refuse
Admins::ShixunAuths::RefuseApplyService.call(current_apply, current_user, params)
render_success_js
end
private
def current_apply
@_current_apply ||= ApplyAction.where(container_type: 'ApplyShixun').find(params[:id])
end
end

@ -0,0 +1,49 @@
class Admins::SubjectAuthorizationsController < Admins::BaseController
def index
params[:status] ||= 'pending'
applies = ApplyAction.where(container_type: 'ApplySubject')
status =
case params[:status]
when 'pending' then 0
when 'processed' then [1, 2]
when 'agreed' then 1
when 'refused' then 2
else 0
end
applies = applies.where(status: status) if status.present?
# 关键字模糊查询
keyword = params[:keyword].to_s.strip
if keyword.present?
applies = applies.joins('JOIN subjects ON subjects.id = apply_actions.container_id')
.where('subjects.name LIKE :keyword', keyword: "%#{keyword}%")
end
applies = applies.order(updated_at: :desc)
@applies = paginate applies.includes(user: :user_extension)
subject_ids = @applies.map(&:container_id)
@subject_map = Subject.where(id: subject_ids).each_with_object({}) { |s, h| h[s.id] = s }
@challenge_count_map = Challenge.joins(shixun: :stage_shixuns).where(st: 0, stage_shixuns: { subject_id: subject_ids}).group('subject_id').count
end
def agree
Admins::SubjectAuths::AgreeApplyService.call(current_apply, current_user)
render_success_js
end
def refuse
Admins::SubjectAuths::RefuseApplyService.call(current_apply, current_user, params)
render_success_js
end
private
def current_apply
@_current_apply ||= ApplyAction.where(container_type: 'ApplySubject').find(params[:id])
end
end

@ -2,7 +2,7 @@ module Admins::ErrorRescueHandler
extend ActiveSupport::Concern
included do
rescue_from Exception, Educoder::TipException do |e|
rescue_from Exception do |e|
raise e if Rails.env.development?
Util.logger_error e

@ -28,7 +28,7 @@ module Admins::RenderHelper
def internal_server_error
respond_to do |format|
format.html { render 'admins/shared/500' }
format.js { render_js_error(message) }
format.js { render_js_error('系统错误') }
format.json { render status: 500, json: { message: '系统错误' } }
end
end
@ -40,6 +40,7 @@ module Admins::RenderHelper
def render_delete_success
render_js_template 'admins/shared/delete'
end
alias_method :render_success_js, :render_delete_success
def render_js_error(message)
render_js_template 'admins/shared/error', locals: { message: message }

@ -34,8 +34,7 @@ module GitHelper
rescue Exception => e
Rails.logger.error(e.message)
Rails.logger.error("#####__________文档内容获取异常")
# raise Educoder::TipException.new("文档内容获取异常")
raise Educoder::TipException.new("文档内容获取异常")
end
end

@ -26,14 +26,14 @@ class CoursesController < ApplicationController
:base_info, :get_historical_courses, :create_group_by_importing_file,
:attahcment_category_list,:export_member_scores_excel, :duplicate_course,
:switch_to_teacher, :switch_to_assistant, :switch_to_student, :exit_course,
:informs, :update_informs, :online_learning, :update_task_position, :tasks_list, :join_excellent_course]
:informs, :update_informs, :new_informs, :online_learning, :update_task_position, :tasks_list, :join_excellent_course]
before_action :user_course_identity, except: [:join_excellent_course, :index, :create, :new, :apply_to_join_course,
:search_course_list, :get_historical_course_students, :mine, :search_slim, :board_list]
before_action :teacher_allowed, only: [:update, :destroy, :settings, :search_teacher_candidate,
:transfer_to_course_group, :delete_from_course,
:search_users, :add_students_by_search, :get_historical_courses, :add_teacher_popup, :add_teacher]
before_action :admin_allowed, only: [:set_invite_code_halt, :set_public_or_private, :change_course_admin,
:set_course_group, :create_group_by_importing_file, :update_informs,
:set_course_group, :create_group_by_importing_file, :update_informs, :new_informs,
:update_task_position, :tasks_list]
before_action :teacher_or_admin_allowed, only: [:graduation_group_list, :create_graduation_group, :join_graduation_group,
:change_course_teacher, :export_member_scores_excel, :course_group_list,
@ -42,6 +42,7 @@ class CoursesController < ApplicationController
before_action :find_board, only: :board_list
before_action :validate_page_size, only: :mine
before_action :course_tasks, only: [:tasks_list, :update_task_position]
before_action :validate_inform_params, only: [:update_informs, :new_informs]
if RUBY_PLATFORM =~ /linux/
require 'simple_xlsx_reader'
@ -159,7 +160,7 @@ class CoursesController < ApplicationController
CourseMember.create!(course_id: @course.id, user_id: s_member.user_id, role: 2)
end
Inform.create(container: @course, description: @subject.learning_notes)
Inform.create(container: @course, description: @subject.learning_notes, name: "学习须知")
end
course_module_types = params[:course_module_types]
@ -234,14 +235,20 @@ class CoursesController < ApplicationController
end
def informs
@informs = @course.informs
end
def update_informs
tip_exception("公告内容不能为空") if params[:description].blank?
inform = @course.inform || Inform.new(container: @course)
def new_informs
inform = Inform.new(container: @course)
inform.name = params[:name]
inform.description = params[:description]
inform.save!
normal_status("创建成功")
end
def update_informs
inform = @course.informs.find_by(id: params[:inform_id])
inform.update_attributes!(name: params[:name], description: params[:description])
normal_status("更新成功")
end
@ -700,12 +707,12 @@ class CoursesController < ApplicationController
if order == 1
# REDO:Extension
@students = @students.includes(user: :user_extension).order("user_extensions.student_id, course_members.id")
@students = @students.includes(user: :user_extension).order("user_extensions.student_id, users.login")
elsif order == 2
@students = @students.includes(:course_group).order("course_groups.position, course_members.id")
@students = @students.includes(:course_group).order("course_groups.position, users.login")
else
# REDO:Extension
@students = @students.includes(user: :user_extension).order("user_extensions.student_id, course_members.id")
@students = @students.includes(user: :user_extension).order("user_extensions.student_id, users.login")
end
if course_group_id.present?
@ -1259,6 +1266,11 @@ class CoursesController < ApplicationController
end
end
def validate_inform_params
tip_exception("公告标题不能为空") if params[:name].blank?
tip_exception("公告内容不能为空") if params[:description].blank?
end
# def find_container
# case params[:container_type]
# when 'shixun_homework', 'common_homework', 'group_homework'

@ -688,7 +688,8 @@ class ExercisesController < ApplicationController
def publish
tip_exception("缺少截止时间参数") if params[:end_time].blank?
tip_exception("截止时间不能早于当前时间") if params[:end_time] <= strf_time(Time.now)
tip_exception("截止时间不能晚于课堂结束时间") if @course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day)
tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day)
ActiveRecord::Base.transaction do
begin
check_ids = Exercise.where(id: params[:check_ids])

@ -13,8 +13,8 @@ class GamesController < ApplicationController
def show
uid_logger("--games show start")
# 防止评测中途ajaxE被取消;3改成0是为了处理首次进入下一关的问题
@game.update_attribute(:status, 0) if @game.status == 1
@game.update_attributes(status: 0, open_time: Time.now) if @game.status == 3
update_game_parameter(@game)
game_challenge = Challenge.base_attrs.find(@game.challenge_id)
# 选择题类型的实训关卡总分
@ -964,4 +964,13 @@ class GamesController < ApplicationController
nil
end
end
# 更新关卡状态和一些学习进度
def update_game_parameter game
game.update_attribute(:status, 0) if game.status == 1
game.update_attributes(status: 0, open_time: Time.now) if game.status == 3
# 开启实训更新myshixuns的时间方便跟踪用于的学习进度。
game.myshixun.update_column(:updated_at, Time.now)
end
end

@ -322,6 +322,8 @@ class GraduationTasksController < ApplicationController
def publish_task
tip_exception("缺少截止时间参数") if params[:end_time].blank?
tip_exception("截止时间必须晚于当前时间") if params[:end_time] <= strf_time(Time.now)
tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && params[:end_time] > @course.end_date.end_of_day
ActiveRecord::Base.transaction do
begin
@ -397,8 +399,8 @@ class GraduationTasksController < ApplicationController
tip_exception("发布时间不能早于当前时间") if params[:publish_time] <= Time.now.strftime("%Y-%m-%d %H:%M:%S")
tip_exception("截止时间不能早于当前时间") if params[:end_time] <= Time.now.strftime("%Y-%m-%d %H:%M:%S")
tip_exception("截止时间不能早于发布时间") if params[:publish_time] > params[:end_time]
tip_exception("截止时间不能早于课堂结束时间") if @course.end_date.present? &&
params[:end_time] > @course.end_date.end_of_day
tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && params[:end_time] > @course.end_date.end_of_day
@task.publish_time = params[:publish_time]
@task.end_time = params[:end_time]
@ -410,7 +412,8 @@ class GraduationTasksController < ApplicationController
elsif @task.status < 2
tip_exception("截止时间不能为空") if params[:end_time].blank?
tip_exception("截止时间不能早于当前时间") if params[:end_time] <= Time.now.strftime("%Y-%m-%d %H:%M:%S")
tip_exception("截止时间不能早于课堂结束时间") if @course.end_date.present? && params[:end_time] > @course.end_date.end_of_day
tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && params[:end_time] > @course.end_date.end_of_day
@task.end_time = params[:end_time]
end
@ -421,8 +424,8 @@ class GraduationTasksController < ApplicationController
if params[:allow_late].to_i == 1
tip_exception("补交结束时间不能为空") if params[:late_time].blank?
tip_exception("补交结束时间不能早于截止时间") if params[:late_time] <= @task.end_time
tip_exception("补交结束时间不能晚于课堂结束时间") if @course.end_date.present? && params[:late_time] >
@course.end_date.end_of_day
tip_exception("补交结束时间不能晚于课堂结束时间#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && params[:late_time] > @course.end_date.end_of_day
tip_exception("迟交扣分应为正整数") if params[:late_penalty] && params[:late_penalty].to_i < 0
@task.allow_late = true

@ -447,7 +447,8 @@ class HomeworkCommonsController < ApplicationController
tip_exception("发布时间不能早于当前时间") if params[:publish_time] <= Time.now.strftime("%Y-%m-%d %H:%M:%S")
tip_exception("截止时间不能早于当前时间") if params[:end_time] <= Time.now.strftime("%Y-%m-%d %H:%M:%S")
tip_exception("截止时间不能早于发布时间") if params[:publish_time] > params[:end_time]
tip_exception("截止时间不能晚于课堂结束时间") if @course.end_date.present? && params[:end_time] > @course.end_date.end_of_day
tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && params[:end_time] > @course.end_date.end_of_day
@homework.unified_setting = 1
@homework.homework_group_settings.destroy_all
@ -469,7 +470,8 @@ class HomeworkCommonsController < ApplicationController
tip_exception("发布时间不能早于当前时间") if setting[:publish_time] <= strf_time(Time.now)
tip_exception("截止时间不能早于当前时间") if setting[:end_time] <= strf_time(Time.now)
tip_exception("截止时间不能早于发布时间") if setting[:publish_time] > setting[:end_time]
tip_exception("截止时间不能晚于课堂结束时间") if @course.end_date.present? && setting[:end_time] > @course.end_date.end_of_day
tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && setting[:end_time] > @course.end_date.end_of_day
publish_time = setting[:publish_time] == "" ? Time.now : setting[:publish_time]
@ -503,7 +505,8 @@ class HomeworkCommonsController < ApplicationController
if @homework.end_time > Time.now && @homework.unified_setting
tip_exception("截止时间不能为空") if params[:end_time].blank?
tip_exception("截止时间不能早于当前时间") if params[:end_time] <= strf_time(Time.now)
tip_exception("截止时间不能晚于课堂结束时间") if @course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day)
tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day)
@homework.end_time = params[:end_time]
@ -521,7 +524,8 @@ class HomeworkCommonsController < ApplicationController
tip_exception("截止时间不能早于等于当前时间") if setting[:end_time] <= strf_time(Time.now)
tip_exception("截止时间不能早于发布时间") if setting[:publish_time] > setting[:end_time]
tip_exception("截止时间不能晚于课堂结束时间") if @course.end_date.present? && setting[:end_time] > strf_time(@course.end_date.end_of_day)
tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && setting[:end_time] > strf_time(@course.end_date.end_of_day)
group_settings.none_published.update_all(publish_time: setting[:publish_time])
group_settings.none_end.update_all(end_time: setting[:end_time])
@ -539,7 +543,7 @@ class HomeworkCommonsController < ApplicationController
current_late_penalty = @homework.late_penalty
if params[:allow_late]
tip_exception("补交结束时间必须晚于截止时间") if params[:late_time] <= strf_time(@homework.end_time)
tip_exception("补交结束时间不能晚于课堂结束时间") if @course.end_date.present? && params[:late_time] >
tip_exception("补交结束时间不能晚于课堂结束时间#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if @course.end_date.present? && params[:late_time] >
strf_time(@course.end_date.end_of_day)
tip_exception("迟交扣分不能小于0") if params[:late_penalty] && params[:late_penalty].to_i < 0
@ -647,7 +651,7 @@ class HomeworkCommonsController < ApplicationController
tip_exception("匿评开启时间不能早于截止时间") if params[:evaluation_start] < strf_time(@homework.end_time)
tip_exception("匿评结束时间不能为空") if params[:evaluation_end].blank?
tip_exception("匿评截止时间必须晚于匿评开启时间") if params[:evaluation_end] <= params[:evaluation_start]
tip_exception("匿评截止时间不能晚于课堂结束时间") if @course.end_date.present? && params[:evaluation_end] >
tip_exception("匿评截止时间不能晚于课堂结束时间#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if @course.end_date.present? && params[:evaluation_end] >
strf_time(@course.end_date.end_of_day)
tip_exception("匿评数必须为正整数") if params[:evaluation_num].blank? || params[:evaluation_num].to_i < 1
tip_exception("缺评扣分不能为空") if params[:absence_penalty].blank?
@ -675,7 +679,7 @@ class HomeworkCommonsController < ApplicationController
tip_exception("匿评结束时间不能为空") if @homework.anonymous_comment && params[:evaluation_end].blank?
tip_exception("匿评截止时间必须晚于匿评开启时间") if @homework.anonymous_comment &&
params[:evaluation_end] <= strf_time(@homework_detail_manual.evaluation_start)
tip_exception("匿评截止时间不能晚于课堂结束时间") if @homework.anonymous_comment &&
tip_exception("匿评截止时间不能晚于课堂结束时间#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if @homework.anonymous_comment &&
@course.end_date.present? && params[:evaluation_end] > strf_time(@course.end_date.end_of_day)
@homework_detail_manual.evaluation_end = !@homework.anonymous_comment ? nil : params[:evaluation_end]
@ -701,7 +705,7 @@ class HomeworkCommonsController < ApplicationController
tip_exception("匿评申诉结束时间不能为空") if @homework.anonymous_appeal && params[:appeal_time].blank?
tip_exception("匿评开启时间不能早于匿评截止时间") if @homework.anonymous_appeal &&
params[:appeal_time] <= strf_time(@homework_detail_manual.evaluation_end)
tip_exception("匿评申诉结束不能晚于课堂结束时间") if @homework.anonymous_appeal &&
tip_exception("匿评申诉结束不能晚于课堂结束时间#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if @homework.anonymous_appeal &&
@course.end_date.present? && params[:appeal_time] > strf_time(@course.end_date.end_of_day)
@homework_detail_manual.appeal_time = @homework.anonymous_appeal ? params[:appeal_time] : nil
@ -1054,7 +1058,8 @@ class HomeworkCommonsController < ApplicationController
tip_exception("请至少选择一个分班") if params[:group_ids].blank? && @course.course_groups.size != 0
tip_exception("缺少截止时间参数") if params[:end_time].blank?
tip_exception("截止时间不能早于当前时间") if params[:end_time] <= strf_time(Time.now)
tip_exception("截止时间不能晚于课堂结束时间") if @course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day)
tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day)
homeworks = @course.homework_commons.where(id: params[:homework_ids])
homeworks = homeworks.includes(:homework_group_settings, :homework_detail_manual)

@ -246,7 +246,8 @@ class PollsController < ApplicationController
def publish
tip_exception("缺少截止时间参数") if params[:end_time].blank?
tip_exception("截止时间不能早于当前时间") if params[:end_time] <= strf_time(Time.now)
tip_exception("截止时间不能晚于课堂结束时间") if @course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day)
tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day)
ActiveRecord::Base.transaction do
begin
check_ids = Poll.where(id: params[:check_ids])

@ -109,7 +109,7 @@ class ShixunsController < ApplicationController
can_fork = current_user.is_certification_teacher || current_user.admin?
unless can_fork
@can_fork = {can_fork: "已经职业认证的教师才能fork实训",
certi_url: edu_setting('old_edu_host') + "/account/professional_certification"}
certi_url: "/account/certification"}
end
@current_myshixun = @shixun.current_myshixun(current_user)
if @shixun.fork_from

@ -0,0 +1,102 @@
module Admins::BaseHelper
def sidebar_item_group(url, text, **opts)
link_opts = url.start_with?('/') ? {} : { 'data-toggle': 'collapse', 'aria-expanded': false }
content =
link_to url, link_opts do
content_tag(:i, '', class: "fa fa-#{opts[:icon]}", 'data-toggle': 'tooltip', 'data-placement': 'right', 'data-boundary': 'window', title: text) +
content_tag(:span, text)
end
content +=
content_tag(:ul, id: url[1..-1], class: 'collapse list-unstyled', "data-parent": '#sidebar') do
yield
end
raw content
end
def sidebar_item(url, text, **opts)
content =
link_to url, 'data-controller': opts[:controller] do
content_tag(:i, '', class: "fa fa-#{opts[:icon]}", 'data-toggle': 'tooltip', 'data-placement': 'right', 'data-boundary': 'window', title: text) +
content_tag(:span, text)
end
raw content
end
def admin_sidebar_controller
key = params[:controller].to_s.gsub(/\//, '-')
SidebarUtil.controller_name(key) || key
end
def define_admin_breadcrumbs(&block)
content_for(:setup_admin_breadcrumb, &block)
end
def add_admin_breadcrumb(text, url = nil)
@_admin_breadcrumbs ||= []
@_admin_breadcrumbs << OpenStruct.new(text: text, url: url)
end
def display_text(str, default = '--')
str.presence || default
end
def overflow_hidden_span(text, width: 300)
opts = { class: 'd-inline-block text-truncate', style: "max-width: #{width}px" }
opts.merge!('data-toggle': 'tooltip', title: text) if text != '--'
content_tag(:span, text, opts)
end
def sort_tag(content = '', **opts)
options = {}
options[:sort_by] = opts.delete(:name)
is_current_sort = params[:sort_by].to_s == options[:sort_by]
options[:sort_direction] = is_current_sort && params[:sort_direction].to_s == 'desc' ? 'asc' : 'desc'
path = opts.delete(:path) + "?" + unsafe_params.merge(options).to_query
arrow_class = case params[:sort_direction].to_s
when 'desc' then 'fa-sort-amount-desc'
when 'asc' then 'fa-sort-amount-asc'
else ''
end
opts[:style] = "#{opts[:style]} ;position: relative;"
content_tag(:span, opts) do
link_to path, remote: true do
content = content_tag(:span) { yield } if block_given?
content += content_tag(:i, '', class: "fa color-light-green ml-1 #{arrow_class}", style: 'position: absolute;top:0;') if is_current_sort
raw content
end
end
end
def javascript_void_link(name, **opts)
raw link_to(name, 'javascript:void(0)', opts)
end
def agree_link(name, url, **opts)
klass = ['action agree-action', opts.delete(:class)].compact.join(' ')
refresh_url_data = "refresh_url=#{CGI::escape(request.fullpath)}"
url = url + (url.index('?') ? '&' : '?') + refresh_url_data
raw link_to(name, url, { method: :post, remote: true, class: klass, 'data-confirm': '确认审核通过?'}.merge(opts))
end
def delete_link(name, url, **opts)
klass = ['action delete-action', opts.delete(:class)].compact.join(' ')
refresh_url_data = "refresh_url=#{CGI::escape(request.fullpath)}"
url = url + (url.index('?') ? '&' : '?') + refresh_url_data
raw link_to(name, url, { method: :delete, remote: true, class: klass, 'data-confirm': '确认删除?'}.merge(opts))
end
def unsafe_params
params.except(:controller, :action).to_unsafe_h
end
end

@ -158,10 +158,22 @@ module ApplicationHelper
disk_auth_filename('UserAuthentication', source_id, 'ID')
end
def auth_file_url(source_type, source_id, type)
File.join('/images', relative_path, source_type, "#{source_id}#{type}")
end
def real_name_auth_file_url(source_id)
auth_file_url('UserAuthentication', source_id, 'ID')
end
def disk_professional_auth_filename(source_id)
disk_auth_filename('UserAuthentication', source_id, 'PRO')
end
def professional_auth_file_url(source_id)
auth_file_url('UserAuthentication', source_id, 'PRO')
end
def shixun_url_to_avatar(shixun)
if File.exist?(disk_filename(shixun.class, shixun.id))
File.join("images/#{relative_path}", "#{shixun.class}", "#{shixun.id}")
@ -386,95 +398,6 @@ module ApplicationHelper
m_t&.include?("src=\"") ? m_t&.gsub("src=\"","src=\"#{origin_url}") : m_t
end
# =========== Admin Helpers Begin ===========
def sidebar_item_group(url, text, **opts)
link_opts = url.start_with?('/') ? {} : { 'data-toggle': 'collapse', 'aria-expanded': false }
content =
link_to url, link_opts do
content_tag(:i, '', class: "fa fa-#{opts[:icon]}", 'data-toggle': 'tooltip', 'data-placement': 'right', 'data-boundary': 'window', title: text) +
content_tag(:span, text)
end
content +=
content_tag(:ul, id: url[1..-1], class: 'collapse list-unstyled', "data-parent": '#sidebar') do
yield
end
raw content
end
def sidebar_item(url, text, **opts)
content =
link_to url, 'data-controller': opts[:controller] do
content_tag(:i, '', class: "fa fa-#{opts[:icon]}", 'data-toggle': 'tooltip', 'data-placement': 'right', 'data-boundary': 'window', title: text) +
content_tag(:span, text)
end
raw content
end
def admin_sidebar_controller
key = params[:controller].to_s.gsub(/\//, '-')
SidebarUtil.controller_name(key) || key
end
def define_admin_breadcrumbs(&block)
content_for(:setup_admin_breadcrumb, &block)
end
def add_admin_breadcrumb(text, url = nil)
@_admin_breadcrumbs ||= []
@_admin_breadcrumbs << OpenStruct.new(text: text, url: url)
end
def display_text(str, default = '--')
str.presence || default
end
def overflow_hidden_span(text, width: 300)
opts = { class: 'd-inline-block text-truncate', style: "max-width: #{width}px" }
opts.merge!('data-toggle': 'tooltip', title: text) if text != '--'
content_tag(:span, text, opts)
end
def sort_tag(content = '', **opts)
options = {}
options[:sort_by] = opts.delete(:name)
is_current_sort = params[:sort_by].to_s == options[:sort_by]
options[:sort_direction] = is_current_sort && params[:sort_direction].to_s == 'desc' ? 'asc' : 'desc'
path = opts.delete(:path) + "?" + params.slice(:action, :controller).merge(options).to_unsafe_h.to_query
arrow_class = case params[:sort_direction].to_s
when 'desc' then 'fa-sort-amount-desc'
when 'asc' then 'fa-sort-amount-asc'
else ''
end
opts[:style] = "#{opts[:style]} ;position: relative;"
content_tag(:span, opts) do
link_to path, remote: true do
content = content_tag(:span) { yield } if block_given?
content += content_tag(:i, '', class: "fa color-light-green ml-1 #{arrow_class}", style: 'position: absolute;top:0;') if is_current_sort
raw content
end
end
end
def javascript_void_link(name, **opts)
raw link_to(name, 'javascript:void(0)', opts)
end
def delete_link(name, url, **opts)
klass = ['action delete-action', opts.delete(:class)].compact.join(' ')
refresh_url_data = "refresh_url=#{CGI::escape(request.fullpath)}"
url = url + (url.index('?') ? '&' : '?') + refresh_url_data
raw link_to(name, url, { method: :delete, remote: true, class: klass, 'data-confirm': '确认删除?'}.merge(opts))
end
# =========== Admin Helpers End ===========
end

@ -32,7 +32,7 @@ module ExercisesHelper
ques_score = 0.0
end
else
ques_score = answers_content.select(:score).pluck(:score).sum
ques_score = answers_content.score_reviewed.select(:score).pluck(:score).sum
end
if ques_score >= q.question_score #满分作答为正确
@ -484,7 +484,11 @@ module ExercisesHelper
game_code = game_challenge
code = game_code.try(:new_code)
else
code = git_fle_content(game.myshixun.repo_path,cha_path)
begin
code = git_fle_content(game.myshixun.repo_path,cha_path)
rescue
code = ""
end
end
end
if ex_shixun_answer_content.blank? #把关卡的答案存入试卷的实训里
@ -554,7 +558,7 @@ module ExercisesHelper
standard_answer = standard_answer.first.to_s.split("").map(&:to_i)
end
if user_answer_content == standard_answer #答案一致,多选或单选才给分,答案不对不给分
if user_answer_content == standard_answer.sort #答案一致,多选或单选才给分,答案不对不给分
if standard_answer.size > 0
q_score_1 = q.question_score
else

@ -0,0 +1,40 @@
module Util::FileManage
module_function
# 不同的类型扩展不同的目录
def relative_path
"avatars"
end
def storage_path
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}")
end
def disk_auth_filename(source_type, source_id, type)
File.join(storage_path, "#{source_type}", "#{source_id}#{type}")
end
def disk_real_name_auth_filename(source_id)
disk_auth_filename('UserAuthentication', source_id, 'ID')
end
def auth_file_url(source_type, source_id, type)
File.join('/images', relative_path, source_type, "#{source_id}#{type}")
end
def real_name_auth_file_url(source_id)
auth_file_url('UserAuthentication', source_id, 'ID')
end
def disk_professional_auth_filename(source_id)
disk_auth_filename('UserAuthentication', source_id, 'PRO')
end
def professional_auth_file_url(source_id)
auth_file_url('UserAuthentication', source_id, 'PRO')
end
end

@ -1,8 +1,16 @@
# 申请消息
class ApplyAction < ApplicationRecord
belongs_to :user
has_many :tidings, :as => :container, :dependent => :destroy
after_create :send_tiding
def status_text
I18n.t!("apply_action.status.#{status}")
rescue I18n::MissingTranslationData
nil
end
def send_tiding
if container_type == 'TrialAuthorization' && status == 1
tidings.create(user_id: user_id, trigger_user_id: 0, status: 1, viewed: 0, tiding_type: 'System',

@ -12,6 +12,12 @@ class ApplyUserAuthentication < ApplicationRecord
after_create :send_tiding
def status_text
I18n.t!("apply_user_authentication.status.#{status}")
rescue I18n::MissingTranslationData
nil
end
private
def send_tiding

@ -9,7 +9,7 @@ class Course < ApplicationRecord
# 所属实践课程
belongs_to :subject, optional: true
has_one :inform, as: :container, dependent: :destroy
has_many :informs, as: :container, dependent: :destroy
has_many :course_infos, dependent: :destroy
# 课堂左侧导航栏的模块

@ -68,23 +68,18 @@ class HomeworkCommon < ApplicationRecord
def category_info
case self.homework_type
when 'normal'
{category_id: course.common_course_modules.first.try(:id), category_name: course.common_course_modules.first.try(:module_name)}
{category_id: course.common_course_modules.first.try(:id), category_name: course.common_course_modules.first.try(:module_name), main: 1}
when 'group'
{category_id: course.group_course_modules.first.try(:id), category_name: course.group_course_modules.first.try(:module_name)}
{category_id: course.group_course_modules.first.try(:id), category_name: course.group_course_modules.first.try(:module_name), main: 1}
when 'practice'
if self.course_second_category.present?
{category_id: self.course_second_category.try(:id), category_name: self.course_second_category.try(:name)}
{category_id: self.course_second_category.try(:id), category_name: self.course_second_category.try(:name), main: 0}
else
{category_id: course.shixun_course_modules.take.try(:id), category_name: course.shixun_course_modules.take.try(:module_name)}
{category_id: course.shixun_course_modules.take.try(:id), category_name: course.shixun_course_modules.take.try(:module_name), main: 1}
end
end
end
# 实训作业的主目录信息
def main_category_info
{category_id: course.shixun_course_modules.take.try(:id), category_name: course.shixun_course_modules.take.try(:module_name)}
end
# 根据是否统一发布获取作业的作品列表
def all_works
student_works = self.unified_setting ? self.student_works :

@ -1,3 +1,6 @@
class Inform < ApplicationRecord
belongs_to :container, polymorphic: true, optional: true
validates :name, length: { maximum: 60 }
validates :description, length: { maximum: 5000 }
end

@ -22,8 +22,8 @@ class Subject < ApplicationRecord
has_many :courses, -> { where("is_delete = 0").order("courses.created_at ASC") }
validates :name, length: { maximum: 60 }
validates :description, length: { maximum: 5000 }
validates :learning_notes, length: { maximum: 500 }
validates :description, length: { maximum: 8000 }
validates :learning_notes, length: { maximum: 2000 }
scope :visible, lambda{where(status: 2)}
scope :published, lambda{where(status: 1)}
@ -39,6 +39,11 @@ class Subject < ApplicationRecord
courses.pluck(:end_date).max
end
# 是否有已开课的课堂
def has_course_start?
courses.where("start_date <= '#{Date.today}' and end_date >= '#{Date.today}'").count > 0
end
# 挑战过路径的成员数(金课统计去重后的报名人数)
def member_count
excellent && CourseMember.where(role: 4, course_id: courses.pluck(:id)).pluck(:user_id).uniq.length > shixuns.pluck(:myshixuns_count).sum ?
@ -78,7 +83,7 @@ class Subject < ApplicationRecord
def my_subject_progress
my_challenge_count = Game.joins(:challenge).where(user_id: User.current.id, status: 2, challenges: {shixun_id: shixuns.published_closed}).
pluck(:challenge_id).uniq.size
pluck(:challenge_id).uniq.size
count = self.subject_challenge_count == 0 ? 0 : ((my_challenge_count.to_f / self.subject_challenge_count).round(2) * 100).to_i
end

@ -0,0 +1,34 @@
class Admins::ApplyUserAuthenticationQuery < ApplicationQuery
include CustomSortable
attr_reader :params
sort_columns :updated_at, default_by: :updated_at, default_direction: :desc
def initialize(params)
@params = params
end
def call
applies = ApplyUserAuthentication.where(auth_type: params[:type].presence || 1)
status =
case params[:status]
when 'pending' then 0
when 'processed' then [1, 2]
when 'agreed' then 1
when 'refused' then 2
else 0
end
applies = applies.where(status: status) if status.present?
# 关键字模糊查询
keyword = params[:keyword].to_s.strip
if keyword.present?
applies = applies.joins(user: { user_extension: :school })
.where('CONCAT(lastname,firstname) LIKE :keyword OR schools.name LIKE :keyword', keyword: "%#{keyword}%")
end
custom_sort(applies, params[:sort_by], params[:sort_direction])
end
end

@ -0,0 +1,38 @@
class Admins::IdentityAuths::AgreeApplyService < ApplicationService
attr_reader :apply, :user
def initialize(apply)
@apply = apply
@user = apply.user
end
def call
ActiveRecord::Base.transaction do
apply.update!(status: 1)
user.update!(authentication: true)
RewardGradeService.call(user, container_id: user.id, container_type: 'Authentication', score: 500)
deal_tiding!
delete_auth_file!
end
end
private
def deal_tiding!
apply.tidings.where(tiding_type: 'Apply').update_all(status: 1)
Tiding.create!(user_id: apply.user_id, trigger_user_id: 0,
container_id: apply.id, container_type: 'ApplyUserAuthentication',
belong_container_id: apply.user_id, belong_container_type: 'User',
status: 1, tiding_type: 'System')
end
def delete_auth_file!
path = Util::FileManage.disk_real_name_auth_filename(user.id)
File.delete(path) if File.exists?(path)
apply.update!(is_delete: true)
end
end

@ -0,0 +1,40 @@
class Admins::IdentityAuths::RefuseApplyService < ApplicationService
attr_reader :apply, :user, :params
def initialize(apply, params)
@apply = apply
@user = apply.user
@params = params
end
def call
ActiveRecord::Base.transaction do
apply.update!(status: 2, remarks: reason)
deal_tiding!
delete_auth_file!
end
end
private
def reason
params[:reason].to_s.strip
end
def deal_tiding!
apply.tidings.where(tiding_type: 'Apply').update_all(status: 1)
Tiding.create!(user_id: apply.user_id, trigger_user_id: 0,
container_id: apply.id, container_type: 'ApplyUserAuthentication',
belong_container_id: apply.user_id, belong_container_type: 'User',
status: 2, tiding_type: 'System')
end
def delete_auth_file!
path = Util::FileManage.disk_real_name_auth_filename(user.id)
File.delete(path) if File.exists?(path)
apply.update!(is_delete: true)
end
end

@ -0,0 +1,38 @@
class Admins::ProfessionalAuths::AgreeApplyService < ApplicationService
attr_reader :apply, :user
def initialize(apply)
@apply = apply
@user = apply.user
end
def call
ActiveRecord::Base.transaction do
apply.update!(status: 1)
user.update!(professional_certification: true)
RewardGradeService.call(user, container_id: user.id, container_type: 'Professional', score: 500)
deal_tiding!
delete_auth_file!
end
end
private
def deal_tiding!
apply.tidings.where(tiding_type: 'Apply').update_all(status: 1)
Tiding.create!(user_id: apply.user_id, trigger_user_id: 0,
container_id: apply.id, container_type: 'ApplyUserAuthentication',
belong_container_id: apply.user_id, belong_container_type: 'User',
status: 1, tiding_type: 'System')
end
def delete_auth_file!
path = Util::FileManage.disk_professional_auth_filename(user.id)
File.delete(path) if File.exists?(path)
apply.update!(is_delete: true)
end
end

@ -0,0 +1,40 @@
class Admins::ProfessionalAuths::RefuseApplyService < ApplicationService
attr_reader :apply, :user, :params
def initialize(apply, params)
@apply = apply
@user = apply.user
@params = params
end
def call
ActiveRecord::Base.transaction do
apply.update!(status: 2, remarks: reason)
deal_tiding!
delete_auth_file!
end
end
private
def reason
params[:reason].to_s.strip
end
def deal_tiding!
apply.tidings.where(tiding_type: 'Apply').update_all(status: 1)
Tiding.create!(user_id: apply.user_id, trigger_user_id: 0,
container_id: apply.id, container_type: 'ApplyUserAuthentication',
belong_container_id: apply.user_id, belong_container_type: 'User',
status: 2, tiding_type: 'System')
end
def delete_auth_file!
path = Util::FileManage.disk_professional_auth_filename(user.id)
File.delete(path) if File.exists?(path)
apply.update!(is_delete: true)
end
end

@ -0,0 +1,43 @@
class Admins::ShixunAuths::AgreeApplyService < ApplicationService
attr_reader :apply, :user, :shixun
def initialize(apply, user)
@apply = apply
@user = user
@shixun = Shixun.find(apply.container_id)
end
def call
ActiveRecord::Base.transaction do
apply.update!(status: 1, dealer_id: user.id)
shixun.update!(status: 2, publish_time: Time.now)
# 奖励金币、经验
reward_grade_and_experience!
deal_tiding!
end
end
private
def reward_grade_and_experience!
score = shixun.all_score
shixun_creator = shixun.user
RewardGradeService.call(shixun_creator, container_id: shixun.id, container_type: 'shixunPublish', score: score)
Experience.create!(user_id: shixun_creator.id, container_id: shixun.id, container_type: 'shixunPublish', score: score)
shixun_creator.update_column(:experience, shixun_creator.experience.to_i + score)
end
def deal_tiding!
apply.tidings.where(tiding_type: 'Apply', status: 0).update_all(status: 1)
Tiding.create!(user_id: apply.user_id, trigger_user_id: 0,
container_id: apply.id, container_type: 'ApplyAction',
parent_container_id: apply.container_id, parent_container_type: apply.container_type,
belong_container_id: apply.container_id, belong_container_type: 'Shixun',
status: 1, tiding_type: 'System')
end
end

@ -0,0 +1,35 @@
class Admins::ShixunAuths::RefuseApplyService < ApplicationService
attr_reader :apply, :user, :shixun, :params
def initialize(apply, user, params)
@apply = apply
@user = user
@shixun = Shixun.find(apply.container_id)
@params = params
end
def call
ActiveRecord::Base.transaction do
shixun.update!(status: 0)
apply.update!(status: 2, reason: reason, dealer_id: user.id)
deal_tiding!
end
end
private
def reason
params[:reason].to_s.strip
end
def deal_tiding!
apply.tidings.where(tiding_type: 'Apply', status: 0).update_all(status: 1)
Tiding.create!(user_id: apply.user_id, trigger_user_id: 0,
container_id: apply.id, container_type: 'ApplyAction',
parent_container_id: apply.container_id, parent_container_type: apply.container_type,
belong_container_id: apply.container_id, belong_container_type: 'Shixun',
status: 2, tiding_type: 'System')
end
end

@ -0,0 +1,30 @@
class Admins::SubjectAuths::AgreeApplyService < ApplicationService
attr_reader :apply, :user, :subject
def initialize(apply, user)
@apply = apply
@user = user
@subject = Subject.find(apply.container_id)
end
def call
ActiveRecord::Base.transaction do
apply.update!(status: 1, dealer_id: user.id)
subject.update!(status: 2, publish_time: Time.now)
deal_tiding!
end
end
private
def deal_tiding!
apply.tidings.where(tiding_type: 'Apply', status: 0).update_all(status: 1)
Tiding.create!(user_id: apply.user_id, trigger_user_id: 0,
container_id: apply.id, container_type: 'ApplyAction',
parent_container_id: apply.container_id, parent_container_type: apply.container_type,
belong_container_id: apply.container_id, belong_container_type: 'Subject',
status: 1, tiding_type: 'System')
end
end

@ -0,0 +1,35 @@
class Admins::SubjectAuths::RefuseApplyService < ApplicationService
attr_reader :apply, :user, :subject, :params
def initialize(apply, user, params)
@apply = apply
@user = user
@subject = Subject.find(apply.container_id)
@params = params
end
def call
ActiveRecord::Base.transaction do
subject.update!(status: 0)
apply.update!(status: 2, reason: reason, dealer_id: user.id)
deal_tiding!
end
end
private
def reason
params[:reason].to_s.strip
end
def deal_tiding!
apply.tidings.where(tiding_type: 'Apply', status: 0).update_all(status: 1)
Tiding.create!(user_id: apply.user_id, trigger_user_id: 0,
container_id: apply.id, container_type: 'ApplyAction',
parent_container_id: apply.container_id, parent_container_type: apply.container_type,
belong_container_id: apply.container_id, belong_container_type: 'Subject',
status: 2, tiding_type: 'System')
end
end

@ -760,9 +760,9 @@ html>body #ajax-indicator { position: fixed; }
.paddingLeft28{padding-left:28px;}
.ant-modal-header{
border-radius: 10px;
}
/*.ant-modal-header{*/
/*border-radius: 10px;*/
/*}*/
.color656565{
color:#656565;

@ -0,0 +1,32 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('实名认证') %>
<% end %>
<div class="box search-form-container flex-column mb-0 pb-0 identity-authentication-list-form">
<ul class="nav nav-tabs w-100 search-form-tabs">
<li class="nav-item">
<%= link_to '待审批', admins_identity_authentications_path(status: :pending), remote: true, 'data-value': 'pending',
class: "nav-link search-form-tab #{params[:status] == 'pending' ? 'active' : ''}" %>
</li>
<li class="nav-item">
<%= link_to '已审批', admins_identity_authentications_path(status: :processed), remote: true, 'data-value': 'processed',
class: "nav-link search-form-tab #{params[:status] != 'pending' ? 'active' : ''}" %>
</li>
</ul>
<%= form_tag(admins_identity_authentications_path(unsafe_params), method: :get, class: 'form-inline search-form justify-content-end mt-3', remote: true) do %>
<div class="form-group status-filter" style="<%= params[:status] != 'pending' ? '' : 'display: none;' %>">
<label for="status">审核状态:</label>
<% status_options = [['全部', 'processed'], ['已同意', 'agreed'], ['已拒绝', 'refused']] %>
<%= select_tag(:status, options_for_select(status_options), class: 'form-control') %>
</div>
<%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '姓名/学校/单位检索') %>
<%= submit_tag('搜索', class: 'btn btn-primary ml-3') %>
<% end %>
</div>
<div class="box identity-authentication-list-container">
<%= render(partial: 'admins/identity_authentications/shared/list', locals: { applies: @applies }) %>
</div>
<%= render(partial: 'admins/shared/admin_common_refuse_modal') %>

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

@ -0,0 +1,74 @@
<% is_processed = params[:status].to_s != 'pending' %>
<table class="table table-hover text-center identity-authentication-list-table">
<thead class="thead-light">
<tr>
<th width="8%">头像</th>
<th width="10%">姓名</th>
<th width="14%">身份证号</th>
<th width="20%">学校/单位</th>
<th width="12%">职称</th>
<% unless is_processed %>
<th width="8%">
照片
<i class="fa fa-question-circle" data-toggle="tooltip" data-html="true" data-placement="top" title="审核完成后自动删除图片"></i>
</th>
<% end %>
<th width="16%">时间</th>
<% if is_processed %>
<th width="14%">拒绝原因</th>
<th width="8%">状态</th>
<% else %>
<th width="20%">操作</th>
<% end %>
</tr>
</thead>
<tbody>
<% if applies.present? %>
<% applies.each do |apply| %>
<% user = apply.user %>
<tr class="identity-authentication-item identity-authentication-<%= apply.id %>">
<td>
<%= link_to "/users/#{user.login}", class: 'identity-authentication-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %>
<img src="/images/<%= url_to_avatar(user) %>" class="rounded-circle" width="40" height="40" />
<% end %>
</td>
<td><%= user.real_name %></td>
<td><%= user.ID_number %></td>
<td><%= raw [user.school_name.presence, user.department_name.presence].compact.join('<br/>') %></td>
<td><%= user.identity %> <%= raw user.user_extension.student? && user.student_id ? "<br/>#{user.student_id}" : '' %></td>
<% unless is_processed %>
<td>
<% if File.exists?(disk_real_name_auth_filename(user.id)) %>
<%= image_tag(real_name_auth_file_url(user.id).to_s + "?#{Time.now.to_i}", width: 40, height: 40, class: 'preview-image auth-image', data: { toggle: 'tooltip', title: '点击预览' }) %>
<% else %>
<%= content_tag(:span, '图片已删除', class: 'text-secondary') %>
<% end %>
</td>
<% end %>
<td><%= apply.updated_at.strftime('%Y-%m-%d %H:%M') %></td>
<% if is_processed %>
<td class="text-secondary"><%= overflow_hidden_span apply.remarks, width: 140 %></td>
<td><span class="apply-status-<%= apply.status %>"><%= apply.status_text %></span></td>
<% else %>
<td class="action-container">
<%= agree_link '同意', agree_admins_identity_authentication_path(apply, element: ".identity-authentication-#{apply.id}"), 'data-confirm': '确认审核通过?' %>
<%= javascript_void_link('拒绝', class: 'action refuse-action',
data: {
toggle: 'modal', target: '.admin-common-refuse-modal', id: apply.id,
url: refuse_admins_identity_authentication_path(apply, element: ".identity-authentication-#{apply.id}")
}) %>
</td>
<% end %>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: applies } %>

@ -0,0 +1,32 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('职业认证') %>
<% end %>
<div class="box search-form-container flex-column mb-0 pb-0 professional-authentication-list-form">
<ul class="nav nav-tabs w-100 search-form-tabs">
<li class="nav-item">
<%= link_to '待审批', admins_professional_authentications_path(status: :pending), remote: true, 'data-value': 'pending',
class: "nav-link search-form-tab #{params[:status] == 'pending' ? 'active' : ''}" %>
</li>
<li class="nav-item">
<%= link_to '已审批', admins_professional_authentications_path(status: :processed), remote: true, 'data-value': 'processed',
class: "nav-link search-form-tab #{params[:status] != 'pending' ? 'active' : ''}" %>
</li>
</ul>
<%= form_tag(admins_professional_authentications_path(unsafe_params), method: :get, class: 'form-inline search-form justify-content-end mt-3', remote: true) do %>
<div class="form-group status-filter" style="<%= params[:status] != 'pending' ? '' : 'display: none;' %>">
<label for="status">审核状态:</label>
<% status_options = [['全部', 'processed'], ['已同意', 'agreed'], ['已拒绝', 'refused']] %>
<%= select_tag(:status, options_for_select(status_options), class: 'form-control') %>
</div>
<%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '姓名/学校/单位检索') %>
<%= submit_tag('搜索', class: 'btn btn-primary ml-3') %>
<% end %>
</div>
<div class="box professional-authentication-list-container">
<%= render(partial: 'admins/professional_authentications/shared/list', locals: { applies: @applies }) %>
</div>
<%= render(partial: 'admins/shared/admin_common_refuse_modal') %>

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

@ -0,0 +1,72 @@
<% is_processed = params[:status].to_s != 'pending' %>
<table class="table table-hover text-center professional-authentication-list-table">
<thead class="thead-light">
<tr>
<th width="8%">头像</th>
<th width="14%">姓名</th>
<th width="28%">学校/单位</th>
<th width="12%">职称</th>
<% unless is_processed %>
<th width="10%">
照片
<i class="fa fa-question-circle" data-toggle="tooltip" data-html="true" data-placement="top" title="审核完成后自动删除图片"></i>
</th>
<% end %>
<th width="16%">时间</th>
<% if is_processed %>
<th width="14%">拒绝原因</th>
<th width="8%">状态</th>
<% else %>
<th width="18%">操作</th>
<% end %>
</tr>
</thead>
<tbody>
<% if applies.present? %>
<% applies.each do |apply| %>
<% user = apply.user %>
<tr class="professional-authentication-item professional-authentication-<%= apply.id %>">
<td>
<%= link_to "/users/#{user.login}", class: 'professional-authentication-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %>
<img src="/images/<%= url_to_avatar(user) %>" class="rounded-circle" width="40" height="40" />
<% end %>
</td>
<td><%= user.real_name %></td>
<td><%= raw [user.school_name.presence, user.department_name.presence].compact.join('<br/>') %></td>
<td><%= user.identity %> <%= raw user.user_extension.student? && user.student_id ? "<br/>#{user.student_id}" : '' %></td>
<% unless is_processed %>
<td>
<% if File.exists?(disk_professional_auth_filename(user.id)) %>
<%= image_tag(professional_auth_file_url(user.id).to_s + "?#{Time.now.to_i}", width: 40, height: 40, class: 'preview-image auth-image', data: { toggle: 'tooltip', title: '点击预览' }) %>
<% else %>
<%= content_tag(:span, '图片已删除', class: 'text-secondary') %>
<% end %>
</td>
<% end %>
<td><%= apply.updated_at.strftime('%Y-%m-%d %H:%M') %></td>
<% if is_processed %>
<td class="text-secondary"><%= overflow_hidden_span apply.remarks, width: 140 %></td>
<td><span class="apply-status-<%= apply.status %>"><%= apply.status_text %></span></td>
<% else %>
<td class="action-container">
<%= agree_link '同意', agree_admins_professional_authentication_path(apply, element: ".professional-authentication-#{apply.id}"), 'data-confirm': '确认审核通过?' %>
<%= javascript_void_link('拒绝', class: 'action refuse-action',
data: {
toggle: 'modal', target: '.admin-common-refuse-modal', id: apply.id,
url: refuse_admins_professional_authentication_path(apply, element: ".professional-authentication-#{apply.id}")
}) %>
</td>
<% end %>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: applies } %>

@ -32,9 +32,9 @@
<div class="type-box ml-3">
<%= hidden_field_tag :data_type, params[:data_type] || 'grow' %>
<%= javascript_void_link '时段对比', class: "btn btn-outline-primary btn-sm contrast-btn #{params[:data_type] == 'contrast' ? 'active' : ''}",
<%= javascript_void_link '时段对比', class: "btn btn-outline-info btn-sm contrast-btn #{params[:data_type] == 'contrast' ? 'active' : ''}",
data: { toggle: 'tooltip', trigger: 'hover', title: '请在左侧分别选择需进行对比的两个时段具体从当日5:00开始计算下表显示两时段选定指标两时段变化情况对比' } %>
<%= javascript_void_link '数据变化', class: "btn btn-outline-primary btn-sm grow-btn #{params[:data_type] == 'contrast' ? '' : 'active'}",
<%= javascript_void_link '数据变化', class: "btn btn-outline-info btn-sm grow-btn #{params[:data_type] == 'contrast' ? '' : 'active'}",
data: { toggle: 'tooltip', trigger: 'hover', title: '请在左侧选择时间段具体从当日5:00开始计算下表显示选定时间段内各项指标数据变化情况' } %>
</div>

@ -0,0 +1,26 @@
<div class="modal fade admin-common-refuse-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" id="exampleModalLongTitle">拒绝原因</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form class="admin-common-refuse-form">
<%= hidden_field_tag(:apply_id, nil) %>
<div class="form-group">
<label for="reason" class="col-form-label">原因:</label>
<%= text_area_tag(:reason, nil, class: 'form-control', placeholder: '我得说点儿什么最多200字') %>
</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>

@ -37,6 +37,15 @@
<% end %>
</li>
<li>
<%= sidebar_item_group('#apply-review-submenu', '审核', icon: 'gavel') do %>
<li><%= sidebar_item(admins_identity_authentications_path, '实名认证', icon: 'id-card-o', controller: 'admins-identity_authentications') %></li>
<li><%= sidebar_item(admins_professional_authentications_path, '职业认证', icon: 'drivers-license', controller: 'admins-professional_authentications') %></li>
<li><%= sidebar_item(admins_shixun_authorizations_path, '实训发布', icon: 'object-ungroup', controller: 'admins-shixun_authorizations') %></li>
<li><%= sidebar_item(admins_subject_authorizations_path, '实践课程发布', icon: 'object-group', controller: 'admins-subject_authorizations') %></li>
<% end %>
</li>
<li><%= sidebar_item('/', '返回主站', icon: 'sign-out', controller: 'root') %></li>
</ul>
</nav>

@ -1,3 +1,4 @@
;
$('[data-toggle="tooltip"]').tooltip();
$('[data-toggle="popover"]').popover();
$('[data-toggle="popover"]').popover();
$('img.preview-image').bootstrapViewer();

@ -1,6 +1,8 @@
var deleteRow = $('<%= params[:element] %>');
var refreshUrl = '<%= params[:refresh_url] %>';
$.notify({ message: '操作成功' },{ type: 'success' });
var refreshFunc = function(url) {
$.ajax({
url: url.length > 0 ? url : window.location.href,

@ -4,4 +4,7 @@ setTimeout(function() {
if ($('.admin-alert-container button.close').length > 0) {
$('.admin-alert-container button.close').trigger('click');
}
}, 2000)
}, 5000)
$(".admin-body-container").animate({
scrollTop: 0
}, 200);

@ -0,0 +1,32 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('实训发布') %>
<% end %>
<div class="box search-form-container flex-column mb-0 pb-0 shixun-authorization-list-form">
<ul class="nav nav-tabs w-100 search-form-tabs">
<li class="nav-item">
<%= link_to '待审批', admins_shixun_authorizations_path(status: :pending), remote: true, 'data-value': 'pending',
class: "nav-link search-form-tab #{params[:status] == 'pending' ? 'active' : ''}" %>
</li>
<li class="nav-item">
<%= link_to '已审批', admins_shixun_authorizations_path(status: :processed), remote: true, 'data-value': 'processed',
class: "nav-link search-form-tab #{params[:status] != 'pending' ? 'active' : ''}" %>
</li>
</ul>
<%= form_tag(admins_shixun_authorizations_path(unsafe_params), method: :get, class: 'form-inline search-form justify-content-end mt-3', remote: true) do %>
<div class="form-group status-filter" style="<%= params[:status] != 'pending' ? '' : 'display: none;' %>">
<label for="status">审核状态:</label>
<% status_options = [['全部', 'processed'], ['已同意', 'agreed'], ['已拒绝', 'refused']] %>
<%= select_tag(:status, options_for_select(status_options), class: 'form-control') %>
</div>
<%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '实训名称检索') %>
<%= submit_tag('搜索', class: 'btn btn-primary ml-3') %>
<% end %>
</div>
<div class="box shixun-authorization-list-container">
<%= render(partial: 'admins/shixun_authorizations/shared/list', locals: { applies: @applies, shixun_map: @shixun_map }) %>
</div>
<%= render(partial: 'admins/shared/admin_common_refuse_modal') %>

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

@ -0,0 +1,60 @@
<% is_processed = params[:status].to_s != 'pending' %>
<table class="table table-hover text-center shixun-authorization-list-table">
<thead class="thead-light">
<tr>
<th width="8%">头像</th>
<th width="14%">创建者</th>
<th width="28%" class="text-left">实训名称</th>
<th width="12%">任务数</th>
<th width="16%">时间</th>
<% if is_processed %>
<th width="14%">拒绝原因</th>
<th width="8%">状态</th>
<% else %>
<th width="22%">操作</th>
<% end %>
</tr>
</thead>
<tbody>
<% if applies.present? %>
<% applies.each do |apply| %>
<% user = apply.user %>
<% shixun = shixun_map[apply.container_id] %>
<tr class="shixun-authorization-item shixun-authorization-<%= apply.id %>">
<td>
<%= link_to "/users/#{user.login}", class: 'shixun-authorization-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %>
<img src="/images/<%= url_to_avatar(user) %>" class="rounded-circle" width="40" height="40" />
<% end %>
</td>
<td><%= user.real_name %></td>
<td class="text-left">
<%= link_to "/shixuns/#{shixun.identifier}", target: '_blank' do %>
<%= overflow_hidden_span shixun.name, width: 300 %>
<% end %>
</td>
<td><%= shixun.challenges_count %></td>
<td><%= apply.updated_at.strftime('%Y-%m-%d %H:%M') %></td>
<% if is_processed %>
<td class="text-secondary"><%= overflow_hidden_span apply.reason, width: 140 %></td>
<td><span class="apply-status-<%= apply.status %>"><%= apply.status_text %></span></td>
<% else %>
<td class="action-container">
<%= agree_link '同意', agree_admins_shixun_authorization_path(apply, element: ".shixun-authorization-#{apply.id}"), 'data-confirm': '确认审核通过?' %>
<%= javascript_void_link('拒绝', class: 'action refuse-action',
data: {
toggle: 'modal', target: '.admin-common-refuse-modal', id: apply.id,
url: refuse_admins_shixun_authorization_path(apply, element: ".shixun-authorization-#{apply.id}")
}) %>
</td>
<% end %>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: applies } %>

@ -0,0 +1,33 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('实践课程发布') %>
<% end %>
<div class="box search-form-container flex-column mb-0 pb-0 subject-authorization-list-form">
<ul class="nav nav-tabs w-100 search-form-tabs">
<li class="nav-item">
<%= link_to '待审批', admins_subject_authorizations_path(status: :pending), remote: true, 'data-value': 'pending',
class: "nav-link search-form-tab #{params[:status] == 'pending' ? 'active' : ''}" %>
</li>
<li class="nav-item">
<%= link_to '已审批', admins_subject_authorizations_path(status: :processed), remote: true, 'data-value': 'processed',
class: "nav-link search-form-tab #{params[:status] != 'pending' ? 'active' : ''}" %>
</li>
</ul>
<%= form_tag(admins_subject_authorizations_path(unsafe_params), method: :get, class: 'form-inline search-form justify-content-end mt-3', remote: true) do %>
<div class="form-group status-filter" style="<%= params[:status] != 'pending' ? '' : 'display: none;' %>">
<label for="status">审核状态:</label>
<% status_options = [['全部', 'processed'], ['已同意', 'agreed'], ['已拒绝', 'refused']] %>
<%= select_tag(:status, options_for_select(status_options), class: 'form-control') %>
</div>
<%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '实训课程名称检索') %>
<%= submit_tag('搜索', class: 'btn btn-primary ml-3') %>
<% end %>
</div>
<div class="box subject-authorization-list-container">
<%= render(partial: 'admins/subject_authorizations/shared/list',
locals: { applies: @applies, subject_map: @subject_map, challenge_count_map: @challenge_count_map }) %>
</div>
<%= render(partial: 'admins/shared/admin_common_refuse_modal') %>

@ -0,0 +1 @@
$('.subject-authorization-list-container').html("<%= j( render partial: 'admins/subject_authorizations/shared/list', locals: { applies: @applies, subject_map: @subject_map, challenge_count_map: @challenge_count_map } ) %>");

@ -0,0 +1,64 @@
<% is_processed = params[:status].to_s != 'pending' %>
<table class="table table-hover text-center subject-authorization-list-table">
<thead class="thead-light">
<tr>
<th width="8%">头像</th>
<th width="10%">创建者</th>
<th width="28%" class="text-left">实践课程名称</th>
<th width="6%">阶段数</th>
<th width="6%">实训数</th>
<th width="6%">关卡数</th>
<th width="14%">时间</th>
<% if is_processed %>
<th width="14%">拒绝原因</th>
<th width="8%">状态</th>
<% else %>
<th width="22%">操作</th>
<% end %>
</tr>
</thead>
<tbody>
<% if applies.present? %>
<% applies.each do |apply| %>
<% user = apply.user %>
<% subject = subject_map[apply.container_id] %>
<tr class="subject-authorization-item subject-authorization-<%= apply.id %>">
<td>
<%= link_to "/users/#{user.login}", class: 'subject-authorization-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %>
<img src="/images/<%= url_to_avatar(user) %>" class="rounded-circle" width="40" height="40" />
<% end %>
</td>
<td><%= user.real_name %></td>
<td class="text-left">
<%= link_to "/paths/#{subject.id}", target: '_blank' do %>
<%= overflow_hidden_span subject.name, width: 300 %>
<% end %>
</td>
<td><%= subject.stages_count %></td>
<td><%= subject.shixuns_count %></td>
<td><%= challenge_count_map.fetch(subject.id, 0) %></td>
<td><%= apply.updated_at.strftime('%Y-%m-%d %H:%M') %></td>
<% if is_processed %>
<td class="text-secondary"><%= overflow_hidden_span apply.reason, width: 140 %></td>
<td><span class="apply-status-<%= apply.status %>"><%= apply.status_text %></span></td>
<% else %>
<td class="action-container">
<%= agree_link '同意', agree_admins_subject_authorization_path(apply, element: ".subject-authorization-#{apply.id}"), 'data-confirm': '确认审核通过?' %>
<%= javascript_void_link('拒绝', class: 'action refuse-action',
data: {
toggle: 'modal', target: '.admin-common-refuse-modal', id: apply.id,
url: refuse_admins_subject_authorization_path(apply, element: ".subject-authorization-#{apply.id}")
}) %>
</td>
<% end %>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: applies } %>

@ -1,29 +1,23 @@
<table class="table table-hover users-list-table">
<thead class="thead-light">
<tr>
<th width="10%">ID</th>
<th width="10%">真实姓名</th>
<th width="10%" class="text-left">真实姓名</th>
<th width="16%">邮件地址</th>
<th width="10%">手机号码</th>
<th width="14%">单位</th>
<th width="12%"><%= sort_tag('创建于', name: 'created_on', path: admins_users_path) %></th>
<th width="12%"><%= sort_tag('最后登录', name: 'last_login_on', path: admins_users_path) %></th>
<th width="10%"><%= sort_tag('经验值', name: 'experience', path: admins_users_path) %></th>
<th width="8%"><%= sort_tag('金币', name: 'grade', path: admins_users_path) %></th>
<th width="14%">操作</th>
<th width="10%"><%= sort_tag('创建于', name: 'created_on', path: admins_users_path) %></th>
<th width="10%"><%= sort_tag('最后登录', name: 'last_login_on', path: admins_users_path) %></th>
<th width="6%"><%= sort_tag('经验值', name: 'experience', path: admins_users_path) %></th>
<th width="6%"><%= sort_tag('金币', name: 'grade', path: admins_users_path) %></th>
<th width="20%">操作</th>
</tr>
</thead>
<tbody>
<% if users.present? %>
<% users.each do |user| %>
<tr class="user-item-<%= user.id %>">
<td>
<td class="text-left">
<%= link_to "/users/#{user.login}", target: '_blank' do %>
<%= overflow_hidden_span user.login, width: 100 %>
<% end %>
</td>
<td>
<%= link_to edit_admins_user_path(user) do %>
<%= overflow_hidden_span user.real_name, width: 100 %>
<% end %>
</td>
@ -35,6 +29,8 @@
<td><%= user.experience.to_i %></td>
<td class="grade-content"><%= user.grade.to_i %></td>
<td class="action-container">
<%= link_to '编辑', edit_admins_user_path(user), class: 'action' %>
<%= javascript_void_link('奖励', class: 'action reward-grade-action', data: { toggle: 'modal', target: '.admin-users-reward-grade-modal', id: user.id }) %>
<%= javascript_void_link '解锁', class: 'action unlock-action', data: { id: user.id, confirm: '确认解锁吗?' }, style: user.locked? ? '' : 'display: none;' %>

@ -1 +1,5 @@
json.description @course.inform&.description
json.informs @informs do |inform|
json.id inform.id
json.name inform.name
json.description inform.description
end

@ -36,6 +36,14 @@ if question.question_type <= 2 #当为选择题或判断题时,只显示选
if exercise_type == 1 || exercise_type == 4 #1为老师4为试卷截止且答案公开的情况
json.standard_answer standard_answers_array
if question.question_type == 2 #返回答案的文字
standard_text = standard_answers_array.first.to_i == 1 ? "正确" : "错误"
else
array_text_answer = []
standard_answers_array.each{|a| array_text_answer.push((a+64).chr)}
standard_text = array_text_answer.join("")
end
json.standard_answer_show standard_text
end
if exercise_type == 3 || exercise_type == 4
json.user_answer user_answer

@ -31,8 +31,9 @@ json.shixun_detail do
if shixun_challenge.challenge&.path.present?
if game.try(:lastest_code).blank?
cha_path = challenge_path(shixun_challenge.challenge&.path)
latest_code = git_fle_content(game.myshixun.repo_path,cha_path)
if latest_code.to_s == "true"
begin
latest_code = git_fle_content(game.myshixun.repo_path,cha_path)
rescue
latest_code = ""
end
else

@ -2,7 +2,6 @@ json.course_id course.id
json.course_name course.name
json.is_end course.is_end
json.category homework.category_info
json.main_category homework.main_category_info if homework.homework_type == "practice"
member = course.course_members.find_by(user_id: user.id, is_active: 1)
curr_status = homework_curr_status(homework, user.course_identity(course), course, member, member&.teacher_course_groups)
json.homework_status curr_status[:status]

@ -14,6 +14,7 @@ json.allow_add_member @is_manager
json.is_creator @is_creator
if @subject.excellent
json.has_start @subject.has_course_start?
json.courses @courses do |course|
json.course_id course.id
json.first_category_url module_url(course.none_hidden_course_modules.first, course)

@ -55,6 +55,8 @@ Rails.application.configure do
# Suppress logger output for asset requests.
config.assets.quiet = true
# config.assets.prefix = '/dev-assets'
# Raises error for missing translations
# config.action_view.raise_on_missing_translations = true

@ -11,4 +11,4 @@ Rails.application.config.assets.paths << Rails.root.join('node_modules')
# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in the app/assets
# folder are already added.
Rails.application.config.assets.precompile += %w( admin.js admin.scss )
Rails.application.config.assets.precompile += %w( admin.js admin.css )

@ -0,0 +1,7 @@
zh-CN:
apply_action:
status:
'0': '待处理'
'1': '已同意'
'2': '已拒绝'
'3': '已撤销'

@ -0,0 +1,7 @@
zh-CN:
apply_user_authentication:
status:
'0': '待处理'
'1': '已同意'
'2': '已拒绝'
'3': '已撤销'

@ -349,6 +349,7 @@ Rails.application.routes.draw do
post 'exit_course'
get 'informs'
post 'update_informs'
post 'new_informs'
get 'online_learning'
post 'join_excellent_course'
get 'tasks_list'
@ -752,7 +753,7 @@ Rails.application.routes.draw do
get :contrast, on: :collection
end
resources :users, only: [:index, :edit, :update] do
resources :users, only: [:index, :edit, :update, :destroy] do
member do
post :reward_grade
post :lock
@ -760,6 +761,31 @@ Rails.application.routes.draw do
post :active
end
end
resources :identity_authentications, only: [:index] do
member do
post :agree
post :refuse
end
end
resources :professional_authentications, only: [:index] do
member do
post :agree
post :refuse
end
end
resources :shixun_authorizations, only: [:index] do
member do
post :agree
post :refuse
end
end
resources :subject_authorizations, only: [:index] do
member do
post :agree
post :refuse
end
end
end
#git 认证回调

@ -1,4 +1,4 @@
class ChangeExericse1936Scores < ActiveRecord::Migration[5.2]
class RechangeExercise1936Scores < ActiveRecord::Migration[5.2]
include ExercisesHelper
def change
#1936的试卷成绩有问题。

@ -0,0 +1,9 @@
class MigrateSubjectShixunCount < ActiveRecord::Migration[5.2]
def change
Subject.reset_column_information
Subject.all.each do |subject|
Subject.reset_counters subject.id, :stage_shixuns
Subject.reset_counters subject.id, :shixuns
end
end
end

@ -2,7 +2,7 @@
namespace :course do
desc "course end"
task :end => :environment do
courses = Course.where("end_date <= '#{Date.today}' and is_end = 0")
courses = Course.where("end_date < '#{Date.today}' and is_end = 0")
courses.each do |course|
course.update_attribute(:is_end, 1)
end

@ -149,7 +149,12 @@ namespace :excellent_course_exercise do
game_code = game_challenge
code = game_code.try(:new_code)
else
code = git_fle_content(game.myshixun.repo_path,cha_path)
begin #8-23,hs
code = git_fle_content(game.myshixun.repo_path,cha_path)
rescue
code = ""
end
# code = git_fle_content(game.myshixun.repo_path,cha_path)
end
end
if ex_shixun_answer_content.blank? #把关卡的答案存入试卷的实训里

@ -0,0 +1 @@
{"files":{"admin-6575f1399953fb1935c037a7b8bd28c4aff07b70bed9b41faf6899a89af4b57d.js":{"logical_path":"admin.js","mtime":"2019-08-26T15:21:11+08:00","size":907839,"digest":"6575f1399953fb1935c037a7b8bd28c4aff07b70bed9b41faf6899a89af4b57d","integrity":"sha256-ZXXxOZlT+xk1wDenuL0oxK/we3C+2bQfr2iZqJr0tX0="},"admin-8a2b03cb8a055dc63f45443b304cae77382331beba55b1570b3d3c8aa42442d5.css":{"logical_path":"admin.css","mtime":"2019-08-26T15:21:47+08:00","size":655571,"digest":"8a2b03cb8a055dc63f45443b304cae77382331beba55b1570b3d3c8aa42442d5","integrity":"sha256-iisDy4oFXcY/RUQ7MEyudzgjMb66VbFXCz08iqQkQtU="},"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="},"logo-7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423.png":{"logical_path":"logo.png","mtime":"2019-08-21T15:10:12+08:00","size":2816,"digest":"7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423","integrity":"sha256-f/ESVocJv5f5iY/ockm3qPIA/x9I1TfYWvhyFfGHBCM="},"application-a3a4f3549d68670572bb07700c85a9ac11e536edc73fef6e7489723bf535e533.js":{"logical_path":"application.js","mtime":"2019-08-26T15:21:11+08:00","size":1042232,"digest":"a3a4f3549d68670572bb07700c85a9ac11e536edc73fef6e7489723bf535e533","integrity":"sha256-o6TzVJ1oZwVyuwdwDIWprBHlNu3HP+9udIlyO/U15TM="},"application-a7508b88eb6a69a5b301602bddc14745cec09853ea7d91c6fae856b96e788f46.css":{"logical_path":"application.css","mtime":"2019-08-26T15:21:47+08:00","size":1182859,"digest":"a7508b88eb6a69a5b301602bddc14745cec09853ea7d91c6fae856b96e788f46","integrity":"sha256-p1CLiOtqaaWzAWAr3cFHRc7AmFPqfZHG+uhWuW54j0Y="}},"assets":{"admin.js":"admin-6575f1399953fb1935c037a7b8bd28c4aff07b70bed9b41faf6899a89af4b57d.js","admin.css":"admin-8a2b03cb8a055dc63f45443b304cae77382331beba55b1570b3d3c8aa42442d5.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","logo.png":"logo-7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423.png","application.js":"application-a3a4f3549d68670572bb07700c85a9ac11e536edc73fef6e7489723bf535e533.js","application.css":"application-a7508b88eb6a69a5b301602bddc14745cec09853ea7d91c6fae856b96e788f46.css"}}

File diff suppressed because one or more lines are too long

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

Loading…
Cancel
Save