diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js index 27844be53..72a2ff118 100644 --- a/app/assets/javascripts/admin.js +++ b/app/assets/javascripts/admin.js @@ -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 () { }); \ No newline at end of file diff --git a/app/assets/javascripts/admins/common-refuse-modal.js b/app/assets/javascripts/admins/common-refuse-modal.js new file mode 100644 index 000000000..5eb2f3f46 --- /dev/null +++ b/app/assets/javascripts/admins/common-refuse-modal.js @@ -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'); + }); + } + }); + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/identity_authentications/index.js b/app/assets/javascripts/admins/identity_authentications/index.js new file mode 100644 index 000000000..91ce5c7ec --- /dev/null +++ b/app/assets/javascripts/admins/identity_authentications/index.js @@ -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'); + } + }); + } +}) \ No newline at end of file diff --git a/app/assets/javascripts/admins/professional_authentications/index.js b/app/assets/javascripts/admins/professional_authentications/index.js new file mode 100644 index 000000000..82a1da5af --- /dev/null +++ b/app/assets/javascripts/admins/professional_authentications/index.js @@ -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'); + } + }); + } +}) \ No newline at end of file diff --git a/app/assets/javascripts/admins/search-form-tab.js b/app/assets/javascripts/admins/search-form-tab.js new file mode 100644 index 000000000..6009412d5 --- /dev/null +++ b/app/assets/javascripts/admins/search-form-tab.js @@ -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'); + }); + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/shixun_authorizations/index.js b/app/assets/javascripts/admins/shixun_authorizations/index.js new file mode 100644 index 000000000..b98354c65 --- /dev/null +++ b/app/assets/javascripts/admins/shixun_authorizations/index.js @@ -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'); + } + }); + } +}) \ No newline at end of file diff --git a/app/assets/javascripts/admins/subject_authorizations/index.js b/app/assets/javascripts/admins/subject_authorizations/index.js new file mode 100644 index 000000000..bfba16fc1 --- /dev/null +++ b/app/assets/javascripts/admins/subject_authorizations/index.js @@ -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'); + } + }); + } +}) \ No newline at end of file diff --git a/app/assets/javascripts/bootstrap-notify.js b/app/assets/javascripts/bootstrap-notify.js old mode 100755 new mode 100644 diff --git a/app/assets/javascripts/bootstrap-notify.min.js b/app/assets/javascripts/bootstrap-notify.min.js old mode 100755 new mode 100644 diff --git a/app/assets/javascripts/bootstrap.viewer.js b/app/assets/javascripts/bootstrap.viewer.js new file mode 100755 index 000000000..4c6aa549a --- /dev/null +++ b/app/assets/javascripts/bootstrap.viewer.js @@ -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 = $(''); + $('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' +} \ No newline at end of file diff --git a/app/assets/javascripts/i18n/select2-i18n.zh-CN.js b/app/assets/javascripts/i18n/select2-i18n.zh-CN.js old mode 100755 new mode 100644 diff --git a/app/assets/javascripts/jquery.cxselect.js b/app/assets/javascripts/jquery.cxselect.js old mode 100755 new mode 100644 index 9bd674664..aeb3c17dd --- a/app/assets/javascripts/jquery.cxselect.js +++ b/app/assets/javascripts/jquery.cxselect.js @@ -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 ? '' : ''; - - // 区分标题、值的数据 - 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 += ''; - }; - - // 数组即为值的数据 - } else { - for (var i = 0, l = data.length; i < l; i++) { - _html += ''; - }; - }; - - _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 ? '' : ''; + + // 区分标题、值的数据 + 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 += ''; + }; + + // 数组即为值的数据 + } else { + for (var i = 0, l = data.length; i < l; i++) { + _html += ''; + }; + }; + + _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; + }; +})); diff --git a/app/assets/javascripts/jquery.cxselect.min.js b/app/assets/javascripts/jquery.cxselect.min.js old mode 100755 new mode 100644 index 54549a920..9aabba447 --- a/app/assets/javascripts/jquery.cxselect.min.js +++ b/app/assets/javascripts/jquery.cxselect.min.js @@ -1,11 +1,11 @@ -/*! - * 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 - */ +/*! + * 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(a){"function"==typeof define&&define.amd?define(["jquery"],a):a(window.jQuery||window.Zepto||window.$)}(function(a){var b=function(){var d,e,f,g,h,i;for(g=0,h=arguments.length;h>g;g++)b.isJquery(arguments[g])||b.isZepto(arguments[g])?d=arguments[g]:b.isElement(arguments[g])?d=a(arguments[g]):"function"==typeof arguments[g]?f=arguments[g]:"object"==typeof arguments[g]&&(e=arguments[g]);return i=new b.init(d,e),"function"==typeof f&&f(i),i};b.isElement=function(a){return a&&("function"==typeof HTMLElement||"object"==typeof HTMLElement)&&a instanceof HTMLElement?!0:a&&a.nodeType&&1===a.nodeType?!0:!1},b.isJquery=function(a){return a&&a.length&&("function"==typeof jQuery||"object"==typeof jQuery)&&a instanceof jQuery?!0:!1},b.isZepto=function(a){return a&&a.length&&("function"==typeof Zepto||"object"==typeof Zepto)&&Zepto.zepto.isZ(a)?!0:!1},b.getIndex=function(a,b){return b?a:a-1},b.getData=function(a,b){if("string"==typeof b&&b.length){b=b.split(".");for(var c=0,d=b.length;d>c;c++)a=a[b[c]]}return a},b.init=function(c,d){var f,g,e=this;(b.isJquery(c)||b.isZepto(c))&&(f={dom:{box:c}},e.attach=b.attach.bind(f),e.detach=b.detach.bind(f),e.setOptions=b.setOptions.bind(f),e.clear=b.clear.bind(f),f.changeEvent=function(){b.selectChange.call(f,this.className)},f.settings=a.extend({},a.cxSelect.defaults,d,{url:f.dom.box.data("url"),emptyStyle:f.dom.box.data("emptyStyle"),required:f.dom.box.data("required"),firstTitle:f.dom.box.data("firstTitle"),firstValue:f.dom.box.data("firstValue"),jsonSpace:f.dom.box.data("jsonSpace"),jsonName:f.dom.box.data("jsonName"),jsonValue:f.dom.box.data("jsonValue"),jsonSub:f.dom.box.data("jsonSub")}),g=f.dom.box.data("selects"),"string"==typeof g&&g.length&&(f.settings.selects=g.split(",")),e.setOptions(),e.attach(),f.settings.url||f.settings.data?a.isArray(f.settings.data)?b.start.call(f,f.settings.data):"string"==typeof f.settings.url&&f.settings.url.length&&a.getJSON(f.settings.url,function(a){b.start.call(f,a)}):b.start.apply(f))},b.setOptions=function(c){var e,f,g,d=this;if(c&&a.extend(d.settings,c),(!a.isArray(d.selectArray)||!d.selectArray.length||c&&c.selects)&&(d.selectArray=[],a.isArray(d.settings.selects)&&d.settings.selects.length))for(f=0,g=d.settings.selects.length;g>f&&(e=d.dom.box.find("select."+d.settings.selects[f]),e&&e.length);f++)d.selectArray.push(e);c&&(!a.isArray(c.data)&&"string"==typeof c.url&&c.url.length?a.getJSON(d.settings.url,function(a){b.start.call(d,a)}):b.start.call(d,c.data))},b.attach=function(){var a=this;a.attachStatus||a.dom.box.on("change","select",a.changeEvent),"boolean"==typeof a.attachStatus&&b.start.call(a),a.attachStatus=!0},b.detach=function(){var a=this;a.dom.box.off("change","select",a.changeEvent),a.attachStatus=!1},b.clear=function(a){var d,e,b=this,c={display:"",visibility:""};for(a=isNaN(a)?0:a,d=a,e=b.selectArray.length;e>d;d++)b.selectArray[d].empty().prop("disabled",!0),"none"===b.settings.emptyStyle?c.display="none":"hidden"===b.settings.emptyStyle&&(c.visibility="hidden"),b.selectArray[d].css(c)},b.start=function(c){var e,f,d=this;if(a.isArray(c)&&(d.settings.data=b.getData(c,d.settings.jsonSpace)),d.selectArray.length){for(e=0,f=d.selectArray.length;f>e;e++)"string"!=typeof d.selectArray[e].attr("data-value")&&d.selectArray[e][0].options.length&&d.selectArray[e].attr("data-value",d.selectArray[e].val());d.settings.data||"string"==typeof d.selectArray[0].data("url")&&d.selectArray[0].data("url").length?b.getOptionData.call(d,0):d.selectArray[0].prop("disabled",!1).css({display:"",visibility:""})}},b.getOptionData=function(c){var f,g,h,i,j,k,l,m,n,o,p,d=this;if(!("number"!=typeof c||isNaN(c)||0>c||c>=d.selectArray.length))if(f=d.selectArray[c],i=f.data("url"),j="undefined"==typeof f.data("jsonSpace")?d.settings.jsonSpace:f.data("jsonSpace"),k={},b.clear.call(d,c),"string"==typeof i&&i.length){if(c>0)for(o=0,p=1;c>o;o++,p++)l=d.selectArray[p].data("queryName"),m=d.selectArray[o].attr("name"),n=d.selectArray[o].val(),"string"==typeof l&&l.length?k[l]=n:"string"==typeof m&&m.length&&(k[m]=n);a.getJSON(i,k,function(a){g=b.getData(a,j),b.buildOption.call(d,c,g)})}else if(d.settings.data&&"object"==typeof d.settings.data){for(g=d.settings.data,o=0;c>o;o++){if(h=b.getIndex(d.selectArray[o][0].selectedIndex,"boolean"==typeof d.selectArray[o].data("required")?d.selectArray[o].data("required"):d.settings.required),"object"!=typeof g[h]||!a.isArray(g[h][d.settings.jsonSub])||!g[h][d.settings.jsonSub].length){g=null;break}g=g[h][d.settings.jsonSub]}b.buildOption.call(d,c,g)}},b.buildOption=function(b,c){var k,l,m,d=this,e=d.selectArray[b],f="boolean"==typeof e.data("required")?e.data("required"):d.settings.required,g="undefined"==typeof e.data("firstTitle")?d.settings.firstTitle:e.data("firstTitle"),h="undefined"==typeof e.data("firstValue")?d.settings.firstValue:e.data("firstValue"),i="undefined"==typeof e.data("jsonName")?d.settings.jsonName:e.data("jsonName"),j="undefined"==typeof e.data("jsonValue")?d.settings.jsonValue:e.data("jsonValue");if(a.isArray(c)){if(k=f?"":'","string"==typeof i&&i.length)for("string"==typeof j&&j.length||(j=i),l=0,m=c.length;m>l;l++)k+='";else for(l=0,m=c.length;m>l;l++)k+='";e.html(k).prop("disabled",!1).css({display:"",visibility:""}),"string"==typeof e.attr("data-value")&&(e.val(String(e.attr("data-value"))).removeAttr("data-value"),e[0].selectedIndex<0&&(e[0].options[0].selected=!0)),(f||e[0].selectedIndex>0)&&e.trigger("change")}},b.selectChange=function(a){var d,e,f,c=this;if("string"==typeof a&&a.length){for(a=a.replace(/\s+/g,","),a=","+a+",",e=0,f=c.selectArray.length;f>e;e++)if(a.indexOf(","+c.settings.selects[e]+",")>-1){d=e;break}"number"==typeof d&&d>-1&&(d+=1,b.getOptionData.call(c,d))}},a.cxSelect=function(){return b.apply(this,arguments)},a.cxSelect.defaults={selects:[],url:null,data:null,emptyStyle:null,required:!1,firstTitle:"请选择",firstValue:"",jsonSpace:"",jsonName:"n",jsonValue:"",jsonSub:"s"},a.fn.cxSelect=function(b,c){return this.each(function(){a.cxSelect(this,b,c)}),this}}); \ No newline at end of file diff --git a/app/assets/javascripts/jquery.validate.js b/app/assets/javascripts/jquery.validate.js index 70297bd5c..d025319db 100644 --- a/app/assets/javascripts/jquery.validate.js +++ b/app/assets/javascripts/jquery.validate.js @@ -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 $; })); \ No newline at end of file diff --git a/app/assets/javascripts/select2.js b/app/assets/javascripts/select2.js old mode 100755 new mode 100644 diff --git a/app/assets/javascripts/select2.min.js b/app/assets/javascripts/select2.min.js old mode 100755 new mode 100644 diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index 33d585d83..0aa1329ca 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -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; } \ No newline at end of file diff --git a/app/assets/stylesheets/admins/common.scss b/app/assets/stylesheets/admins/common.scss index 9830df3c7..2254166fd 100644 --- a/app/assets/stylesheets/admins/common.scss +++ b/app/assets/stylesheets/admins/common.scss @@ -100,5 +100,11 @@ font-size: 24px; } } + + .nav-tabs { + .nav-link { + padding: 0.5rem 2rem; + } + } } diff --git a/app/assets/stylesheets/admins/daily-school-statistics.scss b/app/assets/stylesheets/admins/daily_school_statistics.scss similarity index 100% rename from app/assets/stylesheets/admins/daily-school-statistics.scss rename to app/assets/stylesheets/admins/daily_school_statistics.scss diff --git a/app/assets/stylesheets/admins/identity_authentications.scss b/app/assets/stylesheets/admins/identity_authentications.scss new file mode 100644 index 000000000..417581058 --- /dev/null +++ b/app/assets/stylesheets/admins/identity_authentications.scss @@ -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; } + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/admins/professional_authentications.scss b/app/assets/stylesheets/admins/professional_authentications.scss new file mode 100644 index 000000000..6ef78f92d --- /dev/null +++ b/app/assets/stylesheets/admins/professional_authentications.scss @@ -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; } + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/admins/school-statistics.scss b/app/assets/stylesheets/admins/school_statistics.scss similarity index 100% rename from app/assets/stylesheets/admins/school-statistics.scss rename to app/assets/stylesheets/admins/school_statistics.scss diff --git a/app/assets/stylesheets/admins/shixun_authorizations.scss b/app/assets/stylesheets/admins/shixun_authorizations.scss new file mode 100644 index 000000000..05bcbc2bb --- /dev/null +++ b/app/assets/stylesheets/admins/shixun_authorizations.scss @@ -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; } + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/admins/subject_authorizations.scss b/app/assets/stylesheets/admins/subject_authorizations.scss new file mode 100644 index 000000000..f16c30151 --- /dev/null +++ b/app/assets/stylesheets/admins/subject_authorizations.scss @@ -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; } + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/select2-bootstrap4.min.scss b/app/assets/stylesheets/select2-bootstrap4.min.scss old mode 100755 new mode 100644 diff --git a/app/assets/stylesheets/select2.min.scss b/app/assets/stylesheets/select2.min.scss old mode 100755 new mode 100644 diff --git a/app/controllers/admins/identity_authentications_controller.rb b/app/controllers/admins/identity_authentications_controller.rb new file mode 100644 index 000000000..90758ace8 --- /dev/null +++ b/app/controllers/admins/identity_authentications_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/admins/professional_authentications_controller.rb b/app/controllers/admins/professional_authentications_controller.rb new file mode 100644 index 000000000..327ee696c --- /dev/null +++ b/app/controllers/admins/professional_authentications_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/admins/shixun_authorizations_controller.rb b/app/controllers/admins/shixun_authorizations_controller.rb new file mode 100644 index 000000000..31bd5faf7 --- /dev/null +++ b/app/controllers/admins/shixun_authorizations_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/admins/subject_authorizations_controller.rb b/app/controllers/admins/subject_authorizations_controller.rb new file mode 100644 index 000000000..3d3733fa4 --- /dev/null +++ b/app/controllers/admins/subject_authorizations_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/concerns/admins/error_rescue_handler.rb b/app/controllers/concerns/admins/error_rescue_handler.rb index b1e29d5ce..ceb810f36 100644 --- a/app/controllers/concerns/admins/error_rescue_handler.rb +++ b/app/controllers/concerns/admins/error_rescue_handler.rb @@ -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 diff --git a/app/controllers/concerns/admins/render_helper.rb b/app/controllers/concerns/admins/render_helper.rb index c77c0ad32..0ccc16a09 100644 --- a/app/controllers/concerns/admins/render_helper.rb +++ b/app/controllers/concerns/admins/render_helper.rb @@ -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 } diff --git a/app/controllers/concerns/git_helper.rb b/app/controllers/concerns/git_helper.rb index 6a3765401..eeb4671a4 100644 --- a/app/controllers/concerns/git_helper.rb +++ b/app/controllers/concerns/git_helper.rb @@ -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 diff --git a/app/controllers/courses_controller.rb b/app/controllers/courses_controller.rb index 470315bdc..e29fa8d7d 100644 --- a/app/controllers/courses_controller.rb +++ b/app/controllers/courses_controller.rb @@ -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' diff --git a/app/controllers/exercises_controller.rb b/app/controllers/exercises_controller.rb index c9162448b..5db704fce 100644 --- a/app/controllers/exercises_controller.rb +++ b/app/controllers/exercises_controller.rb @@ -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]) diff --git a/app/controllers/games_controller.rb b/app/controllers/games_controller.rb index c7a598a43..58e4683e0 100644 --- a/app/controllers/games_controller.rb +++ b/app/controllers/games_controller.rb @@ -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 diff --git a/app/controllers/graduation_tasks_controller.rb b/app/controllers/graduation_tasks_controller.rb index 0885eeae2..727bdb77e 100644 --- a/app/controllers/graduation_tasks_controller.rb +++ b/app/controllers/graduation_tasks_controller.rb @@ -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 diff --git a/app/controllers/homework_commons_controller.rb b/app/controllers/homework_commons_controller.rb index b95d0d2b4..081e2fdf1 100644 --- a/app/controllers/homework_commons_controller.rb +++ b/app/controllers/homework_commons_controller.rb @@ -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) diff --git a/app/controllers/polls_controller.rb b/app/controllers/polls_controller.rb index ba3030424..0f301dd4f 100644 --- a/app/controllers/polls_controller.rb +++ b/app/controllers/polls_controller.rb @@ -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]) diff --git a/app/controllers/shixuns_controller.rb b/app/controllers/shixuns_controller.rb index bfd386e1c..d9ab31b8d 100644 --- a/app/controllers/shixuns_controller.rb +++ b/app/controllers/shixuns_controller.rb @@ -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 diff --git a/app/helpers/admins/base_helper.rb b/app/helpers/admins/base_helper.rb new file mode 100644 index 000000000..c655be2e7 --- /dev/null +++ b/app/helpers/admins/base_helper.rb @@ -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 \ No newline at end of file diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 0f536433d..44169dc59 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -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 diff --git a/app/helpers/exercises_helper.rb b/app/helpers/exercises_helper.rb index 0b18ff6f8..2c417ea74 100644 --- a/app/helpers/exercises_helper.rb +++ b/app/helpers/exercises_helper.rb @@ -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 diff --git a/app/libs/util/file_manage.rb b/app/libs/util/file_manage.rb new file mode 100644 index 000000000..620fd7f01 --- /dev/null +++ b/app/libs/util/file_manage.rb @@ -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 \ No newline at end of file diff --git a/app/models/apply_action.rb b/app/models/apply_action.rb index 2d31f394d..54bdaa396 100644 --- a/app/models/apply_action.rb +++ b/app/models/apply_action.rb @@ -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', diff --git a/app/models/apply_user_authentication.rb b/app/models/apply_user_authentication.rb index d467eb850..c379fb82e 100644 --- a/app/models/apply_user_authentication.rb +++ b/app/models/apply_user_authentication.rb @@ -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 diff --git a/app/models/course.rb b/app/models/course.rb index e2fc5140b..5a2a065ba 100644 --- a/app/models/course.rb +++ b/app/models/course.rb @@ -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 # 课堂左侧导航栏的模块 diff --git a/app/models/homework_common.rb b/app/models/homework_common.rb index 790a9b7ab..ac11a54b7 100644 --- a/app/models/homework_common.rb +++ b/app/models/homework_common.rb @@ -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 : diff --git a/app/models/inform.rb b/app/models/inform.rb index faf1d48e6..5caf80c5f 100644 --- a/app/models/inform.rb +++ b/app/models/inform.rb @@ -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 diff --git a/app/models/subject.rb b/app/models/subject.rb index 3c94ad870..4b7f4dbcd 100644 --- a/app/models/subject.rb +++ b/app/models/subject.rb @@ -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 diff --git a/app/queries/admins/apply_user_authentication_query.rb b/app/queries/admins/apply_user_authentication_query.rb new file mode 100644 index 000000000..a4d1c2a4a --- /dev/null +++ b/app/queries/admins/apply_user_authentication_query.rb @@ -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 \ No newline at end of file diff --git a/app/services/admins/identity_auths/agree_apply_service.rb b/app/services/admins/identity_auths/agree_apply_service.rb new file mode 100644 index 000000000..65a3b2376 --- /dev/null +++ b/app/services/admins/identity_auths/agree_apply_service.rb @@ -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 \ No newline at end of file diff --git a/app/services/admins/identity_auths/refuse_apply_service.rb b/app/services/admins/identity_auths/refuse_apply_service.rb new file mode 100644 index 000000000..57581dd40 --- /dev/null +++ b/app/services/admins/identity_auths/refuse_apply_service.rb @@ -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 \ No newline at end of file diff --git a/app/services/admins/professional_auths/agree_apply_service.rb b/app/services/admins/professional_auths/agree_apply_service.rb new file mode 100644 index 000000000..81654f0d3 --- /dev/null +++ b/app/services/admins/professional_auths/agree_apply_service.rb @@ -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 \ No newline at end of file diff --git a/app/services/admins/professional_auths/refuse_apply_service.rb b/app/services/admins/professional_auths/refuse_apply_service.rb new file mode 100644 index 000000000..b5332c999 --- /dev/null +++ b/app/services/admins/professional_auths/refuse_apply_service.rb @@ -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 \ No newline at end of file diff --git a/app/services/admins/shixun_auths/agree_apply_service.rb b/app/services/admins/shixun_auths/agree_apply_service.rb new file mode 100644 index 000000000..4734e03bb --- /dev/null +++ b/app/services/admins/shixun_auths/agree_apply_service.rb @@ -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 \ No newline at end of file diff --git a/app/services/admins/shixun_auths/refuse_apply_service.rb b/app/services/admins/shixun_auths/refuse_apply_service.rb new file mode 100644 index 000000000..49416a2b0 --- /dev/null +++ b/app/services/admins/shixun_auths/refuse_apply_service.rb @@ -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 \ No newline at end of file diff --git a/app/services/admins/subject_auths/agree_apply_service.rb b/app/services/admins/subject_auths/agree_apply_service.rb new file mode 100644 index 000000000..465e3e903 --- /dev/null +++ b/app/services/admins/subject_auths/agree_apply_service.rb @@ -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 \ No newline at end of file diff --git a/app/services/admins/subject_auths/refuse_apply_service.rb b/app/services/admins/subject_auths/refuse_apply_service.rb new file mode 100644 index 000000000..f51d55185 --- /dev/null +++ b/app/services/admins/subject_auths/refuse_apply_service.rb @@ -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 \ No newline at end of file diff --git a/app/templates/shared/main.css b/app/templates/shared/main.css index c77818f16..9e9bc4f50 100644 --- a/app/templates/shared/main.css +++ b/app/templates/shared/main.css @@ -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; diff --git a/app/views/admins/identity_authentications/index.html.erb b/app/views/admins/identity_authentications/index.html.erb new file mode 100644 index 000000000..170a8fc4a --- /dev/null +++ b/app/views/admins/identity_authentications/index.html.erb @@ -0,0 +1,32 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('实名认证') %> +<% end %> + +
+ + + <%= form_tag(admins_identity_authentications_path(unsafe_params), method: :get, class: 'form-inline search-form justify-content-end mt-3', remote: true) do %> +
+ + <% status_options = [['全部', 'processed'], ['已同意', 'agreed'], ['已拒绝', 'refused']] %> + <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %> +
+ <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '姓名/学校/单位检索') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3') %> + <% end %> +
+ +
+ <%= render(partial: 'admins/identity_authentications/shared/list', locals: { applies: @applies }) %> +
+ +<%= render(partial: 'admins/shared/admin_common_refuse_modal') %> \ No newline at end of file diff --git a/app/views/admins/identity_authentications/index.js.erb b/app/views/admins/identity_authentications/index.js.erb new file mode 100644 index 000000000..81d108b0d --- /dev/null +++ b/app/views/admins/identity_authentications/index.js.erb @@ -0,0 +1 @@ +$('.identity-authentication-list-container').html("<%= j( render partial: 'admins/identity_authentications/shared/list', locals: { applies: @applies } ) %>"); \ No newline at end of file diff --git a/app/views/admins/identity_authentications/shared/_list.html.erb b/app/views/admins/identity_authentications/shared/_list.html.erb new file mode 100644 index 000000000..f6aa53dd1 --- /dev/null +++ b/app/views/admins/identity_authentications/shared/_list.html.erb @@ -0,0 +1,74 @@ +<% is_processed = params[:status].to_s != 'pending' %> + + + + + + + + + + <% unless is_processed %> + + <% end %> + + <% if is_processed %> + + + <% else %> + + <% end %> + + + + <% if applies.present? %> + <% applies.each do |apply| %> + <% user = apply.user %> + + + + + + + + <% unless is_processed %> + + <% end %> + + + + <% if is_processed %> + + + <% else %> + + <% end %> + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
头像姓名身份证号学校/单位职称 + 照片 + + 时间拒绝原因状态操作
+ <%= link_to "/users/#{user.login}", class: 'identity-authentication-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %> + + <% end %> + <%= user.real_name %><%= user.ID_number %><%= raw [user.school_name.presence, user.department_name.presence].compact.join('
') %>
<%= user.identity %> <%= raw user.user_extension.student? && user.student_id ? "
#{user.student_id}" : '' %>
+ <% 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 %> + <%= apply.updated_at.strftime('%Y-%m-%d %H:%M') %><%= overflow_hidden_span apply.remarks, width: 140 %><%= apply.status_text %> + <%= 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}") + }) %> +
+ +<%= render partial: 'admins/shared/paginate', locals: { objects: applies } %> \ No newline at end of file diff --git a/app/views/admins/professional_authentications/index.html.erb b/app/views/admins/professional_authentications/index.html.erb new file mode 100644 index 000000000..e10d2bd80 --- /dev/null +++ b/app/views/admins/professional_authentications/index.html.erb @@ -0,0 +1,32 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('职业认证') %> +<% end %> + +
+ + + <%= form_tag(admins_professional_authentications_path(unsafe_params), method: :get, class: 'form-inline search-form justify-content-end mt-3', remote: true) do %> +
+ + <% status_options = [['全部', 'processed'], ['已同意', 'agreed'], ['已拒绝', 'refused']] %> + <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %> +
+ <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '姓名/学校/单位检索') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3') %> + <% end %> +
+ +
+ <%= render(partial: 'admins/professional_authentications/shared/list', locals: { applies: @applies }) %> +
+ +<%= render(partial: 'admins/shared/admin_common_refuse_modal') %> \ No newline at end of file diff --git a/app/views/admins/professional_authentications/index.js.erb b/app/views/admins/professional_authentications/index.js.erb new file mode 100644 index 000000000..468c131d6 --- /dev/null +++ b/app/views/admins/professional_authentications/index.js.erb @@ -0,0 +1 @@ +$('.professional-authentication-list-container').html("<%= j( render partial: 'admins/professional_authentications/shared/list', locals: { applies: @applies } ) %>"); \ No newline at end of file diff --git a/app/views/admins/professional_authentications/shared/_list.html.erb b/app/views/admins/professional_authentications/shared/_list.html.erb new file mode 100644 index 000000000..fd7685f98 --- /dev/null +++ b/app/views/admins/professional_authentications/shared/_list.html.erb @@ -0,0 +1,72 @@ +<% is_processed = params[:status].to_s != 'pending' %> + + + + + + + + + <% unless is_processed %> + + <% end %> + + <% if is_processed %> + + + <% else %> + + <% end %> + + + + <% if applies.present? %> + <% applies.each do |apply| %> + <% user = apply.user %> + + + + + + + <% unless is_processed %> + + <% end %> + + + + <% if is_processed %> + + + <% else %> + + <% end %> + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
头像姓名学校/单位职称 + 照片 + + 时间拒绝原因状态操作
+ <%= link_to "/users/#{user.login}", class: 'professional-authentication-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %> + + <% end %> + <%= user.real_name %><%= raw [user.school_name.presence, user.department_name.presence].compact.join('
') %>
<%= user.identity %> <%= raw user.user_extension.student? && user.student_id ? "
#{user.student_id}" : '' %>
+ <% 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 %> + <%= apply.updated_at.strftime('%Y-%m-%d %H:%M') %><%= overflow_hidden_span apply.remarks, width: 140 %><%= apply.status_text %> + <%= 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}") + }) %> +
+ +<%= render partial: 'admins/shared/paginate', locals: { objects: applies } %> \ No newline at end of file diff --git a/app/views/admins/school_statistics/index.html.erb b/app/views/admins/school_statistics/index.html.erb index fd0c7cac1..0453dfffa 100644 --- a/app/views/admins/school_statistics/index.html.erb +++ b/app/views/admins/school_statistics/index.html.erb @@ -32,9 +32,9 @@
<%= 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开始计算,下表显示选定时间段内各项指标数据变化情况' } %>
diff --git a/app/views/admins/shared/_admin_common_refuse_modal.html.erb b/app/views/admins/shared/_admin_common_refuse_modal.html.erb new file mode 100644 index 000000000..a2daf7f0c --- /dev/null +++ b/app/views/admins/shared/_admin_common_refuse_modal.html.erb @@ -0,0 +1,26 @@ + \ No newline at end of file diff --git a/app/views/admins/shared/_sidebar.html.erb b/app/views/admins/shared/_sidebar.html.erb index cd64f3d0f..ab30e8bd3 100644 --- a/app/views/admins/shared/_sidebar.html.erb +++ b/app/views/admins/shared/_sidebar.html.erb @@ -37,6 +37,15 @@ <% end %> +
  • + <%= sidebar_item_group('#apply-review-submenu', '审核', icon: 'gavel') do %> +
  • <%= sidebar_item(admins_identity_authentications_path, '实名认证', icon: 'id-card-o', controller: 'admins-identity_authentications') %>
  • +
  • <%= sidebar_item(admins_professional_authentications_path, '职业认证', icon: 'drivers-license', controller: 'admins-professional_authentications') %>
  • +
  • <%= sidebar_item(admins_shixun_authorizations_path, '实训发布', icon: 'object-ungroup', controller: 'admins-shixun_authorizations') %>
  • +
  • <%= sidebar_item(admins_subject_authorizations_path, '实践课程发布', icon: 'object-group', controller: 'admins-subject_authorizations') %>
  • + <% end %> + +
  • <%= sidebar_item('/', '返回主站', icon: 'sign-out', controller: 'root') %>
  • \ No newline at end of file diff --git a/app/views/admins/shared/after_render_js_hook.js.erb b/app/views/admins/shared/after_render_js_hook.js.erb index 9ceb13f5c..efc1e9295 100644 --- a/app/views/admins/shared/after_render_js_hook.js.erb +++ b/app/views/admins/shared/after_render_js_hook.js.erb @@ -1,3 +1,4 @@ ; $('[data-toggle="tooltip"]').tooltip(); -$('[data-toggle="popover"]').popover(); \ No newline at end of file +$('[data-toggle="popover"]').popover(); +$('img.preview-image').bootstrapViewer(); \ No newline at end of file diff --git a/app/views/admins/shared/delete.js.erb b/app/views/admins/shared/delete.js.erb index a10e1ae28..74512d55b 100644 --- a/app/views/admins/shared/delete.js.erb +++ b/app/views/admins/shared/delete.js.erb @@ -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, diff --git a/app/views/admins/shared/error.js.erb b/app/views/admins/shared/error.js.erb index ebb78aec6..261796d0b 100644 --- a/app/views/admins/shared/error.js.erb +++ b/app/views/admins/shared/error.js.erb @@ -4,4 +4,7 @@ setTimeout(function() { if ($('.admin-alert-container button.close').length > 0) { $('.admin-alert-container button.close').trigger('click'); } -}, 2000) \ No newline at end of file +}, 5000) +$(".admin-body-container").animate({ + scrollTop: 0 +}, 200); \ No newline at end of file diff --git a/app/views/admins/shixun_authorizations/index.html.erb b/app/views/admins/shixun_authorizations/index.html.erb new file mode 100644 index 000000000..743f2e1b2 --- /dev/null +++ b/app/views/admins/shixun_authorizations/index.html.erb @@ -0,0 +1,32 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('实训发布') %> +<% end %> + +
    + + + <%= form_tag(admins_shixun_authorizations_path(unsafe_params), method: :get, class: 'form-inline search-form justify-content-end mt-3', remote: true) do %> +
    + + <% status_options = [['全部', 'processed'], ['已同意', 'agreed'], ['已拒绝', 'refused']] %> + <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %> +
    + <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '实训名称检索') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3') %> + <% end %> +
    + +
    + <%= render(partial: 'admins/shixun_authorizations/shared/list', locals: { applies: @applies, shixun_map: @shixun_map }) %> +
    + +<%= render(partial: 'admins/shared/admin_common_refuse_modal') %> \ No newline at end of file diff --git a/app/views/admins/shixun_authorizations/index.js.erb b/app/views/admins/shixun_authorizations/index.js.erb new file mode 100644 index 000000000..caa8bb389 --- /dev/null +++ b/app/views/admins/shixun_authorizations/index.js.erb @@ -0,0 +1 @@ +$('.shixun-authorization-list-container').html("<%= j( render partial: 'admins/shixun_authorizations/shared/list', locals: { applies: @applies, shixun_map: @shixun_map } ) %>"); \ No newline at end of file diff --git a/app/views/admins/shixun_authorizations/shared/_list.html.erb b/app/views/admins/shixun_authorizations/shared/_list.html.erb new file mode 100644 index 000000000..ce3b4ca43 --- /dev/null +++ b/app/views/admins/shixun_authorizations/shared/_list.html.erb @@ -0,0 +1,60 @@ +<% is_processed = params[:status].to_s != 'pending' %> + + + + + + + + + + <% if is_processed %> + + + <% else %> + + <% end %> + + + + <% if applies.present? %> + <% applies.each do |apply| %> + <% user = apply.user %> + <% shixun = shixun_map[apply.container_id] %> + + + + + + + + <% if is_processed %> + + + <% else %> + + <% end %> + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
    头像创建者实训名称任务数时间拒绝原因状态操作
    + <%= link_to "/users/#{user.login}", class: 'shixun-authorization-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %> + + <% end %> + <%= user.real_name %> + <%= link_to "/shixuns/#{shixun.identifier}", target: '_blank' do %> + <%= overflow_hidden_span shixun.name, width: 300 %> + <% end %> + <%= shixun.challenges_count %><%= apply.updated_at.strftime('%Y-%m-%d %H:%M') %><%= overflow_hidden_span apply.reason, width: 140 %><%= apply.status_text %> + <%= 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}") + }) %> +
    + +<%= render partial: 'admins/shared/paginate', locals: { objects: applies } %> \ No newline at end of file diff --git a/app/views/admins/subject_authorizations/index.html.erb b/app/views/admins/subject_authorizations/index.html.erb new file mode 100644 index 000000000..3d5539663 --- /dev/null +++ b/app/views/admins/subject_authorizations/index.html.erb @@ -0,0 +1,33 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('实践课程发布') %> +<% end %> + +
    + + + <%= form_tag(admins_subject_authorizations_path(unsafe_params), method: :get, class: 'form-inline search-form justify-content-end mt-3', remote: true) do %> +
    + + <% status_options = [['全部', 'processed'], ['已同意', 'agreed'], ['已拒绝', 'refused']] %> + <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %> +
    + <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '实训课程名称检索') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3') %> + <% end %> +
    + +
    + <%= render(partial: 'admins/subject_authorizations/shared/list', + locals: { applies: @applies, subject_map: @subject_map, challenge_count_map: @challenge_count_map }) %> +
    + +<%= render(partial: 'admins/shared/admin_common_refuse_modal') %> \ No newline at end of file diff --git a/app/views/admins/subject_authorizations/index.js.erb b/app/views/admins/subject_authorizations/index.js.erb new file mode 100644 index 000000000..a21809041 --- /dev/null +++ b/app/views/admins/subject_authorizations/index.js.erb @@ -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 } ) %>"); \ No newline at end of file diff --git a/app/views/admins/subject_authorizations/shared/_list.html.erb b/app/views/admins/subject_authorizations/shared/_list.html.erb new file mode 100644 index 000000000..7b8da800d --- /dev/null +++ b/app/views/admins/subject_authorizations/shared/_list.html.erb @@ -0,0 +1,64 @@ +<% is_processed = params[:status].to_s != 'pending' %> + + + + + + + + + + + + <% if is_processed %> + + + <% else %> + + <% end %> + + + + <% if applies.present? %> + <% applies.each do |apply| %> + <% user = apply.user %> + <% subject = subject_map[apply.container_id] %> + + + + + + + + + + <% if is_processed %> + + + <% else %> + + <% end %> + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
    头像创建者实践课程名称阶段数实训数关卡数时间拒绝原因状态操作
    + <%= link_to "/users/#{user.login}", class: 'subject-authorization-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %> + + <% end %> + <%= user.real_name %> + <%= link_to "/paths/#{subject.id}", target: '_blank' do %> + <%= overflow_hidden_span subject.name, width: 300 %> + <% end %> + <%= subject.stages_count %><%= subject.shixuns_count %><%= challenge_count_map.fetch(subject.id, 0) %><%= apply.updated_at.strftime('%Y-%m-%d %H:%M') %><%= overflow_hidden_span apply.reason, width: 140 %><%= apply.status_text %> + <%= 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}") + }) %> +
    + +<%= render partial: 'admins/shared/paginate', locals: { objects: applies } %> \ No newline at end of file diff --git a/app/views/admins/users/shared/_user_list.html.erb b/app/views/admins/users/shared/_user_list.html.erb index e23e918fb..36b5d0f6e 100644 --- a/app/views/admins/users/shared/_user_list.html.erb +++ b/app/views/admins/users/shared/_user_list.html.erb @@ -1,29 +1,23 @@ - - + - - - - - + + + + + <% if users.present? %> <% users.each do |user| %> - - @@ -35,6 +29,8 @@
    ID真实姓名真实姓名 邮件地址 手机号码 单位<%= sort_tag('创建于', name: 'created_on', path: admins_users_path) %><%= sort_tag('最后登录', name: 'last_login_on', path: admins_users_path) %><%= sort_tag('经验值', name: 'experience', path: admins_users_path) %><%= sort_tag('金币', name: 'grade', path: admins_users_path) %>操作<%= sort_tag('创建于', name: 'created_on', path: admins_users_path) %><%= sort_tag('最后登录', name: 'last_login_on', path: admins_users_path) %><%= sort_tag('经验值', name: 'experience', path: admins_users_path) %><%= sort_tag('金币', name: 'grade', path: admins_users_path) %>操作
    + <%= link_to "/users/#{user.login}", target: '_blank' do %> - <%= overflow_hidden_span user.login, width: 100 %> - <% end %> - - <%= link_to edit_admins_user_path(user) do %> <%= overflow_hidden_span user.real_name, width: 100 %> <% end %> <%= user.experience.to_i %> <%= user.grade.to_i %> + <%= 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;' %> diff --git a/app/views/courses/informs.json.jbuilder b/app/views/courses/informs.json.jbuilder index d584be917..7fc396184 100644 --- a/app/views/courses/informs.json.jbuilder +++ b/app/views/courses/informs.json.jbuilder @@ -1 +1,5 @@ -json.description @course.inform&.description \ No newline at end of file +json.informs @informs do |inform| + json.id inform.id + json.name inform.name + json.description inform.description +end \ No newline at end of file diff --git a/app/views/exercise_questions/_exercise_questions.json.jbuilder b/app/views/exercise_questions/_exercise_questions.json.jbuilder index bbdf45440..940022a9f 100644 --- a/app/views/exercise_questions/_exercise_questions.json.jbuilder +++ b/app/views/exercise_questions/_exercise_questions.json.jbuilder @@ -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 diff --git a/app/views/exercises/_shixun_details.json.jbuilder b/app/views/exercises/_shixun_details.json.jbuilder index 29a39a594..bd559d37d 100644 --- a/app/views/exercises/_shixun_details.json.jbuilder +++ b/app/views/exercises/_shixun_details.json.jbuilder @@ -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 diff --git a/app/views/homework_commons/_homework_public_navigation.json.jbuilder b/app/views/homework_commons/_homework_public_navigation.json.jbuilder index b5067e3c7..243c6aecd 100644 --- a/app/views/homework_commons/_homework_public_navigation.json.jbuilder +++ b/app/views/homework_commons/_homework_public_navigation.json.jbuilder @@ -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] diff --git a/app/views/subjects/show.json.jbuilder b/app/views/subjects/show.json.jbuilder index 53e76a96f..997e0c3d6 100644 --- a/app/views/subjects/show.json.jbuilder +++ b/app/views/subjects/show.json.jbuilder @@ -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) diff --git a/config/environments/development.rb b/config/environments/development.rb index 0b31f5828..f0b3f1775 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -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 diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 5298b4ab6..922969938 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -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 ) diff --git a/config/locales/apply_actions/zh-CN.yml b/config/locales/apply_actions/zh-CN.yml new file mode 100644 index 000000000..933ef6223 --- /dev/null +++ b/config/locales/apply_actions/zh-CN.yml @@ -0,0 +1,7 @@ +zh-CN: + apply_action: + status: + '0': '待处理' + '1': '已同意' + '2': '已拒绝' + '3': '已撤销' \ No newline at end of file diff --git a/config/locales/apply_user_authentications/zh-CN.yml b/config/locales/apply_user_authentications/zh-CN.yml new file mode 100644 index 000000000..e886c2695 --- /dev/null +++ b/config/locales/apply_user_authentications/zh-CN.yml @@ -0,0 +1,7 @@ +zh-CN: + apply_user_authentication: + status: + '0': '待处理' + '1': '已同意' + '2': '已拒绝' + '3': '已撤销' \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index de4f46a40..7e2cf72f3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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 认证回调 diff --git a/db/migrate/20190823023738_change_exericse_1936_scores.rb b/db/migrate/20190823090957_rechange_exercise_1936_scores.rb similarity index 93% rename from db/migrate/20190823023738_change_exericse_1936_scores.rb rename to db/migrate/20190823090957_rechange_exercise_1936_scores.rb index ea3f66182..f089714ee 100644 --- a/db/migrate/20190823023738_change_exericse_1936_scores.rb +++ b/db/migrate/20190823090957_rechange_exercise_1936_scores.rb @@ -1,4 +1,4 @@ -class ChangeExericse1936Scores < ActiveRecord::Migration[5.2] +class RechangeExercise1936Scores < ActiveRecord::Migration[5.2] include ExercisesHelper def change #1936的试卷成绩有问题。 diff --git a/db/migrate/20190824032658_migrate_subject_shixun_count.rb b/db/migrate/20190824032658_migrate_subject_shixun_count.rb new file mode 100644 index 000000000..bef29346e --- /dev/null +++ b/db/migrate/20190824032658_migrate_subject_shixun_count.rb @@ -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 diff --git a/lib/tasks/course_end.rake b/lib/tasks/course_end.rake index d8e87eacf..841c30b27 100644 --- a/lib/tasks/course_end.rake +++ b/lib/tasks/course_end.rake @@ -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 diff --git a/lib/tasks/excellent_course_exercise.rake b/lib/tasks/excellent_course_exercise.rake index a7fac1a8f..4309eec26 100644 --- a/lib/tasks/excellent_course_exercise.rake +++ b/lib/tasks/excellent_course_exercise.rake @@ -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? #把关卡的答案存入试卷的实训里 diff --git a/public/assets/.sprockets-manifest-1c370772f16743f825981ab0e5c94237.json b/public/assets/.sprockets-manifest-1c370772f16743f825981ab0e5c94237.json new file mode 100644 index 000000000..5f5a55fd7 --- /dev/null +++ b/public/assets/.sprockets-manifest-1c370772f16743f825981ab0e5c94237.json @@ -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"}} \ No newline at end of file diff --git a/public/assets/admin-6575f1399953fb1935c037a7b8bd28c4aff07b70bed9b41faf6899a89af4b57d.js b/public/assets/admin-6575f1399953fb1935c037a7b8bd28c4aff07b70bed9b41faf6899a89af4b57d.js new file mode 100644 index 000000000..2ff813ff4 --- /dev/null +++ b/public/assets/admin-6575f1399953fb1935c037a7b8bd28c4aff07b70bed9b41faf6899a89af4b57d.js @@ -0,0 +1,28296 @@ +/* +Unobtrusive JavaScript +https://github.com/rails/rails/blob/master/actionview/app/assets/javascripts +Released under the MIT license + */ + + +(function() { + var context = this; + + (function() { + (function() { + this.Rails = { + linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]', + buttonClickSelector: { + selector: 'button[data-remote]:not([form]), button[data-confirm]:not([form])', + exclude: 'form button' + }, + inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]', + formSubmitSelector: 'form', + formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])', + formDisableSelector: 'input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled', + formEnableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled', + fileInputSelector: 'input[name][type=file]:not([disabled])', + linkDisableSelector: 'a[data-disable-with], a[data-disable]', + buttonDisableSelector: 'button[data-remote][data-disable-with], button[data-remote][data-disable]' + }; + + }).call(this); + }).call(context); + + var Rails = context.Rails; + + (function() { + (function() { + var cspNonce; + + cspNonce = Rails.cspNonce = function() { + var meta; + meta = document.querySelector('meta[name=csp-nonce]'); + return meta && meta.content; + }; + + }).call(this); + (function() { + var expando, m; + + m = Element.prototype.matches || Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector; + + Rails.matches = function(element, selector) { + if (selector.exclude != null) { + return m.call(element, selector.selector) && !m.call(element, selector.exclude); + } else { + return m.call(element, selector); + } + }; + + expando = '_ujsData'; + + Rails.getData = function(element, key) { + var ref; + return (ref = element[expando]) != null ? ref[key] : void 0; + }; + + Rails.setData = function(element, key, value) { + if (element[expando] == null) { + element[expando] = {}; + } + return element[expando][key] = value; + }; + + Rails.$ = function(selector) { + return Array.prototype.slice.call(document.querySelectorAll(selector)); + }; + + }).call(this); + (function() { + var $, csrfParam, csrfToken; + + $ = Rails.$; + + csrfToken = Rails.csrfToken = function() { + var meta; + meta = document.querySelector('meta[name=csrf-token]'); + return meta && meta.content; + }; + + csrfParam = Rails.csrfParam = function() { + var meta; + meta = document.querySelector('meta[name=csrf-param]'); + return meta && meta.content; + }; + + Rails.CSRFProtection = function(xhr) { + var token; + token = csrfToken(); + if (token != null) { + return xhr.setRequestHeader('X-CSRF-Token', token); + } + }; + + Rails.refreshCSRFTokens = function() { + var param, token; + token = csrfToken(); + param = csrfParam(); + if ((token != null) && (param != null)) { + return $('form input[name="' + param + '"]').forEach(function(input) { + return input.value = token; + }); + } + }; + + }).call(this); + (function() { + var CustomEvent, fire, matches, preventDefault; + + matches = Rails.matches; + + CustomEvent = window.CustomEvent; + + if (typeof CustomEvent !== 'function') { + CustomEvent = function(event, params) { + var evt; + evt = document.createEvent('CustomEvent'); + evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail); + return evt; + }; + CustomEvent.prototype = window.Event.prototype; + preventDefault = CustomEvent.prototype.preventDefault; + CustomEvent.prototype.preventDefault = function() { + var result; + result = preventDefault.call(this); + if (this.cancelable && !this.defaultPrevented) { + Object.defineProperty(this, 'defaultPrevented', { + get: function() { + return true; + } + }); + } + return result; + }; + } + + fire = Rails.fire = function(obj, name, data) { + var event; + event = new CustomEvent(name, { + bubbles: true, + cancelable: true, + detail: data + }); + obj.dispatchEvent(event); + return !event.defaultPrevented; + }; + + Rails.stopEverything = function(e) { + fire(e.target, 'ujs:everythingStopped'); + e.preventDefault(); + e.stopPropagation(); + return e.stopImmediatePropagation(); + }; + + Rails.delegate = function(element, selector, eventType, handler) { + return element.addEventListener(eventType, function(e) { + var target; + target = e.target; + while (!(!(target instanceof Element) || matches(target, selector))) { + target = target.parentNode; + } + if (target instanceof Element && handler.call(target, e) === false) { + e.preventDefault(); + return e.stopPropagation(); + } + }); + }; + + }).call(this); + (function() { + var AcceptHeaders, CSRFProtection, createXHR, cspNonce, fire, prepareOptions, processResponse; + + cspNonce = Rails.cspNonce, CSRFProtection = Rails.CSRFProtection, fire = Rails.fire; + + AcceptHeaders = { + '*': '*/*', + text: 'text/plain', + html: 'text/html', + xml: 'application/xml, text/xml', + json: 'application/json, text/javascript', + script: 'text/javascript, application/javascript, application/ecmascript, application/x-ecmascript' + }; + + Rails.ajax = function(options) { + var xhr; + options = prepareOptions(options); + xhr = createXHR(options, function() { + var ref, response; + response = processResponse((ref = xhr.response) != null ? ref : xhr.responseText, xhr.getResponseHeader('Content-Type')); + if (Math.floor(xhr.status / 100) === 2) { + if (typeof options.success === "function") { + options.success(response, xhr.statusText, xhr); + } + } else { + if (typeof options.error === "function") { + options.error(response, xhr.statusText, xhr); + } + } + return typeof options.complete === "function" ? options.complete(xhr, xhr.statusText) : void 0; + }); + if ((options.beforeSend != null) && !options.beforeSend(xhr, options)) { + return false; + } + if (xhr.readyState === XMLHttpRequest.OPENED) { + return xhr.send(options.data); + } + }; + + prepareOptions = function(options) { + options.url = options.url || location.href; + options.type = options.type.toUpperCase(); + if (options.type === 'GET' && options.data) { + if (options.url.indexOf('?') < 0) { + options.url += '?' + options.data; + } else { + options.url += '&' + options.data; + } + } + if (AcceptHeaders[options.dataType] == null) { + options.dataType = '*'; + } + options.accept = AcceptHeaders[options.dataType]; + if (options.dataType !== '*') { + options.accept += ', */*; q=0.01'; + } + return options; + }; + + createXHR = function(options, done) { + var xhr; + xhr = new XMLHttpRequest(); + xhr.open(options.type, options.url, true); + xhr.setRequestHeader('Accept', options.accept); + if (typeof options.data === 'string') { + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); + } + if (!options.crossDomain) { + xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + } + CSRFProtection(xhr); + xhr.withCredentials = !!options.withCredentials; + xhr.onreadystatechange = function() { + if (xhr.readyState === XMLHttpRequest.DONE) { + return done(xhr); + } + }; + return xhr; + }; + + processResponse = function(response, type) { + var parser, script; + if (typeof response === 'string' && typeof type === 'string') { + if (type.match(/\bjson\b/)) { + try { + response = JSON.parse(response); + } catch (error) {} + } else if (type.match(/\b(?:java|ecma)script\b/)) { + script = document.createElement('script'); + script.setAttribute('nonce', cspNonce()); + script.text = response; + document.head.appendChild(script).parentNode.removeChild(script); + } else if (type.match(/\b(xml|html|svg)\b/)) { + parser = new DOMParser(); + type = type.replace(/;.+/, ''); + try { + response = parser.parseFromString(response, type); + } catch (error) {} + } + } + return response; + }; + + Rails.href = function(element) { + return element.href; + }; + + Rails.isCrossDomain = function(url) { + var e, originAnchor, urlAnchor; + originAnchor = document.createElement('a'); + originAnchor.href = location.href; + urlAnchor = document.createElement('a'); + try { + urlAnchor.href = url; + return !(((!urlAnchor.protocol || urlAnchor.protocol === ':') && !urlAnchor.host) || (originAnchor.protocol + '//' + originAnchor.host === urlAnchor.protocol + '//' + urlAnchor.host)); + } catch (error) { + e = error; + return true; + } + }; + + }).call(this); + (function() { + var matches, toArray; + + matches = Rails.matches; + + toArray = function(e) { + return Array.prototype.slice.call(e); + }; + + Rails.serializeElement = function(element, additionalParam) { + var inputs, params; + inputs = [element]; + if (matches(element, 'form')) { + inputs = toArray(element.elements); + } + params = []; + inputs.forEach(function(input) { + if (!input.name || input.disabled) { + return; + } + if (matches(input, 'select')) { + return toArray(input.options).forEach(function(option) { + if (option.selected) { + return params.push({ + name: input.name, + value: option.value + }); + } + }); + } else if (input.checked || ['radio', 'checkbox', 'submit'].indexOf(input.type) === -1) { + return params.push({ + name: input.name, + value: input.value + }); + } + }); + if (additionalParam) { + params.push(additionalParam); + } + return params.map(function(param) { + if (param.name != null) { + return (encodeURIComponent(param.name)) + "=" + (encodeURIComponent(param.value)); + } else { + return param; + } + }).join('&'); + }; + + Rails.formElements = function(form, selector) { + if (matches(form, 'form')) { + return toArray(form.elements).filter(function(el) { + return matches(el, selector); + }); + } else { + return toArray(form.querySelectorAll(selector)); + } + }; + + }).call(this); + (function() { + var allowAction, fire, stopEverything; + + fire = Rails.fire, stopEverything = Rails.stopEverything; + + Rails.handleConfirm = function(e) { + if (!allowAction(this)) { + return stopEverything(e); + } + }; + + allowAction = function(element) { + var answer, callback, message; + message = element.getAttribute('data-confirm'); + if (!message) { + return true; + } + answer = false; + if (fire(element, 'confirm')) { + try { + answer = confirm(message); + } catch (error) {} + callback = fire(element, 'confirm:complete', [answer]); + } + return answer && callback; + }; + + }).call(this); + (function() { + var disableFormElement, disableFormElements, disableLinkElement, enableFormElement, enableFormElements, enableLinkElement, formElements, getData, matches, setData, stopEverything; + + matches = Rails.matches, getData = Rails.getData, setData = Rails.setData, stopEverything = Rails.stopEverything, formElements = Rails.formElements; + + Rails.handleDisabledElement = function(e) { + var element; + element = this; + if (element.disabled) { + return stopEverything(e); + } + }; + + Rails.enableElement = function(e) { + var element; + element = e instanceof Event ? e.target : e; + if (matches(element, Rails.linkDisableSelector)) { + return enableLinkElement(element); + } else if (matches(element, Rails.buttonDisableSelector) || matches(element, Rails.formEnableSelector)) { + return enableFormElement(element); + } else if (matches(element, Rails.formSubmitSelector)) { + return enableFormElements(element); + } + }; + + Rails.disableElement = function(e) { + var element; + element = e instanceof Event ? e.target : e; + if (matches(element, Rails.linkDisableSelector)) { + return disableLinkElement(element); + } else if (matches(element, Rails.buttonDisableSelector) || matches(element, Rails.formDisableSelector)) { + return disableFormElement(element); + } else if (matches(element, Rails.formSubmitSelector)) { + return disableFormElements(element); + } + }; + + disableLinkElement = function(element) { + var replacement; + replacement = element.getAttribute('data-disable-with'); + if (replacement != null) { + setData(element, 'ujs:enable-with', element.innerHTML); + element.innerHTML = replacement; + } + element.addEventListener('click', stopEverything); + return setData(element, 'ujs:disabled', true); + }; + + enableLinkElement = function(element) { + var originalText; + originalText = getData(element, 'ujs:enable-with'); + if (originalText != null) { + element.innerHTML = originalText; + setData(element, 'ujs:enable-with', null); + } + element.removeEventListener('click', stopEverything); + return setData(element, 'ujs:disabled', null); + }; + + disableFormElements = function(form) { + return formElements(form, Rails.formDisableSelector).forEach(disableFormElement); + }; + + disableFormElement = function(element) { + var replacement; + replacement = element.getAttribute('data-disable-with'); + if (replacement != null) { + if (matches(element, 'button')) { + setData(element, 'ujs:enable-with', element.innerHTML); + element.innerHTML = replacement; + } else { + setData(element, 'ujs:enable-with', element.value); + element.value = replacement; + } + } + element.disabled = true; + return setData(element, 'ujs:disabled', true); + }; + + enableFormElements = function(form) { + return formElements(form, Rails.formEnableSelector).forEach(enableFormElement); + }; + + enableFormElement = function(element) { + var originalText; + originalText = getData(element, 'ujs:enable-with'); + if (originalText != null) { + if (matches(element, 'button')) { + element.innerHTML = originalText; + } else { + element.value = originalText; + } + setData(element, 'ujs:enable-with', null); + } + element.disabled = false; + return setData(element, 'ujs:disabled', null); + }; + + }).call(this); + (function() { + var stopEverything; + + stopEverything = Rails.stopEverything; + + Rails.handleMethod = function(e) { + var csrfParam, csrfToken, form, formContent, href, link, method; + link = this; + method = link.getAttribute('data-method'); + if (!method) { + return; + } + href = Rails.href(link); + csrfToken = Rails.csrfToken(); + csrfParam = Rails.csrfParam(); + form = document.createElement('form'); + formContent = ""; + if ((csrfParam != null) && (csrfToken != null) && !Rails.isCrossDomain(href)) { + formContent += ""; + } + formContent += ''; + form.method = 'post'; + form.action = href; + form.target = link.target; + form.innerHTML = formContent; + form.style.display = 'none'; + document.body.appendChild(form); + form.querySelector('[type="submit"]').click(); + return stopEverything(e); + }; + + }).call(this); + (function() { + var ajax, fire, getData, isCrossDomain, isRemote, matches, serializeElement, setData, stopEverything, + slice = [].slice; + + matches = Rails.matches, getData = Rails.getData, setData = Rails.setData, fire = Rails.fire, stopEverything = Rails.stopEverything, ajax = Rails.ajax, isCrossDomain = Rails.isCrossDomain, serializeElement = Rails.serializeElement; + + isRemote = function(element) { + var value; + value = element.getAttribute('data-remote'); + return (value != null) && value !== 'false'; + }; + + Rails.handleRemote = function(e) { + var button, data, dataType, element, method, url, withCredentials; + element = this; + if (!isRemote(element)) { + return true; + } + if (!fire(element, 'ajax:before')) { + fire(element, 'ajax:stopped'); + return false; + } + withCredentials = element.getAttribute('data-with-credentials'); + dataType = element.getAttribute('data-type') || 'script'; + if (matches(element, Rails.formSubmitSelector)) { + button = getData(element, 'ujs:submit-button'); + method = getData(element, 'ujs:submit-button-formmethod') || element.method; + url = getData(element, 'ujs:submit-button-formaction') || element.getAttribute('action') || location.href; + if (method.toUpperCase() === 'GET') { + url = url.replace(/\?.*$/, ''); + } + if (element.enctype === 'multipart/form-data') { + data = new FormData(element); + if (button != null) { + data.append(button.name, button.value); + } + } else { + data = serializeElement(element, button); + } + setData(element, 'ujs:submit-button', null); + setData(element, 'ujs:submit-button-formmethod', null); + setData(element, 'ujs:submit-button-formaction', null); + } else if (matches(element, Rails.buttonClickSelector) || matches(element, Rails.inputChangeSelector)) { + method = element.getAttribute('data-method'); + url = element.getAttribute('data-url'); + data = serializeElement(element, element.getAttribute('data-params')); + } else { + method = element.getAttribute('data-method'); + url = Rails.href(element); + data = element.getAttribute('data-params'); + } + ajax({ + type: method || 'GET', + url: url, + data: data, + dataType: dataType, + beforeSend: function(xhr, options) { + if (fire(element, 'ajax:beforeSend', [xhr, options])) { + return fire(element, 'ajax:send', [xhr]); + } else { + fire(element, 'ajax:stopped'); + return false; + } + }, + success: function() { + var args; + args = 1 <= arguments.length ? slice.call(arguments, 0) : []; + return fire(element, 'ajax:success', args); + }, + error: function() { + var args; + args = 1 <= arguments.length ? slice.call(arguments, 0) : []; + return fire(element, 'ajax:error', args); + }, + complete: function() { + var args; + args = 1 <= arguments.length ? slice.call(arguments, 0) : []; + return fire(element, 'ajax:complete', args); + }, + crossDomain: isCrossDomain(url), + withCredentials: (withCredentials != null) && withCredentials !== 'false' + }); + return stopEverything(e); + }; + + Rails.formSubmitButtonClick = function(e) { + var button, form; + button = this; + form = button.form; + if (!form) { + return; + } + if (button.name) { + setData(form, 'ujs:submit-button', { + name: button.name, + value: button.value + }); + } + setData(form, 'ujs:formnovalidate-button', button.formNoValidate); + setData(form, 'ujs:submit-button-formaction', button.getAttribute('formaction')); + return setData(form, 'ujs:submit-button-formmethod', button.getAttribute('formmethod')); + }; + + Rails.handleMetaClick = function(e) { + var data, link, metaClick, method; + link = this; + method = (link.getAttribute('data-method') || 'GET').toUpperCase(); + data = link.getAttribute('data-params'); + metaClick = e.metaKey || e.ctrlKey; + if (metaClick && method === 'GET' && !data) { + return e.stopImmediatePropagation(); + } + }; + + }).call(this); + (function() { + var $, CSRFProtection, delegate, disableElement, enableElement, fire, formSubmitButtonClick, getData, handleConfirm, handleDisabledElement, handleMetaClick, handleMethod, handleRemote, refreshCSRFTokens; + + fire = Rails.fire, delegate = Rails.delegate, getData = Rails.getData, $ = Rails.$, refreshCSRFTokens = Rails.refreshCSRFTokens, CSRFProtection = Rails.CSRFProtection, enableElement = Rails.enableElement, disableElement = Rails.disableElement, handleDisabledElement = Rails.handleDisabledElement, handleConfirm = Rails.handleConfirm, handleRemote = Rails.handleRemote, formSubmitButtonClick = Rails.formSubmitButtonClick, handleMetaClick = Rails.handleMetaClick, handleMethod = Rails.handleMethod; + + if ((typeof jQuery !== "undefined" && jQuery !== null) && (jQuery.ajax != null) && !jQuery.rails) { + jQuery.rails = Rails; + jQuery.ajaxPrefilter(function(options, originalOptions, xhr) { + if (!options.crossDomain) { + return CSRFProtection(xhr); + } + }); + } + + Rails.start = function() { + if (window._rails_loaded) { + throw new Error('rails-ujs has already been loaded!'); + } + window.addEventListener('pageshow', function() { + $(Rails.formEnableSelector).forEach(function(el) { + if (getData(el, 'ujs:disabled')) { + return enableElement(el); + } + }); + return $(Rails.linkDisableSelector).forEach(function(el) { + if (getData(el, 'ujs:disabled')) { + return enableElement(el); + } + }); + }); + delegate(document, Rails.linkDisableSelector, 'ajax:complete', enableElement); + delegate(document, Rails.linkDisableSelector, 'ajax:stopped', enableElement); + delegate(document, Rails.buttonDisableSelector, 'ajax:complete', enableElement); + delegate(document, Rails.buttonDisableSelector, 'ajax:stopped', enableElement); + delegate(document, Rails.linkClickSelector, 'click', handleDisabledElement); + delegate(document, Rails.linkClickSelector, 'click', handleConfirm); + delegate(document, Rails.linkClickSelector, 'click', handleMetaClick); + delegate(document, Rails.linkClickSelector, 'click', disableElement); + delegate(document, Rails.linkClickSelector, 'click', handleRemote); + delegate(document, Rails.linkClickSelector, 'click', handleMethod); + delegate(document, Rails.buttonClickSelector, 'click', handleDisabledElement); + delegate(document, Rails.buttonClickSelector, 'click', handleConfirm); + delegate(document, Rails.buttonClickSelector, 'click', disableElement); + delegate(document, Rails.buttonClickSelector, 'click', handleRemote); + delegate(document, Rails.inputChangeSelector, 'change', handleDisabledElement); + delegate(document, Rails.inputChangeSelector, 'change', handleConfirm); + delegate(document, Rails.inputChangeSelector, 'change', handleRemote); + delegate(document, Rails.formSubmitSelector, 'submit', handleDisabledElement); + delegate(document, Rails.formSubmitSelector, 'submit', handleConfirm); + delegate(document, Rails.formSubmitSelector, 'submit', handleRemote); + delegate(document, Rails.formSubmitSelector, 'submit', function(e) { + return setTimeout((function() { + return disableElement(e); + }), 13); + }); + delegate(document, Rails.formSubmitSelector, 'ajax:send', disableElement); + delegate(document, Rails.formSubmitSelector, 'ajax:complete', enableElement); + delegate(document, Rails.formInputClickSelector, 'click', handleDisabledElement); + delegate(document, Rails.formInputClickSelector, 'click', handleConfirm); + delegate(document, Rails.formInputClickSelector, 'click', formSubmitButtonClick); + document.addEventListener('DOMContentLoaded', refreshCSRFTokens); + return window._rails_loaded = true; + }; + + if (window.Rails === Rails && fire(document, 'rails:attachBindings')) { + Rails.start(); + } + + }).call(this); + }).call(this); + + if (typeof module === "object" && module.exports) { + module.exports = Rails; + } else if (typeof define === "function" && define.amd) { + define(Rails); + } +}).call(this); +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ActiveStorage=e():t.ActiveStorage=e()}(this,function(){return function(t){function e(n){if(r[n])return r[n].exports;var i=r[n]={i:n,l:!1,exports:{}};return t[n].call(i.exports,i,i.exports,e),i.l=!0,i.exports}var r={};return e.m=t,e.c=r,e.d=function(t,r,n){e.o(t,r)||Object.defineProperty(t,r,{configurable:!1,enumerable:!0,get:n})},e.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(r,"a",r),r},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=2)}([function(t,e,r){"use strict";function n(t){var e=a(document.head,'meta[name="'+t+'"]');if(e)return e.getAttribute("content")}function i(t,e){return"string"==typeof t&&(e=t,t=document),o(t.querySelectorAll(e))}function a(t,e){return"string"==typeof t&&(e=t,t=document),t.querySelector(e)}function u(t,e){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},n=t.disabled,i=r.bubbles,a=r.cancelable,u=r.detail,o=document.createEvent("Event");o.initEvent(e,i||!0,a||!0),o.detail=u||{};try{t.disabled=!1,t.dispatchEvent(o)}finally{t.disabled=n}return o}function o(t){return Array.isArray(t)?t:Array.from?Array.from(t):[].slice.call(t)}e.d=n,e.c=i,e.b=a,e.a=u,e.e=o},function(t,e,r){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){if(t&&"function"==typeof t[e]){for(var r=arguments.length,n=Array(r>2?r-2:0),i=2;i1&&void 0!==arguments[1]?arguments[1]:{};return Object(a.a)(this.form,"direct-uploads:"+t,{detail:e})}}]),t}()},function(t,e,r){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}r.d(e,"a",function(){return o});var i=r(1),a=r(0),u=function(){function t(t,e){for(var r=0;r1&&void 0!==arguments[1]?arguments[1]:{};return e.file=this.file,e.id=this.directUpload.id,Object(a.a)(this.input,"direct-upload:"+t,{detail:e})}},{key:"dispatchError",value:function(t){this.dispatch("error",{error:t}).defaultPrevented||alert(t)}},{key:"directUploadWillCreateBlobWithXHR",value:function(t){this.dispatch("before-blob-request",{xhr:t})}},{key:"directUploadWillStoreFileWithXHR",value:function(t){var e=this;this.dispatch("before-storage-request",{xhr:t}),t.upload.addEventListener("progress",function(t){return e.uploadRequestDidProgress(t)})}},{key:"url",get:function(){return this.input.getAttribute("data-direct-upload-url")}}]),t}()},function(t,e,r){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}r.d(e,"a",function(){return s});var i=r(7),a=r.n(i),u=function(){function t(t,e){for(var r=0;r>>25)+n|0,a+=(r&n|~r&i)+e[1]-389564586|0,a=(a<<12|a>>>20)+r|0,i+=(a&r|~a&n)+e[2]+606105819|0,i=(i<<17|i>>>15)+a|0,n+=(i&a|~i&r)+e[3]-1044525330|0,n=(n<<22|n>>>10)+i|0,r+=(n&i|~n&a)+e[4]-176418897|0,r=(r<<7|r>>>25)+n|0,a+=(r&n|~r&i)+e[5]+1200080426|0,a=(a<<12|a>>>20)+r|0,i+=(a&r|~a&n)+e[6]-1473231341|0,i=(i<<17|i>>>15)+a|0,n+=(i&a|~i&r)+e[7]-45705983|0,n=(n<<22|n>>>10)+i|0,r+=(n&i|~n&a)+e[8]+1770035416|0,r=(r<<7|r>>>25)+n|0,a+=(r&n|~r&i)+e[9]-1958414417|0,a=(a<<12|a>>>20)+r|0,i+=(a&r|~a&n)+e[10]-42063|0,i=(i<<17|i>>>15)+a|0,n+=(i&a|~i&r)+e[11]-1990404162|0,n=(n<<22|n>>>10)+i|0,r+=(n&i|~n&a)+e[12]+1804603682|0,r=(r<<7|r>>>25)+n|0,a+=(r&n|~r&i)+e[13]-40341101|0,a=(a<<12|a>>>20)+r|0,i+=(a&r|~a&n)+e[14]-1502002290|0,i=(i<<17|i>>>15)+a|0,n+=(i&a|~i&r)+e[15]+1236535329|0,n=(n<<22|n>>>10)+i|0,r+=(n&a|i&~a)+e[1]-165796510|0,r=(r<<5|r>>>27)+n|0,a+=(r&i|n&~i)+e[6]-1069501632|0,a=(a<<9|a>>>23)+r|0,i+=(a&n|r&~n)+e[11]+643717713|0,i=(i<<14|i>>>18)+a|0,n+=(i&r|a&~r)+e[0]-373897302|0,n=(n<<20|n>>>12)+i|0,r+=(n&a|i&~a)+e[5]-701558691|0,r=(r<<5|r>>>27)+n|0,a+=(r&i|n&~i)+e[10]+38016083|0,a=(a<<9|a>>>23)+r|0,i+=(a&n|r&~n)+e[15]-660478335|0,i=(i<<14|i>>>18)+a|0,n+=(i&r|a&~r)+e[4]-405537848|0,n=(n<<20|n>>>12)+i|0,r+=(n&a|i&~a)+e[9]+568446438|0,r=(r<<5|r>>>27)+n|0,a+=(r&i|n&~i)+e[14]-1019803690|0,a=(a<<9|a>>>23)+r|0,i+=(a&n|r&~n)+e[3]-187363961|0,i=(i<<14|i>>>18)+a|0,n+=(i&r|a&~r)+e[8]+1163531501|0,n=(n<<20|n>>>12)+i|0,r+=(n&a|i&~a)+e[13]-1444681467|0,r=(r<<5|r>>>27)+n|0,a+=(r&i|n&~i)+e[2]-51403784|0,a=(a<<9|a>>>23)+r|0,i+=(a&n|r&~n)+e[7]+1735328473|0,i=(i<<14|i>>>18)+a|0,n+=(i&r|a&~r)+e[12]-1926607734|0,n=(n<<20|n>>>12)+i|0,r+=(n^i^a)+e[5]-378558|0,r=(r<<4|r>>>28)+n|0,a+=(r^n^i)+e[8]-2022574463|0,a=(a<<11|a>>>21)+r|0,i+=(a^r^n)+e[11]+1839030562|0,i=(i<<16|i>>>16)+a|0,n+=(i^a^r)+e[14]-35309556|0,n=(n<<23|n>>>9)+i|0,r+=(n^i^a)+e[1]-1530992060|0,r=(r<<4|r>>>28)+n|0,a+=(r^n^i)+e[4]+1272893353|0,a=(a<<11|a>>>21)+r|0,i+=(a^r^n)+e[7]-155497632|0,i=(i<<16|i>>>16)+a|0,n+=(i^a^r)+e[10]-1094730640|0,n=(n<<23|n>>>9)+i|0,r+=(n^i^a)+e[13]+681279174|0,r=(r<<4|r>>>28)+n|0,a+=(r^n^i)+e[0]-358537222|0,a=(a<<11|a>>>21)+r|0,i+=(a^r^n)+e[3]-722521979|0,i=(i<<16|i>>>16)+a|0,n+=(i^a^r)+e[6]+76029189|0,n=(n<<23|n>>>9)+i|0,r+=(n^i^a)+e[9]-640364487|0,r=(r<<4|r>>>28)+n|0,a+=(r^n^i)+e[12]-421815835|0,a=(a<<11|a>>>21)+r|0,i+=(a^r^n)+e[15]+530742520|0,i=(i<<16|i>>>16)+a|0,n+=(i^a^r)+e[2]-995338651|0,n=(n<<23|n>>>9)+i|0,r+=(i^(n|~a))+e[0]-198630844|0,r=(r<<6|r>>>26)+n|0,a+=(n^(r|~i))+e[7]+1126891415|0,a=(a<<10|a>>>22)+r|0,i+=(r^(a|~n))+e[14]-1416354905|0,i=(i<<15|i>>>17)+a|0,n+=(a^(i|~r))+e[5]-57434055|0,n=(n<<21|n>>>11)+i|0,r+=(i^(n|~a))+e[12]+1700485571|0,r=(r<<6|r>>>26)+n|0,a+=(n^(r|~i))+e[3]-1894986606|0,a=(a<<10|a>>>22)+r|0,i+=(r^(a|~n))+e[10]-1051523|0,i=(i<<15|i>>>17)+a|0,n+=(a^(i|~r))+e[1]-2054922799|0,n=(n<<21|n>>>11)+i|0,r+=(i^(n|~a))+e[8]+1873313359|0,r=(r<<6|r>>>26)+n|0,a+=(n^(r|~i))+e[15]-30611744|0,a=(a<<10|a>>>22)+r|0,i+=(r^(a|~n))+e[6]-1560198380|0,i=(i<<15|i>>>17)+a|0,n+=(a^(i|~r))+e[13]+1309151649|0,n=(n<<21|n>>>11)+i|0,r+=(i^(n|~a))+e[4]-145523070|0,r=(r<<6|r>>>26)+n|0,a+=(n^(r|~i))+e[11]-1120210379|0,a=(a<<10|a>>>22)+r|0,i+=(r^(a|~n))+e[2]+718787259|0,i=(i<<15|i>>>17)+a|0,n+=(a^(i|~r))+e[9]-343485551|0,n=(n<<21|n>>>11)+i|0,t[0]=r+t[0]|0,t[1]=n+t[1]|0,t[2]=i+t[2]|0,t[3]=a+t[3]|0}function r(t){var e,r=[];for(e=0;e<64;e+=4)r[e>>2]=t.charCodeAt(e)+(t.charCodeAt(e+1)<<8)+(t.charCodeAt(e+2)<<16)+(t.charCodeAt(e+3)<<24);return r}function n(t){var e,r=[];for(e=0;e<64;e+=4)r[e>>2]=t[e]+(t[e+1]<<8)+(t[e+2]<<16)+(t[e+3]<<24);return r}function i(t){var n,i,a,u,o,s,f=t.length,c=[1732584193,-271733879,-1732584194,271733878];for(n=64;n<=f;n+=64)e(c,r(t.substring(n-64,n)));for(t=t.substring(n-64),i=t.length,a=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],n=0;n>2]|=t.charCodeAt(n)<<(n%4<<3);if(a[n>>2]|=128<<(n%4<<3),n>55)for(e(c,a),n=0;n<16;n+=1)a[n]=0;return u=8*f,u=u.toString(16).match(/(.*?)(.{0,8})$/),o=parseInt(u[2],16),s=parseInt(u[1],16)||0,a[14]=o,a[15]=s,e(c,a),c}function a(t){var r,i,a,u,o,s,f=t.length,c=[1732584193,-271733879,-1732584194,271733878];for(r=64;r<=f;r+=64)e(c,n(t.subarray(r-64,r)));for(t=r-64>2]|=t[r]<<(r%4<<3);if(a[r>>2]|=128<<(r%4<<3),r>55)for(e(c,a),r=0;r<16;r+=1)a[r]=0;return u=8*f,u=u.toString(16).match(/(.*?)(.{0,8})$/),o=parseInt(u[2],16),s=parseInt(u[1],16)||0,a[14]=o,a[15]=s,e(c,a),c}function u(t){var e,r="";for(e=0;e<4;e+=1)r+=p[t>>8*e+4&15]+p[t>>8*e&15];return r}function o(t){var e;for(e=0;e>16)+(e>>16)+(r>>16)<<16|65535&r},"undefined"==typeof ArrayBuffer||ArrayBuffer.prototype.slice||function(){function e(t,e){return t=0|t||0,t<0?Math.max(t+e,0):Math.min(t,e)}ArrayBuffer.prototype.slice=function(r,n){var i,a,u,o,s=this.byteLength,f=e(r,s),c=s;return n!==t&&(c=e(n,s)),f>c?new ArrayBuffer(0):(i=c-f,a=new ArrayBuffer(i),u=new Uint8Array(a),o=new Uint8Array(this,f,i),u.set(o),a)}}(),d.prototype.append=function(t){return this.appendBinary(s(t)),this},d.prototype.appendBinary=function(t){this._buff+=t,this._length+=t.length;var n,i=this._buff.length;for(n=64;n<=i;n+=64)e(this._hash,r(this._buff.substring(n-64,n)));return this._buff=this._buff.substring(n-64),this},d.prototype.end=function(t){var e,r,n=this._buff,i=n.length,a=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];for(e=0;e>2]|=n.charCodeAt(e)<<(e%4<<3);return this._finish(a,i),r=o(this._hash),t&&(r=l(r)),this.reset(),r},d.prototype.reset=function(){return this._buff="",this._length=0,this._hash=[1732584193,-271733879,-1732584194,271733878],this},d.prototype.getState=function(){return{buff:this._buff,length:this._length,hash:this._hash}},d.prototype.setState=function(t){return this._buff=t.buff,this._length=t.length,this._hash=t.hash,this},d.prototype.destroy=function(){delete this._hash,delete this._buff,delete this._length},d.prototype._finish=function(t,r){var n,i,a,u=r;if(t[u>>2]|=128<<(u%4<<3),u>55)for(e(this._hash,t),u=0;u<16;u+=1)t[u]=0;n=8*this._length,n=n.toString(16).match(/(.*?)(.{0,8})$/),i=parseInt(n[2],16),a=parseInt(n[1],16)||0,t[14]=i,t[15]=a,e(this._hash,t)},d.hash=function(t,e){return d.hashBinary(s(t),e)},d.hashBinary=function(t,e){var r=i(t),n=o(r);return e?l(n):n},d.ArrayBuffer=function(){this.reset()},d.ArrayBuffer.prototype.append=function(t){var r,i=h(this._buff.buffer,t,!0),a=i.length;for(this._length+=t.byteLength,r=64;r<=a;r+=64)e(this._hash,n(i.subarray(r-64,r)));return this._buff=r-64>2]|=n[e]<<(e%4<<3);return this._finish(a,i),r=o(this._hash),t&&(r=l(r)),this.reset(),r},d.ArrayBuffer.prototype.reset=function(){return this._buff=new Uint8Array(0),this._length=0,this._hash=[1732584193,-271733879,-1732584194,271733878],this},d.ArrayBuffer.prototype.getState=function(){var t=d.prototype.getState.call(this);return t.buff=c(t.buff),t},d.ArrayBuffer.prototype.setState=function(t){return t.buff=f(t.buff,!0),d.prototype.setState.call(this,t)},d.ArrayBuffer.prototype.destroy=d.prototype.destroy,d.ArrayBuffer.prototype._finish=d.prototype._finish,d.ArrayBuffer.hash=function(t,e){var r=a(new Uint8Array(t)),n=o(r);return e?l(n):n},d})},function(t,e,r){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}r.d(e,"a",function(){return u});var i=r(0),a=function(){function t(t,e){for(var r=0;r=200&&this.status<300){var e=this.response,r=e.direct_upload;delete e.direct_upload,this.attributes=e,this.directUploadData=r,this.callback(null,this.toJSON())}else this.requestDidError(t)}},{key:"requestDidError",value:function(t){this.callback('Error creating Blob for "'+this.file.name+'". Status: '+this.status)}},{key:"toJSON",value:function(){var t={};for(var e in this.attributes)t[e]=this.attributes[e];return t}},{key:"status",get:function(){return this.xhr.status}},{key:"response",get:function(){var t=this.xhr,e=t.responseType,r=t.response;return"json"==e?r:JSON.parse(r)}}]),t}()},function(t,e,r){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}r.d(e,"a",function(){return a});var i=function(){function t(t,e){for(var r=0;r=200&&r<300?this.callback(null,n):this.requestDidError(t)}},{key:"requestDidError",value:function(t){this.callback('Error storing "'+this.file.name+'". Status: '+this.xhr.status)}}]),t}()}])}); +/* +Turbolinks 5.2.0 +Copyright © 2018 Basecamp, LLC + */ + +(function(){var t=this;(function(){(function(){this.Turbolinks={supported:function(){return null!=window.history.pushState&&null!=window.requestAnimationFrame&&null!=window.addEventListener}(),visit:function(t,r){return e.controller.visit(t,r)},clearCache:function(){return e.controller.clearCache()},setProgressBarDelay:function(t){return e.controller.setProgressBarDelay(t)}}}).call(this)}).call(t);var e=t.Turbolinks;(function(){(function(){var t,r,n,o=[].slice;e.copyObject=function(t){var e,r,n;r={};for(e in t)n=t[e],r[e]=n;return r},e.closest=function(e,r){return t.call(e,r)},t=function(){var t,e;return t=document.documentElement,null!=(e=t.closest)?e:function(t){var e;for(e=this;e;){if(e.nodeType===Node.ELEMENT_NODE&&r.call(e,t))return e;e=e.parentNode}}}(),e.defer=function(t){return setTimeout(t,1)},e.throttle=function(t){var e;return e=null,function(){var r;return r=1<=arguments.length?o.call(arguments,0):[],null!=e?e:e=requestAnimationFrame(function(n){return function(){return e=null,t.apply(n,r)}}(this))}},e.dispatch=function(t,e){var r,o,i,s,a,u;return a=null!=e?e:{},u=a.target,r=a.cancelable,o=a.data,i=document.createEvent("Events"),i.initEvent(t,!0,r===!0),i.data=null!=o?o:{},i.cancelable&&!n&&(s=i.preventDefault,i.preventDefault=function(){return this.defaultPrevented||Object.defineProperty(this,"defaultPrevented",{get:function(){return!0}}),s.call(this)}),(null!=u?u:document).dispatchEvent(i),i},n=function(){var t;return t=document.createEvent("Events"),t.initEvent("test",!0,!0),t.preventDefault(),t.defaultPrevented}(),e.match=function(t,e){return r.call(t,e)},r=function(){var t,e,r,n;return t=document.documentElement,null!=(e=null!=(r=null!=(n=t.matchesSelector)?n:t.webkitMatchesSelector)?r:t.msMatchesSelector)?e:t.mozMatchesSelector}(),e.uuid=function(){var t,e,r;for(r="",t=e=1;36>=e;t=++e)r+=9===t||14===t||19===t||24===t?"-":15===t?"4":20===t?(Math.floor(4*Math.random())+8).toString(16):Math.floor(15*Math.random()).toString(16);return r}}).call(this),function(){e.Location=function(){function t(t){var e,r;null==t&&(t=""),r=document.createElement("a"),r.href=t.toString(),this.absoluteURL=r.href,e=r.hash.length,2>e?this.requestURL=this.absoluteURL:(this.requestURL=this.absoluteURL.slice(0,-e),this.anchor=r.hash.slice(1))}var e,r,n,o;return t.wrap=function(t){return t instanceof this?t:new this(t)},t.prototype.getOrigin=function(){return this.absoluteURL.split("/",3).join("/")},t.prototype.getPath=function(){var t,e;return null!=(t=null!=(e=this.requestURL.match(/\/\/[^\/]*(\/[^?;]*)/))?e[1]:void 0)?t:"/"},t.prototype.getPathComponents=function(){return this.getPath().split("/").slice(1)},t.prototype.getLastPathComponent=function(){return this.getPathComponents().slice(-1)[0]},t.prototype.getExtension=function(){var t,e;return null!=(t=null!=(e=this.getLastPathComponent().match(/\.[^.]*$/))?e[0]:void 0)?t:""},t.prototype.isHTML=function(){return this.getExtension().match(/^(?:|\.(?:htm|html|xhtml))$/)},t.prototype.isPrefixedBy=function(t){var e;return e=r(t),this.isEqualTo(t)||o(this.absoluteURL,e)},t.prototype.isEqualTo=function(t){return this.absoluteURL===(null!=t?t.absoluteURL:void 0)},t.prototype.toCacheKey=function(){return this.requestURL},t.prototype.toJSON=function(){return this.absoluteURL},t.prototype.toString=function(){return this.absoluteURL},t.prototype.valueOf=function(){return this.absoluteURL},r=function(t){return e(t.getOrigin()+t.getPath())},e=function(t){return n(t,"/")?t:t+"/"},o=function(t,e){return t.slice(0,e.length)===e},n=function(t,e){return t.slice(-e.length)===e},t}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.HttpRequest=function(){function r(r,n,o){this.delegate=r,this.requestCanceled=t(this.requestCanceled,this),this.requestTimedOut=t(this.requestTimedOut,this),this.requestFailed=t(this.requestFailed,this),this.requestLoaded=t(this.requestLoaded,this),this.requestProgressed=t(this.requestProgressed,this),this.url=e.Location.wrap(n).requestURL,this.referrer=e.Location.wrap(o).absoluteURL,this.createXHR()}return r.NETWORK_FAILURE=0,r.TIMEOUT_FAILURE=-1,r.timeout=60,r.prototype.send=function(){var t;return this.xhr&&!this.sent?(this.notifyApplicationBeforeRequestStart(),this.setProgress(0),this.xhr.send(),this.sent=!0,"function"==typeof(t=this.delegate).requestStarted?t.requestStarted():void 0):void 0},r.prototype.cancel=function(){return this.xhr&&this.sent?this.xhr.abort():void 0},r.prototype.requestProgressed=function(t){return t.lengthComputable?this.setProgress(t.loaded/t.total):void 0},r.prototype.requestLoaded=function(){return this.endRequest(function(t){return function(){var e;return 200<=(e=t.xhr.status)&&300>e?t.delegate.requestCompletedWithResponse(t.xhr.responseText,t.xhr.getResponseHeader("Turbolinks-Location")):(t.failed=!0,t.delegate.requestFailedWithStatusCode(t.xhr.status,t.xhr.responseText))}}(this))},r.prototype.requestFailed=function(){return this.endRequest(function(t){return function(){return t.failed=!0,t.delegate.requestFailedWithStatusCode(t.constructor.NETWORK_FAILURE)}}(this))},r.prototype.requestTimedOut=function(){return this.endRequest(function(t){return function(){return t.failed=!0,t.delegate.requestFailedWithStatusCode(t.constructor.TIMEOUT_FAILURE)}}(this))},r.prototype.requestCanceled=function(){return this.endRequest()},r.prototype.notifyApplicationBeforeRequestStart=function(){return e.dispatch("turbolinks:request-start",{data:{url:this.url,xhr:this.xhr}})},r.prototype.notifyApplicationAfterRequestEnd=function(){return e.dispatch("turbolinks:request-end",{data:{url:this.url,xhr:this.xhr}})},r.prototype.createXHR=function(){return this.xhr=new XMLHttpRequest,this.xhr.open("GET",this.url,!0),this.xhr.timeout=1e3*this.constructor.timeout,this.xhr.setRequestHeader("Accept","text/html, application/xhtml+xml"),this.xhr.setRequestHeader("Turbolinks-Referrer",this.referrer),this.xhr.onprogress=this.requestProgressed,this.xhr.onload=this.requestLoaded,this.xhr.onerror=this.requestFailed,this.xhr.ontimeout=this.requestTimedOut,this.xhr.onabort=this.requestCanceled},r.prototype.endRequest=function(t){return this.xhr?(this.notifyApplicationAfterRequestEnd(),null!=t&&t.call(this),this.destroy()):void 0},r.prototype.setProgress=function(t){var e;return this.progress=t,"function"==typeof(e=this.delegate).requestProgressed?e.requestProgressed(this.progress):void 0},r.prototype.destroy=function(){var t;return this.setProgress(1),"function"==typeof(t=this.delegate).requestFinished&&t.requestFinished(),this.delegate=null,this.xhr=null},r}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.ProgressBar=function(){function e(){this.trickle=t(this.trickle,this),this.stylesheetElement=this.createStylesheetElement(),this.progressElement=this.createProgressElement()}var r;return r=300,e.defaultCSS=".turbolinks-progress-bar {\n position: fixed;\n display: block;\n top: 0;\n left: 0;\n height: 3px;\n background: #0076ff;\n z-index: 9999;\n transition: width "+r+"ms ease-out, opacity "+r/2+"ms "+r/2+"ms ease-in;\n transform: translate3d(0, 0, 0);\n}",e.prototype.show=function(){return this.visible?void 0:(this.visible=!0,this.installStylesheetElement(),this.installProgressElement(),this.startTrickling())},e.prototype.hide=function(){return this.visible&&!this.hiding?(this.hiding=!0,this.fadeProgressElement(function(t){return function(){return t.uninstallProgressElement(),t.stopTrickling(),t.visible=!1,t.hiding=!1}}(this))):void 0},e.prototype.setValue=function(t){return this.value=t,this.refresh()},e.prototype.installStylesheetElement=function(){return document.head.insertBefore(this.stylesheetElement,document.head.firstChild)},e.prototype.installProgressElement=function(){return this.progressElement.style.width=0,this.progressElement.style.opacity=1,document.documentElement.insertBefore(this.progressElement,document.body),this.refresh()},e.prototype.fadeProgressElement=function(t){return this.progressElement.style.opacity=0,setTimeout(t,1.5*r)},e.prototype.uninstallProgressElement=function(){return this.progressElement.parentNode?document.documentElement.removeChild(this.progressElement):void 0},e.prototype.startTrickling=function(){return null!=this.trickleInterval?this.trickleInterval:this.trickleInterval=setInterval(this.trickle,r)},e.prototype.stopTrickling=function(){return clearInterval(this.trickleInterval),this.trickleInterval=null},e.prototype.trickle=function(){return this.setValue(this.value+Math.random()/100)},e.prototype.refresh=function(){return requestAnimationFrame(function(t){return function(){return t.progressElement.style.width=10+90*t.value+"%"}}(this))},e.prototype.createStylesheetElement=function(){var t;return t=document.createElement("style"),t.type="text/css",t.textContent=this.constructor.defaultCSS,t},e.prototype.createProgressElement=function(){var t;return t=document.createElement("div"),t.className="turbolinks-progress-bar",t},e}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.BrowserAdapter=function(){function r(r){this.controller=r,this.showProgressBar=t(this.showProgressBar,this),this.progressBar=new e.ProgressBar}var n,o,i;return i=e.HttpRequest,n=i.NETWORK_FAILURE,o=i.TIMEOUT_FAILURE,r.prototype.visitProposedToLocationWithAction=function(t,e){return this.controller.startVisitToLocationWithAction(t,e)},r.prototype.visitStarted=function(t){return t.issueRequest(),t.changeHistory(),t.loadCachedSnapshot()},r.prototype.visitRequestStarted=function(t){return this.progressBar.setValue(0),t.hasCachedSnapshot()||"restore"!==t.action?this.showProgressBarAfterDelay():this.showProgressBar()},r.prototype.visitRequestProgressed=function(t){return this.progressBar.setValue(t.progress)},r.prototype.visitRequestCompleted=function(t){return t.loadResponse()},r.prototype.visitRequestFailedWithStatusCode=function(t,e){switch(e){case n:case o:return this.reload();default:return t.loadResponse()}},r.prototype.visitRequestFinished=function(t){return this.hideProgressBar()},r.prototype.visitCompleted=function(t){return t.followRedirect()},r.prototype.pageInvalidated=function(){return this.reload()},r.prototype.showProgressBarAfterDelay=function(){return this.progressBarTimeout=setTimeout(this.showProgressBar,this.controller.progressBarDelay)},r.prototype.showProgressBar=function(){return this.progressBar.show()},r.prototype.hideProgressBar=function(){return this.progressBar.hide(),clearTimeout(this.progressBarTimeout)},r.prototype.reload=function(){return window.location.reload()},r}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.History=function(){function r(e){this.delegate=e,this.onPageLoad=t(this.onPageLoad,this),this.onPopState=t(this.onPopState,this)}return r.prototype.start=function(){return this.started?void 0:(addEventListener("popstate",this.onPopState,!1),addEventListener("load",this.onPageLoad,!1),this.started=!0)},r.prototype.stop=function(){return this.started?(removeEventListener("popstate",this.onPopState,!1),removeEventListener("load",this.onPageLoad,!1),this.started=!1):void 0},r.prototype.push=function(t,r){return t=e.Location.wrap(t),this.update("push",t,r)},r.prototype.replace=function(t,r){return t=e.Location.wrap(t),this.update("replace",t,r)},r.prototype.onPopState=function(t){var r,n,o,i;return this.shouldHandlePopState()&&(i=null!=(n=t.state)?n.turbolinks:void 0)?(r=e.Location.wrap(window.location),o=i.restorationIdentifier,this.delegate.historyPoppedToLocationWithRestorationIdentifier(r,o)):void 0},r.prototype.onPageLoad=function(t){return e.defer(function(t){return function(){return t.pageLoaded=!0}}(this))},r.prototype.shouldHandlePopState=function(){return this.pageIsLoaded()},r.prototype.pageIsLoaded=function(){return this.pageLoaded||"complete"===document.readyState},r.prototype.update=function(t,e,r){var n;return n={turbolinks:{restorationIdentifier:r}},history[t+"State"](n,null,e)},r}()}.call(this),function(){e.HeadDetails=function(){function t(t){var e,r,n,s,a,u;for(this.elements={},n=0,a=t.length;a>n;n++)u=t[n],u.nodeType===Node.ELEMENT_NODE&&(s=u.outerHTML,r=null!=(e=this.elements)[s]?e[s]:e[s]={type:i(u),tracked:o(u),elements:[]},r.elements.push(u))}var e,r,n,o,i;return t.fromHeadElement=function(t){var e;return new this(null!=(e=null!=t?t.childNodes:void 0)?e:[])},t.prototype.hasElementWithKey=function(t){return t in this.elements},t.prototype.getTrackedElementSignature=function(){var t,e;return function(){var r,n;r=this.elements,n=[];for(t in r)e=r[t].tracked,e&&n.push(t);return n}.call(this).join("")},t.prototype.getScriptElementsNotInDetails=function(t){return this.getElementsMatchingTypeNotInDetails("script",t)},t.prototype.getStylesheetElementsNotInDetails=function(t){return this.getElementsMatchingTypeNotInDetails("stylesheet",t)},t.prototype.getElementsMatchingTypeNotInDetails=function(t,e){var r,n,o,i,s,a;o=this.elements,s=[];for(n in o)i=o[n],a=i.type,r=i.elements,a!==t||e.hasElementWithKey(n)||s.push(r[0]);return s},t.prototype.getProvisionalElements=function(){var t,e,r,n,o,i,s;r=[],n=this.elements;for(e in n)o=n[e],s=o.type,i=o.tracked,t=o.elements,null!=s||i?t.length>1&&r.push.apply(r,t.slice(1)):r.push.apply(r,t);return r},t.prototype.getMetaValue=function(t){var e;return null!=(e=this.findMetaElementByName(t))?e.getAttribute("content"):void 0},t.prototype.findMetaElementByName=function(t){var r,n,o,i;r=void 0,i=this.elements;for(o in i)n=i[o].elements,e(n[0],t)&&(r=n[0]);return r},i=function(t){return r(t)?"script":n(t)?"stylesheet":void 0},o=function(t){return"reload"===t.getAttribute("data-turbolinks-track")},r=function(t){var e;return e=t.tagName.toLowerCase(),"script"===e},n=function(t){var e;return e=t.tagName.toLowerCase(),"style"===e||"link"===e&&"stylesheet"===t.getAttribute("rel")},e=function(t,e){var r;return r=t.tagName.toLowerCase(),"meta"===r&&t.getAttribute("name")===e},t}()}.call(this),function(){e.Snapshot=function(){function t(t,e){this.headDetails=t,this.bodyElement=e}return t.wrap=function(t){return t instanceof this?t:"string"==typeof t?this.fromHTMLString(t):this.fromHTMLElement(t)},t.fromHTMLString=function(t){var e;return e=document.createElement("html"),e.innerHTML=t,this.fromHTMLElement(e)},t.fromHTMLElement=function(t){var r,n,o,i;return o=t.querySelector("head"),r=null!=(i=t.querySelector("body"))?i:document.createElement("body"),n=e.HeadDetails.fromHeadElement(o),new this(n,r)},t.prototype.clone=function(){return new this.constructor(this.headDetails,this.bodyElement.cloneNode(!0))},t.prototype.getRootLocation=function(){var t,r;return r=null!=(t=this.getSetting("root"))?t:"/",new e.Location(r)},t.prototype.getCacheControlValue=function(){return this.getSetting("cache-control")},t.prototype.getElementForAnchor=function(t){try{return this.bodyElement.querySelector("[id='"+t+"'], a[name='"+t+"']")}catch(e){}},t.prototype.getPermanentElements=function(){return this.bodyElement.querySelectorAll("[id][data-turbolinks-permanent]")},t.prototype.getPermanentElementById=function(t){return this.bodyElement.querySelector("#"+t+"[data-turbolinks-permanent]")},t.prototype.getPermanentElementsPresentInSnapshot=function(t){var e,r,n,o,i;for(o=this.getPermanentElements(),i=[],r=0,n=o.length;n>r;r++)e=o[r],t.getPermanentElementById(e.id)&&i.push(e);return i},t.prototype.findFirstAutofocusableElement=function(){return this.bodyElement.querySelector("[autofocus]")},t.prototype.hasAnchor=function(t){return null!=this.getElementForAnchor(t)},t.prototype.isPreviewable=function(){return"no-preview"!==this.getCacheControlValue()},t.prototype.isCacheable=function(){return"no-cache"!==this.getCacheControlValue()},t.prototype.isVisitable=function(){return"reload"!==this.getSetting("visit-control")},t.prototype.getSetting=function(t){return this.headDetails.getMetaValue("turbolinks-"+t)},t}()}.call(this),function(){var t=[].slice;e.Renderer=function(){function e(){}var r;return e.render=function(){var e,r,n,o;return n=arguments[0],r=arguments[1],e=3<=arguments.length?t.call(arguments,2):[],o=function(t,e,r){r.prototype=t.prototype;var n=new r,o=t.apply(n,e);return Object(o)===o?o:n}(this,e,function(){}),o.delegate=n,o.render(r),o},e.prototype.renderView=function(t){return this.delegate.viewWillRender(this.newBody),t(),this.delegate.viewRendered(this.newBody)},e.prototype.invalidateView=function(){return this.delegate.viewInvalidated()},e.prototype.createScriptElement=function(t){var e;return"false"===t.getAttribute("data-turbolinks-eval")?t:(e=document.createElement("script"),e.textContent=t.textContent,e.async=!1,r(e,t),e)},r=function(t,e){var r,n,o,i,s,a,u;for(i=e.attributes,a=[],r=0,n=i.length;n>r;r++)s=i[r],o=s.name,u=s.value,a.push(t.setAttribute(o,u));return a},e}()}.call(this),function(){var t,r,n=function(t,e){function r(){this.constructor=t}for(var n in e)o.call(e,n)&&(t[n]=e[n]);return r.prototype=e.prototype,t.prototype=new r,t.__super__=e.prototype,t},o={}.hasOwnProperty;e.SnapshotRenderer=function(e){function o(t,e,r){this.currentSnapshot=t,this.newSnapshot=e,this.isPreview=r,this.currentHeadDetails=this.currentSnapshot.headDetails,this.newHeadDetails=this.newSnapshot.headDetails,this.currentBody=this.currentSnapshot.bodyElement,this.newBody=this.newSnapshot.bodyElement}return n(o,e),o.prototype.render=function(t){return this.shouldRender()?(this.mergeHead(),this.renderView(function(e){return function(){return e.replaceBody(),e.isPreview||e.focusFirstAutofocusableElement(),t()}}(this))):this.invalidateView()},o.prototype.mergeHead=function(){return this.copyNewHeadStylesheetElements(),this.copyNewHeadScriptElements(),this.removeCurrentHeadProvisionalElements(),this.copyNewHeadProvisionalElements()},o.prototype.replaceBody=function(){var t;return t=this.relocateCurrentBodyPermanentElements(),this.activateNewBodyScriptElements(),this.assignNewBody(),this.replacePlaceholderElementsWithClonedPermanentElements(t)},o.prototype.shouldRender=function(){return this.newSnapshot.isVisitable()&&this.trackedElementsAreIdentical()},o.prototype.trackedElementsAreIdentical=function(){return this.currentHeadDetails.getTrackedElementSignature()===this.newHeadDetails.getTrackedElementSignature()},o.prototype.copyNewHeadStylesheetElements=function(){var t,e,r,n,o;for(n=this.getNewHeadStylesheetElements(),o=[],e=0,r=n.length;r>e;e++)t=n[e],o.push(document.head.appendChild(t));return o},o.prototype.copyNewHeadScriptElements=function(){var t,e,r,n,o;for(n=this.getNewHeadScriptElements(),o=[],e=0,r=n.length;r>e;e++)t=n[e],o.push(document.head.appendChild(this.createScriptElement(t)));return o},o.prototype.removeCurrentHeadProvisionalElements=function(){var t,e,r,n,o;for(n=this.getCurrentHeadProvisionalElements(),o=[],e=0,r=n.length;r>e;e++)t=n[e],o.push(document.head.removeChild(t));return o},o.prototype.copyNewHeadProvisionalElements=function(){var t,e,r,n,o;for(n=this.getNewHeadProvisionalElements(),o=[],e=0,r=n.length;r>e;e++)t=n[e],o.push(document.head.appendChild(t));return o},o.prototype.relocateCurrentBodyPermanentElements=function(){var e,n,o,i,s,a,u;for(a=this.getCurrentBodyPermanentElements(),u=[],e=0,n=a.length;n>e;e++)i=a[e],s=t(i),o=this.newSnapshot.getPermanentElementById(i.id),r(i,s.element),r(o,i),u.push(s);return u},o.prototype.replacePlaceholderElementsWithClonedPermanentElements=function(t){var e,n,o,i,s,a,u;for(u=[],o=0,i=t.length;i>o;o++)a=t[o],n=a.element,s=a.permanentElement,e=s.cloneNode(!0),u.push(r(n,e));return u},o.prototype.activateNewBodyScriptElements=function(){var t,e,n,o,i,s;for(i=this.getNewBodyScriptElements(),s=[],e=0,o=i.length;o>e;e++)n=i[e],t=this.createScriptElement(n),s.push(r(n,t));return s},o.prototype.assignNewBody=function(){return document.body=this.newBody},o.prototype.focusFirstAutofocusableElement=function(){var t;return null!=(t=this.newSnapshot.findFirstAutofocusableElement())?t.focus():void 0},o.prototype.getNewHeadStylesheetElements=function(){return this.newHeadDetails.getStylesheetElementsNotInDetails(this.currentHeadDetails)},o.prototype.getNewHeadScriptElements=function(){return this.newHeadDetails.getScriptElementsNotInDetails(this.currentHeadDetails)},o.prototype.getCurrentHeadProvisionalElements=function(){return this.currentHeadDetails.getProvisionalElements()},o.prototype.getNewHeadProvisionalElements=function(){return this.newHeadDetails.getProvisionalElements()},o.prototype.getCurrentBodyPermanentElements=function(){return this.currentSnapshot.getPermanentElementsPresentInSnapshot(this.newSnapshot)},o.prototype.getNewBodyScriptElements=function(){return this.newBody.querySelectorAll("script")},o}(e.Renderer),t=function(t){var e;return e=document.createElement("meta"),e.setAttribute("name","turbolinks-permanent-placeholder"),e.setAttribute("content",t.id),{element:e,permanentElement:t}},r=function(t,e){var r;return(r=t.parentNode)?r.replaceChild(e,t):void 0}}.call(this),function(){var t=function(t,e){function n(){this.constructor=t}for(var o in e)r.call(e,o)&&(t[o]=e[o]);return n.prototype=e.prototype,t.prototype=new n,t.__super__=e.prototype,t},r={}.hasOwnProperty;e.ErrorRenderer=function(e){function r(t){var e;e=document.createElement("html"),e.innerHTML=t,this.newHead=e.querySelector("head"),this.newBody=e.querySelector("body")}return t(r,e),r.prototype.render=function(t){return this.renderView(function(e){return function(){return e.replaceHeadAndBody(),e.activateBodyScriptElements(),t()}}(this))},r.prototype.replaceHeadAndBody=function(){var t,e;return e=document.head,t=document.body,e.parentNode.replaceChild(this.newHead,e),t.parentNode.replaceChild(this.newBody,t)},r.prototype.activateBodyScriptElements=function(){var t,e,r,n,o,i;for(n=this.getScriptElements(),i=[],e=0,r=n.length;r>e;e++)o=n[e],t=this.createScriptElement(o),i.push(o.parentNode.replaceChild(t,o));return i},r.prototype.getScriptElements=function(){return document.documentElement.querySelectorAll("script")},r}(e.Renderer)}.call(this),function(){e.View=function(){function t(t){this.delegate=t,this.htmlElement=document.documentElement}return t.prototype.getRootLocation=function(){return this.getSnapshot().getRootLocation()},t.prototype.getElementForAnchor=function(t){return this.getSnapshot().getElementForAnchor(t)},t.prototype.getSnapshot=function(){return e.Snapshot.fromHTMLElement(this.htmlElement)},t.prototype.render=function(t,e){var r,n,o;return o=t.snapshot,r=t.error,n=t.isPreview,this.markAsPreview(n),null!=o?this.renderSnapshot(o,n,e):this.renderError(r,e)},t.prototype.markAsPreview=function(t){return t?this.htmlElement.setAttribute("data-turbolinks-preview",""):this.htmlElement.removeAttribute("data-turbolinks-preview")},t.prototype.renderSnapshot=function(t,r,n){return e.SnapshotRenderer.render(this.delegate,n,this.getSnapshot(),e.Snapshot.wrap(t),r)},t.prototype.renderError=function(t,r){return e.ErrorRenderer.render(this.delegate,r,t)},t}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.ScrollManager=function(){function r(r){this.delegate=r,this.onScroll=t(this.onScroll,this),this.onScroll=e.throttle(this.onScroll)}return r.prototype.start=function(){return this.started?void 0:(addEventListener("scroll",this.onScroll,!1),this.onScroll(),this.started=!0)},r.prototype.stop=function(){return this.started?(removeEventListener("scroll",this.onScroll,!1),this.started=!1):void 0},r.prototype.scrollToElement=function(t){return t.scrollIntoView()},r.prototype.scrollToPosition=function(t){var e,r;return e=t.x,r=t.y,window.scrollTo(e,r)},r.prototype.onScroll=function(t){return this.updatePosition({x:window.pageXOffset,y:window.pageYOffset})},r.prototype.updatePosition=function(t){var e;return this.position=t,null!=(e=this.delegate)?e.scrollPositionChanged(this.position):void 0},r}()}.call(this),function(){e.SnapshotCache=function(){function t(t){this.size=t,this.keys=[],this.snapshots={}}var r;return t.prototype.has=function(t){var e;return e=r(t),e in this.snapshots},t.prototype.get=function(t){var e;if(this.has(t))return e=this.read(t),this.touch(t),e},t.prototype.put=function(t,e){return this.write(t,e),this.touch(t),e},t.prototype.read=function(t){var e;return e=r(t),this.snapshots[e]},t.prototype.write=function(t,e){var n;return n=r(t),this.snapshots[n]=e},t.prototype.touch=function(t){var e,n;return n=r(t),e=this.keys.indexOf(n),e>-1&&this.keys.splice(e,1),this.keys.unshift(n),this.trim()},t.prototype.trim=function(){var t,e,r,n,o;for(n=this.keys.splice(this.size),o=[],t=0,r=n.length;r>t;t++)e=n[t],o.push(delete this.snapshots[e]);return o},r=function(t){return e.Location.wrap(t).toCacheKey()},t}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.Visit=function(){function r(r,n,o){this.controller=r,this.action=o,this.performScroll=t(this.performScroll,this),this.identifier=e.uuid(),this.location=e.Location.wrap(n),this.adapter=this.controller.adapter,this.state="initialized",this.timingMetrics={}}var n;return r.prototype.start=function(){return"initialized"===this.state?(this.recordTimingMetric("visitStart"),this.state="started",this.adapter.visitStarted(this)):void 0},r.prototype.cancel=function(){var t;return"started"===this.state?(null!=(t=this.request)&&t.cancel(),this.cancelRender(),this.state="canceled"):void 0},r.prototype.complete=function(){var t;return"started"===this.state?(this.recordTimingMetric("visitEnd"),this.state="completed","function"==typeof(t=this.adapter).visitCompleted&&t.visitCompleted(this),this.controller.visitCompleted(this)):void 0},r.prototype.fail=function(){var t;return"started"===this.state?(this.state="failed","function"==typeof(t=this.adapter).visitFailed?t.visitFailed(this):void 0):void 0},r.prototype.changeHistory=function(){var t,e;return this.historyChanged?void 0:(t=this.location.isEqualTo(this.referrer)?"replace":this.action,e=n(t),this.controller[e](this.location,this.restorationIdentifier),this.historyChanged=!0)},r.prototype.issueRequest=function(){return this.shouldIssueRequest()&&null==this.request?(this.progress=0,this.request=new e.HttpRequest(this,this.location,this.referrer),this.request.send()):void 0},r.prototype.getCachedSnapshot=function(){var t;return!(t=this.controller.getCachedSnapshotForLocation(this.location))||null!=this.location.anchor&&!t.hasAnchor(this.location.anchor)||"restore"!==this.action&&!t.isPreviewable()?void 0:t},r.prototype.hasCachedSnapshot=function(){return null!=this.getCachedSnapshot()},r.prototype.loadCachedSnapshot=function(){var t,e;return(e=this.getCachedSnapshot())?(t=this.shouldIssueRequest(),this.render(function(){var r;return this.cacheSnapshot(),this.controller.render({snapshot:e,isPreview:t},this.performScroll),"function"==typeof(r=this.adapter).visitRendered&&r.visitRendered(this),t?void 0:this.complete()})):void 0},r.prototype.loadResponse=function(){return null!=this.response?this.render(function(){var t,e;return this.cacheSnapshot(),this.request.failed?(this.controller.render({error:this.response},this.performScroll),"function"==typeof(t=this.adapter).visitRendered&&t.visitRendered(this),this.fail()):(this.controller.render({snapshot:this.response},this.performScroll),"function"==typeof(e=this.adapter).visitRendered&&e.visitRendered(this),this.complete())}):void 0},r.prototype.followRedirect=function(){return this.redirectedToLocation&&!this.followedRedirect?(this.location=this.redirectedToLocation,this.controller.replaceHistoryWithLocationAndRestorationIdentifier(this.redirectedToLocation,this.restorationIdentifier),this.followedRedirect=!0):void 0},r.prototype.requestStarted=function(){var t;return this.recordTimingMetric("requestStart"),"function"==typeof(t=this.adapter).visitRequestStarted?t.visitRequestStarted(this):void 0},r.prototype.requestProgressed=function(t){var e;return this.progress=t,"function"==typeof(e=this.adapter).visitRequestProgressed?e.visitRequestProgressed(this):void 0},r.prototype.requestCompletedWithResponse=function(t,r){return this.response=t,null!=r&&(this.redirectedToLocation=e.Location.wrap(r)),this.adapter.visitRequestCompleted(this)},r.prototype.requestFailedWithStatusCode=function(t,e){return this.response=e,this.adapter.visitRequestFailedWithStatusCode(this,t)},r.prototype.requestFinished=function(){var t;return this.recordTimingMetric("requestEnd"),"function"==typeof(t=this.adapter).visitRequestFinished?t.visitRequestFinished(this):void 0},r.prototype.performScroll=function(){return this.scrolled?void 0:("restore"===this.action?this.scrollToRestoredPosition()||this.scrollToTop():this.scrollToAnchor()||this.scrollToTop(),this.scrolled=!0)},r.prototype.scrollToRestoredPosition=function(){var t,e;return t=null!=(e=this.restorationData)?e.scrollPosition:void 0,null!=t?(this.controller.scrollToPosition(t),!0):void 0},r.prototype.scrollToAnchor=function(){return null!=this.location.anchor?(this.controller.scrollToAnchor(this.location.anchor),!0):void 0},r.prototype.scrollToTop=function(){return this.controller.scrollToPosition({x:0,y:0})},r.prototype.recordTimingMetric=function(t){var e;return null!=(e=this.timingMetrics)[t]?e[t]:e[t]=(new Date).getTime()},r.prototype.getTimingMetrics=function(){return e.copyObject(this.timingMetrics)},n=function(t){switch(t){case"replace":return"replaceHistoryWithLocationAndRestorationIdentifier";case"advance":case"restore":return"pushHistoryWithLocationAndRestorationIdentifier"}},r.prototype.shouldIssueRequest=function(){return"restore"===this.action?!this.hasCachedSnapshot():!0},r.prototype.cacheSnapshot=function(){return this.snapshotCached?void 0:(this.controller.cacheSnapshot(),this.snapshotCached=!0)},r.prototype.render=function(t){return this.cancelRender(),this.frame=requestAnimationFrame(function(e){return function(){return e.frame=null,t.call(e)}}(this))},r.prototype.cancelRender=function(){return this.frame?cancelAnimationFrame(this.frame):void 0},r}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.Controller=function(){function r(){this.clickBubbled=t(this.clickBubbled,this),this.clickCaptured=t(this.clickCaptured,this),this.pageLoaded=t(this.pageLoaded,this),this.history=new e.History(this),this.view=new e.View(this),this.scrollManager=new e.ScrollManager(this),this.restorationData={},this.clearCache(),this.setProgressBarDelay(500)}return r.prototype.start=function(){return e.supported&&!this.started?(addEventListener("click",this.clickCaptured,!0),addEventListener("DOMContentLoaded",this.pageLoaded,!1),this.scrollManager.start(),this.startHistory(),this.started=!0,this.enabled=!0):void 0},r.prototype.disable=function(){return this.enabled=!1},r.prototype.stop=function(){return this.started?(removeEventListener("click",this.clickCaptured,!0),removeEventListener("DOMContentLoaded",this.pageLoaded,!1),this.scrollManager.stop(),this.stopHistory(),this.started=!1):void 0},r.prototype.clearCache=function(){return this.cache=new e.SnapshotCache(10)},r.prototype.visit=function(t,r){var n,o;return null==r&&(r={}),t=e.Location.wrap(t),this.applicationAllowsVisitingLocation(t)?this.locationIsVisitable(t)?(n=null!=(o=r.action)?o:"advance",this.adapter.visitProposedToLocationWithAction(t,n)):window.location=t:void 0},r.prototype.startVisitToLocationWithAction=function(t,r,n){var o;return e.supported?(o=this.getRestorationDataForIdentifier(n),this.startVisit(t,r,{restorationData:o})):window.location=t},r.prototype.setProgressBarDelay=function(t){return this.progressBarDelay=t},r.prototype.startHistory=function(){return this.location=e.Location.wrap(window.location),this.restorationIdentifier=e.uuid(),this.history.start(),this.history.replace(this.location,this.restorationIdentifier)},r.prototype.stopHistory=function(){return this.history.stop()},r.prototype.pushHistoryWithLocationAndRestorationIdentifier=function(t,r){return this.restorationIdentifier=r,this.location=e.Location.wrap(t),this.history.push(this.location,this.restorationIdentifier)},r.prototype.replaceHistoryWithLocationAndRestorationIdentifier=function(t,r){return this.restorationIdentifier=r,this.location=e.Location.wrap(t),this.history.replace(this.location,this.restorationIdentifier)},r.prototype.historyPoppedToLocationWithRestorationIdentifier=function(t,r){var n;return this.restorationIdentifier=r,this.enabled?(n=this.getRestorationDataForIdentifier(this.restorationIdentifier),this.startVisit(t,"restore",{restorationIdentifier:this.restorationIdentifier,restorationData:n,historyChanged:!0}),this.location=e.Location.wrap(t)):this.adapter.pageInvalidated()},r.prototype.getCachedSnapshotForLocation=function(t){var e;return null!=(e=this.cache.get(t))?e.clone():void 0},r.prototype.shouldCacheSnapshot=function(){return this.view.getSnapshot().isCacheable(); +},r.prototype.cacheSnapshot=function(){var t,r;return this.shouldCacheSnapshot()?(this.notifyApplicationBeforeCachingSnapshot(),r=this.view.getSnapshot(),t=this.lastRenderedLocation,e.defer(function(e){return function(){return e.cache.put(t,r.clone())}}(this))):void 0},r.prototype.scrollToAnchor=function(t){var e;return(e=this.view.getElementForAnchor(t))?this.scrollToElement(e):this.scrollToPosition({x:0,y:0})},r.prototype.scrollToElement=function(t){return this.scrollManager.scrollToElement(t)},r.prototype.scrollToPosition=function(t){return this.scrollManager.scrollToPosition(t)},r.prototype.scrollPositionChanged=function(t){var e;return e=this.getCurrentRestorationData(),e.scrollPosition=t},r.prototype.render=function(t,e){return this.view.render(t,e)},r.prototype.viewInvalidated=function(){return this.adapter.pageInvalidated()},r.prototype.viewWillRender=function(t){return this.notifyApplicationBeforeRender(t)},r.prototype.viewRendered=function(){return this.lastRenderedLocation=this.currentVisit.location,this.notifyApplicationAfterRender()},r.prototype.pageLoaded=function(){return this.lastRenderedLocation=this.location,this.notifyApplicationAfterPageLoad()},r.prototype.clickCaptured=function(){return removeEventListener("click",this.clickBubbled,!1),addEventListener("click",this.clickBubbled,!1)},r.prototype.clickBubbled=function(t){var e,r,n;return this.enabled&&this.clickEventIsSignificant(t)&&(r=this.getVisitableLinkForNode(t.target))&&(n=this.getVisitableLocationForLink(r))&&this.applicationAllowsFollowingLinkToLocation(r,n)?(t.preventDefault(),e=this.getActionForLink(r),this.visit(n,{action:e})):void 0},r.prototype.applicationAllowsFollowingLinkToLocation=function(t,e){var r;return r=this.notifyApplicationAfterClickingLinkToLocation(t,e),!r.defaultPrevented},r.prototype.applicationAllowsVisitingLocation=function(t){var e;return e=this.notifyApplicationBeforeVisitingLocation(t),!e.defaultPrevented},r.prototype.notifyApplicationAfterClickingLinkToLocation=function(t,r){return e.dispatch("turbolinks:click",{target:t,data:{url:r.absoluteURL},cancelable:!0})},r.prototype.notifyApplicationBeforeVisitingLocation=function(t){return e.dispatch("turbolinks:before-visit",{data:{url:t.absoluteURL},cancelable:!0})},r.prototype.notifyApplicationAfterVisitingLocation=function(t){return e.dispatch("turbolinks:visit",{data:{url:t.absoluteURL}})},r.prototype.notifyApplicationBeforeCachingSnapshot=function(){return e.dispatch("turbolinks:before-cache")},r.prototype.notifyApplicationBeforeRender=function(t){return e.dispatch("turbolinks:before-render",{data:{newBody:t}})},r.prototype.notifyApplicationAfterRender=function(){return e.dispatch("turbolinks:render")},r.prototype.notifyApplicationAfterPageLoad=function(t){return null==t&&(t={}),e.dispatch("turbolinks:load",{data:{url:this.location.absoluteURL,timing:t}})},r.prototype.startVisit=function(t,e,r){var n;return null!=(n=this.currentVisit)&&n.cancel(),this.currentVisit=this.createVisit(t,e,r),this.currentVisit.start(),this.notifyApplicationAfterVisitingLocation(t)},r.prototype.createVisit=function(t,r,n){var o,i,s,a,u;return i=null!=n?n:{},a=i.restorationIdentifier,s=i.restorationData,o=i.historyChanged,u=new e.Visit(this,t,r),u.restorationIdentifier=null!=a?a:e.uuid(),u.restorationData=e.copyObject(s),u.historyChanged=o,u.referrer=this.location,u},r.prototype.visitCompleted=function(t){return this.notifyApplicationAfterPageLoad(t.getTimingMetrics())},r.prototype.clickEventIsSignificant=function(t){return!(t.defaultPrevented||t.target.isContentEditable||t.which>1||t.altKey||t.ctrlKey||t.metaKey||t.shiftKey)},r.prototype.getVisitableLinkForNode=function(t){return this.nodeIsVisitable(t)?e.closest(t,"a[href]:not([target]):not([download])"):void 0},r.prototype.getVisitableLocationForLink=function(t){var r;return r=new e.Location(t.getAttribute("href")),this.locationIsVisitable(r)?r:void 0},r.prototype.getActionForLink=function(t){var e;return null!=(e=t.getAttribute("data-turbolinks-action"))?e:"advance"},r.prototype.nodeIsVisitable=function(t){var r;return(r=e.closest(t,"[data-turbolinks]"))?"false"!==r.getAttribute("data-turbolinks"):!0},r.prototype.locationIsVisitable=function(t){return t.isPrefixedBy(this.view.getRootLocation())&&t.isHTML()},r.prototype.getCurrentRestorationData=function(){return this.getRestorationDataForIdentifier(this.restorationIdentifier)},r.prototype.getRestorationDataForIdentifier=function(t){var e;return null!=(e=this.restorationData)[t]?e[t]:e[t]={}},r}()}.call(this),function(){!function(){var t,e;if((t=e=document.currentScript)&&!e.hasAttribute("data-turbolinks-suppress-warning"))for(;t=t.parentNode;)if(t===document.body)return console.warn("You are loading Turbolinks from a + ` + var jsMinAllRegex = / + var result = data + .replace(jsMinAllRegex, code) + // .replace('/js/js_min_all.js', `${cdnHost}/react/build/js/js_min_all.js?v=${newVersion}`) // .replace('/js/js_min_all_2.js', `${cdnHost}/react/build/js/js_min_all_2.js?v=${newVersion}`) // ${cdnHost} 加了cdn后,这个文件里的字体文件加载会有跨域的报错 ../fonts/fontawesome-webfont.eot @@ -204,10 +227,11 @@ function generateNewIndexJsp() { .replace('/css/iconfont.css', `${cdnHost}/react/build/css/iconfont.css?v=${newVersion}`) .replace(/\/js\/create_kindeditor.js/g, `${cdnHost}/react/build/js/create_kindeditor.js?v=${newVersion}`) + .replace(mainRegex, '') // .replace('/react/build/./static/css/main', `${cdnHost}/react/build/./static/css/main`) // .replace('/react/build/./static/js/main', `${cdnHost}/react/build/./static/js/main`) - .replace(/https:\/\/testeduplus2.educoder.net/g, ''); + // .replace(/https:\/\/testeduplus2.educoder.net/g, ''); // .replace(/http:\/\/testbdweb.educoder.net/g, ''); // .replace('/css/css_min_all.css', '/react/build/css/css_min_all.css'); diff --git a/public/react/src/App.css b/public/react/src/App.css index a6f1d45e7..2b3d8d08c 100644 --- a/public/react/src/App.css +++ b/public/react/src/App.css @@ -55,6 +55,10 @@ html, body { .markdown-body p { white-space: pre-wrap; } +/* https://www.educoder.net/courses/2346/group_homeworks/34405/question */ +.renderAsHtml.markdown-body p { + white-space: inherit; +} /* resize */ .editormd .CodeMirror { border-right: none !important; diff --git a/public/react/src/App.js b/public/react/src/App.js index e6f55f5d2..b40af3e0b 100644 --- a/public/react/src/App.js +++ b/public/react/src/App.js @@ -1,4 +1,5 @@ import React, {Component} from 'react'; +import './public-path'; import logo from './logo.svg'; import './App.css'; import {LocaleProvider} from 'antd' diff --git a/public/react/src/common/TextUtil.js b/public/react/src/common/TextUtil.js index 4c83131f1..74cdef3e6 100644 --- a/public/react/src/common/TextUtil.js +++ b/public/react/src/common/TextUtil.js @@ -9,6 +9,7 @@ export function markdownToHTML(oldContent, selector) { window.$('#md_div').html('') // markdown to html if (selector && oldContent && oldContent.startsWith('{ return( -

    - +

    + { item.is_pdf && item.is_pdf == true ? - {item.title} + 30 }> + {item.title} + : - {item.title} + 30 }> + {item.title} + } {item.filesize}

    diff --git a/public/react/src/common/educoder.js b/public/react/src/common/educoder.js index 6f3284db2..8eb34ef55 100644 --- a/public/react/src/common/educoder.js +++ b/public/react/src/common/educoder.js @@ -70,3 +70,5 @@ export { CNotificationHOC as CNotificationHOC } from '../modules/courses/common/ export { default as ModalWrapper } from '../modules/courses/common/ModalWrapper' export { default as NoneData } from '../modules/courses/coursesPublic/NoneData' +export {default as WordNumberTextarea} from '../modules/modals/WordNumberTextarea' + diff --git a/public/react/src/context/TPIContextProvider.js b/public/react/src/context/TPIContextProvider.js index 998d58580..0e0d7be1b 100644 --- a/public/react/src/context/TPIContextProvider.js +++ b/public/react/src/context/TPIContextProvider.js @@ -174,7 +174,7 @@ class TPIContextProvider extends Component { } let testPath = '' if (window.location.port == 3007) { - testPath = 'https://pre-newweb.educoder.net' + testPath = 'http://pre-newweb.educoder.net' } // var url = `${testPath}/api/v1/games/${ game.identifier }/cost_time` var url = `${testPath}/api/tasks/${ game.identifier }/cost_time` diff --git a/public/react/src/index.css b/public/react/src/index.css index ee89ffc11..efd553bf3 100644 --- a/public/react/src/index.css +++ b/public/react/src/index.css @@ -25,9 +25,9 @@ body { .ant-message{ z-index: 20000; } -.ant-modal-header{ - border-radius: 10px; -} +/*.ant-modal-header{*/ + /*border-radius: 10px;*/ +/*}*/ .ant-upload-list-item-info .anticon-loading, .ant-upload-list-item-info .anticon-paper-clip{ color: #29bd8b !important; } diff --git a/public/react/src/modules/courses/Resource/Fileslistitem.js b/public/react/src/modules/courses/Resource/Fileslistitem.js index 925bcc817..b06ab3dc1 100644 --- a/public/react/src/modules/courses/Resource/Fileslistitem.js +++ b/public/react/src/modules/courses/Resource/Fileslistitem.js @@ -129,12 +129,16 @@ class Fileslistitem extends Component{ .catch(function (error) { console.log(error); }); - } + } + + eventStop = (event) =>{ + event.stopPropagation() + } render(){ const { checkBox, - discussMessage, + discussMessage,index } = this.props; return( @@ -190,9 +194,9 @@ class Fileslistitem extends Component{ white-space:nowrap } `} -
    -
    - +
    window.$(`.sourceitem${index} input`).click() }> +
    this.eventStop(event)}> + {checkBox} { @@ -283,16 +287,15 @@ class Fileslistitem extends Component{ {this.props.isAdmin? - - + this.eventStop(event)}> - this.settingList()}>设置 - + this.settingList()}>设置 + :""} {this.props.isStudent===true&&this.props.current_user.login===discussMessage.author.login? - + this.eventStop(event)}> { + const checkBoxValues = this.state.checkBoxValues.slice(0); + const index = checkBoxValues.indexOf(item.id); + if (index != -1) { + _.remove(checkBoxValues, (listItem)=> listItem === item.id) + } else { + checkBoxValues.push(item.id); + } + this.onCheckBoxChange(checkBoxValues) + } PaginationTask=(page)=>{ let {search,order,selectpage,checkAllValue,checkBoxValues}=this.state; @@ -787,7 +798,7 @@ class Fileslists extends Component{ showSearchInput={true} > - {this.props.isAdmin()?
    + {this.props.isAdmin()? files===undefined?'' :files.length===0? "":
    {this.props.isAdmin()? 已选 {checkBoxValues.length} 个:""}
    @@ -897,7 +908,7 @@ class Fileslists extends Component{ { files&&files.map((item, index) => { return ( -
    +
    this.onItemClick(item)}>
    :""} Settingtypes={(id)=>this.Settingtypes(id)} coursesId={this.props.match.params.coursesId} - updatafiledfun={()=>this.updatafiled()} + updatafiledfun={()=>this.updatafiled()} + index={index} >
    @@ -948,21 +960,23 @@ class Fileslists extends Component{ />:""}
    -
    -
    - -

    暂时还没有相关数据哦!

    -
    - - + { + files===undefined?'' :files.length===0?:"" + } ) } } -export default Fileslists; \ No newline at end of file +export default Fileslists; + +{/*
    */} + {/*
    */} + {/**/} + {/*

    暂时还没有相关数据哦!

    */} +{/*
    */} \ No newline at end of file diff --git a/public/react/src/modules/courses/boards/BoardsNew.js b/public/react/src/modules/courses/boards/BoardsNew.js index b02f6a73f..0b27ea8b8 100644 --- a/public/react/src/modules/courses/boards/BoardsNew.js +++ b/public/react/src/modules/courses/boards/BoardsNew.js @@ -337,6 +337,17 @@ class BoardsNew extends Component{ } + + )} @@ -365,10 +376,15 @@ class BoardsNew extends Component{ dropdownRender={menu => (
    {menu} - -
    this.refs['addDirModal'].open()}> - 添加目录 -
    + { + isAdmin && + + +
    this.refs['addDirModal'].open()}> + 添加目录 +
    +
    + }
    )} > diff --git a/public/react/src/modules/courses/boards/TopicDetail.js b/public/react/src/modules/courses/boards/TopicDetail.js index 432c597ee..542157bfb 100644 --- a/public/react/src/modules/courses/boards/TopicDetail.js +++ b/public/react/src/modules/courses/boards/TopicDetail.js @@ -24,7 +24,7 @@ import '../../forums/RightSection.css' import './TopicDetail.css' import '../common/courseMessage.css' import { Pagination, Tooltip } from 'antd' -import { bytesToSize, ConditionToolTip, markdownToHTML, MarkdownToHtml } from 'educoder' +import { bytesToSize, ConditionToolTip, markdownToHTML, MarkdownToHtml , setImagesUrl } from 'educoder' import SendToCourseModal from '../coursesPublic/modal/SendToCourseModal' import CBreadcrumb from '../common/CBreadcrumb' import { generateComments, generateChildComments, _findById, handleContentBeforeCreateNew, addNewComment @@ -57,6 +57,7 @@ class TopicDetail extends Component { pageCount: 1, comments: [], goldRewardDialogOpen: false, + author:undefined } } componentDidMount() { @@ -85,7 +86,8 @@ class TopicDetail extends Component { memo: Object.assign({}, { ...response.data.data, replies_count: response.data.data.total_replies_count - }, {...this.state.memo}) + }, {...this.state.memo}), + author:response.data.data.author }, () => { }) @@ -514,7 +516,7 @@ class TopicDetail extends Component { render() { const { match, history } = this.props const { recommend_shixun, current_user,author_info } = this.props; - const { memo, comments, hasMoreComments, goldRewardDialogOpen, pageCount, total_count } = this.state; + const { memo, comments, hasMoreComments, goldRewardDialogOpen, pageCount, total_count , author } = this.state; const messageId = match.params.topicId if (this.state.memoLoading || !current_user) { return
    @@ -599,51 +601,54 @@ class TopicDetail extends Component { }
    -
    - {moment(memo.created_on).fromNow()} 发布 -
    - -
    -
    - -
    - - {/* { current_user.admin && - - - - } */} - - - - {memo.visits || '1'} - - - { !!memo.total_replies_count && - - - - { memo.total_replies_count } - - - } - {!!memo.praises_count && - - - - { memo.praises_count } +
    + +
    +
    + {author && author.name} + {moment(memo.created_on).fromNow()} 发布 +
    + +
    + + {/* { current_user.admin && + + - + } */} + + + + {memo.visits || '1'} + + + { !!memo.total_replies_count && + + + + { memo.total_replies_count } + + } - - + {!!memo.total_praises_count && + + + + { memo.total_praises_count } + + + } + + +
    +
    diff --git a/public/react/src/modules/courses/boards/index.js b/public/react/src/modules/courses/boards/index.js index db3232b23..60d327799 100644 --- a/public/react/src/modules/courses/boards/index.js +++ b/public/react/src/modules/courses/boards/index.js @@ -363,9 +363,9 @@ class Boards extends Component{ */} - {isAdmin &&
    -
    - {isAdmin && 已选 {checkBoxValues.length} 个} + {messages&&messages.length == 0?"": isAdmin &&
    +
    + {isAdmin&&已选 {checkBoxValues.length} 个}
    { !!isAdmin && diff --git a/public/react/src/modules/courses/busyWork/CommonWorkQuestion.js b/public/react/src/modules/courses/busyWork/CommonWorkQuestion.js index f400cd9a2..d2ce6b8e4 100644 --- a/public/react/src/modules/courses/busyWork/CommonWorkQuestion.js +++ b/public/react/src/modules/courses/busyWork/CommonWorkQuestion.js @@ -91,7 +91,7 @@ class CommonWorkQuestion extends Component{ {/* 内容区 */}
    - + { attachments && attachments.map((item) => { return (
    diff --git a/public/react/src/modules/courses/busyWork/NewWork.js b/public/react/src/modules/courses/busyWork/NewWork.js index ca79e8ac5..525e13922 100644 --- a/public/react/src/modules/courses/busyWork/NewWork.js +++ b/public/react/src/modules/courses/busyWork/NewWork.js @@ -389,6 +389,17 @@ class NewWork extends Component{

    {/* onSubmit={this.handleSubmit} */} +
    + )} -
    - +
    + {homework_name} {/* {homework_name} */} - {category && 返回} + {category && 返回} {this.props.update_atta && diff --git a/public/react/src/modules/courses/busyWork/commonWork.js b/public/react/src/modules/courses/busyWork/commonWork.js index f9c34cfcf..d5ac0be68 100644 --- a/public/react/src/modules/courses/busyWork/commonWork.js +++ b/public/react/src/modules/courses/busyWork/commonWork.js @@ -143,7 +143,9 @@ class commonWork extends Component{ this.setState({ order:e.key==="all"?"":e.key, page:1, - isSpin:true + isSpin:true, + checkBoxValues:[], + checkAll:false }) let {search}=this.state; this.getList(1,search,e.key==="all"?"":e.key); diff --git a/public/react/src/modules/courses/common/CNotificationHOC.js b/public/react/src/modules/courses/common/CNotificationHOC.js index 95e36957f..9a08268b1 100644 --- a/public/react/src/modules/courses/common/CNotificationHOC.js +++ b/public/react/src/modules/courses/common/CNotificationHOC.js @@ -106,11 +106,11 @@ export function CNotificationHOC(options = {}) { confirm = (object) => { - const { title, content, onOk, onCancel, okText } = object; + const { title, content,subContent, onOk, onCancel, okText } = object; this.onCancel = onCancel this.onOk = onOk this.okText = okText || '' - this.setState({ title, content, dialogOpen: true }) + this.setState({ title, content , subContent , dialogOpen: true }) } onDialogOkBtnClick = () => { this.onOk && this.onOk(); @@ -141,17 +141,17 @@ export function CNotificationHOC(options = {}) { this.onOk = null } render() { - const { snackbarOpen, snackbarText, snackbarHorizontal, snackbarVertical, dialogOpen, content ,defineOpen } = this.state; + const { snackbarOpen, snackbarText, snackbarHorizontal, snackbarVertical, dialogOpen, content ,subContent ,defineOpen } = this.state; return ( - this.Navmodalnames(e,2,"course_group",item.id)}>添加分班
    :""} {/*分班*/} - {item.type==="course_group"?
    this.Navmodalnames(e,5,"editname",item.id,item.name)}>重命名
    :
    this.Navmodalnames(e,3,"editname",item.id,item.name)}>重命名
    } + {/*{item.type==="course_group"? :""}*/} +
    this.Navmodalnames(e,3,"editname",item.id,item.name)}>重命名
    this.edithidden(e,item.id)}>隐藏
    this.editSetup(e,item.id)}>置顶
    diff --git a/public/react/src/modules/courses/coursesPublic/AccessoryModal.js b/public/react/src/modules/courses/coursesPublic/AccessoryModal.js index 5541a01a6..71d92cd0c 100644 --- a/public/react/src/modules/courses/coursesPublic/AccessoryModal.js +++ b/public/react/src/modules/courses/coursesPublic/AccessoryModal.js @@ -1,9 +1,11 @@ import React,{ Component } from "react"; -import { Modal,Checkbox,Upload,Button,Icon,message,notification} from "antd"; -import { WordsBtn,getUrl, getUploadActionUrl} from 'educoder'; +import { Modal,Checkbox,Upload,Button,Icon,message,notification,Input} from "antd"; +import { WordsBtn,getUrl, getUploadActionUrl,WordNumberTextarea} from 'educoder'; import axios from 'axios'; import Modals from '../../modals/Modals'; + const CheckboxGroup = Checkbox.Group; +const { TextArea } = Input; class AccessoryModal extends Component{ constructor(props){ @@ -19,6 +21,7 @@ class AccessoryModal extends Component{ updatas:false, shixunsreplace:false, Errormessage:false, + description:undefined } } @@ -238,6 +241,7 @@ class AccessoryModal extends Component{ ModalSave, loadtype, shixunsreplace, + description }=this.state; let {course_groups}=this.props; const uploadProps = { @@ -258,7 +262,6 @@ class AccessoryModal extends Component{ }, }; - return( @@ -335,23 +338,24 @@ class AccessoryModal extends Component{

    - + this.settextarea(e)} + value={description} + maxlength={100} + /> + { this.state.Errormessage && this.state.Errormessage === true ?

    还未上传附件

    - :
    + : "" } {this.state.updatas===true?请上传附件:""} diff --git a/public/react/src/modules/courses/coursesPublic/ModulationModal.js b/public/react/src/modules/courses/coursesPublic/ModulationModal.js index 46dc3becf..974077793 100644 --- a/public/react/src/modules/courses/coursesPublic/ModulationModal.js +++ b/public/react/src/modules/courses/coursesPublic/ModulationModal.js @@ -1,6 +1,6 @@ import React,{ Component } from "react"; import { Modal,Checkbox,Upload,Button,Icon,message,Input} from "antd"; - +import { WordNumberTextarea } from 'educoder'; class ModulationModal extends Component{ constructor(props){ @@ -78,12 +78,20 @@ class ModulationModal extends Component{
    - + {/**/} + + this.settextarea(e)} + value={textareaval} + maxlength={100} + /> +
  • 原因不能为空
  • diff --git a/public/react/src/modules/courses/coursesPublic/NoneData.js b/public/react/src/modules/courses/coursesPublic/NoneData.js index 73406ab5c..35d7a5271 100644 --- a/public/react/src/modules/courses/coursesPublic/NoneData.js +++ b/public/react/src/modules/courses/coursesPublic/NoneData.js @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import {getImageUrl} from 'educoder'; +import { getImageUrl , getUrl } from 'educoder'; class NoneData extends Component{ constructor(props) { @@ -9,7 +9,20 @@ class NoneData extends Component{ const { style } = this.props; return(
    - + +

    暂时还没有相关数据哦!

    ) diff --git a/public/react/src/modules/courses/coursesPublic/PathModal.js b/public/react/src/modules/courses/coursesPublic/PathModal.js index 07ce55024..33a2cb68d 100644 --- a/public/react/src/modules/courses/coursesPublic/PathModal.js +++ b/public/react/src/modules/courses/coursesPublic/PathModal.js @@ -180,11 +180,13 @@ class PathModal extends Component{ }else{ // this.homeworkstart //调用立即发布弹窗 + // this.props.showNotification(response.data.message) this.props.hidecouseShixunModal(); - this.props.courseshomeworkstart(response.data.category_id,response.data.homework_ids) + this.props.updataleftNavfun() + // this.props.courseshomeworkstart(response.data.category_id,response.data.homework_ids) // this.props.showNotification("选用成功") // this.props.showNotification(response.data.message) - // this.props.homeworkupdatalists(Coursename,page,order); + this.props.homeworkupdatalists(this.props.Coursename,this.props.page,this.props.order); } // if(response.status===200) { diff --git a/public/react/src/modules/courses/coursesPublic/ShixunModal.js b/public/react/src/modules/courses/coursesPublic/ShixunModal.js index 2a53cf104..24bb7310e 100644 --- a/public/react/src/modules/courses/coursesPublic/ShixunModal.js +++ b/public/react/src/modules/courses/coursesPublic/ShixunModal.js @@ -186,7 +186,8 @@ class ShixunModal extends Component{ // this.props.showNotification(response.data.message) }else{ - this.props.courseshomeworkstart(response.data.category_id,response.data.homework_ids) + // this.props.courseshomeworkstart(response.data.category_id,response.data.homework_ids) + this.props.homeworkupdatalists(this.props.Coursename,this.props.page,this.props.order); this.props.hidecouseShixunModal() } this.setState({ diff --git a/public/react/src/modules/courses/coursesPublic/modal/SendToCourseModal.js b/public/react/src/modules/courses/coursesPublic/modal/SendToCourseModal.js index af3fc1a30..c4c306a12 100644 --- a/public/react/src/modules/courses/coursesPublic/modal/SendToCourseModal.js +++ b/public/react/src/modules/courses/coursesPublic/modal/SendToCourseModal.js @@ -1,209 +1,209 @@ -import React, { Component } from "react"; -import { Modal, Checkbox, Input, Spin} from "antd"; -import axios from 'axios' -import ModalWrapper from "../../common/ModalWrapper" -import InfiniteScroll from 'react-infinite-scroller'; - -const Search = Input.Search -const pageCount = 15; -class SendToCourseModal extends Component{ - constructor(props){ - super(props); - this.state={ - checkBoxValues: [], - course_lists: [], - course_lists_after_filter: [], - searchValue: '', - hasMore: true, - loading: false, - page: 1 - } - } - fetchCourseList = (arg_page) => { - const page = arg_page || this.state.page; - // search=''& - let url = `/courses/mine.json?page=${page}&page_size=${pageCount}` - const searchValue = this.state.searchValue.trim() - if (searchValue) { - url += `&search=${searchValue}` - } - this.setState({ loading: true }) - axios.get(url, { - }) - .then((response) => { - if (!response.data.data || response.data.data.length == 0) { - this.setState({ - course_lists: page == 1 ? [] : this.state.course_lists, - page, - loading: false, - hasMore: false, - }) - } else { - this.setState({ - course_lists: page == 1 ? response.data.data : this.state.course_lists.concat(response.data.data), - course_lists_after_filter: response.data.data, - page, - loading: false, - hasMore: response.data.data.length == pageCount - }) - } - - }) - .catch(function (error) { - console.log(error); - }); - } - componentDidMount() { - setTimeout(() => { - this.fetchCourseList() - }, 500) - - } - setVisible = (visible) => { - this.refs.modalWrapper.setVisible(visible) - if (visible == false) { - this.setState({ - checkBoxValues: [] - }) - } - } - - onSendOk = () => { - if (!this.state.checkBoxValues || this.state.checkBoxValues.length == 0) { - this.props.showNotification('请先选择要发送至的课堂') - return; - } - if(this.props.url==="/files/bulk_send.json"){ - axios.post("/files/bulk_send.json", { - course_id:this.props.match.params.coursesId, - ids: this.props.selectedMessageIds, - to_course_ids: this.state.checkBoxValues - }) - .then((response) => { - if (response.data.status == 0) { - this.setVisible(false) - this.props.gobackonSend(response.data.message) - } - }) - .catch(function (error) { - console.log(error); - }); - }else{ - const bid = this.props.match.params.boardId - const url = `/boards/${bid}/messages/bulk_send.json` - axios.post(url, { - ids: this.props.selectedMessageIds, - to_course_ids: this.state.checkBoxValues - }) - .then((response) => { - if (response.data.status == 0) { - this.setVisible(false) - this.props.showNotification('发送成功') - } - }) - .catch(function (error) { - console.log(error); - }); - } - - } - - onOk = () => { - const { course_lists, checkBoxValues } = this.state - this.onSendOk() - // this.props.onOk && this.props.onOk(checkBoxValues) - - // this.refs.modalWrapper.setVisible(false) - } - - onCheckBoxChange = (checkBoxValues) => { - this.setState({ - checkBoxValues: checkBoxValues - }) - } - - onSearchChange = (e) => { - this.setState({ - searchValue: e.target.value - }) - } - handleInfiniteOnLoad = () => { - console.log('loadmore...') - this.fetchCourseList(this.state.page + 1) - } - - onSearch = () => { - // const course_lists_after_filter = this.state.course_lists.filter( item => item.name.indexOf(this.state.searchValue) != -1 ) - // this.setState({ course_lists_after_filter }) - this.fetchCourseList(1) - } - render(){ - const { course_lists, checkBoxValues, searchValue, loading, hasMore } = this.state - const { moduleName } = this.props - return( - - -

    选择的{moduleName}发送到指定课堂

    - - - -
    - {/* https://github.com/CassetteRocks/react-infinite-scroller/issues/70 */} -
    - - - - { course_lists && course_lists.map( course => { - return ( -

    - - -

    - ) - }) } -
    - {loading && hasMore && ( -
    - -
    - )} - {/* TODO */} - {/* { - !hasMore &&
    没有更多了。。
    - } */} -
    -
    -
    -
    - ) - } -} -export default SendToCourseModal; - - +import React, { Component } from "react"; +import { Modal, Checkbox, Input, Spin} from "antd"; +import axios from 'axios' +import ModalWrapper from "../../common/ModalWrapper" +import InfiniteScroll from 'react-infinite-scroller'; + +const Search = Input.Search +const pageCount = 15; +class SendToCourseModal extends Component{ + constructor(props){ + super(props); + this.state={ + checkBoxValues: [], + course_lists: [], + course_lists_after_filter: [], + searchValue: '', + hasMore: true, + loading: false, + page: 1 + } + } + fetchCourseList = (arg_page) => { + const page = arg_page || this.state.page; + // search=''& + let url = `/courses/mine.json?page=${page}&page_size=${pageCount}` + const searchValue = this.state.searchValue.trim() + if (searchValue) { + url += `&search=${searchValue}` + } + this.setState({ loading: true }) + axios.get(url, { + }) + .then((response) => { + if (!response.data.data || response.data.data.length == 0) { + this.setState({ + course_lists: page == 1 ? [] : this.state.course_lists, + page, + loading: false, + hasMore: false, + }) + } else { + this.setState({ + course_lists: page == 1 ? response.data.data : this.state.course_lists.concat(response.data.data), + course_lists_after_filter: response.data.data, + page, + loading: false, + hasMore: response.data.data.length == pageCount + }) + } + + }) + .catch(function (error) { + console.log(error); + }); + } + componentDidMount() { + setTimeout(() => { + this.fetchCourseList() + }, 500) + + } + setVisible = (visible) => { + this.refs.modalWrapper.setVisible(visible) + if (visible == false) { + this.setState({ + checkBoxValues: [] + }) + } + } + + onSendOk = () => { + if (!this.state.checkBoxValues || this.state.checkBoxValues.length == 0) { + this.props.showNotification('请先选择要发送至的课堂') + return; + } + if(this.props.url==="/files/bulk_send.json"){ + axios.post("/files/bulk_send.json", { + course_id:this.props.match.params.coursesId, + ids: this.props.selectedMessageIds, + to_course_ids: this.state.checkBoxValues + }) + .then((response) => { + if (response.data.status == 0) { + this.setVisible(false) + this.props.gobackonSend(response.data.message) + } + }) + .catch(function (error) { + console.log(error); + }); + }else{ + const bid = this.props.match.params.boardId + const url = `/boards/${bid}/messages/bulk_send.json` + axios.post(url, { + ids: this.props.selectedMessageIds, + to_course_ids: this.state.checkBoxValues + }) + .then((response) => { + if (response.data.status == 0) { + this.setVisible(false) + this.props.showNotification('发送成功') + } + }) + .catch(function (error) { + console.log(error); + }); + } + + } + + onOk = () => { + const { course_lists, checkBoxValues } = this.state + this.onSendOk() + // this.props.onOk && this.props.onOk(checkBoxValues) + + // this.refs.modalWrapper.setVisible(false) + } + + onCheckBoxChange = (checkBoxValues) => { + this.setState({ + checkBoxValues: checkBoxValues + }) + } + + onSearchChange = (e) => { + this.setState({ + searchValue: e.target.value + }) + } + handleInfiniteOnLoad = () => { + console.log('loadmore...') + this.fetchCourseList(this.state.page + 1) + } + + onSearch = () => { + // const course_lists_after_filter = this.state.course_lists.filter( item => item.name.indexOf(this.state.searchValue) != -1 ) + // this.setState({ course_lists_after_filter }) + this.fetchCourseList(1) + } + render(){ + const { course_lists, checkBoxValues, searchValue, loading, hasMore } = this.state + const { moduleName } = this.props + return( + + +

    选择的{moduleName}发送到指定课堂

    + + + +
    + {/* https://github.com/CassetteRocks/react-infinite-scroller/issues/70 */} +
    + + + + { course_lists && course_lists.map( course => { + return ( +

    + + +

    + ) + }) } +
    + {loading && hasMore && ( +
    + +
    + )} + {/* TODO */} + {/* { + !hasMore &&
    没有更多了。。
    + } */} +
    +
    +
    +
    + ) + } +} +export default SendToCourseModal; + + diff --git a/public/react/src/modules/courses/css/members.css b/public/react/src/modules/courses/css/members.css index ff6f0d99d..df3f884d9 100644 --- a/public/react/src/modules/courses/css/members.css +++ b/public/react/src/modules/courses/css/members.css @@ -1,80 +1,80 @@ -.studentList_operation_ul{ - color: #999; - font-size: 12px; - float: right; - margin-top: 2px; -} -.studentList_operation_ul li{ - float: left; - padding:0px 20px; - position: relative; - cursor: pointer; - flex: 0 0 26px; - line-height: 26px; -} -.studentList_operation_ul li.li_line:after{ - position: absolute; - content: ''; - width: 1px; - height: 12px; - background-color: #EDEDED; - right: 0px; - top:6px; -} -.studentList_operation_ul li:last-child{ - padding-right: 0px; -} -.studentList_operation_ul li:last-child:after{ - width: 0px; -} - -/* 基础的下拉列表、列如排序等 */ -.drop_down_normal li{ - padding: 0px 20px; - height: 34px; - line-height: 34px; - min-width: 96px; - color: #333; - font-size: 14px; - cursor: pointer; - width: 100%; -} - -.stu_table table{ - line-height: 1.2; -} -.stu_table .classesName{ - display: block; - max-width: 428px; -} -.stu_table .ant-table-thead > tr > th{ - padding:21px 16px; - border-bottom: none; -} -.stu_table .ant-table-tbody tr:last-child td{ - border-bottom: none; -} -.stu_table table .ant-table-tbody > tr:hover:not(.ant-table-expanded-row) > td{ - background-color: #fff; -} - -.stu_head{ - padding-bottom: 15px; -} -.ant-modal-body{ - padding:30px 40px; -} -.color-dark-21{ - color: #212121; -} -.tabletd { - background-color:#E6F7FF; -} - -.yslminheigth{ - min-height: 20px; -} - -.yslminheigths{ - min-height: 21px; +.studentList_operation_ul{ + color: #999; + font-size: 12px; + float: right; + margin-top: 2px; +} +.studentList_operation_ul li{ + float: left; + padding:0px 20px; + position: relative; + cursor: pointer; + flex: 0 0 26px; + line-height: 26px; +} +.studentList_operation_ul li.li_line:after{ + position: absolute; + content: ''; + width: 1px; + height: 12px; + background-color: #EDEDED; + right: 0px; + top:6px; +} +.studentList_operation_ul li:last-child{ + padding-right: 0px; +} +.studentList_operation_ul li:last-child:after{ + width: 0px; +} + +/* 基础的下拉列表、列如排序等 */ +.drop_down_normal li{ + padding: 0px 20px; + height: 34px; + line-height: 34px; + min-width: 96px; + color: #333; + font-size: 14px; + cursor: pointer; + width: 100%; +} + +.stu_table table{ + line-height: 1.2; +} +.stu_table .classesName{ + display: block; + max-width: 428px; +} +.stu_table .ant-table-thead > tr > th{ + padding:21px 16px; + border-bottom: none; +} +.stu_table .ant-table-tbody tr:last-child td{ + border-bottom: none; +} +.stu_table table .ant-table-tbody > tr:hover:not(.ant-table-expanded-row) > td{ + background-color: #fff; +} + +.stu_head{ + padding-bottom: 15px; +} +.ant-modal-body{ + padding:30px 40px; +} +.color-dark-21{ + color: #212121; +} +.tabletd { + background-color:#E6F7FF; +} + +.yslminheigth{ + min-height: 20px; +} + +.yslminheigths{ + min-height: 21px; } \ No newline at end of file diff --git a/public/react/src/modules/courses/exercise/Exercise.js b/public/react/src/modules/courses/exercise/Exercise.js index 4331bdc6f..83227fb5a 100644 --- a/public/react/src/modules/courses/exercise/Exercise.js +++ b/public/react/src/modules/courses/exercise/Exercise.js @@ -171,7 +171,20 @@ class Exercise extends Component{ checkAllValue: checkedValues.length == exercises.length }) } - // 全选or反选 + + + onItemClick = (item) => { + const checkBoxValues = this.state.checkBoxValues.slice(0); + const index = checkBoxValues.indexOf(item.id); + if (index != -1) { + _.remove(checkBoxValues, (listItem)=> listItem === item.id) + } else { + checkBoxValues.push(item.id) + } + this.onCheckBoxChange(checkBoxValues) + } + + // 全选or反选 onCheckAll = (e) => { this.setState({ checkAllValue: e.target.checked @@ -507,7 +520,7 @@ class Exercise extends Component{
  • - {this.props.isAdmin()?
    + {this.props.isAdmin()?exercises && exercises.length ===0?"":
    已选 {checkBoxValues.length} 个
    @@ -559,9 +572,9 @@ class Exercise extends Component{ {...this.props} {...this.state} item={item} - key={key} - checkBox={ this.onItemClick(item)} + index={key} + onItemClick={this.onItemClick} + checkBox={} > ) diff --git a/public/react/src/modules/courses/exercise/ExerciseListItem.js b/public/react/src/modules/courses/exercise/ExerciseListItem.js index e890b92ef..5248f4c1a 100644 --- a/public/react/src/modules/courses/exercise/ExerciseListItem.js +++ b/public/react/src/modules/courses/exercise/ExerciseListItem.js @@ -52,17 +52,17 @@ class ExerciseListItem extends Component{ }) } render(){ - let{item,checkBox}=this.props; + let{item,checkBox,index}=this.props; let {coursesId,Id}=this.props.match.params const IsAdmin =this.props.isAdmin(); const IsStudent =this.props.isStudent(); // console.log(this.props.current_user.user_id) return( -
    +
    window.$(`.exerciseitem${index} input`).click() }> { - IsAdmin && - + IsAdmin && + {checkBox} } @@ -96,20 +96,20 @@ class ExerciseListItem extends Component{ {/*{item.exercise_name}*/} { - this.props.isAdmin()? {item.exercise_name}:"" + to={`/courses/${coursesId}/exercises/${item.id}/student_exercise_list?tab=0`}>{item.exercise_name}:"" } { this.props.isStudent()? - {item.exercise_name}:"" + {item.exercise_name}:"" } { this.props.isNotMember()? item.lock_status === 0 ? {item.exercise_name} - : {item.exercise_name}:"" + : {item.exercise_name}:"" } { @@ -165,8 +165,8 @@ class ExerciseListItem extends Component{ { IsAdmin &&
    - 编辑 - 设置 + 编辑 + 设置
    }

    @@ -193,7 +193,7 @@ class ExerciseListItem extends Component{
    {item.current_status ===0&&item.exercise_status>1?
  • 继续答题
  • : item.current_status ===1&&item.exercise_status>1?
  • 查看答题
  • : - item.current_status ===2&&item.exercise_status>1?
  • this.setgameexercise(`/courses/${coursesId}/exercises/${item.id}/users/${this.props.current_user.login}`)}>开始答题
  • :""} + item.current_status ===2&&item.exercise_status>1?
  • this.setgameexercise(`/courses/${coursesId}/exercises/${item.id}/users/${this.props.current_user.login}`)}>开始答题
  • :""}
    }
    diff --git a/public/react/src/modules/courses/exercise/ExerciseNew.js b/public/react/src/modules/courses/exercise/ExerciseNew.js index 6e8444411..017fff71e 100644 --- a/public/react/src/modules/courses/exercise/ExerciseNew.js +++ b/public/react/src/modules/courses/exercise/ExerciseNew.js @@ -48,6 +48,36 @@ class ExerciceNew extends Component{ editMode: !this.props.match.params.Id, } } + + // 已发布试卷编辑保存的确认弹框 + changeScore = (question_id,answerArray) =>{ + this.props.confirm({ + content:'修改了标准答案', + subContent:"是否重新计算学生答题的成绩?", + onOk:()=>{ + this.sureChangeScore(question_id,answerArray) + }, + onCancel:()=>{ + this.addSuccess(); + } + }) + } + + // 已发布试卷修改答案确认修改分数 + sureChangeScore = (question_id,answerArray) =>{ + let url=`/exercise_questions/${question_id}/update_scores.json` + axios.post((url),{ + standard_answers:answerArray + }).then((result)=>{ + if(result){ + this.props.showNotification(`${result.data.message}`); + this.addSuccess(); + } + }).catch((error)=>{ + console.log(error); + }) + } + fetchExercise = () => { const Id = this.props.match.params.Id this.isEdit = !!Id @@ -382,6 +412,7 @@ class ExerciceNew extends Component{ addSuccess: this.addSuccess, addQuestion: this.addQuestion, onEditorCancel: this.onEditorCancel, + changeScore:this.changeScore, editQestion: this.editQestion, onSortDown: this.onSortDown, onSortUp: this.onSortUp, @@ -450,8 +481,19 @@ class ExerciceNew extends Component{ max: 20, message: '最大限制为20个字符', }], })( */} - + { + ` + .exercicenewinputysl .ant-input{ + border-right: none !important; + height: 40px !important; + } + + ` + } + + {/* )} */} @@ -507,7 +549,7 @@ class ExerciceNew extends Component{ { exercise_questions.map((item, index) => { if (item.question_type == 0 || item.question_type == 1) { if (item.isNew) { - return + return } else { return + return } else { return } } else if (item.question_type == 3) { if (item.isNew) { - return + return } else { return } diff --git a/public/react/src/modules/courses/exercise/ExerciseReviewAndAnswer.js b/public/react/src/modules/courses/exercise/ExerciseReviewAndAnswer.js index cdbc7218b..f38ad7435 100644 --- a/public/react/src/modules/courses/exercise/ExerciseReviewAndAnswer.js +++ b/public/react/src/modules/courses/exercise/ExerciseReviewAndAnswer.js @@ -541,6 +541,18 @@ class ExerciseReviewAndAnswer extends Component{ .inputNumber30 .ant-input-number-input-wrap .ant-input-number-input{ height: 28px; } + .setRadioStyle{ + width:100%; + cursor:pointer; + } + .setRadioStyle span:last-child{ + flex:1; + display:flex; + } + .setRadioStyle .ant-radio,.setRadioStyle .ant-checkbox{ + height:16px; + margin-top:2px; + } `} {/*

    */}

    - {this.props.coursedata.name} + {this.props.coursedata.name} > - 试卷 + 试卷 > 试卷详情

    @@ -322,7 +322,7 @@ class Testpapersettinghomepage extends Component{ - 返回 + 返回

    diff --git a/public/react/src/modules/courses/exercise/new/JudgeEditor.js b/public/react/src/modules/courses/exercise/new/JudgeEditor.js index ab93b5bc5..41bc5bac4 100644 --- a/public/react/src/modules/courses/exercise/new/JudgeEditor.js +++ b/public/react/src/modules/courses/exercise/new/JudgeEditor.js @@ -93,10 +93,12 @@ class SingleEditor extends Component{ question_choices, standard_answers: answerArray, insert_id: question_id_to_insert_after || undefined - }) - .then((response) => { + }).then((response) => { if (response.data.status == 0) { this.props.addSuccess() + }else if(response.data.status == 3){ + // 已发布试卷编辑保存 + this.props.changeScore(question_id,answerArray); } }) .catch(function (error) { diff --git a/public/react/src/modules/courses/exercise/new/NullEditor.js b/public/react/src/modules/courses/exercise/new/NullEditor.js index dca67032f..85a8aa336 100644 --- a/public/react/src/modules/courses/exercise/new/NullEditor.js +++ b/public/react/src/modules/courses/exercise/new/NullEditor.js @@ -133,10 +133,12 @@ class NullEditor extends Component{ standard_answers: answerArray, insert_id: question_id_to_insert_after || undefined, is_ordered:newis_ordered, - }) - .then((response) => { + }).then((response) => { if (response.data.status == 0) { this.props.addSuccess() + }else if(response.data.status == 3){ + // 已发布试卷编辑保存 + this.props.changeScore(question_id,answerArray); } }) .catch(function (error) { diff --git a/public/react/src/modules/courses/exercise/new/SingleEditor.js b/public/react/src/modules/courses/exercise/new/SingleEditor.js index b979b62c1..50e594a59 100644 --- a/public/react/src/modules/courses/exercise/new/SingleEditor.js +++ b/public/react/src/modules/courses/exercise/new/SingleEditor.js @@ -105,8 +105,6 @@ class SingleEditor extends Component{ if(!question_title) { this.refs['titleEditor'].showError() this.props.showNotification('题目:不能为空'); return; - - } for(let i = 0; i < question_choices.length; i++) { @@ -115,7 +113,6 @@ class SingleEditor extends Component{ this.props.showNotification(`请先输入 ${tagArray[i]} 选项的内容`); return; } } - /** { "question_title":"同学朋友间常用的沟通工具是什么?", @@ -134,15 +131,17 @@ class SingleEditor extends Component{ question_choices, standard_answers: answerArray, insert_id: question_id_to_insert_after || undefined + }).then((response) => { + if (response.data.status == 0) { + this.props.addSuccess() + }else if(response.data.status == 3){ + // 已发布试卷编辑保存 + this.props.changeScore(question_id,answerArray); + } }) - .then((response) => { - if (response.data.status == 0) { - this.props.addSuccess() - } - }) - .catch(function (error) { - console.log(error); - }); + .catch(function (error) { + console.log(error); + }); } else { const url = `/exercises/${Id}/exercise_questions.json` @@ -167,6 +166,9 @@ class SingleEditor extends Component{ onCancel = () => { this.props.onEditorCancel() } + + + componentDidMount = () => { } diff --git a/public/react/src/modules/courses/exercise/question/multiple.js b/public/react/src/modules/courses/exercise/question/multiple.js index 76195f8fc..4e74b68ce 100644 --- a/public/react/src/modules/courses/exercise/question/multiple.js +++ b/public/react/src/modules/courses/exercise/question/multiple.js @@ -44,19 +44,19 @@ class Multiple extends Component{ console.log(questionType); return(
    - + { questionType.question_choices && questionType.question_choices.map((item,key)=>{ let prefix = `${tagArray[key]}.` return(

    - {prefix} - {/* */} - {/* */} - + + {prefix} + +

    ) }) diff --git a/public/react/src/modules/courses/exercise/question/single.js b/public/react/src/modules/courses/exercise/question/single.js index a879bad74..5156019d4 100644 --- a/public/react/src/modules/courses/exercise/question/single.js +++ b/public/react/src/modules/courses/exercise/question/single.js @@ -40,18 +40,18 @@ class single extends Component{ let isJudge = questionType.question_type == 2 return(
    - + { questionType.question_choices && questionType.question_choices.map((item,key)=>{ let prefix = isJudge ? undefined : `${tagArray[key]}.` return( -

    - {prefix} - {/* */} - {/* */} - +

    + + {prefix} + +

    ) }) diff --git a/public/react/src/modules/courses/gradinforms/Bullsubdirectory.js b/public/react/src/modules/courses/gradinforms/Bullsubdirectory.js new file mode 100644 index 000000000..45da1773b --- /dev/null +++ b/public/react/src/modules/courses/gradinforms/Bullsubdirectory.js @@ -0,0 +1,345 @@ +import React,{ Component } from "react"; +import { Input,Checkbox,Table, Pagination, Modal,Menu, Tooltip,Spin,Button,Form,Row, Col } from "antd"; +import { WordsBtn,on, off, trigger,markdownToHTML,getImageUrl} from 'educoder'; +import './myysleduinforms.css' +import axios from 'axios'; +import TPMMDEditor from "../../tpm/challengesnew/TPMMDEditor"; +import moment from "../new/CoursesNew"; +import Fileslistitem from "../Resource/Fileslistitem"; +// 公告栏 +class Bullsubdirectory extends Component{ + constructor(props){ + super(props); + this.messageRef = React.createRef(); + + this.state={ + description:null, + isSpinysl:false, + whethertoeditysl:false, + addonAfter:0, + eduintits:"", + informs:[], + + + } + } + + componentDidMount() { + console.log("获取到数据"); + console.log(this.props); + let{id,myname,mydescription} =this.props + this.props.form.setFieldsValue({ + id:id, + eduintits:myname, + description:mydescription, + }); + this.setState({ + id:id, + eduintits:myname, + description:mydescription, + + }) + if(myname!=undefined){ + this.setState({ + addonAfter:myname.length + }) + } + + } + + + bianji = (bians)=>{ + this.setState({ + whethertoeditysl:bians, + }) + if(bians===true){ + this.props.getyslbooltrue(); + }else { + this.props.getyslboolfalse(); + } + }; + changeTopicName = (e) => { + console.log("调用了changeTopicName"); + let num = e.target.value.length; + + if(num>60){ + return; + } + this.setState({ + addonAfter: num < 0 ? 0 : num + }); + if(num<=60){ + this.setState({ + eduintits: e.target.value + }) + + this.props.form.setFieldsValue({ + eduintits: e.target.value, + }); + } + + + } + handleSubmit=(e) => { + e.preventDefault(); + this.props.form.validateFields((err, values) => { + if (!err) { + console.log(values.description); + if(values.eduintits === undefined|| values.eduintits === "" || values.eduintits ===null){ + this.props.showNotification(`请输入标题`); + return + + } + if(values.description === undefined|| values.description === "" || values.description ===null){ + this.props.showNotification(`请输入内容`); + return + + } + var id=this.props.match.params.coursesId + var titname=""; + try { + if(values.eduintits.length>0){ + if( values.eduintits.length>60){ + var str=values.eduintits; + titname=str.substring(0,60); + }else { + titname=values.eduintits; + } + }else { + titname=values.eduintits; + } + }catch (e) { + titname=values.eduintits; + } + var url = `/courses/${id}/update_informs.json`; + axios.post(url,{ + inform_id:this.state.id, + name:titname, + description:values.description, + }).then((result) => { + if(result){ + if(result.data){ + if(result.data.status === 0){ + this.props.form.setFieldsValue({ + id:this.state.id, + eduintits:titname, + description:values.description, + }); + this.setState({ + whethertoeditysl:false, + id:this.state.id, + eduintits:titname, + description:values.description, + }); + this.props.getinputdata(); + this.props.getyslboolfalse(); + this.props.showNotification(result.data.message); + }else { + this.props.showNotification(result.data.message); + + } + } + + } + }).catch((error) => { + console.log(error) + }) + }else{ + console.log(err); + } + + }); + } + + + render(){ + let{description,whethertoeditysl,addonAfter,eduintits,informs,isSpinysl} =this.state; + let{myname,mydescription}=this.props; + const {getFieldDecorator} = this.props.form; + + return( + +
    + +
    + { + whethertoeditysl === false? +
    +
    +
    +
    + {myname} +
    +
    + + { + this.props.isAdmin() === true ? + (this.props.yslbool===false? + this.bianji(true)}> + : + "" + ) + :"" + } + +
    +
    +
    +
    +
    +
    +
    + : +
    + + + + {getFieldDecorator('eduintits',{ initialValue: this.state.eduintits }, { + rules: [{ + required: true, message: '请在此输入标题,最多60个字符', + }], + })( +
    +
    + * +
    +
    + + +
    + +
    + )} +
    + + +
    +
    + + + {getFieldDecorator('description', { initialValue: this.state.description },{ + rules: [{ + required: true, message: '请在此输入内容,最多5000个字符', + }, { + len: 5000, message: '最大限制为5000个字符', + }], + })( + + )} + +
    +
    + + +
    + this.bianji(false)}>取消 + +
    +
    + +
    +
    + } +
    + + +
    + + ) + } +} +const Bullsubdirectorys = Form.create({ name: 'bullsubdirectorys' })(Bullsubdirectory); +export default Bullsubdirectorys; diff --git a/public/react/src/modules/courses/gradinforms/Eduinforms.js b/public/react/src/modules/courses/gradinforms/Eduinforms.js index a50bc0369..10beb4507 100644 --- a/public/react/src/modules/courses/gradinforms/Eduinforms.js +++ b/public/react/src/modules/courses/gradinforms/Eduinforms.js @@ -4,7 +4,12 @@ import { WordsBtn,on, off, trigger,markdownToHTML,getImageUrl} from 'educoder'; import './myysleduinforms.css' import axios from 'axios'; import TPMMDEditor from "../../tpm/challengesnew/TPMMDEditor"; +import Bullsubdirectory from "./Bullsubdirectory"; +import moment from "../new/CoursesNew"; +import Fileslistitem from "../Resource/Fileslistitem"; // 公告栏 +// var isOnComposition = false; +// const isChrome = !!window.chrome && !!window.chrome.webstore class Eduinforms extends Component{ constructor(props){ super(props); @@ -14,33 +19,57 @@ class Eduinforms extends Component{ description:null, isSpin:true, whethertoedit:false, - + addonAfter:0, + eduintits:"", + informs:[], + yslbool:false, } } componentDidMount() { console.log("获取到数据"); console.log(this.props); - let url = `/courses/${this.props.match.params.coursesId}/informs.json`; + + this.getinputdata(); + } + + getyslbooltrue(){ + console.log("调用了getyslbooltrue"); + this.setState({ + yslbool:true, + }); + } + + getyslboolfalse(){ + console.log("调用了getyslboolfalse"); + this.setState({ + yslbool:false, + }); + } + getinputdata=()=>{ + this.setState({ + isSpin:true, + }) + let url = `/courses/${this.props.match.params.coursesId}/informs.json`; // axios.get(url).then((response) => { if(response){ - if(response.data){ - this.setState({ - description:response.data.description, - isSpin:false, - }) - }else { - this.setState({ - description:null, - isSpin:false, - - }) - - } + if(response.data){ + this.setState({ + informs:response.data.informs, + isSpin:false, + }) + }else { + this.setState({ + informs:[], + isSpin:false, + + }) + + } }else { this.setState({ - description:null, + informs:[], isSpin:false, }) @@ -48,14 +77,12 @@ class Eduinforms extends Component{ }).catch((error) => { console.log(error) this.setState({ - description:null, + informs:[], isSpin:false, }) }); - } - componentDidUpdate = (prevProps) => { @@ -64,33 +91,113 @@ class Eduinforms extends Component{ bianji = (bians)=>{ this.setState({ whethertoedit:bians, - }) + description:"", + eduintits:"", + addonAfter:0, + }); + this.props.form.setFieldsValue({ + description:"", + eduintits:"", + }); + if(bians===true){ + this.getyslbooltrue(); + }else { + this.getyslboolfalse(); + } }; + changeTopicName = (e) => { + console.log("调用了changeTopicName"); + let num = e.target.value.length; + if(num>60){ + return; + } + this.setState({ + addonAfter: num < 0 ? 0 : num + }); + if(num<=60){ + this.setState({ + eduintits: e.target.value + }) + this.props.form.setFieldsValue({ + eduintits: e.target.value, + }); + } + }; + // handleComposition=(e)=>{ + // if (e.type === 'compositionend') { + // // composition is end + // isOnComposition = false + // + // if (!isOnComposition && isChrome) { + // // fire onChange + // console.log(!isOnComposition); + // this.changeTopicName(e); + // } + // } else { + // // in composition + // isOnComposition = true + // } + // }; + // handleComposition = (e) => { + // console.log(e.type + ": " + e.target.value); + // if (e.type === 'compositionend') { + // // composition is end + // const value = e.target.value; + // this.setState({ isOnComposition: false },()=>{ + // // this.handleFixedChange(value); + // }); + // } else { + // // in composition + // this.setState({ isOnComposition: true }); + // } + // } handleSubmit=(e) => { e.preventDefault(); this.props.form.validateFields((err, values) => { if (!err) { console.log(values.description); + if(values.eduintits === undefined|| values.eduintits === "" || values.eduintits ===null){ + this.props.showNotification(`请输入标题`); + return + + } if(values.description === undefined|| values.description === "" || values.description ===null){ - this.props.showNotification(`请输入提交内容`); + this.props.showNotification(`请输入内容`); return } var id=this.props.match.params.coursesId - - var url = `/courses/${id}/update_informs.json`; + var titname=""; + try { + if(values.eduintits.length>0){ + if( values.eduintits.length>60){ + var str=values.eduintits; + titname=str.substring(0,60); + }else { + titname=values.eduintits; + } + }else { + titname=values.eduintits; + } + }catch (e) { + titname=values.eduintits; + } + var url = `/courses/${id}/new_informs.json`; axios.post(url,{ + name:titname, description:values.description, }).then((result) => { if(result){ if(result.data){ if(result.data.status === 0){ this.setState({ - description:values.description, whethertoedit:false, - }) + + }); + this.getinputdata(); + this.getyslboolfalse(); this.props.showNotification(result.data.message); }else { this.props.showNotification(result.data.message); @@ -111,22 +218,25 @@ class Eduinforms extends Component{ render(){ - let{description,whethertoedit} =this.state; + let{description,whethertoedit,addonAfter,eduintits,informs,yslbool} =this.state; const {getFieldDecorator} = this.props.form; return(
    -

    +

    公告栏 { this.props.isAdmin()===true? -

  • this.bianji(true)}> - 编辑 -
  • + (this.state.yslbool===false? +
  • this.bianji(true)}> + 发布公告 +
  • + :"") + :"" } @@ -138,36 +248,79 @@ class Eduinforms extends Component{
    { - whethertoedit === false? -
    - { - description === null || description=== undefined ||description === "" ? -
    -
    -

    暂时还没有相关数据哦!

    -
    - : -
    -
    + whethertoedit === false?"" + : +
    -
    -
    - } -
    - : -
    - -
    -
    + + {getFieldDecorator('eduintits', { + rules: [{ + required: true, message: '请在此输入标题,最多60个字符', + }], + })( +
    +
    + * +
    +
    + + +
    + +
    + )} +
    + + +
    +
    + - - {getFieldDecorator('description', { - rules: [{ - required: true, message: '请在此输入内容,最多5000个字符', - }, { - max: 5000, message: '最大限制为5000个字符', - }], - })( - - )} - -
    -
    + } + + + {getFieldDecorator('description', { + rules: [{ + required: true, message: '请在此输入内容,最多5000个字符', + }, { + max: 5000, message: '最大限制为5000个字符', + }], + })( + + )} + +
    +
    -
    - this.bianji(false)}>取消 - -
    +
    + this.bianji(false)}>取消 + +
    +
    } +
    + { + informs === null || informs=== undefined ||informs.length === 0 ? +
    +
    +

    暂时还没有相关数据哦!

    +
    + : + +
    + { informs&&informs.map((item, index) => { + return ( + this.getyslbooltrue()} + getyslboolfalse={()=>this.getyslboolfalse()} + getinputdata={()=>this.getinputdata()} > + ) + }) + } +
    + } +
    + +
    @@ -257,3 +436,7 @@ class Eduinforms extends Component{ } const Eduinformss = Form.create({ name: 'eduinforms' })(Eduinforms); export default Eduinformss; +{/*
    */} +{/* {item.name===""?"":item.name===undefined?"":item.name===null?"":
    {item.name}
    }*/} +{/*
    */} +{/*
    */} \ No newline at end of file diff --git a/public/react/src/modules/courses/gradinforms/myysleduinforms.css b/public/react/src/modules/courses/gradinforms/myysleduinforms.css index f3fa2fb1a..ab2884ffe 100644 --- a/public/react/src/modules/courses/gradinforms/myysleduinforms.css +++ b/public/react/src/modules/courses/gradinforms/myysleduinforms.css @@ -1,5 +1,5 @@ .yslmt16px{ - padding-top: 25px !important; + padding-top: 12px !important; padding-left: 25px !important; padding-right: 25px !important; padding-bottom: 1px !important; @@ -10,4 +10,73 @@ padding: 25px !important; -} \ No newline at end of file +} + +.bluebkbk{ + border: 1px solid #4CADFF; + width: 79px; + height: 30px; + text-align: center; + line-height: 30px; + border-radius: 2px; +} +.ysleduinwh{ + + padding-right: 25px; + margin-top: 26px; + display: flex; + justify-content:flex-start; +} +.yslduincolorred{ + color: red; + line-height: 40px; + height: 40px; + text-align: center; +} +.yslduinleft{ + width: 100% ; +} +.yslduinlefts{ + width: 25px; + line-height: 40px; + height: 40px; + text-align: center; +} +.mtyslduin25{ + margin-top: 25px; +} + +.newbianji1{ + font-size: 16px !important; + margin-right: 10px; + color: #4CACFF; + margin-bottom: 3px; + display: inline-block; +} + +.ysltitbt{ + float: left; + padding-top: 31px; + padding-left: 25px; + font-size: 16px; + color: #333333; + text-align: left; +} +.markdownysltext{ + font-size: 14px; + color: #999999; +} +.fudonyingxiangysl{ + width: 100%; +} +.yslbianji{ + padding-top: 31px; +} +.yslmaxheigth80{ + max-height: 80px; +} +.ysldashed{ + border:1px dashed #EEE; +} +.yslclear{ clear: both; +} diff --git a/public/react/src/modules/courses/graduation/tasks/GraduateTaskItem.js b/public/react/src/modules/courses/graduation/tasks/GraduateTaskItem.js index d30944ef0..92a4832db 100644 --- a/public/react/src/modules/courses/graduation/tasks/GraduateTaskItem.js +++ b/public/react/src/modules/courses/graduation/tasks/GraduateTaskItem.js @@ -151,16 +151,12 @@ class GraduateTaskItem extends Component{ coursesId, categoryid, taskid, - + index, + isAdmin } = this.props; - // console.log(discussMessage) - - - - return( -
    +
    window.$(`.taskitem${index} input`).click() }> - { checkBox } - + + { checkBox } + {/* style={{borderTop:data===undefined?"":data.course_identity<4?'1px solid #EBEBEB':'1px solid transparent'}} */} diff --git a/public/react/src/modules/courses/graduation/tasks/GraduationTasksSubmitedit.js b/public/react/src/modules/courses/graduation/tasks/GraduationTasksSubmitedit.js index fcbe6b9aa..ce704ef6e 100644 --- a/public/react/src/modules/courses/graduation/tasks/GraduationTasksSubmitedit.js +++ b/public/react/src/modules/courses/graduation/tasks/GraduationTasksSubmitedit.js @@ -77,16 +77,6 @@ class GraduationTasksSubmitedit extends Component{ } - setedit=()=>{ - // let coursesId=this.props.match.params.coursesId; - // let workId=this.props.match.params.work_Id; - // let {workslist}=this.state - // let task_id=workslist&&workslist.task_id; - // window.location.href="/courses/"+coursesId+"/graduation_tasks/"+task_id+"/"+workId+"/works/edit"; - this.goback() - } - - handleSelectChange = (value) => { console.log(value); @@ -103,7 +93,8 @@ class GraduationTasksSubmitedit extends Component{ // }else{ // this.props.history.push(this.props.current_user.first_category_url); // } - this.props.history.goBack() + this.props.history.replace(`/courses/${this.state.workslist.course_id}/graduation_tasks/${this.state.workslist.graduation_id}`); + } @@ -378,6 +369,9 @@ class GraduationTasksSubmitedit extends Component{ } Commoninterface =(fileList,selectmemberslist,workslist)=>{ + let coursesIds=this.props.match.params.coursesId + let workId=this.props.match.params.work_Id; + let userids=[]; for(var list of selectmemberslist){ @@ -424,7 +418,7 @@ class GraduationTasksSubmitedit extends Component{ this.setState({ spinnings:true }) - let workId=this.props.match.params.work_Id; + // if(fileList.length===0){ // this.setState({ @@ -444,8 +438,8 @@ class GraduationTasksSubmitedit extends Component{ spinnings:false }) if(response!== undefined){ - this.setedit() - + // this.goback() + window.location.href=`/courses/${coursesIds}/graduation_tasks/${workId}/appraise` } // if(response.status===200) { // GraduationTasksnewtype=false; diff --git a/public/react/src/modules/courses/graduation/tasks/GraduationTasksSubmitnew.js b/public/react/src/modules/courses/graduation/tasks/GraduationTasksSubmitnew.js index dadc621b2..3c8318796 100644 --- a/public/react/src/modules/courses/graduation/tasks/GraduationTasksSubmitnew.js +++ b/public/react/src/modules/courses/graduation/tasks/GraduationTasksSubmitnew.js @@ -105,7 +105,7 @@ class GraduationTasksSubmitnew extends Component{ // }else{ // this.props.history.push(this.props.current_user.first_category_url); // } - this.props.history.goBack() + this.props.history.replace(`/courses/${this.state.workslist.course_id}/graduation_tasks/${this.state.workslist.graduation_id}`); } @@ -361,9 +361,7 @@ class GraduationTasksSubmitnew extends Component{ return false; } } - gocannel=()=>{ - this.props.history.goBack() - } + //公用数据 Commoninterface = (fileList,selectmemberslist,workslist)=>{ diff --git a/public/react/src/modules/courses/graduation/tasks/GraduationTasksappraise.js b/public/react/src/modules/courses/graduation/tasks/GraduationTasksappraise.js index d01be4676..c03ffc1e0 100644 --- a/public/react/src/modules/courses/graduation/tasks/GraduationTasksappraise.js +++ b/public/react/src/modules/courses/graduation/tasks/GraduationTasksappraise.js @@ -76,7 +76,9 @@ class GraduationTasksappraise extends Component{ // }else{ // this.props.history.push(this.props.current_user.first_category_url); // } - this.props.history.goBack() + + this.props.history.replace(`/courses/${this.state.datalist.course_id}/graduation_tasks/${this.state.datalist.graduation_id}/${this.state.datalist.task_id}/list`); + } Cancelvisible=()=>{ diff --git a/public/react/src/modules/courses/graduation/tasks/GraduationTasksedit.js b/public/react/src/modules/courses/graduation/tasks/GraduationTasksedit.js index 6f1b2db06..869295c59 100644 --- a/public/react/src/modules/courses/graduation/tasks/GraduationTasksedit.js +++ b/public/react/src/modules/courses/graduation/tasks/GraduationTasksedit.js @@ -18,7 +18,7 @@ class GraduationTasksedit extends Component{ this.state={ coursename:"", coursesearch:"", - title_num:60, + title_num:0, title_value:"", fileList: [], contents: [{val:"",id:1}], @@ -53,14 +53,14 @@ class GraduationTasksedit extends Component{ // // } let namelength=result.data.task_name.length; - let sixlength=title_num-namelength + // let sixlength=title_num-namelength this.setState({ // fileList:newfilelist, description:result.data.description, tasktype:result.data.task_type, name:result.data.task_name, data:result.data, - title_num:sixlength, + title_num:namelength, attachments:result.data.attachments, }) @@ -88,15 +88,15 @@ class GraduationTasksedit extends Component{ // }else{ // this.props.history.push(this.props.current_user.first_category_url); // } - this.props.history.goBack() + this.props.history.replace(`/courses/${this.state.data.course_id}/graduation_tasks/${this.state.data.graduation_id}`); } // 输入title changeTitle=(e)=>{ - + // title_num:60-parseInt(e.target.value.length), this.setState({ - title_num:60-parseInt(e.target.value.length), + title_num:e.target.value.length, title_value:e.target.value }) @@ -375,11 +375,21 @@ class GraduationTasksedit extends Component{ (选择确认后,无法修改) - + {getFieldDecorator('name', { rules: [{ required: true, message: "请输入标题" }], - })()} + })()}
    diff --git a/public/react/src/modules/courses/graduation/tasks/GraduationTasksnew.js b/public/react/src/modules/courses/graduation/tasks/GraduationTasksnew.js index efe2bdd07..fae87fb65 100644 --- a/public/react/src/modules/courses/graduation/tasks/GraduationTasksnew.js +++ b/public/react/src/modules/courses/graduation/tasks/GraduationTasksnew.js @@ -19,7 +19,7 @@ class GraduationTasksnew extends Component { this.state = { coursename: "", coursesearch: "", - title_num: 60, + title_num: 0, title_value: "", fileList: [], contents: [{val: "", id: 1}], @@ -113,15 +113,16 @@ class GraduationTasksnew extends Component { // }else{ // this.props.history.push(this.props.current_user.first_category_url); // } - this.props.history.goBack() + // this.props.history.goBack() + this.props.history.replace(`/courses/${this.props.match.params.coursesId}/graduation_tasks/${this.props.match.params.category_id}`); } // 输入title changeTitle = (e) => { - + // title_num: 60 - parseInt(e.target.value.length), this.setState({ - title_num: 60 - parseInt(e.target.value.length), + title_num: e.target.value.length, title_value: e.target.value }) @@ -376,15 +377,23 @@ class GraduationTasksnew extends Component { margin-top: 0px; margin-bottom: 0px; } + .yslgts .ant-input{ + border-right: none !important; + height: 40px !important; + } ` } {getFieldDecorator('name', { rules: [{required: true, message: "不能为空"}], - })()} + })( + + + + )}
    diff --git a/public/react/src/modules/courses/graduation/tasks/GraduationTaskssetting.js b/public/react/src/modules/courses/graduation/tasks/GraduationTaskssetting.js index e679702da..7e5707695 100644 --- a/public/react/src/modules/courses/graduation/tasks/GraduationTaskssetting.js +++ b/public/react/src/modules/courses/graduation/tasks/GraduationTaskssetting.js @@ -771,7 +771,10 @@ class GraduationTaskssettingapp extends Component{ // }else{ // this.props.history.push(this.props.current_user.first_category_url); // } - this.props.history.goBack() + // this.props.history.goBack() + // this.props.history.replace(this.props.current_user.first_category_url); + this.props.history.replace(`/courses/${this.state.settingdata.course_id}/graduation_tasks/${this.state.settingdata.graduation_id}`); + } isgoback=()=>{ diff --git a/public/react/src/modules/courses/graduation/tasks/GraduationTaskssettinglist.js b/public/react/src/modules/courses/graduation/tasks/GraduationTaskssettinglist.js index 816d53526..9718de28a 100644 --- a/public/react/src/modules/courses/graduation/tasks/GraduationTaskssettinglist.js +++ b/public/react/src/modules/courses/graduation/tasks/GraduationTaskssettinglist.js @@ -64,9 +64,10 @@ class GraduationTaskssettinglist extends Component{ // if(courseId===undefined){ // this.props.history.push("/courses"); // }else{ - // this.props.history.push(this.props.current_user.first_category_url); + // this.props.history.push(this.props.current_user.first_category_url); // } - this.props.history.goBack() + // this.props.history.goBack() + this.props.history.replace(`/courses/${this.state.taskslistdata.course_id}/graduation_tasks/${this.state.taskslistdata.graduation_id}`); } seacthdata=(teacher_comment,task_status,course_group,cross_comment,order,b_order,search,pages)=>{ diff --git a/public/react/src/modules/courses/graduation/tasks/GraduationTaskssettingquestions.js b/public/react/src/modules/courses/graduation/tasks/GraduationTaskssettingquestions.js index 22ce6fa04..b8296f9ef 100644 --- a/public/react/src/modules/courses/graduation/tasks/GraduationTaskssettingquestions.js +++ b/public/react/src/modules/courses/graduation/tasks/GraduationTaskssettingquestions.js @@ -62,7 +62,8 @@ class GraduationTasksquestions extends Component{ // this.props.history.push(this.props.current_user.first_category_url); // } - this.props.history.goBack() + // this.props.history.goBack() + this.props.history.replace(`/courses/${this.state.questionslist.course_id}/graduation_tasks/${this.state.questionslist.graduation_id}`); } end=()=>{ diff --git a/public/react/src/modules/courses/graduation/tasks/index.js b/public/react/src/modules/courses/graduation/tasks/index.js index b54815b2f..18e6f3deb 100644 --- a/public/react/src/modules/courses/graduation/tasks/index.js +++ b/public/react/src/modules/courses/graduation/tasks/index.js @@ -13,6 +13,7 @@ import Modals from '../../../modals/Modals'; import UseBank from "../../busyWork/UseBank"; import '../../css/members.css'; import '../style.css'; +import NoneData from "../../coursesPublic/NoneData"; class GraduationTasks extends Component{ @@ -350,8 +351,6 @@ class GraduationTasks extends Component{ checkBoxValues: checkedValues, checkAllValue:type }) - - } @@ -384,7 +383,9 @@ class GraduationTasks extends Component{ this.setState({ order: e.key, - isSpin:true + isSpin:true, + checkBoxValues:[], + checkAllValue:false }); let newkey=e.key; @@ -563,6 +564,7 @@ class GraduationTasks extends Component{ let {search,page,order,all_count} = this.state; this.fetchAll(search,page,order,all_count) + } getcourse_groupslist=(id)=>{ this.setState({ @@ -696,7 +698,7 @@ class GraduationTasks extends Component{ */} - {this.props.isAdmin()?
    + {this.props.isAdmin()?all_count===undefined?'' :all_count===0?"":
    已选 {checkBoxValues.length} 个
    @@ -726,10 +728,10 @@ class GraduationTasks extends Component{ } `} - { tasks.map((item, index) => { + { tasks&&tasks.map((item, index) => { // console.log(item) return ( -
    +
    @@ -770,18 +773,11 @@ class GraduationTasks extends Component{ />
    } -
    -
    - -

    暂时还没有相关数据哦!

    -
    + + { + tasks===undefined?'' :tasks.length===0? :"" + }
    @@ -790,4 +786,16 @@ class GraduationTasks extends Component{ ) } } -export default GraduationTasks; \ No newline at end of file +export default GraduationTasks; + +{/*
    */} + {/*
    */} + {/**/} + {/*

    暂时还没有相关数据哦!

    */} +{/*
    */} diff --git a/public/react/src/modules/courses/graduation/topics/GraduateTopicDetailInfo.js b/public/react/src/modules/courses/graduation/topics/GraduateTopicDetailInfo.js index 0c76cd9de..033033c42 100644 --- a/public/react/src/modules/courses/graduation/topics/GraduateTopicDetailInfo.js +++ b/public/react/src/modules/courses/graduation/topics/GraduateTopicDetailInfo.js @@ -6,7 +6,7 @@ import '../style.css' import axios from "axios"; import GraduateTopicReply from './GraduateTopicReply' -import { ConditionToolTip,MarkdownToHtml } from 'educoder' +import { ConditionToolTip , MarkdownToHtml , AttachmentList } from 'educoder' const $=window.$; const type={1: "设计",2: "论文", 3: "创作"} @@ -59,10 +59,11 @@ class GraduateTopicDetailTable extends Component{
    { topicInfo && topicInfo.attachment_list.length>0 && -

    - { +

    + {/* { topicInfo.attachment_list.map((item,key)=>{ return( +
  • 30 }> @@ -72,8 +73,9 @@ class GraduateTopicDetailTable extends Component{
  • ) }) - } -

    + } */} + +
    }
    diff --git a/public/react/src/modules/courses/graduation/topics/GraduateTopicItem.js b/public/react/src/modules/courses/graduation/topics/GraduateTopicItem.js index 9f61e69d4..bac04c699 100644 --- a/public/react/src/modules/courses/graduation/topics/GraduateTopicItem.js +++ b/public/react/src/modules/courses/graduation/topics/GraduateTopicItem.js @@ -47,7 +47,7 @@ class GraduateTopicItem extends Component{ } -
    +
    window.$(`.topicItem${index} input`).click() }> - { isAdmin ? checkBox : ""} + { isAdmin ? {checkBox} : ""}
    + )}
    diff --git a/public/react/src/modules/courses/members/modal/AddStudentModal.js b/public/react/src/modules/courses/members/modal/AddStudentModal.js index 217bc8332..de95bc741 100644 --- a/public/react/src/modules/courses/members/modal/AddStudentModal.js +++ b/public/react/src/modules/courses/members/modal/AddStudentModal.js @@ -1,288 +1,288 @@ -import React, { Component } from "react"; -import { Modal, Checkbox, Input, Spin, Select, Divider } from "antd"; -import axios from 'axios' -import ModalWrapper from "../../common/ModalWrapper" -import InfiniteScroll from 'react-infinite-scroller'; -import { ROLE_TEACHER_NUM, ROLE_ASSISTANT_NUM } from '../common' -import NoneData from '../../coursesPublic/NoneData' -import { ConditionToolTip, ThemeContext } from 'educoder' -import SchoolSelect from '../../coursesPublic/form/SchoolSelect' - -const Option = Select.Option; -const pageCount = 15; -class AddStudentModal extends Component{ - constructor(props){ - super(props); - this.state={ - checkBoxValues: [], - users: [], - hasMore: true, - loading: false, - courseGroup: '', - page: 1, - isSpin:false - } - } - fetchMemberList = (arg_page) => { - const courseId = this.props.match.params.coursesId - const page = arg_page || this.state.page; - const { name, school_name } = this.state - let url = `/courses/${courseId}/search_users.json?page=${page}&limit=${pageCount}&school_name=${school_name || ''}&name=${name || ''}` - this.setState({ loading: true }) - axios.get(url) - .then((response) => { - if (!response.data.users || response.data.users.length == 0) { - this.setState({ - users: page == 1 ? response.data.users : this.state.users, - page, - loading: false, - hasMore: false, - }) - } else { - this.setState({ - users: page == 1 ? response.data.users : this.state.users.concat(response.data.users), - page, - loading: false, - hasMore: response.data.users.length == pageCount - }) - } - - }) - .catch(function (error) { - console.log(error); - }); - } - componentDidMount() { - - - } - fetchOptions = () => { - // add_teacher_popup - const courseId = this.props.match.params.coursesId - - let url = `/courses/${courseId}/all_course_groups.json` - - axios.get(url, { - }) - .then((response) => { - if (response.data.course_groups && response.data.course_groups.length) { - this.setState({ - course_groups: response.data.course_groups, - courseGroup: response.data.course_groups[0].id - }) - } else { - // showNotification('') - } - }) - .catch(function (error) { - console.log(error); - }); - } - setVisible = (visible) => { - if (visible) { - this.setState({school_name: this.props.user.user_school}) - this.fetchMemberList() - this.fetchOptions() - } - this.refs.modalWrapper.setVisible(visible) - if (visible == false) { - this.setState({ - checkBoxValues: [] - }) - } - } - - onSendOk = () => { - - if(!this.state.checkBoxValues || this.state.checkBoxValues.length == 0) { - this.props.showNotification('请从列表中先选择用户。') - return; - } - this.setState({ - isSpin:true - }) - const courseId = this.props.match.params.coursesId - const url = `/courses/${courseId}/add_students_by_search.json` - const params = { - "user_ids": this.state.checkBoxValues - } - const { courseGroup } = this.state - if (courseGroup) { - params.course_group_id = courseGroup - } - axios.post(url, params) - .then((response) => { - if (response.data.status == 0) { - this.setVisible(false) - this.props.showNotification('添加成功') - this.props.addStudentSuccess && this.props.addStudentSuccess(params) - this.setState({ - isSpin:false - }) - } - }) - .catch(function (error) { - console.log(error); - }); - } - - onOk = () => { - this.onSendOk() - } - - onCheckBoxChange = (checkBoxValues) => { - this.setState({ - checkBoxValues: checkBoxValues - }) - } - - handleInfiniteOnLoad = () => { - this.fetchMemberList(this.state.page + 1) - } - - onSearch = () => { - this.fetchMemberList(1) - } - handleCourseGroupChange = (value) => { - this.setState({ - courseGroup: value - }) - } - render(){ - const { users, checkBoxValues, loading, hasMore, name, school_name - , courseGroup, course_groups,isSpin } = this.state - const { moduleName } = this.props - let theme = this.context; - return( - - -
    - 姓名: - {this.setState({name: e.target.value})}} - style={{ width: '221px'}} - > - 单位: - {/* {this.setState({school_name: e.target.value})}} - style={{ width: '200px'}}> - */} - {this.setState({school_name: value})}} - > - this.fetchMemberList(1)} - style={{ height: '30px', lineHeight: '30px', marginLeft: '10px', width: '70px'}} - >搜索 -
    - {/* */} - - -

    - - - - - - -

    - - { loading || users.length ?
    - {/* https://github.com/CassetteRocks/react-infinite-scroller/issues/70 */} -
    - - - - { users.map( candidate => { - return ( -

    - - - 12 }> - - - - - 12 }> - - - - - - -

    - ) - }) } -
    - {loading && hasMore && ( -
    - -
    - )} - -
    -
    - {course_groups && course_groups.length &&
    - 所选学生分班至(选填): - -
    } -
    : } -
    -
    - ) - } -} - -AddStudentModal.contextType = ThemeContext; -export default AddStudentModal; +import React, { Component } from "react"; +import { Modal, Checkbox, Input, Spin, Select, Divider } from "antd"; +import axios from 'axios' +import ModalWrapper from "../../common/ModalWrapper" +import InfiniteScroll from 'react-infinite-scroller'; +import { ROLE_TEACHER_NUM, ROLE_ASSISTANT_NUM } from '../common' +import NoneData from '../../coursesPublic/NoneData' +import { ConditionToolTip, ThemeContext } from 'educoder' +import SchoolSelect from '../../coursesPublic/form/SchoolSelect' + +const Option = Select.Option; +const pageCount = 15; +class AddStudentModal extends Component{ + constructor(props){ + super(props); + this.state={ + checkBoxValues: [], + users: [], + hasMore: true, + loading: false, + courseGroup: '', + page: 1, + isSpin:false + } + } + fetchMemberList = (arg_page) => { + const courseId = this.props.match.params.coursesId + const page = arg_page || this.state.page; + const { name, school_name } = this.state + let url = `/courses/${courseId}/search_users.json?page=${page}&limit=${pageCount}&school_name=${school_name || ''}&name=${name || ''}` + this.setState({ loading: true }) + axios.get(url) + .then((response) => { + if (!response.data.users || response.data.users.length == 0) { + this.setState({ + users: page == 1 ? response.data.users : this.state.users, + page, + loading: false, + hasMore: false, + }) + } else { + this.setState({ + users: page == 1 ? response.data.users : this.state.users.concat(response.data.users), + page, + loading: false, + hasMore: response.data.users.length == pageCount + }) + } + + }) + .catch(function (error) { + console.log(error); + }); + } + componentDidMount() { + + + } + fetchOptions = () => { + // add_teacher_popup + const courseId = this.props.match.params.coursesId + + let url = `/courses/${courseId}/all_course_groups.json` + + axios.get(url, { + }) + .then((response) => { + if (response.data.course_groups && response.data.course_groups.length) { + this.setState({ + course_groups: response.data.course_groups, + courseGroup: response.data.course_groups[0].id + }) + } else { + // showNotification('') + } + }) + .catch(function (error) { + console.log(error); + }); + } + setVisible = (visible) => { + if (visible) { + this.setState({school_name: this.props.user.user_school}) + this.fetchMemberList() + this.fetchOptions() + } + this.refs.modalWrapper.setVisible(visible) + if (visible == false) { + this.setState({ + checkBoxValues: [] + }) + } + } + + onSendOk = () => { + + if(!this.state.checkBoxValues || this.state.checkBoxValues.length == 0) { + this.props.showNotification('请从列表中先选择用户。') + return; + } + this.setState({ + isSpin:true + }) + const courseId = this.props.match.params.coursesId + const url = `/courses/${courseId}/add_students_by_search.json` + const params = { + "user_ids": this.state.checkBoxValues + } + const { courseGroup } = this.state + if (courseGroup) { + params.course_group_id = courseGroup + } + axios.post(url, params) + .then((response) => { + if (response.data.status == 0) { + this.setVisible(false) + this.props.showNotification('添加成功') + this.props.addStudentSuccess && this.props.addStudentSuccess(params) + this.setState({ + isSpin:false + }) + } + }) + .catch(function (error) { + console.log(error); + }); + } + + onOk = () => { + this.onSendOk() + } + + onCheckBoxChange = (checkBoxValues) => { + this.setState({ + checkBoxValues: checkBoxValues + }) + } + + handleInfiniteOnLoad = () => { + this.fetchMemberList(this.state.page + 1) + } + + onSearch = () => { + this.fetchMemberList(1) + } + handleCourseGroupChange = (value) => { + this.setState({ + courseGroup: value + }) + } + render(){ + const { users, checkBoxValues, loading, hasMore, name, school_name + , courseGroup, course_groups,isSpin } = this.state + const { moduleName } = this.props + let theme = this.context; + return( + + +
    + 姓名: + {this.setState({name: e.target.value})}} + style={{ width: '221px'}} + > + 单位: + {/* {this.setState({school_name: e.target.value})}} + style={{ width: '200px'}}> + */} + {this.setState({school_name: value})}} + > + this.fetchMemberList(1)} + style={{ height: '30px', lineHeight: '30px', marginLeft: '10px', width: '70px'}} + >搜索 +
    + {/* */} + + +

    + + + + + + +

    + + { loading || users.length ?
    + {/* https://github.com/CassetteRocks/react-infinite-scroller/issues/70 */} +
    + + + + { users.map( candidate => { + return ( +

    + + + 12 }> + + + + + 12 }> + + + + + + +

    + ) + }) } +
    + {loading && hasMore && ( +
    + +
    + )} + +
    +
    + {course_groups && course_groups.length &&
    + 所选学生分班至(选填): + +
    } +
    : } +
    +
    + ) + } +} + +AddStudentModal.contextType = ThemeContext; +export default AddStudentModal; diff --git a/public/react/src/modules/courses/members/modal/AddTeacherModal.js b/public/react/src/modules/courses/members/modal/AddTeacherModal.js index 21902a782..b397f7838 100644 --- a/public/react/src/modules/courses/members/modal/AddTeacherModal.js +++ b/public/react/src/modules/courses/members/modal/AddTeacherModal.js @@ -1,355 +1,355 @@ -import React, { Component } from "react"; -import { Modal, Checkbox, Input, Spin, Select, Divider, Icon } from "antd"; -import axios from 'axios' -import ModalWrapper from "../../common/ModalWrapper" -import InfiniteScroll from 'react-infinite-scroller'; -import { ROLE_TEACHER_NUM, ROLE_ASSISTANT_NUM } from '../common' -import { ConditionToolTip, ActionBtn } from 'educoder' -import NoneData from '../../coursesPublic/NoneData' -import AddGraduationGroupModal from './AddGraduationGroupModal' -import SchoolSelect from '../../coursesPublic/form/SchoolSelect' - -const Option = Select.Option; -const pageCount = 15; -let timeout, currentValue -class AddTeacherModal extends Component{ - constructor(props){ - super(props); - this.state={ - school_names: [], - checkBoxValues: [], - candidates: [], - hasMore: true, - loading: false, - page: 1 - } - } - fetchMemberList = (arg_page) => { - const courseId = this.props.match.params.coursesId - const page = arg_page || this.state.page; - const { name, school_name } = this.state - let url = `/courses/${courseId}/search_teacher_candidate.json` - this.setState({ loading: true }) - axios.post(url, { - page: page, - limit: pageCount, - school_name: school_name || '', - name: name || '' - }) - .then((response) => { - if (!response.data.candidates || response.data.candidates.length == 0) { - this.setState({ - candidates: page == 1 ? response.data.candidates : this.state.candidates, - page, - loading: false, - hasMore: false, - }) - } else { - this.setState({ - candidates: page == 1 ? response.data.candidates : this.state.candidates.concat(response.data.candidates), - page, - loading: false, - hasMore: response.data.candidates.length == pageCount - }) - } - - }) - .catch(function (error) { - console.log(error); - }); - } - componentDidMount() { - - - } - onAddGraduationGroupOk = () => { - this.fetchOptions() - } - fetchOptions = () => { - // add_teacher_popup - const courseId = this.props.match.params.coursesId - - let url = `/courses/${courseId}/add_teacher_popup.json` - - axios.get(url, { - }) - .then((response) => { - - if (response.data.school_name) { - this.setState({ - school_name: response.data.school_name - }, () => this.fetchMemberList()) - } else { - this.fetchMemberList() - - } - if (response.data.graduation_groups) { - this.setState({ - graduation_groups: response.data.graduation_groups - }) - } - if (response.data.course_groups) { - this.setState({ - course_groups: response.data.course_groups - }) - } - - }) - .catch(function (error) { - console.log(error); - }); - } - setVisible = (visible) => { - if (visible) { - this.fetchOptions() - } - this.refs.modalWrapper.setVisible(visible) - if (visible == false) { - this.setState({ - checkBoxValues: [] - }) - } - } - - onSendOk = () => { - const courseId = this.props.match.params.coursesId - const url = `/courses/${courseId}/add_teacher.json` - if (this.state.checkBoxValues.length == 0) { - this.props.showNotification('请先在下面列表中选择要添加教师的成员') - return - } - const params = { - "user_list": this.state.checkBoxValues.map (item => { return { 'user_id': item }}) , - // "graduation_group_id": "2", - // "course_group_id": "820", - "role": this.props.isTeacher ? ROLE_TEACHER_NUM : ROLE_ASSISTANT_NUM - } - const { graduationGroup, courseGroup } = this.state - if (graduationGroup) { - params.graduation_group_id = graduationGroup - } - if (courseGroup) { - params.course_group_id = courseGroup - } - axios.post(url, params) - .then((response) => { - if (response.data.status == 0) { - this.setVisible(false) - this.props.showNotification('添加成功') - this.props.addTeacherSuccess && this.props.addTeacherSuccess(params) - } - }) - .catch(function (error) { - console.log(error); - }); - } - - onOk = () => { - this.onSendOk() - } - - onCheckBoxChange = (checkBoxValues) => { - this.setState({ - checkBoxValues: checkBoxValues - }) - } - - handleInfiniteOnLoad = () => { - this.fetchMemberList(this.state.page + 1) - } - - onSearch = () => { - this.fetchMemberList(1) - } - handleGradationGroupChange = (value) => { - this.setState({ - graduationGroup: value - }) - } - handleCourseGroupChange = (value) => { - this.setState({ - courseGroup: value - }) - } - onOrgNameChange = (value) => { - // console.log('school_name: ', value) - this.setState({ school_name: value }) - } - - hasGraduationModule = () => { - const { course_modules } = this.props; - const result = course_modules && course_modules.filter( item => { - return item.type == 'graduation' - }) - return result && result.length > 0 - } - - render(){ - const { candidates, checkBoxValues, loading, hasMore, name, school_name, school_names - , graduationGroup, graduation_groups, courseGroup, course_groups } = this.state - const { moduleName } = this.props - - return( - - - -
    - 姓名: - {this.setState({name: e.target.value})}} - style={{ width: '200px', marginRight: '18px' }}> - - 单位: - - {/* */} - this.fetchMemberList(1)} - style={{ height: '30px', lineHeight: '30px', marginLeft: '10px', width: '70px'}} - >搜索 -
    - {/* graduation_groups && !!graduation_groups.length */} - - -

    - - - - - - -

    - { loading || candidates.length ?
    - {/* https://github.com/CassetteRocks/react-infinite-scroller/issues/70 */} -
    - - - - { candidates && candidates.map( candidate => { - return ( -

    - - - {/* "color":"#4c4c4c" */} - 12 }> - - - - - 12 }> - - - - - - -

    - ) - }) } -
    - {loading && hasMore && ( -
    - -
    - )} - -
    -
    -
    : } -
    - { this.hasGraduationModule() &&
    - 添加至答辩组: - -
    } - - { course_groups && !!course_groups.length &&
    - 管理权限: - -
    } -
    -
    - ) - } -} -export default AddTeacherModal; +import React, { Component } from "react"; +import { Modal, Checkbox, Input, Spin, Select, Divider, Icon } from "antd"; +import axios from 'axios' +import ModalWrapper from "../../common/ModalWrapper" +import InfiniteScroll from 'react-infinite-scroller'; +import { ROLE_TEACHER_NUM, ROLE_ASSISTANT_NUM } from '../common' +import { ConditionToolTip, ActionBtn } from 'educoder' +import NoneData from '../../coursesPublic/NoneData' +import AddGraduationGroupModal from './AddGraduationGroupModal' +import SchoolSelect from '../../coursesPublic/form/SchoolSelect' + +const Option = Select.Option; +const pageCount = 15; +let timeout, currentValue +class AddTeacherModal extends Component{ + constructor(props){ + super(props); + this.state={ + school_names: [], + checkBoxValues: [], + candidates: [], + hasMore: true, + loading: false, + page: 1 + } + } + fetchMemberList = (arg_page) => { + const courseId = this.props.match.params.coursesId + const page = arg_page || this.state.page; + const { name, school_name } = this.state + let url = `/courses/${courseId}/search_teacher_candidate.json` + this.setState({ loading: true }) + axios.post(url, { + page: page, + limit: pageCount, + school_name: school_name || '', + name: name || '' + }) + .then((response) => { + if (!response.data.candidates || response.data.candidates.length == 0) { + this.setState({ + candidates: page == 1 ? response.data.candidates : this.state.candidates, + page, + loading: false, + hasMore: false, + }) + } else { + this.setState({ + candidates: page == 1 ? response.data.candidates : this.state.candidates.concat(response.data.candidates), + page, + loading: false, + hasMore: response.data.candidates.length == pageCount + }) + } + + }) + .catch(function (error) { + console.log(error); + }); + } + componentDidMount() { + + + } + onAddGraduationGroupOk = () => { + this.fetchOptions() + } + fetchOptions = () => { + // add_teacher_popup + const courseId = this.props.match.params.coursesId + + let url = `/courses/${courseId}/add_teacher_popup.json` + + axios.get(url, { + }) + .then((response) => { + + if (response.data.school_name) { + this.setState({ + school_name: response.data.school_name + }, () => this.fetchMemberList()) + } else { + this.fetchMemberList() + + } + if (response.data.graduation_groups) { + this.setState({ + graduation_groups: response.data.graduation_groups + }) + } + if (response.data.course_groups) { + this.setState({ + course_groups: response.data.course_groups + }) + } + + }) + .catch(function (error) { + console.log(error); + }); + } + setVisible = (visible) => { + if (visible) { + this.fetchOptions() + } + this.refs.modalWrapper.setVisible(visible) + if (visible == false) { + this.setState({ + checkBoxValues: [] + }) + } + } + + onSendOk = () => { + const courseId = this.props.match.params.coursesId + const url = `/courses/${courseId}/add_teacher.json` + if (this.state.checkBoxValues.length == 0) { + this.props.showNotification('请先在下面列表中选择要添加教师的成员') + return + } + const params = { + "user_list": this.state.checkBoxValues.map (item => { return { 'user_id': item }}) , + // "graduation_group_id": "2", + // "course_group_id": "820", + "role": this.props.isTeacher ? ROLE_TEACHER_NUM : ROLE_ASSISTANT_NUM + } + const { graduationGroup, courseGroup } = this.state + if (graduationGroup) { + params.graduation_group_id = graduationGroup + } + if (courseGroup) { + params.course_group_id = courseGroup + } + axios.post(url, params) + .then((response) => { + if (response.data.status == 0) { + this.setVisible(false) + this.props.showNotification('添加成功') + this.props.addTeacherSuccess && this.props.addTeacherSuccess(params) + } + }) + .catch(function (error) { + console.log(error); + }); + } + + onOk = () => { + this.onSendOk() + } + + onCheckBoxChange = (checkBoxValues) => { + this.setState({ + checkBoxValues: checkBoxValues + }) + } + + handleInfiniteOnLoad = () => { + this.fetchMemberList(this.state.page + 1) + } + + onSearch = () => { + this.fetchMemberList(1) + } + handleGradationGroupChange = (value) => { + this.setState({ + graduationGroup: value + }) + } + handleCourseGroupChange = (value) => { + this.setState({ + courseGroup: value + }) + } + onOrgNameChange = (value) => { + // console.log('school_name: ', value) + this.setState({ school_name: value }) + } + + hasGraduationModule = () => { + const { course_modules } = this.props; + const result = course_modules && course_modules.filter( item => { + return item.type == 'graduation' + }) + return result && result.length > 0 + } + + render(){ + const { candidates, checkBoxValues, loading, hasMore, name, school_name, school_names + , graduationGroup, graduation_groups, courseGroup, course_groups } = this.state + const { moduleName } = this.props + + return( + + + +
    + 姓名: + {this.setState({name: e.target.value})}} + style={{ width: '200px', marginRight: '18px' }}> + + 单位: + + {/* */} + this.fetchMemberList(1)} + style={{ height: '30px', lineHeight: '30px', marginLeft: '10px', width: '70px'}} + >搜索 +
    + {/* graduation_groups && !!graduation_groups.length */} + + +

    + + + + + + +

    + { loading || candidates.length ?
    + {/* https://github.com/CassetteRocks/react-infinite-scroller/issues/70 */} +
    + + + + { candidates && candidates.map( candidate => { + return ( +

    + + + {/* "color":"#4c4c4c" */} + 12 }> + + + + + 12 }> + + + + + + +

    + ) + }) } +
    + {loading && hasMore && ( +
    + +
    + )} + +
    +
    +
    : } +
    + { this.hasGraduationModule() &&
    + 添加至答辩组: + +
    } + + { course_groups && !!course_groups.length &&
    + 管理权限: + +
    } +
    +
    + ) + } +} +export default AddTeacherModal; diff --git a/public/react/src/modules/courses/members/modal/CreateGroupByImportModal.js b/public/react/src/modules/courses/members/modal/CreateGroupByImportModal.js index fe2a88dce..d5e77da9d 100644 --- a/public/react/src/modules/courses/members/modal/CreateGroupByImportModal.js +++ b/public/react/src/modules/courses/members/modal/CreateGroupByImportModal.js @@ -112,12 +112,13 @@ class CreateGroupByImportModal extends Component{ render(){ const { candidates, checkBoxValues, loading, hasMore, name, school_name, school_names - , graduationGroup, graduation_groups, courseGroup, course_groups } = this.state + , graduationGroup, graduation_groups, courseGroup, course_groups , fileList } = this.state const { moduleName } = this.props const props = { name: 'file', multiple: true, + fileList:fileList, action: getUploadActionUrl(), onRemove: this.onAttachmentRemove, onChange: this.handleChange @@ -131,26 +132,15 @@ class CreateGroupByImportModal extends Component{ onOk={this.onOk} className="createGroupByImport" > - - -
    - - - -
    - -

    - -

    -

    点击或拖拽文件到这里上传

    -

    - 单个文件最大150MB -

    -
    + +

    + +

    +

    点击或拖拽文件到这里上传

    +

    + 单个文件最大150MB +

    +
    ) } diff --git a/public/react/src/modules/courses/members/studentsList.js b/public/react/src/modules/courses/members/studentsList.js index 2a510789e..cc7485101 100644 --- a/public/react/src/modules/courses/members/studentsList.js +++ b/public/react/src/modules/courses/members/studentsList.js @@ -35,6 +35,7 @@ const buildColumns = (that) => { dataIndex: 'login', key: 'login', align:'center', + width:"10%", className:"color-grey-6", render: (login, record) => { return { dataIndex: 'student_id', key: 'student_id', align:'center', + width:"10%", className:"color-grey-6", render: (student_id, record) => { - return 10 ? student_id : ''} + return 10 ? student_id : ''} style={{maxWidth: '160px'}} >{student_id} } }]; @@ -69,7 +71,7 @@ const buildColumns = (that) => { dataIndex: 'course_group_name', key: 'course_group_name', align:'center', - width:"50%", + width:"40%", className:"color-grey-6" }) } diff --git a/public/react/src/modules/courses/new/CoursesNew.js b/public/react/src/modules/courses/new/CoursesNew.js index b87b4e81d..edee26a27 100644 --- a/public/react/src/modules/courses/new/CoursesNew.js +++ b/public/react/src/modules/courses/new/CoursesNew.js @@ -1,11 +1,12 @@ import React, {Component} from "React"; -import {Form, Select, Input, Button, Checkbox, DatePicker,Spin,Icon,AutoComplete,InputNumber} from "antd"; +import {Form, Select, Input, Button, Checkbox, DatePicker,Spin,Icon,AutoComplete,InputNumber,Breadcrumb} from "antd"; import ApplyForAddOrgModal from '../../user/modal/ApplyForAddOrgModal'; import axios from 'axios'; import "../css/Courses.css"; import locale from 'antd/lib/date-picker/locale/zh_CN'; import moment from 'moment'; import Modals from '../../modals/Modals'; +import {WordsBtn, ActionBtn} from 'educoder'; @@ -44,7 +45,9 @@ class CoursesNew extends Component { fetching:false, boolxinjian:false, checkboxgroup:undefined, - checkbofrup:["shixun_homework","common_homework","group_homework","exercise","attachment","course_group","graduation","poll","board"], + checkbofrup:[{module_type:"shixun_homework",module_name:"实训作业"},{module_type:"common_homework",module_name:"普通作业"},{module_type:"group_homework",module_name:"分组作业"} + ,{module_type:"exercise",module_name:"试卷"},{module_type:"poll",module_name:"问卷"},{module_type:"graduation",module_name:"毕业设计"} + ,{module_type:"board",module_name:"讨论"},{module_type:"attachment",module_name:"资源"},{module_type:"course_group",module_name:"分班"}], checkbofrups:[], } } @@ -78,32 +81,19 @@ class CoursesNew extends Component { is_public: data.is_public === 1 ? true : false, Realnamecertification: data.authentication, Professionalcertification:data.professional_certification, - + // checkbofrups:data.course_modules, }); - try { - var datasysl=[]; - var dataysl2=[]; - var dataysl3=[]; - var checkbofrup =this.state.checkbofrup; - dataysl2=data.course_module_types; - datasysl=checkbofrup; - for (var k=0;k { console.log(error); @@ -450,6 +440,7 @@ class CoursesNew extends Component { search: value } }).then((result)=>{ + if(result){ if (result.data.status===0) { this.setState({ searchlistscholl: result.data.school_names, @@ -461,6 +452,7 @@ class CoursesNew extends Component { }) } } + } }).catch((error)=>{ console.log(error) }) @@ -469,7 +461,7 @@ class CoursesNew extends Component { this.applyForAddOrgForm.setVisible(true) } render() { - let {datatime,school,searchlistscholl,checkboxgroup} = this.state; + let {datatime,school,searchlistscholl} = this.state; const {getFieldDecorator} = this.props.form; const propsWithoutForm = Object.assign({}, this.props) delete propsWithoutForm.form @@ -509,8 +501,22 @@ class CoursesNew extends Component { />
    + {/**/} + {/* */} + {/* 翻转课堂*/} + {/* */} + {/* {this.props.match.params.coursesId === undefined ?"新建课堂":"编辑课堂"}*/} + {/**/} + +

    + 翻转课堂 + > + {this.props.match.params.coursesId === undefined ?"新建课堂":"编辑课堂"} +

    + +
    -

    {this.props.match.params.coursesId === undefined ? "新建课堂" : "编辑课堂"}

    +

    {this.props.match.params.coursesId === undefined ? "新建课堂" : "编辑课堂"}

    返回
    + className="searchViewAfter yslpollls">
    -- : - this.Viewstudenttraininginformation(record)}>{record.operating} + onClick={() => this.Viewstudenttraininginformation(record)}>{record.operating} } diff --git a/public/react/src/modules/courses/shixunHomework/ShixunHomeworkPage.js b/public/react/src/modules/courses/shixunHomework/ShixunHomeworkPage.js index f46d3f6b1..dddd39989 100644 --- a/public/react/src/modules/courses/shixunHomework/ShixunHomeworkPage.js +++ b/public/react/src/modules/courses/shixunHomework/ShixunHomeworkPage.js @@ -113,21 +113,19 @@ class ShixunHomeworkPage extends Component { bindRef = ref => { this.child = ref } ///////////////教师截止 gotohome=()=>{ - // let courseId=this.props.match.params.coursesId; - // if(courseId===undefined){ - // this.props.history.push("/courses"); - // }else{ - // this.props.history.push(this.props.current_user.first_category_url); - // } - this.props.history.goBack() + // console.log(this.props) + let {jobsettingsdatapage}=this.state + + this.props.history.replace(`/courses/${this.props.match.params.coursesId}/${jobsettingsdatapage === undefined ? "" : jobsettingsdatapage.data.category.main === 1 ? "shixun_homeworks" :"shixun_homework"}/${jobsettingsdatapage === undefined ? "" : jobsettingsdatapage.data.category.category_id === undefined ? "" : jobsettingsdatapage.data.category.category_id}`); } render() { let {tab, teacherdatapage, jobsettingsdatapage} = this.state; const isAdmin = this.props.isAdmin(); // console.log(119) - // console.log(jobsettingsdatapage); - // console.log(teacherdatapage); + console.log(jobsettingsdatapage); + + return (
    @@ -136,11 +134,11 @@ class ShixunHomeworkPage extends Component {

    - this.gotohome()}>{jobsettingsdatapage === undefined ? "" : jobsettingsdatapage.data.course_name} + {jobsettingsdatapage === undefined ? "" : jobsettingsdatapage.data.course_name} > {jobsettingsdatapage === undefined ? "" : jobsettingsdatapage.data.category.category_name} + href={`/courses/${this.props.match.params.coursesId}/${jobsettingsdatapage === undefined ? "" : jobsettingsdatapage.data.category.main === 1 ? "shixun_homeworks" :"shixun_homework"}/${jobsettingsdatapage === undefined ? "" : jobsettingsdatapage.data.category.category_id === undefined ? "" : jobsettingsdatapage.data.category.category_id}`}>{jobsettingsdatapage === undefined ? "" : jobsettingsdatapage.data.category.category_name} > 作业详情

    @@ -240,14 +238,14 @@ class ShixunHomeworkPage extends Component { this.workshowmodels(this.child)}>代码查重 : "" : ""} {this.state.view_report === true ? + to={`/courses/${this.props.match.params.coursesId}/${jobsettingsdatapage === undefined ? "" : jobsettingsdatapage.data.category.main === 1 ? "shixun_homeworks" :"shixun_homework"}/${teacherdatapage&&teacherdatapage.work_id}/shixun_work_report`}> 查看实训报告 : ""} { teacherdatapage === undefined ? "" : teacherdatapage.commit_des === null || teacherdatapage.commit_des === undefined ? "" : {teacherdatapage.commit_des} + href={`/courses/${this.props.match.params.coursesId}/${jobsettingsdatapage === undefined ? "" : jobsettingsdatapage.data.category.main === 1 ? "shixun_homeworks" :"shixun_homework"}/${teacherdatapage === undefined ? "" : teacherdatapage.id}/commitsummary/${this.props.match.params.homeworkid}`}>{teacherdatapage.commit_des} } {teacherdatapage === undefined ? "" : { - - this.props.history.goBack() + this.props.history.replace(`/courses/${this.props.match.params.coursesId}/shixun_homeworks/${this.state.data.homework_common_id}/list?tab=0`); } render() { let{data} =this.state; @@ -159,7 +158,7 @@ class ShixunWorkReport extends Component { {/*className="fr color-blue font-16"*/} {/*onClick={()=>this.confirmysl(`/student_works/${homeworkid}/export_shixun_work_report.pdf`)}*/} {/*>导出实训报告数据 : ""}*/} - {/*返回*/} + 返回
    diff --git a/public/react/src/modules/courses/shixunHomework/ShixunhomeWorkItem.js b/public/react/src/modules/courses/shixunHomework/ShixunhomeWorkItem.js index 38ab63a4f..942c06800 100644 --- a/public/react/src/modules/courses/shixunHomework/ShixunhomeWorkItem.js +++ b/public/react/src/modules/courses/shixunHomework/ShixunhomeWorkItem.js @@ -124,13 +124,19 @@ class ShixunhomeWorkItem extends Component{ }) } - editname = (name,id) => { + // 实训详情,阻止冒泡 + stopPro = (event) => { + event.stopPropagation() + } + + editname = (name,id,event) => { this.setState({ ModalsRenametype:true, NavmodalValue:name, Navmodalname:"重命名", url:`/homework_commons/${id}/alter_name.json` }) + event.stopPropagation() } cannerNavmoda=()=>{ this.setState({ @@ -157,88 +163,74 @@ class ShixunhomeWorkItem extends Component{ const { checkBox, discussMessage, - taskid, + taskid,index } = this.props; - // - - // allow_late: true //是否开启补交 - - // homework_id: 9250 - - // shixun_identifier: "25ykhpvl" - - // - // console.log("this.props.isAdmin"); - - return( -
    - {this.state.ModalsRenametype===true? - this.cannerNavmoda()} - /> - :""} - - - - {visible===true?:""} - - - -
    -

    实训已经更新了,正在为您重置!

    -
    - -
    - -
    -

    本实训的开启时间:{shixunsmessage}
    开启时间之前不能挑战 -

    -
    -
    - {/*取消*/} - 知道啦 -
    - {/*

    */} - {/*知道了*/} - {/*

    */} -
    - + + { + this.state.ModalsRenametype===true? + this.cannerNavmoda()} + /> + :""} + + {visible===true?:""} + +
    +

    实训已经更新了,正在为您重置!

    +
    + +
    + + +
    +

    本实训的开启时间:{shixunsmessage}
    开启时间之前不能挑战 +

    +
    +
    + {/*取消*/} + 知道啦 +
    + {/*

    */} + {/*知道了*/} + {/*

    */} +
    +
    window.$(`.shixunitem${index} input`).click() } > - {this.props.isAdmin?checkBox:""} + {this.props.isAdmin? + {checkBox} + : + "" + }
    - {this.props.isAdmin?
    - 实训详情 - {this.props.isAdminOrCreator()?this.editname(discussMessage.name,discussMessage.homework_id)} className={"btn colorblue ml20 font-16"}>重命名:""} + {this.props.isAdmin?
    this.stopPro(event)} className={this.props.isAdminOrCreator()?"homepagePostSetting homepagePostSettingname":"homepagePostSetting homepagePostSettingbox"} style={{"right":"-2px","top":"44px","display":"block"}}> + 实训详情 + {this.props.isAdminOrCreator()?this.editname(discussMessage.name,discussMessage.homework_id,event)} className={"btn colorblue ml20 font-16"}>重命名:""} {/* 设置*/} 设置
    :""} @@ -408,6 +404,7 @@ class ShixunhomeWorkItem extends Component{
    + ) } } diff --git a/public/react/src/modules/courses/shixunHomework/shixunHomework.js b/public/react/src/modules/courses/shixunHomework/shixunHomework.js index 74d142129..c7fa2e70a 100644 --- a/public/react/src/modules/courses/shixunHomework/shixunHomework.js +++ b/public/react/src/modules/courses/shixunHomework/shixunHomework.js @@ -587,6 +587,8 @@ class ShixunHomework extends Component{ let {Coursename,page}=this.state; this.setState({ order: e.key, + checkBoxValues:[], + checkedtype:false, isSpin:true }); let newkey=e.key; @@ -655,6 +657,7 @@ class ShixunHomework extends Component{ } + savedelete=()=>{ let {Coursename,page,order,checkBoxValues,datas}=this.state; let category_id=this.props.match.params.category_id; @@ -966,7 +969,7 @@ class ShixunHomework extends Component{ category_id={this.props.match.params.category_id} visible={shixunmodal} shixunmodallist={shixunmodallist} - // funshixunmodallist={(search,type,loading,page)=>this.funshixunmodallist(search,type,loading,page)} + homeworkupdatalists={(Coursename,page,order)=>this.homeworkupdatalist(Coursename,page,order)} hometypepvisible={hometypepvisible} hidecouseShixunModal={this.hidecouseShixunModal} newshixunmodallist={newshixunmodallist} @@ -1019,8 +1022,8 @@ class ShixunHomework extends Component{

    - {datas&&datas.category_name===undefined||datas&&datas.category_name===null?datas&&datas.main_category_name:datas&&datas.category_name+" 作业列表"} - {/* 实训作业*/} + {/*{datas&&datas.category_name===undefined||datas&&datas.category_name===null?datas&&datas.main_category_name:datas&&datas.category_name+" 作业列表"}*/} + 实训作业

  • {this.props.isAdmin()===true?datas&&datas.category_name===undefined||datas&&datas.category_name===null? @@ -1035,7 +1038,7 @@ class ShixunHomework extends Component{

    -

    +

    共 {datas&&datas.all_count}个实训作业 已发布:{datas&&datas.published_count}个 @@ -1062,7 +1065,9 @@ class ShixunHomework extends Component{
    - {this.props.isAdmin()===true?
    + {this.props.isAdmin()===true? + datas===undefined?'' :datas.homeworks.length===0?"": +
    已选 {checkBoxValues&&checkBoxValues.length} 个 @@ -1152,7 +1157,7 @@ class ShixunHomework extends Component{ // console.log("++++++++++++++++++++++++++++++++++++++++++") // console.log(JSON.stringify(this.props)) return ( -
    +
    :""} match={this.props.match} + index={index} coursedata={this.props.coursedata} coursupdata={()=>this.homeworkupdatalist(Coursename,page,order)} course_identity={datas.course_identity} @@ -1203,18 +1209,10 @@ class ShixunHomework extends Component{
    + { + datas===undefined?'' :datas.homeworks.length===0? :"" + } -
    -
    -

    暂时还没有相关数据哦!

    -
    @@ -1223,3 +1221,14 @@ class ShixunHomework extends Component{ } } export default ShixunHomework; +// {/*
    */} +// {/*
    */} +// {/**/} +// {/*

    暂时还没有相关数据哦!

    */} +// {/*
    */} \ No newline at end of file diff --git a/public/react/src/modules/home/shixunsHome.js b/public/react/src/modules/home/shixunsHome.js index d366426c8..690baf28b 100644 --- a/public/react/src/modules/home/shixunsHome.js +++ b/public/react/src/modules/home/shixunsHome.js @@ -294,7 +294,7 @@ class ShixunsHome extends Component { {/*精选实训 改为 开发社区*/}
    -

    开发社区

    +

    实训项目

    DEVELOPMENT COMMUNITY

    更多 diff --git a/public/react/src/modules/modals/WordNumberTextarea.css b/public/react/src/modules/modals/WordNumberTextarea.css new file mode 100644 index 000000000..9bd8c820c --- /dev/null +++ b/public/react/src/modules/modals/WordNumberTextarea.css @@ -0,0 +1,42 @@ +.WordNumberTextarea { + outline: none; /* 去掉输入字符时的默认样式 */ + appearance:none; + -webkit-appearance:none; + -moz-appearance:none; + background-color: white; + text-shadow: none; + -webkit-writing-mode: horizontal-tb !important; + -webkit-tap-highlight-color:rgba(0,0,0,0); + resize:none; /*禁止拉伸*/ + border: none; /*去掉默认边框*/ + width: 100%; + height:150px; + border:none; + padding: 10px; + display: block; +} + +.WordNumbernote { + padding: 0; + margin: 0; + list-style: none; + text-decoration: none; + box-sizing: border-box; + overflow: hidden; + height: auto; + border: 1px solid rgba(234,234,234,1); + border-radius: 0.125rem; + margin: 0.31rem; + padding: 0.19rem; + backgroud:rgba(234,234,234,1); + padding-bottom: 10px; + padding-right: 10px; + +} +.WordNumberTextarea-count { + display: inline-block; + float: right; + font-size: 0.28rem; + color: #adadad; + padding-right: 0.25rem; +} diff --git a/public/react/src/modules/modals/WordNumberTextarea.js b/public/react/src/modules/modals/WordNumberTextarea.js new file mode 100644 index 000000000..52711de2c --- /dev/null +++ b/public/react/src/modules/modals/WordNumberTextarea.js @@ -0,0 +1,23 @@ +import React, { Component } from 'react'; +import './WordNumberTextarea.css'; +class WordNumberTextarea extends Component { + constructor(props) { + super(props); + } +render() { + return( +
    +